diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 5ed2e70..f25acbc 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -15,7 +15,7 @@ jobs: node-version: ${{ matrix.node-version }} - name: Cache node modules - uses: actions/cache@v1 + uses: actions/cache@v4 env: cache-name: cache-node-modules with: diff --git a/.prettierignore b/.prettierignore deleted file mode 100644 index f5cbe3c..0000000 --- a/.prettierignore +++ /dev/null @@ -1 +0,0 @@ -src/generate/templates \ No newline at end of file diff --git a/README.md b/README.md index 6e6862e..86b029b 100644 --- a/README.md +++ b/README.md @@ -7,7 +7,6 @@ The Bedrock Command Line Interface can be used to manage Bedrock projects and de - [Installation](#installation) - [Create a New Project](#create-a-new-project) - [Deploy your Project](#deploy-your-project) -- [Generate Code](#generate-code) ## Installation @@ -32,23 +31,31 @@ bedrock create \ ## Deploy your Project -To use the bedrock `cloud` commands, check the `bedrock-core` Deployment [README.md](https://github.com/bedrockio/bedrock-core/tree/master/deployment), which includes: gcloud setup, deployment and provisioning. +To use the bedrock `cloud` commands, check the `bedrock-core` Deployment +[README.md](https://github.com/bedrockio/bedrock-core/tree/master/deployment), which includes: gcloud setup, deployment +and provisioning. -Provisioning will allow new infrastructure to be created on Google Cloud using Terraform. CLI commands will also let you deploy and control a Kubernetes cluster. +Provisioning will allow new infrastructure to be created on Google Cloud using Terraform. CLI commands will also let you +deploy and control a Kubernetes cluster. ### Google Cloud Platform -Bedrock uses GCP as it's standard cloud solution for hosting and deploying Bedrock projects. Projects are built using docker and deployed with kubernetes. +Bedrock uses GCP as it's standard cloud solution for hosting and deploying Bedrock projects. Projects are built using +docker and deployed with kubernetes. ### Slack integration -The `bedrock cloud deploy` command supports posting message on a Slack channel when starting and finishing the deploy. This requires the following steps: +The `bedrock cloud deploy` command supports posting message on a Slack channel when starting and finishing the deploy. +This requires the following steps: - Create a new Slack App at [api.slack.com](https://api.slack.com/) for your workspace - Optional: Update your Slack with a nice Icon and description -- Create `incoming webhook`. Select this feature in the menu, and hit `Add New Webhook to Workspace`. It will ask you to select the Slack channel in your workspace to post messages to. +- Create `incoming webhook`. Select this feature in the menu, and hit `Add New Webhook to Workspace`. It will ask you to + select the Slack channel in your workspace to post messages to. - Copy the generated Webhook URL, which includes the api token -- Paste the Webhook URL in your project's deployment environment `config.json` (See Deployment [README.md](https://github.com/bedrockio/bedrock-core/tree/master/deployment)). Example `deployment/environments/staging/config.json` (replace webhook value with your own Webhook URL): +- Paste the Webhook URL in your project's deployment environment `config.json` (See Deployment + [README.md](https://github.com/bedrockio/bedrock-core/tree/master/deployment)). Example + `deployment/environments/staging/config.json` (replace webhook value with your own Webhook URL): ```json { @@ -72,13 +79,16 @@ The `bedrock cloud deploy` command supports posting message on a Slack channel w } ``` -Each environment can use the same webhook for the same channel, or you can set up a different channel and webhook for each environment. +Each environment can use the same webhook for the same channel, or you can set up a different channel and webhook for +each environment. ### Authorization -- Use `gcloud auth login` and `gcloud auth application-default login` to login to the right Google account, or `bedrock cloud login` +- Use `gcloud auth login` and `gcloud auth application-default login` to login to the right Google account, or + `bedrock cloud login` - Use `bedrock cloud authorize staging` to get cluster credentials -- If you've used `gcloud auth` with another account, run `gcloud config set account ` or `bedrock cloud account `, then re-run `bedrock cloud authorize`. +- If you've used `gcloud auth` with another account, run `gcloud config set account ` or + `bedrock cloud account `, then re-run `bedrock cloud authorize`. If you get this error when trying to deploy: @@ -110,34 +120,9 @@ There is also the option to build the image remotely using GCP Container Registr - `bedrock cloud build --remote` - `bedrock cloud deploy --remote` -This may be advantageous as it requires less data to transfer (pushing a tarball of the source vs pushing up an entire docker image), also notably docker images take much longer to build on Apple Silicon as they must compile to `x86` targets. - -## Generate Code - -The CLI `generate` command can be used to generate the following: - -- `model` - model definitions -- `routes` - API routes -- `docs` - documentation -- `modal` - modal dialogs for editing objects from anywhere -- `screens` - basic CRUD screens (search/filter, detail page, etc) -- `subscreens` - screens for associated models (for example a page for `products` that belong to a `shop`) - -Running `bedrock generate` can generate one or many of these resources. As the foundation for your generated code, models must be first created to allow generation of other resources so generating other resources will ask you to create a new model or load an existing one. - -### Schema Creation - -Generating a model requires creating a schema. Understanding [Mongoose schema types](https://mongoosejs.com/docs/schematypes.html) will be helpful here. The schema definition can be edited or changed within the CLI, then choosing `Build Schema` will continue on to generate other resources that are derived from it. - -### Integration - -The generator makes a best effort to integrate generated code into your app, and on a fresh Bedrock codebase can even generate new resources without restarting the server. However as your code diverges it may require some manual tweaking to correctly integrate with your app. - -### Re-generating - -As structured JSON data, models can safely be re-generated after manual changes but as a general rule, generated code is expected to diverge as it is modified. However if the code has not been manually changed then it is safe to re-run generators. Another strategy is to allow the generator to overwrite existing files and use `git diff` to manually merge custom and generated code. - -Client side code specifically is expected to diverge, however `modal` dialogs benefit greatly from avoiding manual changes as editiable fields inside them can be re-generated after changes your model definition changes. +This may be advantageous as it requires less data to transfer (pushing a tarball of the source vs pushing up an entire +docker image), also notably docker images take much longer to build on Apple Silicon as they must compile to `x86` +targets. ## More diff --git a/commands.js b/commands.js index 2bd89df..a9ca831 100644 --- a/commands.js +++ b/commands.js @@ -1,35 +1,3 @@ -const generateBaseOptions = [ - { - flags: '--eject', - description: 'Write the AI template to disk to allow tweaking.', - }, - { - flags: '--template [file]', - description: 'Use with "eject" to tweak and pass back in a template.', - }, - { - flags: '-p, --platform [name]', - description: 'The AI platform to use.', - choices: ['openai', 'claude', 'gemini', 'grok'], - }, - { - flags: '--ai-model [name]', - description: 'The AI model to use.', - }, -]; - -const generateSingleResourceOptions = [ - { - flags: '-m, --model [name...]', - description: 'Model(s) to generate for. May be multiple.', - }, - { - flags: '--example [file]', - description: 'File to serve as example input. Defaults to "shops" file.', - }, - ...generateBaseOptions, -]; - export default { name: 'bedrock', description: 'Command line interface for working with Bedrock projects.', @@ -84,50 +52,6 @@ export default { }, ], }, - { - name: 'generate', - description: 'Generate new Bedrock resources with AI.', - commands: [ - { - name: 'model', - description: 'Generate Mongoose models.', - options: generateBaseOptions, - }, - { - name: 'routes', - description: 'Generate API routes.', - options: generateSingleResourceOptions, - }, - { - name: 'tests', - description: 'Generate tests for an API route.', - options: generateSingleResourceOptions, - }, - { - name: 'screens', - description: 'Generate top level screens.', - options: [ - { - flags: '-m, --model [name...]', - description: 'Model(s) to generate for. May be multiple.', - }, - ...generateBaseOptions, - ], - arguments: [ - { - name: 'dir', - description: 'Path to example screens.', - required: true, - }, - ], - }, - { - name: 'modal', - description: 'Generate modal for resource create/update.', - options: generateSingleResourceOptions, - }, - ], - }, { name: 'cloud', description: 'Cloud Controls for deployment and provisioning', diff --git a/package.json b/package.json index dcd7e2e..d712236 100644 --- a/package.json +++ b/package.json @@ -10,7 +10,6 @@ "lint": "eslint" }, "dependencies": { - "@bedrockio/ai": "^0.3.0", "commander": "^13.1.0", "compare-versions": "^3.6.0", "execa": "^4.0.3", @@ -21,8 +20,7 @@ "lodash-es": "^4.17.21", "open": "^7.3.0", "pluralize": "^8.0.0", - "prompts": "^2.4.2", - "server-destroy": "^1.0.1" + "prompts": "^2.4.2" }, "devDependencies": { "@babel/core": "^7.26.8", @@ -39,7 +37,7 @@ "node": ">=20.18.1" }, "volta": { - "node": "22.14.0", + "node": "22.20.0", "yarn": "1.22.22" } } diff --git a/src/generate/modal.js b/src/generate/modal.js deleted file mode 100644 index 3c656b3..0000000 --- a/src/generate/modal.js +++ /dev/null @@ -1,43 +0,0 @@ -import { loadModels } from './utils/model.js'; -import { assertPath } from '../utils/file.js'; -import { assertBedrockWeb } from '../utils/dir.js'; -import { queueTask, runTasks } from '../utils/tasks.js'; -import { getExample } from './utils/files.js'; -import { createAiClient, generateLocalFiles } from './utils/ai.js'; - -const MODALS_DIR = 'src/modals'; - -export async function modal(options) { - await assertBedrockWeb(); - await createAiClient(options); - - const modalsDir = await assertPath(MODALS_DIR); - - const modalExample = await getExample({ - ...options, - reference: 'src/modals/EditShop.js', - }); - - const models = await loadModels(options); - - queueTask('Generating Modals', async () => { - for (let model of models) { - const modelDefinition = model.definition; - const expectedFilename = `${modalsDir}/Edit${model.name}.js`; - - await generateLocalFiles({ - file: options.template || 'modals', - eject: options.eject, - platform: options.platform, - - params: { - expectedFilename, - modelDefinition, - modalExample, - }, - }); - } - }); - - await runTasks(); -} diff --git a/src/generate/model.js b/src/generate/model.js deleted file mode 100644 index 8811276..0000000 --- a/src/generate/model.js +++ /dev/null @@ -1,70 +0,0 @@ -import { assertPath } from '../utils/file.js'; -import { assertBedrockApi } from '../utils/dir.js'; -import { getExample } from './utils/files.js'; -import { prompt } from '../utils/prompt.js'; -import { kebabSingular } from './utils/inflections.js'; -import { queueTask, runTasks } from '../utils/tasks.js'; -import { createAiClient, generateLocalFiles, ejectTemplate } from './utils/ai.js'; - -const MODELS_DIR = 'src/models/definitions'; - -export async function model(options) { - await assertBedrockApi(); - await createAiClient(options); - - const modelsDir = await assertPath(MODELS_DIR); - - const name = await prompt({ - type: 'text', - name: 'name', - message: 'Name your model:', - }); - - let modelDescription; - if (!options.template) { - modelDescription = await prompt({ - type: 'text', - name: 'description', - message: 'Describe your model:', - }); - } - - const exampleDefinition = await getExample({ - ...options, - reference: 'src/models/definitions/shop.json', - }); - - const expectedFilename = `${modelsDir}/${kebabSingular(name)}.json`; - - let templateFile; - - const params = { - expectedFilename, - modelDescription, - exampleDefinition, - }; - - queueTask('Generating Model', async () => { - await generateLocalFiles({ - file: options.template || 'model', - eject: options.eject, - platform: options.platform, - params, - }); - }); - - if (!options.template) { - queueTask('Exporting Template', async () => { - templateFile = await ejectTemplate({ - file: 'model', - params, - }); - }); - } - - await runTasks(); - - if (templateFile) { - console.info(`Template written to "${templateFile}". You can tweak this and pass it back in with --template.`); - } -} diff --git a/src/generate/routes.js b/src/generate/routes.js deleted file mode 100644 index 9f3414f..0000000 --- a/src/generate/routes.js +++ /dev/null @@ -1,50 +0,0 @@ -import { loadModels } from './utils/model.js'; -import { assertPath } from '../utils/file.js'; -import { assertBedrockApi } from '../utils/dir.js'; -import { queueTask, runTasks } from '../utils/tasks.js'; -import { getExample, readLocalFile } from './utils/files.js'; -import { createAiClient, generateLocalFiles } from './utils/ai.js'; -import { kebabPlural } from './utils/inflections.js'; - -const ROUTES_DIR = 'src/routes'; - -export async function routes(options) { - await assertBedrockApi(); - await createAiClient(options); - - const routesDir = await assertPath(ROUTES_DIR); - - const routesExample = await getExample({ - ...options, - reference: 'src/routes/shops.js', - }); - - const routesEntryFilename = `${routesDir}/index.js`; - const routesEntry = await readLocalFile(routesEntryFilename); - - const models = await loadModels(options); - - queueTask('Generating Routes', async () => { - for (let model of models) { - const modelName = model.name; - const modelDefinition = model.definition; - const expectedRoutesFilename = `${routesDir}/${kebabPlural(model.name)}.js`; - - await generateLocalFiles({ - file: options.template || 'routes', - eject: options.eject, - platform: options.platform, - params: { - modelName, - modelDefinition, - expectedRoutesFilename, - routesExample, - routesEntry, - routesEntryFilename, - }, - }); - } - }); - - await runTasks(); -} diff --git a/src/generate/screens.js b/src/generate/screens.js deleted file mode 100644 index 2e063a1..0000000 --- a/src/generate/screens.js +++ /dev/null @@ -1,46 +0,0 @@ -import { loadModels } from './utils/model.js'; -import { assertBedrockWeb } from '../utils/dir.js'; -import { queueTask, runTasks } from '../utils/tasks.js'; -import { readLocalFile } from './utils/files.js'; -import { createAiClient, generateLocalFiles } from './utils/ai.js'; -import { readDirectory } from '../utils/file.js'; - -export async function screens(options) { - await assertBedrockWeb(); - await createAiClient(options); - - const { dir } = options; - const filenames = await readDirectory(dir, '**/*.js'); - - const files = await Promise.all( - filenames.map(async (filename, i) => { - return { - filename, - content: await readLocalFile(filename), - number: i + 1, - }; - }), - ); - - const models = await loadModels(options); - - queueTask('Generating Screens', async () => { - for (let model of models) { - const modelName = model.name; - const modelDefinition = model.definition; - - await generateLocalFiles({ - file: options.template || 'screens', - eject: options.eject, - platform: options.platform, - params: { - files, - modelName, - modelDefinition, - }, - }); - } - }); - - await runTasks(); -} diff --git a/src/generate/templates/modals.md b/src/generate/templates/modals.md deleted file mode 100644 index 9f135be..0000000 --- a/src/generate/templates/modals.md +++ /dev/null @@ -1,30 +0,0 @@ ---- SYSTEM --- - -You will receive multiple tasks. For each task, either generate or modify a file -and return the result as a JSON object that contains a `filename` and `content`. -The final output should be a JSON array of these file objects. - -In your code you will: - -1. Leave off any explanations or comments. -2. Prefer concise, readable variable names if possible. - ---- USER --- - -### Task 1 - Generate a new modal file. - -- Filename: `{{{expectedFilename}}}` - -Generate a create or edit modal (a React component) based on two inputs: - -1. The model definition: - -``` -{{{modelDefinition}}} -``` - -2. An example component. You should follow this as closely as possible: - -``` -{{{modalExample}}} -``` \ No newline at end of file diff --git a/src/generate/templates/model.md b/src/generate/templates/model.md deleted file mode 100644 index 49a8b24..0000000 --- a/src/generate/templates/model.md +++ /dev/null @@ -1,47 +0,0 @@ ---- SYSTEM --- - -You are a helpful AI that generates JSON model definition files. - -### Output - -The final output should be an array of "files" which are JSON objects that have a -`filename` and `content` fields. - -### Approach - -- Use the example definition file as a base. -- Individual fields on the model are defined in the `attributes` field. -- Fields are always camel case. -- String fields can validate special formats when applicable such as `email`, `phone`, `zipcode`, etc. -- Access control can be defined on fields when specifically requested or can be inferred: - - `readAccess` and `writeAccess` can be an array of user roles or `none` for sensitive data. -- Unless explicitly told otherwise, make reasonable assumptions about required fields: - - At least one field should be required. It should be a basic field like `name` or something similar. - - Assume other fields are not required unless there is a strong reason to think so. -- If an `organization` field exists on the example definition then include it as well. -- Do NOT include the fields: - - `created_by` unless specified in the description. - - `created_at` or `updated_at` as timestamps will already be included automatically. - ---- USER --- - -### Task: Generate a new model definition file. - -- Filename: `{{{expectedFilename}}}` - -Generate a model definition based on two inputs: - -1. A description of the model. -2. An example of an existing model definition. - -Here is the description of the model: - -``` -{{{modelDescription}}} -``` - -Here is the existing model definition: - -``` -{{{exampleDefinition}}} -``` \ No newline at end of file diff --git a/src/generate/templates/routes.md b/src/generate/templates/routes.md deleted file mode 100644 index cc5bee3..0000000 --- a/src/generate/templates/routes.md +++ /dev/null @@ -1,52 +0,0 @@ ---- SYSTEM --- - -You will receive multiple tasks. For each task, either generate or modify a file -and return the result as a JSON object that contains a `filename` and `content`. -The final output should be a JSON array of these file objects. - -In your code you will: - -1. Leave off any explanations or comments. -2. Prefer concise, readable variable names if possible. - ---- USER --- - -### Task 1 - Generate a new routes file. - -- Filename: `{{{expectedRoutesFilename}}}` - -Generate a routes file (a Koa router) based on three inputs: - -1. The model name: - -``` -{{{modelName}}} -``` - -2. The model definition: - -``` -{{{modelDefinition}}} -``` - -3. An example routes file. You should follow this as closely as possible: - -``` -{{{routesExample}}} -``` - -### Task 2 - Modify the routes entrypoint. - -- Filename: `{{{routesEntryFilename}}}` - -Modify the following routes entrypoint with the file created from -Task 1 using the following rules: - -- Follow the existing patterns. -- If nothing needs to be appended then output the current file as is. - -Here is the routes entrypoint: - -``` -{{{routesEntry}}} -``` diff --git a/src/generate/templates/screens.md b/src/generate/templates/screens.md deleted file mode 100644 index dee4559..0000000 --- a/src/generate/templates/screens.md +++ /dev/null @@ -1,67 +0,0 @@ ---- SYSTEM --- - -You are a helpful AI that outputs "screen" files which are React components. Your job is -to take the following two inputs: - -1. A description of a model -2. A list of existing files - -And rename the files as well as the variables inside the contents to match -the new model. - -### Rules - -- The name of the old resource is in the filename, for example `src/screens/Shops` - would be `Shops`. -- The filenames must be renamed to the new resource, for example `src/screens/Products`. -- All variables, and paths (URLs) should be modified as well. - - Any instances of the old resource name should be replaced with the new resource name. - - Where the old resource name was plural the new resource name should also be plural. -- In "list" views (ones that run `/search`) you should rewrite the resulting fields in the - table to use fields on the new resource. Only choose a handful of shorter, meaningful - fields in list views, not everything. For example `name` or `code` are good choices, - `description` is not. -- Detail or "Overview" screens should contain a more comprehensive list of fields. - Follow the examples. -- Assume that helpers exist for formatting like: - - `formatDate` - - `formatPhone` - - etc. -- Leave off any explanations or comments. -- Prefer concise, readable variable names if possible. - -### Output - -1. You must output 1 file for each input file. -2. Each file should be a JSON object that has `filename` and `content`. -3. The final output should be an array of these file objects. - ---- USER --- - - -## New Model - -Model name: - -``` -{{{modelName}}} -``` - -Model definition: - -``` -{{{modelDefinition}}} - -## Files to output: - -{{#files}} -### File {{{number}}} - -Example filename: `{{{filename}}}` - -Example file content: -``` -{{{content}}} -``` - -{{/files}} diff --git a/src/generate/templates/tests.md b/src/generate/templates/tests.md deleted file mode 100644 index 476465a..0000000 --- a/src/generate/templates/tests.md +++ /dev/null @@ -1,53 +0,0 @@ ---- SYSTEM --- - -You will receive multiple tasks. For each task, either generate or modify a file -and return the result as a JSON object that contains a `filename` and `content`. -The final output should be a JSON array of these file objects. - -In your code you will: - -1. Leave off any explanations or comments. -2. Prefer concise, readable variable names if possible. - ---- USER --- - -### Task - Generate a test file that tests the router provided below. - -- Filename: `{{{expectedFilename}}}` - -Use the following rules which follow the example file: - -- Use a single "describe" block for the entire router. -- Use a "describe" block for each route. -- Use Jest style matchers. -- Do not comment the code. -- Follow the example file as closely as you can. -- Assume that test helpers like `createUser`, `createAdmin` etc are available. -- Focus on testing the route itself, not the middelware. For example you do not - need to test that the authentication middleware is working for each route, - however it might make sense to do this once in all of the tests. -- If routes rely on time (ie. call `new Date()` etc.) then use the time mocks - available in `utils/testing/time` to wrap each test and mock the time using: - - `mockTime` - - `unmockTime` - - `advanceTime` -- If routes do NOT rely on the time then do not import these helpers. -- Do NOT add unknown fields to request blocks unless they are in the example file. - -Here is the model definition file: - -``` -{{{modelDefinition}}} -``` - -Here is the routes file: - -``` -{{{routesContent}}} -``` - -Here is the example tests file: - -``` -{{{testsExample}}} -``` diff --git a/src/generate/tests.js b/src/generate/tests.js deleted file mode 100644 index c6f0077..0000000 --- a/src/generate/tests.js +++ /dev/null @@ -1,48 +0,0 @@ -import { loadModels } from './utils/model.js'; -import { assertPath } from '../utils/file.js'; -import { assertBedrockApi } from '../utils/dir.js'; -import { kebabPlural } from './utils/inflections.js'; -import { queueTask, runTasks } from '../utils/tasks.js'; -import { getExample, readLocalFile } from './utils/files.js'; -import { createAiClient, generateLocalFiles } from './utils/ai.js'; - -const TESTS_DIR = 'src/routes/__tests__'; -const ROUTES_DIR = 'src/routes'; - -export async function tests(options) { - await assertBedrockApi(); - await createAiClient(options); - - const testsDir = await assertPath(TESTS_DIR); - const routesDir = await assertPath(ROUTES_DIR); - - const testsExample = await getExample({ - ...options, - reference: 'src/routes/__tests__/shops.js', - }); - - const models = await loadModels(options); - - queueTask('Generating Route Tests', async () => { - for (let model of models) { - const plural = kebabPlural(model.name); - const expectedFilename = `${testsDir}/${plural}.js`; - const routesContent = await readLocalFile(`${routesDir}/${plural}.js`); - const modelDefinition = model.definition; - - await generateLocalFiles({ - file: options.template || 'tests', - eject: options.eject, - platform: options.platform, - params: { - expectedFilename, - modelDefinition, - routesContent, - testsExample, - }, - }); - } - }); - - await runTasks(); -} diff --git a/src/generate/utils/ai.js b/src/generate/utils/ai.js deleted file mode 100644 index 28ba446..0000000 --- a/src/generate/utils/ai.js +++ /dev/null @@ -1,83 +0,0 @@ -import { Client } from '@bedrockio/ai'; - -import { loadConfig } from '../../utils/config.js'; - -import { getRelativeFile, writeFile } from '../../utils/file.js'; - -let client; - -export async function ejectTemplate(options) { - const { file, exit } = options; - const output = await client.buildTemplate(options); - const filename = `generate-${file}.md`; - await writeFile(filename, output); - - if (exit) { - console.info(`Template written to "${filename}". Use --template to pass back in to this script.`); - process.exit(0); - } -} - -export async function createAiClient(options) { - const config = await loadConfig(); - const { DEFAULT_AI_PLATFORM: platform = 'openai' } = config; - const name = getConfigForPlatform(platform); - const apiKey = config[name]; - - if (!platform) { - throw new Error('Platform not provided.'); - } else if (!apiKey) { - throw new Error(`API key not found for ${platform}. Use bedrock config to set.`); - } - - client = new Client({ - templates: getRelativeFile(import.meta, '../templates'), - platform, - apiKey, - ...(options.aiModel && { - model: options.aiModel, - }), - }); -} - -function getConfigForPlatform(platform) { - switch (platform) { - case 'openai': - return 'OPENAI_API_KEY'; - case 'anthropic': - return 'ANTHROPIC_API_KEY'; - case 'gemini': - return 'GEMINI_API_KEY'; - case 'xai': - return 'XAI_API_KEY'; - } -} - -export async function generateLocalFiles(options) { - const { eject, ...rest } = options; - - if (eject) { - ejectTemplate({ - ...rest, - exit: true, - }); - } - - const output = await client.prompt({ - ...options, - output: 'json', - }); - - // Sometimes likes to output a `files` array. - let files = output.files; - // Sometimes ignores me and outputs just an object. - files ||= Array.isArray(output) ? output : [output]; - - for (let file of files) { - let { filename, content } = file; - if (typeof content !== 'string') { - content = JSON.stringify(content, null, 2); - } - await writeFile(filename, content); - } -} diff --git a/src/generate/utils/files.js b/src/generate/utils/files.js deleted file mode 100644 index 9b989cf..0000000 --- a/src/generate/utils/files.js +++ /dev/null @@ -1,40 +0,0 @@ -import path from 'path'; -import { readFile } from 'fs/promises'; - -import { exit } from '../../utils/flow.js'; -import { getCurrentRoot } from '../../utils/dir.js'; - -const GITHUB_RAW_BASE = 'https://raw.githubusercontent.com/bedrockio/bedrock-core'; - -export async function getExample(options) { - const { reference, example: exampleFile } = options; - - let example; - - if (exampleFile) { - example = await readLocalFile(exampleFile); - } - - example ||= await readLocalFile(reference); - example ||= await readRemoteFile(reference); - - return example; -} - -export async function readLocalFile(...args) { - try { - return await readFile(path.resolve(...args), 'utf8'); - } catch { - return null; - } -} - -export async function readRemoteFile(...args) { - const file = path.join(getCurrentRoot(), ...args); - const url = `${GITHUB_RAW_BASE}/master/${file}`; - const response = await fetch(url); - if (response.status === 404) { - exit(`Could not fetch URL: ${url}`); - } - return await response.text(); -} diff --git a/src/generate/utils/inflections.js b/src/generate/utils/inflections.js deleted file mode 100644 index 6fd5c4f..0000000 --- a/src/generate/utils/inflections.js +++ /dev/null @@ -1,28 +0,0 @@ -import pluralize from 'pluralize'; -import { kebabCase, camelCase, upperFirst } from 'lodash-es'; - -pluralize.addPluralRule(/z$/i, 'zzes'); - -export function getPlural(str) { - return pluralize.plural(str); -} - -export function getSingular(str) { - return pluralize.singular(str); -} - -export function camelUpper(str) { - return upperFirst(camelCase(str)); -} - -export function kebabPlural(str) { - return kebabCase(getPlural(str)); -} - -export function camelPlural(str) { - return camelCase(getPlural(str)); -} - -export function kebabSingular(str) { - return kebabCase(getSingular(str)); -} diff --git a/src/generate/utils/model.js b/src/generate/utils/model.js deleted file mode 100644 index f931e43..0000000 --- a/src/generate/utils/model.js +++ /dev/null @@ -1,75 +0,0 @@ -import path from 'path'; - -import { kebabCase } from 'lodash-es'; - -import { exit } from '../../utils/flow.js'; -import { camelUpper } from './inflections.js'; -import { readLocalFile } from './files.js'; -import { prompt } from '../../utils/prompt.js'; -import { getCurrentRoot } from '../../utils/dir.js'; -import { assertPath, loadJson, readDirectory } from '../../utils/file.js'; - -const MODELS_DIR = 'services/api/src/models/definitions'; - -export async function loadModels(options) { - let { model: modelNames } = options; - - if (!modelNames) { - modelNames = await promptModelNames(); - } - - const definitions = []; - - for (let name of modelNames) { - const kebab = kebabCase(name); - let definition = await readLocalFile(`${getModelsDir()}/${kebab}.json`); - definition ||= await prompt({ - message: 'Path to model definition:', - initial: 'services/api/src/models/definitions', - }); - if (!definition) { - exit(`Definition file not found for "${name}".`); - } - - definitions.push({ - name, - definition, - }); - } - - return definitions; -} - -async function promptModelNames() { - const models = await getAllModels(); - return await prompt({ - type: 'autocompleteMultiselect', - instructions: false, - message: 'Select models:', - choices: models.map(({ name }) => { - return { - title: name, - value: name, - description: `Generate resources for ${name} model.`, - }; - }), - hint: 'Space to select or Enter for none.', - }); -} - -async function getAllModels() { - const modelsDir = await assertPath(getModelsDir()); - const definitions = await readDirectory(modelsDir, '*.json'); - return await Promise.all( - definitions.map(async (file) => { - return { - name: camelUpper(path.basename(file, '.json')), - definition: await loadJson(file), - }; - }), - ); -} - -function getModelsDir() { - return path.relative(getCurrentRoot(), MODELS_DIR); -} diff --git a/yarn.lock b/yarn.lock index 9185749..6fe2670 100644 --- a/yarn.lock +++ b/yarn.lock @@ -10,19 +10,6 @@ "@jridgewell/gen-mapping" "^0.3.5" "@jridgewell/trace-mapping" "^0.3.24" -"@anthropic-ai/sdk@^0.36.3": - version "0.36.3" - resolved "https://registry.yarnpkg.com/@anthropic-ai/sdk/-/sdk-0.36.3.tgz#0ca754f047e2847c8b1d064550f05e9a8e7e16b3" - integrity sha512-+c0mMLxL/17yFZ4P5+U6bTWiCSFZUKJddrv01ud2aFBWnTPLdRncYV76D3q1tqfnL7aCnhRtykFnoCFzvr4U3Q== - dependencies: - "@types/node" "^18.11.18" - "@types/node-fetch" "^2.6.4" - abort-controller "^3.0.0" - agentkeepalive "^4.2.1" - form-data-encoder "1.7.2" - formdata-node "^4.3.2" - node-fetch "^2.6.7" - "@babel/code-frame@^7.0.0", "@babel/code-frame@^7.12.13", "@babel/code-frame@^7.21.4", "@babel/code-frame@^7.26.2": version "7.26.2" resolved "https://registry.yarnpkg.com/@babel/code-frame/-/code-frame-7.26.2.tgz#4b5fab97d33338eff916235055f0ebc21e573a85" @@ -914,17 +901,6 @@ resolved "https://registry.yarnpkg.com/@bcoe/v8-coverage/-/v8-coverage-0.2.3.tgz#75a2e8b51cb758a7553d6804a5932d7aace75c39" integrity sha512-0hYQ8SB4Db5zvZB4axdMHGwEaQjkZzFjQiN9LVYvIFB2nSUHW9tYpxWriPrWDASIxiaXax83REcLxuSdnGPZtw== -"@bedrockio/ai@^0.3.0": - version "0.3.0" - resolved "https://registry.yarnpkg.com/@bedrockio/ai/-/ai-0.3.0.tgz#55b68161c50cf13e1d4c6f5e75c68dbc0a1a5c21" - integrity sha512-3dyb+0T1bWBiaRWvuLqfya4WQmHhpqC5OjeYYAdfqferE6J4BavogWsAfEAE0Pb2vdTZCb29WT5mni7mvz6ypw== - dependencies: - "@anthropic-ai/sdk" "^0.36.3" - "@google/generative-ai" "^0.21.0" - glob "^11.0.1" - mustache "^4.2.0" - openai "^4.83.0" - "@bedrockio/eslint-plugin@^1.1.7": version "1.1.7" resolved "https://registry.yarnpkg.com/@bedrockio/eslint-plugin/-/eslint-plugin-1.1.7.tgz#4367bf6997cf310d8a1d9664d7a0ff51ae437cee" @@ -1012,11 +988,6 @@ "@eslint/core" "^0.10.0" levn "^0.4.1" -"@google/generative-ai@^0.21.0": - version "0.21.0" - resolved "https://registry.yarnpkg.com/@google/generative-ai/-/generative-ai-0.21.0.tgz#a5011aab9e6082e706937b26ef23445933fa0d15" - integrity sha512-7XhUbtnlkSEZK15kN3t+tzIMxsbKm/dSkKBFalj+20NvPKe1kBY7mR2P7vuijEn+f06z5+A8bVGKO0v39cr6Wg== - "@humanfs/core@^0.19.1": version "0.19.1" resolved "https://registry.yarnpkg.com/@humanfs/core/-/core-0.19.1.tgz#17c55ca7d426733fe3c561906b8173c336b40a77" @@ -1562,14 +1533,6 @@ resolved "https://registry.yarnpkg.com/@types/ms/-/ms-2.1.0.tgz#052aa67a48eccc4309d7f0191b7e41434b90bb78" integrity sha512-GsCCIZDE/p3i96vtEqx+7dBUGXrc7zeSK3wwPHIaRThS+9OhWIXRqzs4d6k1SVU8g91DrNRWxWUGhp5KXQb2VA== -"@types/node-fetch@^2.6.4": - version "2.6.12" - resolved "https://registry.yarnpkg.com/@types/node-fetch/-/node-fetch-2.6.12.tgz#8ab5c3ef8330f13100a7479e2cd56d3386830a03" - integrity sha512-8nneRWKCg3rMtF69nLQJnOYUcbafYeFSjqkw3jCRLsqkWFlHaoQrr5mXmofFGOx3DKn7UfmBMyov8ySvLRVldA== - dependencies: - "@types/node" "*" - form-data "^4.0.0" - "@types/node@*", "@types/node@^22.0.0": version "22.13.2" resolved "https://registry.yarnpkg.com/@types/node/-/node-22.13.2.tgz#6f401c5ccadac75354f5652128e9fcc3b0cf23b7" @@ -1577,13 +1540,6 @@ dependencies: undici-types "~6.20.0" -"@types/node@^18.11.18": - version "18.19.75" - resolved "https://registry.yarnpkg.com/@types/node/-/node-18.19.75.tgz#be932799d1ab40779ffd16392a2b2300f81b565d" - integrity sha512-UIksWtThob6ZVSyxcOqCLOUNg/dyO1Qvx4McgeuhrEtHTLFTf7BBhEazaE4K806FGTPtzd/2sE90qn4fVr7cyw== - dependencies: - undici-types "~5.26.4" - "@types/stack-utils@^2.0.0": version "2.0.3" resolved "https://registry.yarnpkg.com/@types/stack-utils/-/stack-utils-2.0.3.tgz#6209321eb2c1712a7e7466422b8cb1fc0d9dd5d8" @@ -1666,13 +1622,6 @@ abbrev@^2.0.0: resolved "https://registry.yarnpkg.com/abbrev/-/abbrev-2.0.0.tgz#cf59829b8b4f03f89dda2771cb7f3653828c89bf" integrity sha512-6/mh1E2u2YgEsCHdY0Yx5oW+61gZU+1vXaoiHHrpKeuRNNgFvS+/jrwHiQhB5apAf5oB7UB7E19ol2R2LKH8hQ== -abort-controller@^3.0.0: - version "3.0.0" - resolved "https://registry.yarnpkg.com/abort-controller/-/abort-controller-3.0.0.tgz#eaf54d53b62bae4138e809ca225c8439a6efb392" - integrity sha512-h8lQ8tacZYnR3vNQTgibj+tODHI5/+l06Au2Pcriv/Gmet0eaj4TwWH41sO9wnHDiQsEj19q0drzdWdeAHtweg== - dependencies: - event-target-shim "^5.0.0" - acorn-jsx@^5.0.0, acorn-jsx@^5.3.2: version "5.3.2" resolved "https://registry.yarnpkg.com/acorn-jsx/-/acorn-jsx-5.3.2.tgz#7ed5bb55908b3b2f1bc55c6af1653bada7f07937" @@ -1683,13 +1632,6 @@ acorn@^8.0.0, acorn@^8.11.3, acorn@^8.14.0, acorn@^8.9.0: resolved "https://registry.yarnpkg.com/acorn/-/acorn-8.14.0.tgz#063e2c70cac5fb4f6467f0b11152e04c682795b0" integrity sha512-cl669nCJTZBsL97OF4kUQm5g5hC2uihk0NxY3WENAC0TYdILVkAyHymAntgxGkl7K+t0cXIrH5siy5S4XkFycA== -agentkeepalive@^4.2.1: - version "4.6.0" - resolved "https://registry.yarnpkg.com/agentkeepalive/-/agentkeepalive-4.6.0.tgz#35f73e94b3f40bf65f105219c623ad19c136ea6a" - integrity sha512-kja8j7PjmncONqaTsB8fQ+wE2mSU2DJ9D4XKoJ5PFWIdRMa6SLSN1ff4mOr4jCbfRSsxR4keIiySJU0N9T5hIQ== - dependencies: - humanize-ms "^1.2.1" - ajv@^6.12.4: version "6.12.6" resolved "https://registry.yarnpkg.com/ajv/-/ajv-6.12.6.tgz#baf5a62e802b07d977034586f8c3baf5adf26df4" @@ -1854,11 +1796,6 @@ async-function@^1.0.0: resolved "https://registry.yarnpkg.com/async-function/-/async-function-1.0.0.tgz#509c9fca60eaf85034c6829838188e4e4c8ffb2b" integrity sha512-hsU18Ae8CDTR6Kgu9DYf0EbCr/a5iGL0rytQDobUcdpYOKokk8LEjVphnXkDkgpi0wYVsqrXuP0bZxJaTqdgoA== -asynckit@^0.4.0: - version "0.4.0" - resolved "https://registry.yarnpkg.com/asynckit/-/asynckit-0.4.0.tgz#c79ed97f7f34cb8f2ba1bc9790bcc366474b4b79" - integrity sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q== - available-typed-arrays@^1.0.7: version "1.0.7" resolved "https://registry.yarnpkg.com/available-typed-arrays/-/available-typed-arrays-1.0.7.tgz#a5cc375d6a03c2efc87a553f3e0b1522def14846" @@ -2172,13 +2109,6 @@ colorette@^2.0.20: resolved "https://registry.yarnpkg.com/colorette/-/colorette-2.0.20.tgz#9eb793e6833067f7235902fcd3b09917a000a95a" integrity sha512-IfEDxwoWIjkeXL1eXcDiow4UbKjhLdq6/EuSVR9GMN7KVH3r9gQ83e73hsz1Nd1T3ijd5xv1wcWRYO+D6kCI2w== -combined-stream@^1.0.8: - version "1.0.8" - resolved "https://registry.yarnpkg.com/combined-stream/-/combined-stream-1.0.8.tgz#c3d45a8b34fd730631a110a8a2520682b31d5a7f" - integrity sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg== - dependencies: - delayed-stream "~1.0.0" - commander@^13.1.0: version "13.1.0" resolved "https://registry.yarnpkg.com/commander/-/commander-13.1.0.tgz#776167db68c78f38dcce1f9b8d7b8b9a488abf46" @@ -2319,11 +2249,6 @@ define-properties@^1.1.3, define-properties@^1.2.1: has-property-descriptors "^1.0.0" object-keys "^1.1.1" -delayed-stream@~1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/delayed-stream/-/delayed-stream-1.0.0.tgz#df3ae199acadfb7d440aaae0b29e2272b24ec619" - integrity sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ== - dequal@^2.0.0: version "2.0.3" resolved "https://registry.yarnpkg.com/dequal/-/dequal-2.0.3.tgz#2644214f1997d39ed0ee0ece72335490a7ac67be" @@ -2801,11 +2726,6 @@ esutils@^2.0.2: resolved "https://registry.yarnpkg.com/esutils/-/esutils-2.0.3.tgz#74d2eb4de0b8da1293711910d50775b9b710ef64" integrity sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g== -event-target-shim@^5.0.0: - version "5.0.1" - resolved "https://registry.yarnpkg.com/event-target-shim/-/event-target-shim-5.0.1.tgz#5d4d3ebdf9583d63a5333ce2deb7480ab2b05789" - integrity sha512-i/2XbnSz/uxRCU6+NdVJgKWDTM427+MqYbkQzD321DuCQJUqOuJKIA0IM2+W2xtYHdKOmZ4dR6fExsd4SXL+WQ== - eventemitter3@^5.0.1: version "5.0.1" resolved "https://registry.yarnpkg.com/eventemitter3/-/eventemitter3-5.0.1.tgz#53f5ffd0a492ac800721bb42c66b841de96423c4" @@ -2960,28 +2880,6 @@ foreground-child@^3.1.0: cross-spawn "^7.0.0" signal-exit "^4.0.1" -form-data-encoder@1.7.2: - version "1.7.2" - resolved "https://registry.yarnpkg.com/form-data-encoder/-/form-data-encoder-1.7.2.tgz#1f1ae3dccf58ed4690b86d87e4f57c654fbab040" - integrity sha512-qfqtYan3rxrnCk1VYaA4H+Ms9xdpPqvLZa6xmMgFvhO32x7/3J/ExcTd6qpxM0vH2GdMI+poehyBZvqfMTto8A== - -form-data@^4.0.0: - version "4.0.1" - resolved "https://registry.yarnpkg.com/form-data/-/form-data-4.0.1.tgz#ba1076daaaa5bfd7e99c1a6cb02aa0a5cff90d48" - integrity sha512-tzN8e4TX8+kkxGPK8D5u0FNmjPUjw3lwC9lSLxxoB/+GtsJG91CO8bSWy73APlgAZzZbXEYZJuxjkHH2w+Ezhw== - dependencies: - asynckit "^0.4.0" - combined-stream "^1.0.8" - mime-types "^2.1.12" - -formdata-node@^4.3.2: - version "4.4.1" - resolved "https://registry.yarnpkg.com/formdata-node/-/formdata-node-4.4.1.tgz#23f6a5cb9cb55315912cbec4ff7b0f59bbd191e2" - integrity sha512-0iirZp3uVDjVGt9p49aTaqjk84TrglENEDuqfdlZQ1roC9CWlPk6Avf8EEnZNcAqPonwkG35x4n3ww/1THYAeQ== - dependencies: - node-domexception "1.0.0" - web-streams-polyfill "4.0.0-beta.3" - fs.realpath@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/fs.realpath/-/fs.realpath-1.0.0.tgz#1504ad2523158caa40db4a2787cb01411994ea4f" @@ -3227,13 +3125,6 @@ human-signals@^2.1.0: resolved "https://registry.yarnpkg.com/human-signals/-/human-signals-2.1.0.tgz#dc91fcba42e4d06e4abaed33b3e7a3c02f514ea0" integrity sha512-B4FFZ6q/T2jhhksgkbEW3HBvWIfDW85snkQgawt07S7J5QXTk6BkNV+0yAeZrM5QpMAdYlocGoljn0sJ/WQkFw== -humanize-ms@^1.2.1: - version "1.2.1" - resolved "https://registry.yarnpkg.com/humanize-ms/-/humanize-ms-1.2.1.tgz#c46e3159a293f6b896da29316d8b6fe8bb79bbed" - integrity sha512-Fl70vYtsAFb/C06PTS9dZBo7ihau+Tu/DNCk/OyHhea07S+aeMWpFFkUaXRa8fI+ScZbEI8dfSxwY7gxZ9SAVQ== - dependencies: - ms "^2.0.0" - ignore@^5.2.0: version "5.3.2" resolved "https://registry.yarnpkg.com/ignore/-/ignore-5.3.2.tgz#3cd40e729f3643fd87cb04e50bf0eb722bc596f5" @@ -4686,18 +4577,6 @@ micromatch@^4.0.4, micromatch@^4.0.8: braces "^3.0.3" picomatch "^2.3.1" -mime-db@1.52.0: - version "1.52.0" - resolved "https://registry.yarnpkg.com/mime-db/-/mime-db-1.52.0.tgz#bbabcdc02859f4987301c856e3387ce5ec43bf70" - integrity sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg== - -mime-types@^2.1.12: - version "2.1.35" - resolved "https://registry.yarnpkg.com/mime-types/-/mime-types-2.1.35.tgz#381a871b62a734450660ae3deee44813f70d959a" - integrity sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw== - dependencies: - mime-db "1.52.0" - mimic-fn@^2.1.0: version "2.1.0" resolved "https://registry.yarnpkg.com/mimic-fn/-/mimic-fn-2.1.0.tgz#7ed2c2ccccaf84d3ffcb7a69b57711fc2083401b" @@ -4744,33 +4623,16 @@ mri@^1.1.0: resolved "https://registry.yarnpkg.com/mri/-/mri-1.2.0.tgz#6721480fec2a11a4889861115a48b6cbe7cc8f0b" integrity sha512-tzzskb3bG8LvYGFF/mDTpq3jpI6Q9wc3LEmBaghu+DdCssd1FakN7Bc0hVNmEyGq1bq3RgfkCb3cmQLpNPOroA== -ms@^2.0.0, ms@^2.1.1, ms@^2.1.3: +ms@^2.1.1, ms@^2.1.3: version "2.1.3" resolved "https://registry.yarnpkg.com/ms/-/ms-2.1.3.tgz#574c8138ce1d2b5861f0b44579dbadd60c6615b2" integrity sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA== -mustache@^4.2.0: - version "4.2.0" - resolved "https://registry.yarnpkg.com/mustache/-/mustache-4.2.0.tgz#e5892324d60a12ec9c2a73359edca52972bf6f64" - integrity sha512-71ippSywq5Yb7/tVYyGbkBggbU8H3u5Rz56fH60jGFgr8uHwxs+aSKeqmluIVzM0m0kB7xQjKS6qPfd0b2ZoqQ== - natural-compare@^1.4.0: version "1.4.0" resolved "https://registry.yarnpkg.com/natural-compare/-/natural-compare-1.4.0.tgz#4abebfeed7541f2c27acfb29bdbbd15c8d5ba4f7" integrity sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw== -node-domexception@1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/node-domexception/-/node-domexception-1.0.0.tgz#6888db46a1f71c0b76b3f7555016b63fe64766e5" - integrity sha512-/jKZoMpw0F8GRwl4/eLROPA3cfcXtLApP0QzLmUT/HuPCZWyB7IY9ZrMeKw2O/nFIqPQB3PVM9aYm0F312AXDQ== - -node-fetch@^2.6.7: - version "2.7.0" - resolved "https://registry.yarnpkg.com/node-fetch/-/node-fetch-2.7.0.tgz#d0f0fa6e3e2dc1d27efcd8ad99d550bda94d187d" - integrity sha512-c4FRfUm/dbcWZ7U+1Wq0AwCyFL+3nt2bEw05wfxSz+DWpWsitgmSgYmy2dQdWyKC1694ELPqMs/YzUSNozLt8A== - dependencies: - whatwg-url "^5.0.0" - node-int64@^0.4.0: version "0.4.0" resolved "https://registry.yarnpkg.com/node-int64/-/node-int64-0.4.0.tgz#87a9065cdb355d3182d8f94ce11188b825c68a3b" @@ -4935,19 +4797,6 @@ open@^7.3.0: is-docker "^2.0.0" is-wsl "^2.1.1" -openai@^4.83.0: - version "4.85.1" - resolved "https://registry.yarnpkg.com/openai/-/openai-4.85.1.tgz#639058653cca92bebd74939751858e7958257670" - integrity sha512-jkX2fntHljUvSH3MkWh4jShl10oNkb+SsCj4auKlbu2oF4KWAnmHLNR5EpnUHK1ZNW05Rp0fjbJzYwQzMsH8ZA== - dependencies: - "@types/node" "^18.11.18" - "@types/node-fetch" "^2.6.4" - abort-controller "^3.0.0" - agentkeepalive "^4.2.1" - form-data-encoder "1.7.2" - formdata-node "^4.3.2" - node-fetch "^2.6.7" - optionator@^0.9.3: version "0.9.4" resolved "https://registry.yarnpkg.com/optionator/-/optionator-0.9.4.tgz#7ea1c1a5d91d764fb282139c88fe11e182a3a734" @@ -5458,11 +5307,6 @@ semver@^7.1.1, semver@^7.3.5, semver@^7.5.3, semver@^7.5.4, semver@^7.6.0: resolved "https://registry.yarnpkg.com/semver/-/semver-7.7.1.tgz#abd5098d82b18c6c81f6074ff2647fd3e7220c9f" integrity sha512-hlq8tAfn0m/61p4BVRcPzIGr6LKiMwo4VM6dGi6pt4qcRkmNzTcWq6eCEjEh+qXjkMDvPlOFFSGwQjoEa6gyMA== -server-destroy@^1.0.1: - version "1.0.1" - resolved "https://registry.yarnpkg.com/server-destroy/-/server-destroy-1.0.1.tgz#f13bf928e42b9c3e79383e61cc3998b5d14e6cdd" - integrity sha512-rb+9B5YBIEzYcD6x2VKidaa+cqYBJQKnU4oe4E3ANwRRN56yk/ua1YCJT1n21NTS8w6CcOclAKNP3PhdCXKYtQ== - set-function-length@^1.2.2: version "1.2.2" resolved "https://registry.yarnpkg.com/set-function-length/-/set-function-length-1.2.2.tgz#aac72314198eaed975cf77b2c3b6b880695e5449" @@ -5854,11 +5698,6 @@ to-regex-range@^5.0.1: dependencies: is-number "^7.0.0" -tr46@~0.0.3: - version "0.0.3" - resolved "https://registry.yarnpkg.com/tr46/-/tr46-0.0.3.tgz#8184fd347dac9cdc185992f3a6622e14b9d9ab6a" - integrity sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw== - trough@^2.0.0: version "2.2.0" resolved "https://registry.yarnpkg.com/trough/-/trough-2.2.0.tgz#94a60bd6bd375c152c1df911a4b11d5b0256f50f" @@ -5966,11 +5805,6 @@ unbox-primitive@^1.1.0: has-symbols "^1.1.0" which-boxed-primitive "^1.1.1" -undici-types@~5.26.4: - version "5.26.5" - resolved "https://registry.yarnpkg.com/undici-types/-/undici-types-5.26.5.tgz#bcd539893d00b56e964fd2657a4866b221a65617" - integrity sha512-JlCMO+ehdEIKqlFxk6IfVoAUVmgz7cU7zD/h9XZ0qzeosSHmUJVOzSQvvYSYWXkFXC+IfLKSIffhv0sVZup6pA== - undici-types@~6.20.0: version "6.20.0" resolved "https://registry.yarnpkg.com/undici-types/-/undici-types-6.20.0.tgz#8171bf22c1f588d1554d55bf204bc624af388433" @@ -6201,24 +6035,6 @@ walker@^1.0.8: dependencies: makeerror "1.0.12" -web-streams-polyfill@4.0.0-beta.3: - version "4.0.0-beta.3" - resolved "https://registry.yarnpkg.com/web-streams-polyfill/-/web-streams-polyfill-4.0.0-beta.3.tgz#2898486b74f5156095e473efe989dcf185047a38" - integrity sha512-QW95TCTaHmsYfHDybGMwO5IJIM93I/6vTRk+daHTWFPhwh+C8Cg7j7XyKrwrj8Ib6vYXe0ocYNrmzY4xAAN6ug== - -webidl-conversions@^3.0.0: - version "3.0.1" - resolved "https://registry.yarnpkg.com/webidl-conversions/-/webidl-conversions-3.0.1.tgz#24534275e2a7bc6be7bc86611cc16ae0a5654871" - integrity sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ== - -whatwg-url@^5.0.0: - version "5.0.0" - resolved "https://registry.yarnpkg.com/whatwg-url/-/whatwg-url-5.0.0.tgz#966454e8765462e37644d3626f6742ce8b70965d" - integrity sha512-saE57nupxk6v3HY35+jzBwYa0rKSy0XR8JSxZPwgLr7ys0IBzhGviA1/TUGJLmSVqs8pb9AnvICXEuOHLprYTw== - dependencies: - tr46 "~0.0.3" - webidl-conversions "^3.0.0" - which-boxed-primitive@^1.1.0, which-boxed-primitive@^1.1.1: version "1.1.1" resolved "https://registry.yarnpkg.com/which-boxed-primitive/-/which-boxed-primitive-1.1.1.tgz#d76ec27df7fa165f18d5808374a5fe23c29b176e"