@sanity/cli 6.0.0-alpha.4 → 6.0.0-alpha.5
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/actions/dev/startStudioDevServer.js +3 -8
- package/dist/actions/dev/startStudioDevServer.js.map +1 -1
- package/dist/actions/dev/types.d.ts +1 -3
- package/dist/actions/dev/types.js.map +1 -1
- package/dist/actions/documents/validate.d.ts +0 -2
- package/dist/actions/documents/validate.js +21 -1
- package/dist/actions/documents/validate.js.map +1 -1
- package/dist/actions/exec/execScript.js +1 -1
- package/dist/actions/exec/execScript.js.map +1 -1
- package/dist/actions/graphql/__tests__/getGraphQLAPIs.test.js +1 -1
- package/dist/actions/graphql/__tests__/getGraphQLAPIs.test.js.map +1 -1
- package/dist/actions/graphql/getGraphQLAPIs.js +1 -1
- package/dist/actions/graphql/getGraphQLAPIs.js.map +1 -1
- package/dist/actions/manifest/extractManifest.js +1 -4
- package/dist/actions/manifest/extractManifest.js.map +1 -1
- package/dist/actions/schema/deleteSchemaAction.d.ts +13 -5
- package/dist/actions/schema/deleteSchemaAction.js +12 -17
- package/dist/actions/schema/deleteSchemaAction.js.map +1 -1
- package/dist/actions/schema/deploySchemas.d.ts +15 -0
- package/dist/actions/schema/deploySchemas.js +98 -0
- package/dist/actions/schema/deploySchemas.js.map +1 -0
- package/dist/actions/schema/listSchemas.d.ts +12 -0
- package/dist/actions/schema/listSchemas.js +119 -0
- package/dist/actions/schema/listSchemas.js.map +1 -0
- package/dist/actions/schema/schemaStoreTypes.d.ts +0 -11
- package/dist/actions/schema/schemaStoreTypes.js.map +1 -1
- package/dist/actions/schema/utils/debug.d.ts +2 -0
- package/dist/actions/schema/utils/debug.js +5 -0
- package/dist/actions/schema/utils/debug.js.map +1 -0
- package/dist/actions/schema/utils/manifestExtractor.d.ts +3 -8
- package/dist/actions/schema/utils/manifestExtractor.js +12 -17
- package/dist/actions/schema/utils/manifestExtractor.js.map +1 -1
- package/dist/actions/schema/utils/manifestReader.d.ts +2 -9
- package/dist/actions/schema/utils/manifestReader.js +6 -12
- package/dist/actions/schema/utils/manifestReader.js.map +1 -1
- package/dist/actions/schema/utils/schemaStoreOutStrings.d.ts +0 -1
- package/dist/actions/schema/utils/schemaStoreOutStrings.js +1 -1
- package/dist/actions/schema/utils/schemaStoreOutStrings.js.map +1 -1
- package/dist/actions/schema/utils/schemaStoreValidation.d.ts +10 -62
- package/dist/actions/schema/utils/schemaStoreValidation.js +38 -125
- package/dist/actions/schema/utils/schemaStoreValidation.js.map +1 -1
- package/dist/actions/schema/utils/uniqByProjectIdDataset.d.ts +14 -0
- package/dist/actions/schema/utils/uniqByProjectIdDataset.js +9 -0
- package/dist/actions/schema/utils/uniqByProjectIdDataset.js.map +1 -0
- package/dist/actions/users/getMembersForProject.d.ts +1 -3
- package/dist/actions/users/getMembersForProject.js +6 -17
- package/dist/actions/users/getMembersForProject.js.map +1 -1
- package/dist/actions/users/types.d.ts +0 -11
- package/dist/actions/users/types.js.map +1 -1
- package/dist/commands/__tests__/debug.test.js +113 -220
- package/dist/commands/__tests__/debug.test.js.map +1 -1
- package/dist/commands/__tests__/deploy.test.js +325 -293
- package/dist/commands/__tests__/deploy.test.js.map +1 -1
- package/dist/commands/__tests__/dev.test.js +62 -19
- package/dist/commands/__tests__/dev.test.js.map +1 -1
- package/dist/commands/__tests__/init/init.authentication.test.js +40 -27
- package/dist/commands/__tests__/init/init.authentication.test.js.map +1 -1
- package/dist/commands/__tests__/init/init.create-new-project.test.js +84 -85
- package/dist/commands/__tests__/init/init.create-new-project.test.js.map +1 -1
- package/dist/commands/__tests__/init/init.plan.test.js +103 -44
- package/dist/commands/__tests__/init/init.plan.test.js.map +1 -1
- package/dist/commands/__tests__/init/init.setup.test.js +85 -29
- package/dist/commands/__tests__/init/init.setup.test.js.map +1 -1
- package/dist/commands/__tests__/install.test.js +46 -22
- package/dist/commands/__tests__/install.test.js.map +1 -1
- package/dist/commands/__tests__/logout.test.js +8 -5
- package/dist/commands/__tests__/logout.test.js.map +1 -1
- package/dist/commands/__tests__/manage.test.js +29 -24
- package/dist/commands/__tests__/manage.test.js.map +1 -1
- package/dist/commands/__tests__/versions.test.js +22 -14
- package/dist/commands/__tests__/versions.test.js.map +1 -1
- package/dist/commands/backup/__tests__/disable.test.js +72 -75
- package/dist/commands/backup/__tests__/disable.test.js.map +1 -1
- package/dist/commands/backup/__tests__/download.test.js +166 -77
- package/dist/commands/backup/__tests__/download.test.js.map +1 -1
- package/dist/commands/backup/__tests__/enable.test.js +109 -140
- package/dist/commands/backup/__tests__/enable.test.js.map +1 -1
- package/dist/commands/backup/__tests__/list.test.js +84 -75
- package/dist/commands/backup/__tests__/list.test.js.map +1 -1
- package/dist/commands/backup/disable.js +5 -11
- package/dist/commands/backup/disable.js.map +1 -1
- package/dist/commands/backup/enable.js +5 -11
- package/dist/commands/backup/enable.js.map +1 -1
- package/dist/commands/backup/list.js +7 -8
- package/dist/commands/backup/list.js.map +1 -1
- package/dist/commands/cors/__tests__/add.test.js +68 -38
- package/dist/commands/cors/__tests__/add.test.js.map +1 -1
- package/dist/commands/cors/__tests__/delete.test.js +52 -37
- package/dist/commands/cors/__tests__/delete.test.js.map +1 -1
- package/dist/commands/cors/__tests__/list.test.js +80 -57
- package/dist/commands/cors/__tests__/list.test.js.map +1 -1
- package/dist/commands/cors/add.js +5 -13
- package/dist/commands/cors/add.js.map +1 -1
- package/dist/commands/cors/delete.js +7 -15
- package/dist/commands/cors/delete.js.map +1 -1
- package/dist/commands/cors/list.js +2 -10
- package/dist/commands/cors/list.js.map +1 -1
- package/dist/commands/dataset/__tests__/copy.test.js +197 -89
- package/dist/commands/dataset/__tests__/copy.test.js.map +1 -1
- package/dist/commands/dataset/__tests__/create.test.js +147 -117
- package/dist/commands/dataset/__tests__/create.test.js.map +1 -1
- package/dist/commands/dataset/__tests__/delete.test.js +75 -68
- package/dist/commands/dataset/__tests__/delete.test.js.map +1 -1
- package/dist/commands/dataset/__tests__/export.test.js +123 -83
- package/dist/commands/dataset/__tests__/export.test.js.map +1 -1
- package/dist/commands/dataset/__tests__/list.test.js +107 -65
- package/dist/commands/dataset/__tests__/list.test.js.map +1 -1
- package/dist/commands/dataset/alias/__tests__/create.test.js +114 -74
- package/dist/commands/dataset/alias/__tests__/create.test.js.map +1 -1
- package/dist/commands/dataset/alias/__tests__/delete.test.js +40 -29
- package/dist/commands/dataset/alias/__tests__/delete.test.js.map +1 -1
- package/dist/commands/dataset/alias/__tests__/link.test.js +114 -74
- package/dist/commands/dataset/alias/__tests__/link.test.js.map +1 -1
- package/dist/commands/dataset/alias/__tests__/unlink.test.js +44 -29
- package/dist/commands/dataset/alias/__tests__/unlink.test.js.map +1 -1
- package/dist/commands/dataset/export.js +4 -4
- package/dist/commands/dataset/export.js.map +1 -1
- package/dist/commands/dataset/visibility/__tests__/get.test.js +48 -67
- package/dist/commands/dataset/visibility/__tests__/get.test.js.map +1 -1
- package/dist/commands/dataset/visibility/__tests__/set.test.js +76 -123
- package/dist/commands/dataset/visibility/__tests__/set.test.js.map +1 -1
- package/dist/commands/dev.js +0 -1
- package/dist/commands/dev.js.map +1 -1
- package/dist/commands/docs/__tests__/search.test.js +8 -7
- package/dist/commands/docs/__tests__/search.test.js.map +1 -1
- package/dist/commands/documents/__tests__/create.test.js +328 -265
- package/dist/commands/documents/__tests__/create.test.js.map +1 -1
- package/dist/commands/documents/__tests__/delete.test.js +119 -87
- package/dist/commands/documents/__tests__/delete.test.js.map +1 -1
- package/dist/commands/documents/__tests__/get.test.js +68 -95
- package/dist/commands/documents/__tests__/get.test.js.map +1 -1
- package/dist/commands/documents/__tests__/query.test.js +84 -189
- package/dist/commands/documents/__tests__/query.test.js.map +1 -1
- package/dist/commands/documents/__tests__/validate.test.js +52 -29
- package/dist/commands/documents/__tests__/validate.test.js.map +1 -1
- package/dist/commands/documents/create.d.ts +1 -0
- package/dist/commands/documents/create.js +10 -9
- package/dist/commands/documents/create.js.map +1 -1
- package/dist/commands/documents/delete.js +2 -3
- package/dist/commands/documents/delete.js.map +1 -1
- package/dist/commands/documents/get.js +2 -3
- package/dist/commands/documents/get.js.map +1 -1
- package/dist/commands/documents/query.js +2 -3
- package/dist/commands/documents/query.js.map +1 -1
- package/dist/commands/documents/validate.js +0 -20
- package/dist/commands/documents/validate.js.map +1 -1
- package/dist/commands/graphql/__tests__/list.test.js +57 -45
- package/dist/commands/graphql/__tests__/list.test.js.map +1 -1
- package/dist/commands/graphql/__tests__/undeploy.test.js +85 -59
- package/dist/commands/graphql/__tests__/undeploy.test.js.map +1 -1
- package/dist/commands/graphql/list.js +2 -2
- package/dist/commands/graphql/list.js.map +1 -1
- package/dist/commands/graphql/undeploy.js +4 -9
- package/dist/commands/graphql/undeploy.js.map +1 -1
- package/dist/commands/hook/__tests__/attempt.test.js +48 -33
- package/dist/commands/hook/__tests__/attempt.test.js.map +1 -1
- package/dist/commands/hook/__tests__/create.test.js +49 -51
- package/dist/commands/hook/__tests__/create.test.js.map +1 -1
- package/dist/commands/hook/__tests__/delete.test.js +43 -30
- package/dist/commands/hook/__tests__/delete.test.js.map +1 -1
- package/dist/commands/hook/__tests__/list.test.js +38 -31
- package/dist/commands/hook/__tests__/list.test.js.map +1 -1
- package/dist/commands/hook/__tests__/logs.test.js +68 -40
- package/dist/commands/hook/__tests__/logs.test.js.map +1 -1
- package/dist/commands/hook/create.js +2 -6
- package/dist/commands/hook/create.js.map +1 -1
- package/dist/commands/hook/delete.js +5 -17
- package/dist/commands/hook/delete.js.map +1 -1
- package/dist/commands/hook/list.js +2 -8
- package/dist/commands/hook/list.js.map +1 -1
- package/dist/commands/manifest/__tests__/extract.test.js +22 -13
- package/dist/commands/manifest/__tests__/extract.test.js.map +1 -1
- package/dist/commands/media/__tests__/create-aspect.test.js +41 -28
- package/dist/commands/media/__tests__/create-aspect.test.js.map +1 -1
- package/dist/commands/media/__tests__/delete-aspect.test.js +44 -35
- package/dist/commands/media/__tests__/delete-aspect.test.js.map +1 -1
- package/dist/commands/media/__tests__/deploy-aspect.test.js +67 -80
- package/dist/commands/media/__tests__/deploy-aspect.test.js.map +1 -1
- package/dist/commands/media/__tests__/export.test.js +365 -66
- package/dist/commands/media/__tests__/export.test.js.map +1 -1
- package/dist/commands/media/__tests__/import.test.js +171 -105
- package/dist/commands/media/__tests__/import.test.js.map +1 -1
- package/dist/commands/media/export.js +2 -2
- package/dist/commands/media/export.js.map +1 -1
- package/dist/commands/media/import.js +2 -2
- package/dist/commands/media/import.js.map +1 -1
- package/dist/commands/projects/__tests__/list.test.js +5 -4
- package/dist/commands/projects/__tests__/list.test.js.map +1 -1
- package/dist/commands/projects/list.js +2 -6
- package/dist/commands/projects/list.js.map +1 -1
- package/dist/commands/schema/__tests__/delete.test.js +396 -151
- package/dist/commands/schema/__tests__/delete.test.js.map +1 -1
- package/dist/commands/schema/__tests__/deploy.test.js +348 -0
- package/dist/commands/schema/__tests__/deploy.test.js.map +1 -0
- package/dist/commands/schema/__tests__/extract.test.js +19 -11
- package/dist/commands/schema/__tests__/extract.test.js.map +1 -1
- package/dist/commands/schema/__tests__/list.test.js +399 -0
- package/dist/commands/schema/__tests__/list.test.js.map +1 -0
- package/dist/commands/schema/__tests__/validate.test.js +27 -10
- package/dist/commands/schema/__tests__/validate.test.js.map +1 -1
- package/dist/commands/schema/delete.d.ts +1 -1
- package/dist/commands/schema/delete.js +20 -23
- package/dist/commands/schema/delete.js.map +1 -1
- package/dist/commands/schema/deploy.d.ts +16 -0
- package/dist/commands/schema/deploy.js +98 -0
- package/dist/commands/schema/deploy.js.map +1 -0
- package/dist/commands/schema/list.d.ts +15 -0
- package/dist/commands/schema/list.js +104 -0
- package/dist/commands/schema/list.js.map +1 -0
- package/dist/commands/telemetry/__tests__/disable.test.js +7 -5
- package/dist/commands/telemetry/__tests__/disable.test.js.map +1 -1
- package/dist/commands/telemetry/__tests__/enable.test.js +7 -5
- package/dist/commands/telemetry/__tests__/enable.test.js.map +1 -1
- package/dist/commands/telemetry/__tests__/status.test.js +7 -5
- package/dist/commands/telemetry/__tests__/status.test.js.map +1 -1
- package/dist/commands/tokens/__tests__/add.test.js +55 -40
- package/dist/commands/tokens/__tests__/add.test.js.map +1 -1
- package/dist/commands/tokens/__tests__/delete.test.js +72 -42
- package/dist/commands/tokens/__tests__/delete.test.js.map +1 -1
- package/dist/commands/tokens/__tests__/list.test.js +87 -60
- package/dist/commands/tokens/__tests__/list.test.js.map +1 -1
- package/dist/commands/tokens/add.js +3 -5
- package/dist/commands/tokens/add.js.map +1 -1
- package/dist/commands/users/__tests__/invite.test.js +100 -79
- package/dist/commands/users/__tests__/invite.test.js.map +1 -1
- package/dist/commands/users/__tests__/list.test.js +186 -180
- package/dist/commands/users/__tests__/list.test.js.map +1 -1
- package/dist/commands/users/invite.js +6 -17
- package/dist/commands/users/invite.js.map +1 -1
- package/dist/commands/users/list.js +4 -7
- package/dist/commands/users/list.js.map +1 -1
- package/dist/config/createCliConfig.d.ts +4 -4
- package/dist/services/backup.d.ts +8 -0
- package/dist/services/backup.js +19 -0
- package/dist/services/backup.js.map +1 -1
- package/dist/services/cors.d.ts +23 -0
- package/dist/services/cors.js +38 -0
- package/dist/services/cors.js.map +1 -0
- package/dist/services/graphql.d.ts +7 -0
- package/dist/services/graphql.js +11 -0
- package/dist/services/graphql.js.map +1 -1
- package/dist/services/hooks.d.ts +2 -0
- package/dist/services/hooks.js +19 -0
- package/dist/services/hooks.js.map +1 -1
- package/dist/services/organizations.d.ts +1 -1
- package/dist/services/organizations.js +1 -1
- package/dist/services/organizations.js.map +1 -1
- package/dist/services/projects.d.ts +11 -0
- package/dist/services/projects.js +41 -0
- package/dist/services/projects.js.map +1 -1
- package/dist/services/schemas.d.ts +4 -0
- package/dist/services/schemas.js +40 -0
- package/dist/services/schemas.js.map +1 -0
- package/dist/services/user.d.ts +8 -0
- package/dist/services/user.js +15 -2
- package/dist/services/user.js.map +1 -1
- package/dist/util/__tests__/getCliVersion.test.js +2 -2
- package/dist/util/__tests__/getCliVersion.test.js.map +1 -1
- package/dist/util/errorMessages.d.ts +1 -0
- package/dist/util/errorMessages.js +1 -0
- package/dist/util/errorMessages.js.map +1 -1
- package/dist/util/getCliVersion.js +1 -1
- package/dist/util/getCliVersion.js.map +1 -1
- package/dist/util/readPackageJson.d.ts +1 -15
- package/dist/util/readPackageJson.js +1 -1
- package/dist/util/readPackageJson.js.map +1 -1
- package/dist/util/uniqBy.d.ts +1 -0
- package/dist/util/uniqBy.js +14 -0
- package/dist/util/uniqBy.js.map +1 -0
- package/oclif.manifest.json +172 -27
- package/package.json +27 -28
- package/dist/actions/cors/constants.d.ts +0 -1
- package/dist/actions/cors/constants.js +0 -3
- package/dist/actions/cors/constants.js.map +0 -1
- package/dist/actions/cors/types.d.ts +0 -9
- package/dist/actions/cors/types.js +0 -3
- package/dist/actions/cors/types.js.map +0 -1
- package/dist/actions/schema/__tests__/deleteSchemaAction.test.js +0 -294
- package/dist/actions/schema/__tests__/deleteSchemaAction.test.js.map +0 -1
- package/dist/actions/schema/schemaStoreConstants.d.ts +0 -1
- package/dist/actions/schema/schemaStoreConstants.js +0 -4
- package/dist/actions/schema/schemaStoreConstants.js.map +0 -1
- package/dist/actions/schema/utils/schemaActionHelpers.d.ts +0 -1
- package/dist/actions/schema/utils/schemaActionHelpers.js +0 -5
- package/dist/actions/schema/utils/schemaActionHelpers.js.map +0 -1
- package/dist/actions/schema/utils/schemaApiClient.d.ts +0 -6
- package/dist/actions/schema/utils/schemaApiClient.js +0 -17
- package/dist/actions/schema/utils/schemaApiClient.js.map +0 -1
- package/dist/actions/users/apiVersion.d.ts +0 -6
- package/dist/actions/users/apiVersion.js +0 -7
- package/dist/actions/users/apiVersion.js.map +0 -1
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../../src/commands/backup/list.ts"],"sourcesContent":["import {Args, Flags} from '@oclif/core'\nimport {SanityCommand, subdebug} from '@sanity/cli-core'\nimport {select} from '@sanity/cli-core/ux'\nimport {type DatasetsResponse} from '@sanity/client'\nimport {Table} from 'console-table-printer'\nimport {isAfter, isValid, lightFormat, parse} from 'date-fns'\n\nimport {assertDatasetExists} from '../../actions/backup/assertDatasetExist.js'\nimport {BACKUP_API_VERSION} from '../../actions/backup/constants.js'\nimport {listDatasets} from '../../services/datasets.js'\nimport {NO_PROJECT_ID} from '../../util/errorMessages.js'\n\nconst listBackupDebug = subdebug('backup:list')\n\nconst DEFAULT_LIST_BACKUP_LIMIT = 30\n\ntype ListBackupRequestQueryParams = {\n after?: string\n before?: string\n limit: string\n}\n\ntype ListBackupResponse = {\n backups: ListBackupResponseItem[]\n}\n\ntype ListBackupResponseItem = {\n createdAt: string\n id: string\n}\n\nexport class ListBackupCommand extends SanityCommand<typeof ListBackupCommand> {\n static override args = {\n dataset: Args.string({\n description: 'Dataset name to list backups for',\n required: false,\n }),\n }\n\n static override description = 'List available backups for a dataset.'\n\n static override examples = [\n {\n command: '<%= config.bin %> <%= command.id %>',\n description: 'List backups for a dataset interactively',\n },\n {\n command: '<%= config.bin %> <%= command.id %> production',\n description: 'List backups for the production dataset',\n },\n {\n command: '<%= config.bin %> <%= command.id %> production --limit 50',\n description: 'List up to 50 backups for the production dataset',\n },\n {\n command: '<%= config.bin %> <%= command.id %> production --after 2024-01-31 --limit 10',\n description: 'List up to 10 backups created after 2024-01-31',\n },\n ]\n\n static override flags = {\n after: Flags.string({\n description: 'Only return backups after this date (inclusive, YYYY-MM-DD format)',\n }),\n before: Flags.string({\n description: 'Only return backups before this date (exclusive, YYYY-MM-DD format)',\n }),\n limit: Flags.integer({\n char: 'l',\n default: DEFAULT_LIST_BACKUP_LIMIT,\n description: 'Maximum number of backups returned',\n }),\n }\n\n public async run(): Promise<void> {\n const {args, flags} = await this.parse(ListBackupCommand)\n let {dataset} = args\n\n const projectId = await this.getProjectId()\n if (!projectId) {\n this.error(NO_PROJECT_ID, {exit: 1})\n }\n\n const client = await this.getGlobalApiClient({\n apiVersion: BACKUP_API_VERSION,\n requireUser: true,\n })\n\n let datasets: DatasetsResponse\n\n try {\n datasets = await listDatasets(projectId)\n } catch (error) {\n const message = error instanceof Error ? error.message : String(error)\n listBackupDebug(`Failed to list datasets: ${message}`, error)\n this.error(`Failed to list datasets: ${message}`, {exit: 1})\n }\n\n if (datasets.length === 0) {\n this.error('No datasets found in this project.', {exit: 1})\n }\n\n if (dataset) {\n assertDatasetExists(datasets, dataset)\n } else {\n dataset = await this.promptForDataset(datasets)\n }\n\n // Validate date flags\n if (flags.before || flags.after) {\n try {\n const parsedBefore = this.processDateFlag(flags.before, 'before')\n const parsedAfter = this.processDateFlag(flags.after, 'after')\n\n if (parsedAfter && parsedBefore && isAfter(parsedAfter, parsedBefore)) {\n this.error('--after date must be before --before', {exit: 1})\n }\n } catch (err) {\n this.error(`Parsing date flags: ${err instanceof Error ? err.message : err}`, {exit: 1})\n }\n }\n\n // Validate limit flag\n if (flags.limit < 1 || flags.limit > Number.MAX_SAFE_INTEGER) {\n this.error(`Parsing --limit: must be an integer between 1 and ${Number.MAX_SAFE_INTEGER}`, {\n exit: 1,\n })\n }\n\n const query: ListBackupRequestQueryParams = {\n limit: flags.limit.toString(),\n }\n\n if (flags.after) {\n query.after = flags.after\n }\n\n if (flags.before) {\n query.before = flags.before\n }\n\n try {\n const response = await client.request<ListBackupResponse>({\n query,\n uri: `/projects/${projectId}/datasets/${dataset}/backups`,\n })\n\n if (response.backups.length === 0) {\n this.log('No backups found.')\n return\n }\n\n const table = new Table({\n columns: [\n {alignment: 'left', name: 'resource', title: 'RESOURCE'},\n {alignment: 'left', name: 'createdAt', title: 'CREATED AT'},\n {alignment: 'left', name: 'backupId', title: 'BACKUP ID'},\n ],\n })\n\n for (const backup of response.backups) {\n const {createdAt, id} = backup\n table.addRow({\n backupId: id,\n createdAt: lightFormat(Date.parse(createdAt), 'yyyy-MM-dd HH:mm:ss'),\n resource: 'Dataset',\n })\n }\n\n table.printTable()\n\n listBackupDebug(\n `Successfully listed ${response.backups.length} backups for dataset ${dataset}`,\n )\n } catch (error) {\n const message = error instanceof Error ? error.message : String(error)\n listBackupDebug(`Failed to list backups for dataset ${dataset}:`, error)\n this.error(`List dataset backup failed: ${message}`, {exit: 1})\n }\n }\n\n private processDateFlag(date: string | undefined, flagName: string): Date | undefined {\n if (!date) return undefined\n const parsedDate = parse(date, 'yyyy-MM-dd', new Date())\n if (isValid(parsedDate)) {\n return parsedDate\n }\n\n throw new Error(`Invalid date format for '--${flagName}' flag. Use YYYY-MM-DD`)\n }\n\n private async promptForDataset(datasets: DatasetsResponse): Promise<string> {\n try {\n const choices = datasets.map((dataset) => ({\n name: dataset.name,\n value: dataset.name,\n }))\n\n return select({\n choices,\n message: 'Select the dataset name:',\n })\n } catch (error) {\n listBackupDebug(`Error selecting dataset`, error)\n this.error(`Failed to select dataset:\\n${error instanceof Error ? error.message : error}`, {\n exit: 1,\n })\n }\n }\n}\n"],"names":["Args","Flags","SanityCommand","subdebug","select","Table","isAfter","isValid","lightFormat","parse","assertDatasetExists","BACKUP_API_VERSION","listDatasets","NO_PROJECT_ID","listBackupDebug","DEFAULT_LIST_BACKUP_LIMIT","ListBackupCommand","args","dataset","string","description","required","examples","command","flags","after","before","limit","integer","char","default","run","projectId","getProjectId","error","exit","client","getGlobalApiClient","apiVersion","requireUser","datasets","message","Error","String","length","promptForDataset","parsedBefore","processDateFlag","parsedAfter","err","Number","MAX_SAFE_INTEGER","query","toString","response","request","uri","backups","log","table","columns","alignment","name","title","backup","createdAt","id","addRow","backupId","Date","resource","printTable","date","flagName","undefined","parsedDate","choices","map","value"],"mappings":"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"}
|
|
1
|
+
{"version":3,"sources":["../../../src/commands/backup/list.ts"],"sourcesContent":["import {Args, Flags} from '@oclif/core'\nimport {SanityCommand, subdebug} from '@sanity/cli-core'\nimport {select} from '@sanity/cli-core/ux'\nimport {type DatasetsResponse} from '@sanity/client'\nimport {Table} from 'console-table-printer'\nimport {isAfter, isValid, lightFormat, parse} from 'date-fns'\n\nimport {assertDatasetExists} from '../../actions/backup/assertDatasetExist.js'\nimport {listBackups} from '../../services/backup.js'\nimport {listDatasets} from '../../services/datasets.js'\nimport {NO_PROJECT_ID} from '../../util/errorMessages.js'\n\nconst listBackupDebug = subdebug('backup:list')\n\nconst DEFAULT_LIST_BACKUP_LIMIT = 30\n\ntype ListBackupRequestQueryParams = {\n after?: string\n before?: string\n limit: string\n}\n\nexport class ListBackupCommand extends SanityCommand<typeof ListBackupCommand> {\n static override args = {\n dataset: Args.string({\n description: 'Dataset name to list backups for',\n required: false,\n }),\n }\n\n static override description = 'List available backups for a dataset.'\n\n static override examples = [\n {\n command: '<%= config.bin %> <%= command.id %>',\n description: 'List backups for a dataset interactively',\n },\n {\n command: '<%= config.bin %> <%= command.id %> production',\n description: 'List backups for the production dataset',\n },\n {\n command: '<%= config.bin %> <%= command.id %> production --limit 50',\n description: 'List up to 50 backups for the production dataset',\n },\n {\n command: '<%= config.bin %> <%= command.id %> production --after 2024-01-31 --limit 10',\n description: 'List up to 10 backups created after 2024-01-31',\n },\n ]\n\n static override flags = {\n after: Flags.string({\n description: 'Only return backups after this date (inclusive, YYYY-MM-DD format)',\n }),\n before: Flags.string({\n description: 'Only return backups before this date (exclusive, YYYY-MM-DD format)',\n }),\n limit: Flags.integer({\n char: 'l',\n default: DEFAULT_LIST_BACKUP_LIMIT,\n description: 'Maximum number of backups returned',\n }),\n }\n\n public async run(): Promise<void> {\n const {args, flags} = await this.parse(ListBackupCommand)\n let {dataset} = args\n\n const projectId = await this.getProjectId()\n if (!projectId) {\n this.error(NO_PROJECT_ID, {exit: 1})\n }\n\n let datasets: DatasetsResponse\n\n try {\n datasets = await listDatasets(projectId)\n } catch (error) {\n const message = error instanceof Error ? error.message : String(error)\n listBackupDebug(`Failed to list datasets: ${message}`, error)\n this.error(`Failed to list datasets: ${message}`, {exit: 1})\n }\n\n if (datasets.length === 0) {\n this.error('No datasets found in this project.', {exit: 1})\n }\n\n if (dataset) {\n assertDatasetExists(datasets, dataset)\n } else {\n dataset = await this.promptForDataset(datasets)\n }\n\n // Validate date flags\n if (flags.before || flags.after) {\n try {\n const parsedBefore = this.processDateFlag(flags.before, 'before')\n const parsedAfter = this.processDateFlag(flags.after, 'after')\n\n if (parsedAfter && parsedBefore && isAfter(parsedAfter, parsedBefore)) {\n this.error('--after date must be before --before', {exit: 1})\n }\n } catch (err) {\n this.error(`Parsing date flags: ${err instanceof Error ? err.message : err}`, {exit: 1})\n }\n }\n\n // Validate limit flag\n if (flags.limit < 1 || flags.limit > Number.MAX_SAFE_INTEGER) {\n this.error(`Parsing --limit: must be an integer between 1 and ${Number.MAX_SAFE_INTEGER}`, {\n exit: 1,\n })\n }\n\n const query: ListBackupRequestQueryParams = {\n limit: flags.limit.toString(),\n }\n\n if (flags.after) {\n query.after = flags.after\n }\n\n if (flags.before) {\n query.before = flags.before\n }\n\n try {\n const response = await listBackups({\n after: flags.after,\n before: flags.before,\n datasetName: dataset,\n limit: flags.limit,\n projectId,\n })\n\n if (response.backups.length === 0) {\n this.log('No backups found.')\n return\n }\n\n const table = new Table({\n columns: [\n {alignment: 'left', name: 'resource', title: 'RESOURCE'},\n {alignment: 'left', name: 'createdAt', title: 'CREATED AT'},\n {alignment: 'left', name: 'backupId', title: 'BACKUP ID'},\n ],\n })\n\n for (const backup of response.backups) {\n const {createdAt, id} = backup\n table.addRow({\n backupId: id,\n createdAt: lightFormat(Date.parse(createdAt), 'yyyy-MM-dd HH:mm:ss'),\n resource: 'Dataset',\n })\n }\n\n table.printTable()\n\n listBackupDebug(\n `Successfully listed ${response.backups.length} backups for dataset ${dataset}`,\n )\n } catch (error) {\n const message = error instanceof Error ? error.message : String(error)\n listBackupDebug(`Failed to list backups for dataset ${dataset}:`, error)\n this.error(`List dataset backup failed: ${message}`, {exit: 1})\n }\n }\n\n private processDateFlag(date: string | undefined, flagName: string): Date | undefined {\n if (!date) return undefined\n const parsedDate = parse(date, 'yyyy-MM-dd', new Date())\n if (isValid(parsedDate)) {\n return parsedDate\n }\n\n throw new Error(`Invalid date format for '--${flagName}' flag. Use YYYY-MM-DD`)\n }\n\n private async promptForDataset(datasets: DatasetsResponse): Promise<string> {\n try {\n const choices = datasets.map((dataset) => ({\n name: dataset.name,\n value: dataset.name,\n }))\n\n return select({\n choices,\n message: 'Select the dataset name:',\n })\n } catch (error) {\n listBackupDebug(`Error selecting dataset`, error)\n this.error(`Failed to select dataset:\\n${error instanceof Error ? error.message : error}`, {\n exit: 1,\n })\n }\n }\n}\n"],"names":["Args","Flags","SanityCommand","subdebug","select","Table","isAfter","isValid","lightFormat","parse","assertDatasetExists","listBackups","listDatasets","NO_PROJECT_ID","listBackupDebug","DEFAULT_LIST_BACKUP_LIMIT","ListBackupCommand","args","dataset","string","description","required","examples","command","flags","after","before","limit","integer","char","default","run","projectId","getProjectId","error","exit","datasets","message","Error","String","length","promptForDataset","parsedBefore","processDateFlag","parsedAfter","err","Number","MAX_SAFE_INTEGER","query","toString","response","datasetName","backups","log","table","columns","alignment","name","title","backup","createdAt","id","addRow","backupId","Date","resource","printTable","date","flagName","undefined","parsedDate","choices","map","value"],"mappings":"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"}
|
|
@@ -1,10 +1,9 @@
|
|
|
1
1
|
import fs from 'node:fs';
|
|
2
|
-
import { getCliConfig } from '@sanity/cli-core';
|
|
3
2
|
import { confirm } from '@sanity/cli-core/ux';
|
|
4
3
|
import { mockApi, testCommand } from '@sanity/cli-test';
|
|
5
4
|
import nock from 'nock';
|
|
6
5
|
import { afterEach, beforeEach, describe, expect, test, vi } from 'vitest';
|
|
7
|
-
import { CORS_API_VERSION } from '../../../
|
|
6
|
+
import { CORS_API_VERSION } from '../../../services/cors.js';
|
|
8
7
|
import { NO_PROJECT_ID } from '../../../util/errorMessages.js';
|
|
9
8
|
import { Add } from '../add.js';
|
|
10
9
|
vi.mock('@sanity/cli-core/ux', async ()=>{
|
|
@@ -19,25 +18,20 @@ vi.mock('node:fs', ()=>({
|
|
|
19
18
|
existsSync: vi.fn()
|
|
20
19
|
}
|
|
21
20
|
}));
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
}));
|
|
36
|
-
vi.mock('../../../../../cli-core/src/services/getCliToken.js', ()=>({
|
|
37
|
-
getCliToken: vi.fn().mockResolvedValue('test-token')
|
|
38
|
-
}));
|
|
21
|
+
const defaultMocks = {
|
|
22
|
+
cliConfig: {
|
|
23
|
+
api: {
|
|
24
|
+
projectId: 'test-project'
|
|
25
|
+
}
|
|
26
|
+
},
|
|
27
|
+
projectRoot: {
|
|
28
|
+
directory: '/test/path',
|
|
29
|
+
path: '/test/path/sanity.config.ts',
|
|
30
|
+
type: 'studio'
|
|
31
|
+
},
|
|
32
|
+
token: 'test-token'
|
|
33
|
+
};
|
|
39
34
|
const mockConfirm = vi.mocked(confirm);
|
|
40
|
-
const mockGetCliConfig = vi.mocked(getCliConfig);
|
|
41
35
|
const mockExistsSync = vi.mocked(fs.existsSync);
|
|
42
36
|
const setupSuccessfulApiMock = ()=>{
|
|
43
37
|
return mockApi({
|
|
@@ -89,7 +83,9 @@ describe('#cors:add', ()=>{
|
|
|
89
83
|
const { stdout } = await testCommand(Add, [
|
|
90
84
|
origin,
|
|
91
85
|
'--credentials'
|
|
92
|
-
]
|
|
86
|
+
], {
|
|
87
|
+
mocks: defaultMocks
|
|
88
|
+
});
|
|
93
89
|
expect(stdout).toContain('CORS origin added successfully');
|
|
94
90
|
});
|
|
95
91
|
test('adds CORS origin with no-credentials flag', async ()=>{
|
|
@@ -117,16 +113,26 @@ describe('#cors:add', ()=>{
|
|
|
117
113
|
const { stdout } = await testCommand(Add, [
|
|
118
114
|
origin,
|
|
119
115
|
'--no-credentials'
|
|
120
|
-
]
|
|
116
|
+
], {
|
|
117
|
+
mocks: defaultMocks
|
|
118
|
+
});
|
|
121
119
|
expect(stdout).toContain('CORS origin added successfully');
|
|
122
120
|
});
|
|
123
121
|
test('fails when no project ID is available', async ()=>{
|
|
124
|
-
mockGetCliConfig.mockResolvedValueOnce({
|
|
125
|
-
api: {}
|
|
126
|
-
});
|
|
127
122
|
const { error } = await testCommand(Add, [
|
|
128
123
|
'https://example.com'
|
|
129
|
-
]
|
|
124
|
+
], {
|
|
125
|
+
mocks: {
|
|
126
|
+
cliConfig: {
|
|
127
|
+
api: {}
|
|
128
|
+
},
|
|
129
|
+
projectRoot: {
|
|
130
|
+
directory: '/test/path',
|
|
131
|
+
path: '/test/path/sanity.config.ts',
|
|
132
|
+
type: 'studio'
|
|
133
|
+
}
|
|
134
|
+
}
|
|
135
|
+
});
|
|
130
136
|
expect(error?.message).toContain(NO_PROJECT_ID);
|
|
131
137
|
});
|
|
132
138
|
const errorHandlingCases = [
|
|
@@ -156,7 +162,9 @@ describe('#cors:add', ()=>{
|
|
|
156
162
|
const { error } = await testCommand(Add, [
|
|
157
163
|
'https://example.com',
|
|
158
164
|
'--credentials'
|
|
159
|
-
]
|
|
165
|
+
], {
|
|
166
|
+
mocks: defaultMocks
|
|
167
|
+
});
|
|
160
168
|
expect(error?.message).toContain(expectedError);
|
|
161
169
|
expect(error?.oclif?.exit).toBe(1);
|
|
162
170
|
});
|
|
@@ -166,7 +174,9 @@ describe('#cors:add', ()=>{
|
|
|
166
174
|
const { stderr } = await testCommand(Add, [
|
|
167
175
|
'https://example.com',
|
|
168
176
|
'--credentials'
|
|
169
|
-
]
|
|
177
|
+
], {
|
|
178
|
+
mocks: defaultMocks
|
|
179
|
+
});
|
|
170
180
|
expect(stderr).toContain('Remember to quote values');
|
|
171
181
|
});
|
|
172
182
|
describe('wildcard origins', ()=>{
|
|
@@ -193,7 +203,9 @@ describe('#cors:add', ()=>{
|
|
|
193
203
|
setupMocks();
|
|
194
204
|
const result = await testCommand(Add, [
|
|
195
205
|
'https://*.example.com'
|
|
196
|
-
]
|
|
206
|
+
], {
|
|
207
|
+
mocks: defaultMocks
|
|
208
|
+
});
|
|
197
209
|
expect(confirm).toHaveBeenCalledWith(expect.objectContaining({
|
|
198
210
|
default: false,
|
|
199
211
|
message: expect.stringContaining('absolutely sure')
|
|
@@ -211,7 +223,9 @@ describe('#cors:add', ()=>{
|
|
|
211
223
|
setupSuccessfulApiMock();
|
|
212
224
|
const { stdout } = await testCommand(Add, [
|
|
213
225
|
'*'
|
|
214
|
-
]
|
|
226
|
+
], {
|
|
227
|
+
mocks: defaultMocks
|
|
228
|
+
});
|
|
215
229
|
expect(stdout).toContain('http://www.some-malicious.site');
|
|
216
230
|
expect(stdout).toContain('https://not.what-you-were-expecting.com');
|
|
217
231
|
});
|
|
@@ -222,7 +236,9 @@ describe('#cors:add', ()=>{
|
|
|
222
236
|
setupSuccessfulApiMock();
|
|
223
237
|
const { stdout } = await testCommand(Add, [
|
|
224
238
|
'https://example.com'
|
|
225
|
-
]
|
|
239
|
+
], {
|
|
240
|
+
mocks: defaultMocks
|
|
241
|
+
});
|
|
226
242
|
expect(confirm).toHaveBeenCalledWith(expect.objectContaining({
|
|
227
243
|
default: false,
|
|
228
244
|
message: expect.stringContaining('Allow credentials')
|
|
@@ -235,7 +251,9 @@ describe('#cors:add', ()=>{
|
|
|
235
251
|
setupSuccessfulApiMock();
|
|
236
252
|
const { stdout } = await testCommand(Add, [
|
|
237
253
|
'https://*.example.com'
|
|
238
|
-
]
|
|
254
|
+
], {
|
|
255
|
+
mocks: defaultMocks
|
|
256
|
+
});
|
|
239
257
|
expect(stdout).toContain('HIGHLY');
|
|
240
258
|
expect(stdout).toContain('recommend NOT allowing credentials');
|
|
241
259
|
expect(stdout).toContain('on origins containing wildcards');
|
|
@@ -258,7 +276,9 @@ describe('#cors:add', ()=>{
|
|
|
258
276
|
const { error, stdout } = await testCommand(Add, [
|
|
259
277
|
origin,
|
|
260
278
|
'--credentials'
|
|
261
|
-
]
|
|
279
|
+
], {
|
|
280
|
+
mocks: defaultMocks
|
|
281
|
+
});
|
|
262
282
|
expect(error).toBeUndefined();
|
|
263
283
|
expect(stdout).toContain('CORS origin added successfully');
|
|
264
284
|
});
|
|
@@ -268,7 +288,9 @@ describe('#cors:add', ()=>{
|
|
|
268
288
|
const { error, stdout } = await testCommand(Add, [
|
|
269
289
|
origin,
|
|
270
290
|
'--credentials'
|
|
271
|
-
]
|
|
291
|
+
], {
|
|
292
|
+
mocks: defaultMocks
|
|
293
|
+
});
|
|
272
294
|
expect(error).toBeUndefined();
|
|
273
295
|
expect(stdout).toContain('CORS origin added successfully');
|
|
274
296
|
});
|
|
@@ -280,7 +302,9 @@ describe('#cors:add', ()=>{
|
|
|
280
302
|
const { error } = await testCommand(Add, [
|
|
281
303
|
origin,
|
|
282
304
|
'--credentials'
|
|
283
|
-
]
|
|
305
|
+
], {
|
|
306
|
+
mocks: defaultMocks
|
|
307
|
+
});
|
|
284
308
|
expect(error).toBeDefined();
|
|
285
309
|
expect(error?.message).toContain('Invalid origin');
|
|
286
310
|
expect(error?.oclif?.exit).toBe(1);
|
|
@@ -289,7 +313,9 @@ describe('#cors:add', ()=>{
|
|
|
289
313
|
const { error } = await testCommand(Add, [
|
|
290
314
|
'file://localhost/path',
|
|
291
315
|
'--credentials'
|
|
292
|
-
]
|
|
316
|
+
], {
|
|
317
|
+
mocks: defaultMocks
|
|
318
|
+
});
|
|
293
319
|
expect(error).toBeDefined();
|
|
294
320
|
expect(error?.message).toContain('Only a local file wildcard is currently allowed: file:///*');
|
|
295
321
|
expect(error?.oclif?.exit).toBe(1);
|
|
@@ -319,7 +345,9 @@ describe('#cors:add', ()=>{
|
|
|
319
345
|
const { stdout } = await testCommand(Add, [
|
|
320
346
|
input,
|
|
321
347
|
'--credentials'
|
|
322
|
-
]
|
|
348
|
+
], {
|
|
349
|
+
mocks: defaultMocks
|
|
350
|
+
});
|
|
323
351
|
if (shouldNormalize) {
|
|
324
352
|
expect(stdout).toContain(expectedOutput);
|
|
325
353
|
} else {
|
|
@@ -337,7 +365,9 @@ describe('#cors:add', ()=>{
|
|
|
337
365
|
const { stdout } = await testCommand(Add, [
|
|
338
366
|
'https://example.com',
|
|
339
367
|
'--credentials'
|
|
340
|
-
]
|
|
368
|
+
], {
|
|
369
|
+
mocks: defaultMocks
|
|
370
|
+
});
|
|
341
371
|
expect(stdout).toContain('CORS origin added successfully');
|
|
342
372
|
});
|
|
343
373
|
});
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../../../src/commands/cors/__tests__/add.test.ts"],"sourcesContent":["import fs from 'node:fs'\n\nimport {getCliConfig} from '@sanity/cli-core'\nimport {confirm} from '@sanity/cli-core/ux'\nimport {mockApi, testCommand} from '@sanity/cli-test'\nimport nock from 'nock'\nimport {afterEach, beforeEach, describe, expect, test, vi} from 'vitest'\n\nimport {CORS_API_VERSION} from '../../../actions/cors/constants.js'\nimport {NO_PROJECT_ID} from '../../../util/errorMessages.js'\nimport {Add} from '../add.js'\n\nvi.mock('@sanity/cli-core/ux', async () => {\n const actual = await vi.importActual<typeof import('@sanity/cli-core/ux')>('@sanity/cli-core/ux')\n return {\n ...actual,\n confirm: vi.fn(),\n }\n})\n\nvi.mock('node:fs', () => ({\n default: {\n existsSync: vi.fn(),\n },\n}))\n\nvi.mock('../../../../../cli-core/src/config/findProjectRoot.js', () => ({\n findProjectRoot: vi.fn().mockResolvedValue({\n directory: '/test/path',\n root: '/test/path',\n type: 'studio',\n }),\n}))\n\nvi.mock('../../../../../cli-core/src/config/cli/getCliConfig.js', () => ({\n getCliConfig: vi.fn().mockResolvedValue({\n api: {\n projectId: 'test-project',\n },\n }),\n}))\n\nvi.mock('../../../../../cli-core/src/services/getCliToken.js', () => ({\n getCliToken: vi.fn().mockResolvedValue('test-token'),\n}))\n\nconst mockConfirm = vi.mocked(confirm)\nconst mockGetCliConfig = vi.mocked(getCliConfig)\nconst mockExistsSync = vi.mocked(fs.existsSync)\n\nconst setupSuccessfulApiMock = () => {\n return mockApi({\n apiVersion: CORS_API_VERSION,\n method: 'post',\n uri: '/projects/test-project/cors',\n }).reply(201, {\n allowCredentials: true,\n createdAt: '2023-01-01T00:00:00Z',\n deletedAt: null,\n id: 1,\n origin: 'https://example.com',\n projectId: 'test-project',\n updatedAt: null,\n })\n}\n\ndescribe('#cors:add', () => {\n beforeEach(() => {\n mockExistsSync.mockReturnValue(false)\n })\n\n afterEach(() => {\n vi.clearAllMocks()\n const pending = nock.pendingMocks()\n nock.cleanAll()\n expect(pending, 'pending mocks').toEqual([])\n })\n\n test('adds CORS origin with credentials flag', async () => {\n const origin = 'https://example.com'\n const expectedAllowCredentials = true\n\n mockApi({\n apiVersion: CORS_API_VERSION,\n method: 'post',\n uri: '/projects/test-project/cors',\n }).reply(201, function (_, requestBody) {\n expect(requestBody).toEqual({\n allowCredentials: expectedAllowCredentials,\n origin: origin,\n })\n\n return {\n allowCredentials: expectedAllowCredentials,\n createdAt: '2023-01-01T00:00:00Z',\n deletedAt: null,\n id: 1,\n origin: origin,\n projectId: 'test-project',\n updatedAt: null,\n }\n })\n\n const {stdout} = await testCommand(Add, [origin, '--credentials'])\n\n expect(stdout).toContain('CORS origin added successfully')\n })\n\n test('adds CORS origin with no-credentials flag', async () => {\n const origin = 'http://localhost:3000'\n const expectedAllowCredentials = false\n\n mockApi({\n apiVersion: CORS_API_VERSION,\n method: 'post',\n uri: '/projects/test-project/cors',\n }).reply(201, function (_, requestBody) {\n expect(requestBody).toEqual({\n allowCredentials: expectedAllowCredentials,\n origin: origin,\n })\n\n return {\n allowCredentials: expectedAllowCredentials,\n createdAt: '2023-01-01T00:00:00Z',\n deletedAt: null,\n id: 2,\n origin: origin,\n projectId: 'test-project',\n updatedAt: null,\n }\n })\n\n const {stdout} = await testCommand(Add, [origin, '--no-credentials'])\n\n expect(stdout).toContain('CORS origin added successfully')\n })\n\n test('fails when no project ID is available', async () => {\n mockGetCliConfig.mockResolvedValueOnce({\n api: {},\n })\n\n const {error} = await testCommand(Add, ['https://example.com'])\n\n expect(error?.message).toContain(NO_PROJECT_ID)\n })\n\n const errorHandlingCases = [\n {\n description: 'handles API errors gracefully',\n expectedError: 'CORS origin addition failed',\n setupMock: () =>\n mockApi({\n apiVersion: CORS_API_VERSION,\n method: 'post',\n uri: '/projects/test-project/cors',\n }).reply(400, {message: 'Invalid origin'}),\n },\n {\n description: 'handles network errors during API call',\n expectedError: 'CORS origin addition failed',\n setupMock: () =>\n mockApi({\n apiVersion: CORS_API_VERSION,\n method: 'post',\n uri: '/projects/test-project/cors',\n }).replyWithError(new Error('Network Error')),\n },\n ]\n\n test.each(errorHandlingCases)('$description', async ({expectedError, setupMock}) => {\n setupMock()\n\n const {error} = await testCommand(Add, ['https://example.com', '--credentials'])\n\n expect(error?.message).toContain(expectedError)\n expect(error?.oclif?.exit).toBe(1)\n })\n\n test('warns when origin looks like a file path', async () => {\n mockExistsSync.mockReturnValue(true)\n setupSuccessfulApiMock()\n\n const {stderr} = await testCommand(Add, ['https://example.com', '--credentials'])\n\n expect(stderr).toContain('Remember to quote values')\n })\n\n describe('wildcard origins', () => {\n const wildcardConfirmationCases = [\n {\n confirmWildcard: true,\n description: 'prompts for confirmation with wildcard origins and proceeds',\n expectedError: undefined,\n expectedOutput: 'CORS origin added successfully',\n setupMocks: () => {\n mockConfirm.mockResolvedValueOnce(true).mockResolvedValueOnce(false)\n setupSuccessfulApiMock()\n },\n },\n {\n confirmWildcard: false,\n description: 'cancels operation when wildcard confirmation is denied',\n expectedError: 'Operation cancelled',\n expectedOutput: undefined,\n setupMocks: () => mockConfirm.mockResolvedValueOnce(false),\n },\n ]\n\n test.each(wildcardConfirmationCases)(\n '$description',\n async ({expectedError, expectedOutput, setupMocks}) => {\n setupMocks()\n\n const result = await testCommand(Add, ['https://*.example.com'])\n\n expect(confirm).toHaveBeenCalledWith(\n expect.objectContaining({\n default: false,\n message: expect.stringContaining('absolutely sure'),\n }),\n )\n\n if (expectedOutput) {\n expect(result.stdout).toContain(expectedOutput)\n }\n if (expectedError) {\n expect(result.error?.message).toContain(expectedError)\n expect(result.error?.oclif?.exit).toBe(1)\n }\n },\n )\n\n test('shows specific examples for full wildcard', async () => {\n mockConfirm.mockResolvedValueOnce(true).mockResolvedValueOnce(false)\n setupSuccessfulApiMock()\n\n const {stdout} = await testCommand(Add, ['*'])\n\n expect(stdout).toContain('http://www.some-malicious.site')\n expect(stdout).toContain('https://not.what-you-were-expecting.com')\n })\n })\n\n describe('credentials prompts', () => {\n test('prompts for credentials when flag not provided', async () => {\n mockConfirm.mockResolvedValueOnce(true)\n setupSuccessfulApiMock()\n\n const {stdout} = await testCommand(Add, ['https://example.com'])\n\n expect(confirm).toHaveBeenCalledWith(\n expect.objectContaining({\n default: false,\n message: expect.stringContaining('Allow credentials'),\n }),\n )\n expect(stdout).toContain('CORS origin added successfully')\n })\n\n test('shows warning about wildcard credentials', async () => {\n mockConfirm\n .mockResolvedValueOnce(true) // Confirm wildcard\n .mockResolvedValueOnce(false) // Deny credentials\n setupSuccessfulApiMock()\n\n const {stdout} = await testCommand(Add, ['https://*.example.com'])\n\n expect(stdout).toContain('HIGHLY')\n expect(stdout).toContain('recommend NOT allowing credentials')\n expect(stdout).toContain('on origins containing wildcards')\n })\n })\n\n describe('origin validation and filtering', () => {\n const validNonWildcardOrigins = [\n 'https://example.com',\n 'http://localhost:3000',\n 'https://sub.example.com:8080',\n 'null',\n ]\n\n const validWildcardOrigins = ['https://*.example.com', '*', 'file:///*']\n\n test.each(validNonWildcardOrigins)('accepts valid non-wildcard origin: %s', async (origin) => {\n setupSuccessfulApiMock()\n\n const {error, stdout} = await testCommand(Add, [origin, '--credentials'])\n\n expect(error).toBeUndefined()\n expect(stdout).toContain('CORS origin added successfully')\n })\n\n test.each(validWildcardOrigins)('accepts valid wildcard origin: %s', async (origin) => {\n mockConfirm.mockResolvedValueOnce(true)\n setupSuccessfulApiMock()\n\n const {error, stdout} = await testCommand(Add, [origin, '--credentials'])\n\n expect(error).toBeUndefined()\n expect(stdout).toContain('CORS origin added successfully')\n })\n\n const invalidOrigins = [\n 'not-a-url',\n 'example.com', // missing protocol\n ]\n\n test.each(invalidOrigins)('rejects invalid origin: %s', async (origin) => {\n const {error} = await testCommand(Add, [origin, '--credentials'])\n\n expect(error).toBeDefined()\n expect(error?.message).toContain('Invalid origin')\n expect(error?.oclif?.exit).toBe(1)\n })\n\n test('rejects file:// origins other than file:///*', async () => {\n const {error} = await testCommand(Add, ['file://localhost/path', '--credentials'])\n\n expect(error).toBeDefined()\n expect(error?.message).toContain('Only a local file wildcard is currently allowed: file:///*')\n expect(error?.oclif?.exit).toBe(1)\n })\n\n const portNormalizationCases = [\n {\n description: 'normalizes HTTPS default port',\n expectedOutput: 'Normalized origin to: https://example.com',\n input: 'https://example.com:443',\n shouldNormalize: true,\n },\n {\n description: 'normalizes HTTP default port',\n expectedOutput: 'Normalized origin to: http://example.com',\n input: 'http://example.com:80',\n shouldNormalize: true,\n },\n {\n description: 'preserves non-default ports',\n expectedOutput: 'CORS origin added successfully',\n input: 'https://example.com:8080',\n shouldNormalize: false,\n },\n ]\n\n test.each(portNormalizationCases)(\n '$description',\n async ({expectedOutput, input, shouldNormalize}) => {\n setupSuccessfulApiMock()\n\n const {stdout} = await testCommand(Add, [input, '--credentials'])\n\n if (shouldNormalize) {\n expect(stdout).toContain(expectedOutput)\n } else {\n expect(stdout).not.toContain('Normalized origin')\n expect(stdout).toContain(expectedOutput)\n }\n },\n )\n })\n\n describe('edge cases', () => {\n test('handles file system check errors gracefully', async () => {\n mockExistsSync.mockImplementation(() => {\n throw new Error('Permission denied')\n })\n setupSuccessfulApiMock()\n\n const {stdout} = await testCommand(Add, ['https://example.com', '--credentials'])\n\n expect(stdout).toContain('CORS origin added successfully')\n })\n })\n})\n"],"names":["fs","getCliConfig","confirm","mockApi","testCommand","nock","afterEach","beforeEach","describe","expect","test","vi","CORS_API_VERSION","NO_PROJECT_ID","Add","mock","actual","importActual","fn","default","existsSync","findProjectRoot","mockResolvedValue","directory","root","type","api","projectId","getCliToken","mockConfirm","mocked","mockGetCliConfig","mockExistsSync","setupSuccessfulApiMock","apiVersion","method","uri","reply","allowCredentials","createdAt","deletedAt","id","origin","updatedAt","mockReturnValue","clearAllMocks","pending","pendingMocks","cleanAll","toEqual","expectedAllowCredentials","_","requestBody","stdout","toContain","mockResolvedValueOnce","error","message","errorHandlingCases","description","expectedError","setupMock","replyWithError","Error","each","oclif","exit","toBe","stderr","wildcardConfirmationCases","confirmWildcard","undefined","expectedOutput","setupMocks","result","toHaveBeenCalledWith","objectContaining","stringContaining","validNonWildcardOrigins","validWildcardOrigins","toBeUndefined","invalidOrigins","toBeDefined","portNormalizationCases","input","shouldNormalize","not","mockImplementation"],"mappings":"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"}
|
|
1
|
+
{"version":3,"sources":["../../../../src/commands/cors/__tests__/add.test.ts"],"sourcesContent":["import fs from 'node:fs'\n\nimport {confirm} from '@sanity/cli-core/ux'\nimport {mockApi, testCommand} from '@sanity/cli-test'\nimport nock from 'nock'\nimport {afterEach, beforeEach, describe, expect, test, vi} from 'vitest'\n\nimport {CORS_API_VERSION} from '../../../services/cors.js'\nimport {NO_PROJECT_ID} from '../../../util/errorMessages.js'\nimport {Add} from '../add.js'\n\nvi.mock('@sanity/cli-core/ux', async () => {\n const actual = await vi.importActual<typeof import('@sanity/cli-core/ux')>('@sanity/cli-core/ux')\n return {\n ...actual,\n confirm: vi.fn(),\n }\n})\n\nvi.mock('node:fs', () => ({\n default: {\n existsSync: vi.fn(),\n },\n}))\n\nconst defaultMocks = {\n cliConfig: {api: {projectId: 'test-project'}},\n projectRoot: {\n directory: '/test/path',\n path: '/test/path/sanity.config.ts',\n type: 'studio' as const,\n },\n token: 'test-token',\n}\n\nconst mockConfirm = vi.mocked(confirm)\nconst mockExistsSync = vi.mocked(fs.existsSync)\n\nconst setupSuccessfulApiMock = () => {\n return mockApi({\n apiVersion: CORS_API_VERSION,\n method: 'post',\n uri: '/projects/test-project/cors',\n }).reply(201, {\n allowCredentials: true,\n createdAt: '2023-01-01T00:00:00Z',\n deletedAt: null,\n id: 1,\n origin: 'https://example.com',\n projectId: 'test-project',\n updatedAt: null,\n })\n}\n\ndescribe('#cors:add', () => {\n beforeEach(() => {\n mockExistsSync.mockReturnValue(false)\n })\n\n afterEach(() => {\n vi.clearAllMocks()\n const pending = nock.pendingMocks()\n nock.cleanAll()\n expect(pending, 'pending mocks').toEqual([])\n })\n\n test('adds CORS origin with credentials flag', async () => {\n const origin = 'https://example.com'\n const expectedAllowCredentials = true\n\n mockApi({\n apiVersion: CORS_API_VERSION,\n method: 'post',\n uri: '/projects/test-project/cors',\n }).reply(201, function (_, requestBody) {\n expect(requestBody).toEqual({\n allowCredentials: expectedAllowCredentials,\n origin: origin,\n })\n\n return {\n allowCredentials: expectedAllowCredentials,\n createdAt: '2023-01-01T00:00:00Z',\n deletedAt: null,\n id: 1,\n origin: origin,\n projectId: 'test-project',\n updatedAt: null,\n }\n })\n\n const {stdout} = await testCommand(Add, [origin, '--credentials'], {mocks: defaultMocks})\n\n expect(stdout).toContain('CORS origin added successfully')\n })\n\n test('adds CORS origin with no-credentials flag', async () => {\n const origin = 'http://localhost:3000'\n const expectedAllowCredentials = false\n\n mockApi({\n apiVersion: CORS_API_VERSION,\n method: 'post',\n uri: '/projects/test-project/cors',\n }).reply(201, function (_, requestBody) {\n expect(requestBody).toEqual({\n allowCredentials: expectedAllowCredentials,\n origin: origin,\n })\n\n return {\n allowCredentials: expectedAllowCredentials,\n createdAt: '2023-01-01T00:00:00Z',\n deletedAt: null,\n id: 2,\n origin: origin,\n projectId: 'test-project',\n updatedAt: null,\n }\n })\n\n const {stdout} = await testCommand(Add, [origin, '--no-credentials'], {mocks: defaultMocks})\n\n expect(stdout).toContain('CORS origin added successfully')\n })\n\n test('fails when no project ID is available', async () => {\n const {error} = await testCommand(Add, ['https://example.com'], {\n mocks: {\n cliConfig: {api: {}},\n projectRoot: {\n directory: '/test/path',\n path: '/test/path/sanity.config.ts',\n type: 'studio' as const,\n },\n },\n })\n\n expect(error?.message).toContain(NO_PROJECT_ID)\n })\n\n const errorHandlingCases = [\n {\n description: 'handles API errors gracefully',\n expectedError: 'CORS origin addition failed',\n setupMock: () =>\n mockApi({\n apiVersion: CORS_API_VERSION,\n method: 'post',\n uri: '/projects/test-project/cors',\n }).reply(400, {message: 'Invalid origin'}),\n },\n {\n description: 'handles network errors during API call',\n expectedError: 'CORS origin addition failed',\n setupMock: () =>\n mockApi({\n apiVersion: CORS_API_VERSION,\n method: 'post',\n uri: '/projects/test-project/cors',\n }).replyWithError(new Error('Network Error')),\n },\n ]\n\n test.each(errorHandlingCases)('$description', async ({expectedError, setupMock}) => {\n setupMock()\n\n const {error} = await testCommand(Add, ['https://example.com', '--credentials'], {\n mocks: defaultMocks,\n })\n\n expect(error?.message).toContain(expectedError)\n expect(error?.oclif?.exit).toBe(1)\n })\n\n test('warns when origin looks like a file path', async () => {\n mockExistsSync.mockReturnValue(true)\n setupSuccessfulApiMock()\n\n const {stderr} = await testCommand(Add, ['https://example.com', '--credentials'], {\n mocks: defaultMocks,\n })\n\n expect(stderr).toContain('Remember to quote values')\n })\n\n describe('wildcard origins', () => {\n const wildcardConfirmationCases = [\n {\n confirmWildcard: true,\n description: 'prompts for confirmation with wildcard origins and proceeds',\n expectedError: undefined,\n expectedOutput: 'CORS origin added successfully',\n setupMocks: () => {\n mockConfirm.mockResolvedValueOnce(true).mockResolvedValueOnce(false)\n setupSuccessfulApiMock()\n },\n },\n {\n confirmWildcard: false,\n description: 'cancels operation when wildcard confirmation is denied',\n expectedError: 'Operation cancelled',\n expectedOutput: undefined,\n setupMocks: () => mockConfirm.mockResolvedValueOnce(false),\n },\n ]\n\n test.each(wildcardConfirmationCases)(\n '$description',\n async ({expectedError, expectedOutput, setupMocks}) => {\n setupMocks()\n\n const result = await testCommand(Add, ['https://*.example.com'], {mocks: defaultMocks})\n\n expect(confirm).toHaveBeenCalledWith(\n expect.objectContaining({\n default: false,\n message: expect.stringContaining('absolutely sure'),\n }),\n )\n\n if (expectedOutput) {\n expect(result.stdout).toContain(expectedOutput)\n }\n if (expectedError) {\n expect(result.error?.message).toContain(expectedError)\n expect(result.error?.oclif?.exit).toBe(1)\n }\n },\n )\n\n test('shows specific examples for full wildcard', async () => {\n mockConfirm.mockResolvedValueOnce(true).mockResolvedValueOnce(false)\n setupSuccessfulApiMock()\n\n const {stdout} = await testCommand(Add, ['*'], {mocks: defaultMocks})\n\n expect(stdout).toContain('http://www.some-malicious.site')\n expect(stdout).toContain('https://not.what-you-were-expecting.com')\n })\n })\n\n describe('credentials prompts', () => {\n test('prompts for credentials when flag not provided', async () => {\n mockConfirm.mockResolvedValueOnce(true)\n setupSuccessfulApiMock()\n\n const {stdout} = await testCommand(Add, ['https://example.com'], {mocks: defaultMocks})\n\n expect(confirm).toHaveBeenCalledWith(\n expect.objectContaining({\n default: false,\n message: expect.stringContaining('Allow credentials'),\n }),\n )\n expect(stdout).toContain('CORS origin added successfully')\n })\n\n test('shows warning about wildcard credentials', async () => {\n mockConfirm\n .mockResolvedValueOnce(true) // Confirm wildcard\n .mockResolvedValueOnce(false) // Deny credentials\n setupSuccessfulApiMock()\n\n const {stdout} = await testCommand(Add, ['https://*.example.com'], {mocks: defaultMocks})\n\n expect(stdout).toContain('HIGHLY')\n expect(stdout).toContain('recommend NOT allowing credentials')\n expect(stdout).toContain('on origins containing wildcards')\n })\n })\n\n describe('origin validation and filtering', () => {\n const validNonWildcardOrigins = [\n 'https://example.com',\n 'http://localhost:3000',\n 'https://sub.example.com:8080',\n 'null',\n ]\n\n const validWildcardOrigins = ['https://*.example.com', '*', 'file:///*']\n\n test.each(validNonWildcardOrigins)('accepts valid non-wildcard origin: %s', async (origin) => {\n setupSuccessfulApiMock()\n\n const {error, stdout} = await testCommand(Add, [origin, '--credentials'], {\n mocks: defaultMocks,\n })\n\n expect(error).toBeUndefined()\n expect(stdout).toContain('CORS origin added successfully')\n })\n\n test.each(validWildcardOrigins)('accepts valid wildcard origin: %s', async (origin) => {\n mockConfirm.mockResolvedValueOnce(true)\n setupSuccessfulApiMock()\n\n const {error, stdout} = await testCommand(Add, [origin, '--credentials'], {\n mocks: defaultMocks,\n })\n\n expect(error).toBeUndefined()\n expect(stdout).toContain('CORS origin added successfully')\n })\n\n const invalidOrigins = [\n 'not-a-url',\n 'example.com', // missing protocol\n ]\n\n test.each(invalidOrigins)('rejects invalid origin: %s', async (origin) => {\n const {error} = await testCommand(Add, [origin, '--credentials'], {mocks: defaultMocks})\n\n expect(error).toBeDefined()\n expect(error?.message).toContain('Invalid origin')\n expect(error?.oclif?.exit).toBe(1)\n })\n\n test('rejects file:// origins other than file:///*', async () => {\n const {error} = await testCommand(Add, ['file://localhost/path', '--credentials'], {\n mocks: defaultMocks,\n })\n\n expect(error).toBeDefined()\n expect(error?.message).toContain('Only a local file wildcard is currently allowed: file:///*')\n expect(error?.oclif?.exit).toBe(1)\n })\n\n const portNormalizationCases = [\n {\n description: 'normalizes HTTPS default port',\n expectedOutput: 'Normalized origin to: https://example.com',\n input: 'https://example.com:443',\n shouldNormalize: true,\n },\n {\n description: 'normalizes HTTP default port',\n expectedOutput: 'Normalized origin to: http://example.com',\n input: 'http://example.com:80',\n shouldNormalize: true,\n },\n {\n description: 'preserves non-default ports',\n expectedOutput: 'CORS origin added successfully',\n input: 'https://example.com:8080',\n shouldNormalize: false,\n },\n ]\n\n test.each(portNormalizationCases)(\n '$description',\n async ({expectedOutput, input, shouldNormalize}) => {\n setupSuccessfulApiMock()\n\n const {stdout} = await testCommand(Add, [input, '--credentials'], {mocks: defaultMocks})\n\n if (shouldNormalize) {\n expect(stdout).toContain(expectedOutput)\n } else {\n expect(stdout).not.toContain('Normalized origin')\n expect(stdout).toContain(expectedOutput)\n }\n },\n )\n })\n\n describe('edge cases', () => {\n test('handles file system check errors gracefully', async () => {\n mockExistsSync.mockImplementation(() => {\n throw new Error('Permission denied')\n })\n setupSuccessfulApiMock()\n\n const {stdout} = await testCommand(Add, ['https://example.com', '--credentials'], {\n mocks: defaultMocks,\n })\n\n expect(stdout).toContain('CORS origin added successfully')\n })\n })\n})\n"],"names":["fs","confirm","mockApi","testCommand","nock","afterEach","beforeEach","describe","expect","test","vi","CORS_API_VERSION","NO_PROJECT_ID","Add","mock","actual","importActual","fn","default","existsSync","defaultMocks","cliConfig","api","projectId","projectRoot","directory","path","type","token","mockConfirm","mocked","mockExistsSync","setupSuccessfulApiMock","apiVersion","method","uri","reply","allowCredentials","createdAt","deletedAt","id","origin","updatedAt","mockReturnValue","clearAllMocks","pending","pendingMocks","cleanAll","toEqual","expectedAllowCredentials","_","requestBody","stdout","mocks","toContain","error","message","errorHandlingCases","description","expectedError","setupMock","replyWithError","Error","each","oclif","exit","toBe","stderr","wildcardConfirmationCases","confirmWildcard","undefined","expectedOutput","setupMocks","mockResolvedValueOnce","result","toHaveBeenCalledWith","objectContaining","stringContaining","validNonWildcardOrigins","validWildcardOrigins","toBeUndefined","invalidOrigins","toBeDefined","portNormalizationCases","input","shouldNormalize","not","mockImplementation"],"mappings":"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"}
|
|
@@ -2,10 +2,9 @@ import { runCommand } from '@oclif/test';
|
|
|
2
2
|
import { mockApi, testCommand } from '@sanity/cli-test';
|
|
3
3
|
import nock from 'nock';
|
|
4
4
|
import { afterEach, describe, expect, test, vi } from 'vitest';
|
|
5
|
-
import { CORS_API_VERSION } from '../../../
|
|
5
|
+
import { CORS_API_VERSION } from '../../../services/cors.js';
|
|
6
6
|
import { NO_PROJECT_ID } from '../../../util/errorMessages.js';
|
|
7
7
|
import { Delete } from '../delete.js';
|
|
8
|
-
// Test fixtures
|
|
9
8
|
const createCorsOrigin = (overrides)=>({
|
|
10
9
|
allowCredentials: true,
|
|
11
10
|
createdAt: '2023-01-01T00:00:00Z',
|
|
@@ -37,25 +36,20 @@ const TEST_ORIGINS = {
|
|
|
37
36
|
origin: 'https://café.example.com'
|
|
38
37
|
})
|
|
39
38
|
};
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
}));
|
|
55
|
-
vi.mock('../../../../../cli-core/src/services/getCliToken.js', ()=>({
|
|
56
|
-
getCliToken: vi.fn().mockResolvedValue('test-token')
|
|
57
|
-
}));
|
|
58
|
-
// Mock inquirer prompts
|
|
39
|
+
const testProjectId = 'test-project';
|
|
40
|
+
const defaultMocks = {
|
|
41
|
+
cliConfig: {
|
|
42
|
+
api: {
|
|
43
|
+
projectId: testProjectId
|
|
44
|
+
}
|
|
45
|
+
},
|
|
46
|
+
projectRoot: {
|
|
47
|
+
directory: '/test/path',
|
|
48
|
+
path: '/test/path/sanity.config.ts',
|
|
49
|
+
type: 'studio'
|
|
50
|
+
},
|
|
51
|
+
token: 'test-token'
|
|
52
|
+
};
|
|
59
53
|
vi.mock('@sanity/cli-core/ux', async ()=>{
|
|
60
54
|
const actual = await vi.importActual('@sanity/cli-core/ux');
|
|
61
55
|
return {
|
|
@@ -92,7 +86,9 @@ describe('#cors:delete', ()=>{
|
|
|
92
86
|
}).reply(204);
|
|
93
87
|
const { stdout } = await testCommand(Delete, [
|
|
94
88
|
'https://example.com'
|
|
95
|
-
]
|
|
89
|
+
], {
|
|
90
|
+
mocks: defaultMocks
|
|
91
|
+
});
|
|
96
92
|
expect(stdout).toBe('Origin deleted\n');
|
|
97
93
|
});
|
|
98
94
|
test('prompts user to select origin when none specified', async ()=>{
|
|
@@ -110,7 +106,9 @@ describe('#cors:delete', ()=>{
|
|
|
110
106
|
method: 'delete',
|
|
111
107
|
uri: '/projects/test-project/cors/2'
|
|
112
108
|
}).reply(204);
|
|
113
|
-
const { stdout } = await testCommand(Delete
|
|
109
|
+
const { stdout } = await testCommand(Delete, [], {
|
|
110
|
+
mocks: defaultMocks
|
|
111
|
+
});
|
|
114
112
|
expect(stdout).toBe('Origin deleted\n');
|
|
115
113
|
expect(select).toHaveBeenCalledWith({
|
|
116
114
|
choices: [
|
|
@@ -140,7 +138,9 @@ describe('#cors:delete', ()=>{
|
|
|
140
138
|
}).reply(204);
|
|
141
139
|
const { stdout } = await testCommand(Delete, [
|
|
142
140
|
'https://example.com'
|
|
143
|
-
]
|
|
141
|
+
], {
|
|
142
|
+
mocks: defaultMocks
|
|
143
|
+
});
|
|
144
144
|
expect(stdout).toBe('Origin deleted\n');
|
|
145
145
|
});
|
|
146
146
|
test('throws error when specified origin is not found', async ()=>{
|
|
@@ -152,7 +152,9 @@ describe('#cors:delete', ()=>{
|
|
|
152
152
|
]);
|
|
153
153
|
const { error } = await testCommand(Delete, [
|
|
154
154
|
'https://nonexistent.com'
|
|
155
|
-
]
|
|
155
|
+
], {
|
|
156
|
+
mocks: defaultMocks
|
|
157
|
+
});
|
|
156
158
|
expect(error).toBeInstanceOf(Error);
|
|
157
159
|
expect(error?.message).toEqual('Origin "https://nonexistent.com" not found');
|
|
158
160
|
expect(error?.oclif?.exit).toBe(1);
|
|
@@ -164,7 +166,9 @@ describe('#cors:delete', ()=>{
|
|
|
164
166
|
}).reply(200, []);
|
|
165
167
|
const { error } = await testCommand(Delete, [
|
|
166
168
|
'https://example.com'
|
|
167
|
-
]
|
|
169
|
+
], {
|
|
170
|
+
mocks: defaultMocks
|
|
171
|
+
});
|
|
168
172
|
expect(error).toBeInstanceOf(Error);
|
|
169
173
|
expect(error?.message).toEqual('No CORS origins configured for this project.');
|
|
170
174
|
expect(error?.oclif?.exit).toBe(1);
|
|
@@ -189,7 +193,9 @@ describe('#cors:delete', ()=>{
|
|
|
189
193
|
});
|
|
190
194
|
const { error } = await testCommand(Delete, [
|
|
191
195
|
'https://example.com'
|
|
192
|
-
]
|
|
196
|
+
], {
|
|
197
|
+
mocks: defaultMocks
|
|
198
|
+
});
|
|
193
199
|
expect(error).toBeInstanceOf(Error);
|
|
194
200
|
expect(error?.message).toContain('Failed to fetch CORS origins');
|
|
195
201
|
expect(error?.message).toContain(message);
|
|
@@ -222,7 +228,9 @@ describe('#cors:delete', ()=>{
|
|
|
222
228
|
});
|
|
223
229
|
const { error } = await testCommand(Delete, [
|
|
224
230
|
'https://example.com'
|
|
225
|
-
]
|
|
231
|
+
], {
|
|
232
|
+
mocks: defaultMocks
|
|
233
|
+
});
|
|
226
234
|
expect(error).toBeInstanceOf(Error);
|
|
227
235
|
expect(error?.message).toContain('Origin deletion failed');
|
|
228
236
|
expect(error?.message).toContain(message);
|
|
@@ -238,15 +246,18 @@ describe('#cors:delete', ()=>{
|
|
|
238
246
|
projectId: ''
|
|
239
247
|
}
|
|
240
248
|
])('throws error when $desc', async ({ projectId })=>{
|
|
241
|
-
const { getCliConfig } = await import('../../../../../cli-core/src/config/cli/getCliConfig.js');
|
|
242
|
-
vi.mocked(getCliConfig).mockResolvedValueOnce({
|
|
243
|
-
api: {
|
|
244
|
-
projectId
|
|
245
|
-
}
|
|
246
|
-
});
|
|
247
249
|
const { error } = await testCommand(Delete, [
|
|
248
250
|
'https://example.com'
|
|
249
|
-
]
|
|
251
|
+
], {
|
|
252
|
+
mocks: {
|
|
253
|
+
...defaultMocks,
|
|
254
|
+
cliConfig: {
|
|
255
|
+
api: {
|
|
256
|
+
projectId
|
|
257
|
+
}
|
|
258
|
+
}
|
|
259
|
+
}
|
|
260
|
+
});
|
|
250
261
|
expect(error).toBeInstanceOf(Error);
|
|
251
262
|
expect(error?.message).toEqual(NO_PROJECT_ID);
|
|
252
263
|
expect(error?.oclif?.exit).toBe(1);
|
|
@@ -255,7 +266,9 @@ describe('#cors:delete', ()=>{
|
|
|
255
266
|
// Don't set up any mock to simulate network failure
|
|
256
267
|
const { error } = await testCommand(Delete, [
|
|
257
268
|
'https://example.com'
|
|
258
|
-
]
|
|
269
|
+
], {
|
|
270
|
+
mocks: defaultMocks
|
|
271
|
+
});
|
|
259
272
|
expect(error).toBeInstanceOf(Error);
|
|
260
273
|
expect(error?.message).toContain('Failed to fetch CORS origins');
|
|
261
274
|
expect(error?.oclif?.exit).toBe(1);
|
|
@@ -285,7 +298,9 @@ describe('#cors:delete', ()=>{
|
|
|
285
298
|
}).reply(204);
|
|
286
299
|
const { stdout } = await testCommand(Delete, [
|
|
287
300
|
input
|
|
288
|
-
]
|
|
301
|
+
], {
|
|
302
|
+
mocks: defaultMocks
|
|
303
|
+
});
|
|
289
304
|
expect(stdout).toBe('Origin deleted\n');
|
|
290
305
|
});
|
|
291
306
|
});
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../../../src/commands/cors/__tests__/delete.test.ts"],"sourcesContent":["import {runCommand} from '@oclif/test'\nimport {mockApi, testCommand} from '@sanity/cli-test'\nimport nock from 'nock'\nimport {afterEach, describe, expect, test, vi} from 'vitest'\n\nimport {CORS_API_VERSION} from '../../../actions/cors/constants.js'\nimport {type CorsOrigin} from '../../../actions/cors/types.js'\nimport {NO_PROJECT_ID} from '../../../util/errorMessages.js'\nimport {Delete} from '../delete.js'\n\n// Test fixtures\nconst createCorsOrigin = (\n overrides: Partial<CorsOrigin> & {id: number; origin: string},\n): CorsOrigin => ({\n allowCredentials: true,\n createdAt: '2023-01-01T00:00:00Z',\n deletedAt: null,\n projectId: 'test-project',\n updatedAt: null,\n ...overrides,\n})\n\nconst TEST_ORIGINS = {\n APP_EXAMPLE: createCorsOrigin({\n allowCredentials: false,\n id: 2,\n origin: 'https://app.example.com',\n }),\n CASE_MIXED: createCorsOrigin({id: 1, origin: 'https://Example.Com'}),\n EXAMPLE: createCorsOrigin({id: 1, origin: 'https://example.com'}),\n LOCALHOST: createCorsOrigin({id: 1, origin: 'http://localhost:3000'}),\n SPECIAL_CHARS: createCorsOrigin({id: 1, origin: 'https://café.example.com'}),\n} as const\n\n// Mock the config functions with relative paths\nvi.mock('../../../../../cli-core/src/config/findProjectRoot.js', () => ({\n findProjectRoot: vi.fn().mockResolvedValue({\n directory: '/test/path',\n root: '/test/path',\n type: 'studio',\n }),\n}))\n\nvi.mock('../../../../../cli-core/src/config/cli/getCliConfig.js', () => ({\n getCliConfig: vi.fn().mockResolvedValue({\n api: {\n projectId: 'test-project',\n },\n }),\n}))\n\nvi.mock('../../../../../cli-core/src/services/getCliToken.js', () => ({\n getCliToken: vi.fn().mockResolvedValue('test-token'),\n}))\n\n// Mock inquirer prompts\nvi.mock('@sanity/cli-core/ux', async () => {\n const actual = await vi.importActual<typeof import('@sanity/cli-core/ux')>('@sanity/cli-core/ux')\n return {\n ...actual,\n select: vi.fn(),\n }\n})\n\ndescribe('#cors:delete', () => {\n afterEach(() => {\n vi.clearAllMocks()\n const pending = nock.pendingMocks()\n nock.cleanAll()\n expect(pending, 'pending mocks').toEqual([])\n })\n\n test('--help works', async () => {\n const {stdout} = await runCommand(['cors delete', '--help'])\n expect(stdout).toContain('Delete an existing CORS origin from your project')\n })\n\n test('deletes a specific CORS origin', async () => {\n mockApi({\n apiVersion: CORS_API_VERSION,\n uri: '/projects/test-project/cors',\n }).reply(200, [TEST_ORIGINS.EXAMPLE, TEST_ORIGINS.APP_EXAMPLE])\n\n mockApi({\n apiVersion: CORS_API_VERSION,\n method: 'delete',\n uri: '/projects/test-project/cors/1',\n }).reply(204)\n\n const {stdout} = await testCommand(Delete, ['https://example.com'])\n expect(stdout).toBe('Origin deleted\\n')\n })\n\n test('prompts user to select origin when none specified', async () => {\n const {select} = await import('@sanity/cli-core/ux')\n vi.mocked(select).mockResolvedValue(2)\n\n mockApi({\n apiVersion: CORS_API_VERSION,\n uri: '/projects/test-project/cors',\n }).reply(200, [TEST_ORIGINS.EXAMPLE, TEST_ORIGINS.APP_EXAMPLE])\n\n mockApi({\n apiVersion: CORS_API_VERSION,\n method: 'delete',\n uri: '/projects/test-project/cors/2',\n }).reply(204)\n\n const {stdout} = await testCommand(Delete)\n expect(stdout).toBe('Origin deleted\\n')\n expect(select).toHaveBeenCalledWith({\n choices: [\n {name: 'https://example.com', value: 1},\n {name: 'https://app.example.com', value: 2},\n ],\n message: 'Select origin to delete',\n })\n })\n\n test('handles case-insensitive origin matching', async () => {\n mockApi({\n apiVersion: CORS_API_VERSION,\n uri: '/projects/test-project/cors',\n }).reply(200, [TEST_ORIGINS.CASE_MIXED])\n\n mockApi({\n apiVersion: CORS_API_VERSION,\n method: 'delete',\n uri: '/projects/test-project/cors/1',\n }).reply(204)\n\n const {stdout} = await testCommand(Delete, ['https://example.com'])\n expect(stdout).toBe('Origin deleted\\n')\n })\n\n test('throws error when specified origin is not found', async () => {\n mockApi({\n apiVersion: CORS_API_VERSION,\n uri: '/projects/test-project/cors',\n }).reply(200, [TEST_ORIGINS.EXAMPLE])\n\n const {error} = await testCommand(Delete, ['https://nonexistent.com'])\n expect(error).toBeInstanceOf(Error)\n expect(error?.message).toEqual('Origin \"https://nonexistent.com\" not found')\n expect(error?.oclif?.exit).toBe(1)\n })\n\n test('throws error when no CORS origins exist', async () => {\n mockApi({\n apiVersion: CORS_API_VERSION,\n uri: '/projects/test-project/cors',\n }).reply(200, [])\n\n const {error} = await testCommand(Delete, ['https://example.com'])\n expect(error).toBeInstanceOf(Error)\n expect(error?.message).toEqual('No CORS origins configured for this project.')\n expect(error?.oclif?.exit).toBe(1)\n })\n\n test.each([\n {desc: 'when fetching origins', message: 'Internal Server Error', statusCode: 500},\n {desc: 'with 404 error when fetching origins', message: 'Project not found', statusCode: 404},\n ])('handles API error $desc', async ({message, statusCode}) => {\n mockApi({\n apiVersion: CORS_API_VERSION,\n uri: '/projects/test-project/cors',\n }).reply(statusCode, {message})\n\n const {error} = await testCommand(Delete, ['https://example.com'])\n expect(error).toBeInstanceOf(Error)\n expect(error?.message).toContain('Failed to fetch CORS origins')\n expect(error?.message).toContain(message)\n expect(error?.oclif?.exit).toBe(1)\n })\n\n test.each([\n {desc: 'when deleting origin', message: 'Failed to delete', statusCode: 500},\n {desc: 'with 404 error when deleting origin', message: 'Origin not found', statusCode: 404},\n ])('handles API error $desc', async ({message, statusCode}) => {\n mockApi({\n apiVersion: CORS_API_VERSION,\n uri: '/projects/test-project/cors',\n }).reply(200, [TEST_ORIGINS.EXAMPLE])\n\n mockApi({\n apiVersion: CORS_API_VERSION,\n method: 'delete',\n uri: '/projects/test-project/cors/1',\n }).reply(statusCode, {message})\n\n const {error} = await testCommand(Delete, ['https://example.com'])\n expect(error).toBeInstanceOf(Error)\n expect(error?.message).toContain('Origin deletion failed')\n expect(error?.message).toContain(message)\n expect(error?.oclif?.exit).toBe(1)\n })\n\n test.each([\n {desc: 'no project ID is found', projectId: undefined},\n {desc: 'project ID is empty string', projectId: ''},\n ])('throws error when $desc', async ({projectId}) => {\n const {getCliConfig} = await import('../../../../../cli-core/src/config/cli/getCliConfig.js')\n vi.mocked(getCliConfig).mockResolvedValueOnce({\n api: {projectId},\n })\n\n const {error} = await testCommand(Delete, ['https://example.com'])\n expect(error).toBeInstanceOf(Error)\n expect(error?.message).toEqual(NO_PROJECT_ID)\n expect(error?.oclif?.exit).toBe(1)\n })\n\n test('handles network errors when fetching origins', async () => {\n // Don't set up any mock to simulate network failure\n const {error} = await testCommand(Delete, ['https://example.com'])\n expect(error).toBeInstanceOf(Error)\n expect(error?.message).toContain('Failed to fetch CORS origins')\n expect(error?.oclif?.exit).toBe(1)\n })\n\n test.each([\n {\n desc: 'special characters',\n input: 'https://café.example.com',\n origin: TEST_ORIGINS.SPECIAL_CHARS,\n },\n {desc: 'ports', input: 'http://localhost:3000', origin: TEST_ORIGINS.LOCALHOST},\n ])('handles $desc in origin names', async ({input, origin}) => {\n mockApi({\n apiVersion: CORS_API_VERSION,\n uri: '/projects/test-project/cors',\n }).reply(200, [origin])\n\n mockApi({\n apiVersion: CORS_API_VERSION,\n method: 'delete',\n uri: '/projects/test-project/cors/1',\n }).reply(204)\n\n const {stdout} = await testCommand(Delete, [input])\n expect(stdout).toBe('Origin deleted\\n')\n })\n})\n"],"names":["runCommand","mockApi","testCommand","nock","afterEach","describe","expect","test","vi","CORS_API_VERSION","NO_PROJECT_ID","Delete","createCorsOrigin","overrides","allowCredentials","createdAt","deletedAt","projectId","updatedAt","TEST_ORIGINS","APP_EXAMPLE","id","origin","CASE_MIXED","EXAMPLE","LOCALHOST","SPECIAL_CHARS","mock","findProjectRoot","fn","mockResolvedValue","directory","root","type","getCliConfig","api","getCliToken","actual","importActual","select","clearAllMocks","pending","pendingMocks","cleanAll","toEqual","stdout","toContain","apiVersion","uri","reply","method","toBe","mocked","toHaveBeenCalledWith","choices","name","value","message","error","toBeInstanceOf","Error","oclif","exit","each","desc","statusCode","undefined","mockResolvedValueOnce","input"],"mappings":"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"}
|
|
1
|
+
{"version":3,"sources":["../../../../src/commands/cors/__tests__/delete.test.ts"],"sourcesContent":["import {runCommand} from '@oclif/test'\nimport {mockApi, testCommand} from '@sanity/cli-test'\nimport nock from 'nock'\nimport {afterEach, describe, expect, test, vi} from 'vitest'\n\nimport {CORS_API_VERSION, type CorsOrigin} from '../../../services/cors.js'\nimport {NO_PROJECT_ID} from '../../../util/errorMessages.js'\nimport {Delete} from '../delete.js'\n\nconst createCorsOrigin = (\n overrides: Partial<CorsOrigin> & {id: number; origin: string},\n): CorsOrigin => ({\n allowCredentials: true,\n createdAt: '2023-01-01T00:00:00Z',\n deletedAt: null,\n projectId: 'test-project',\n updatedAt: null,\n ...overrides,\n})\n\nconst TEST_ORIGINS = {\n APP_EXAMPLE: createCorsOrigin({\n allowCredentials: false,\n id: 2,\n origin: 'https://app.example.com',\n }),\n CASE_MIXED: createCorsOrigin({id: 1, origin: 'https://Example.Com'}),\n EXAMPLE: createCorsOrigin({id: 1, origin: 'https://example.com'}),\n LOCALHOST: createCorsOrigin({id: 1, origin: 'http://localhost:3000'}),\n SPECIAL_CHARS: createCorsOrigin({id: 1, origin: 'https://café.example.com'}),\n} as const\n\nconst testProjectId = 'test-project'\n\nconst defaultMocks = {\n cliConfig: {api: {projectId: testProjectId}},\n projectRoot: {\n directory: '/test/path',\n path: '/test/path/sanity.config.ts',\n type: 'studio' as const,\n },\n token: 'test-token',\n}\n\nvi.mock('@sanity/cli-core/ux', async () => {\n const actual = await vi.importActual<typeof import('@sanity/cli-core/ux')>('@sanity/cli-core/ux')\n return {\n ...actual,\n select: vi.fn(),\n }\n})\n\ndescribe('#cors:delete', () => {\n afterEach(() => {\n vi.clearAllMocks()\n const pending = nock.pendingMocks()\n nock.cleanAll()\n expect(pending, 'pending mocks').toEqual([])\n })\n\n test('--help works', async () => {\n const {stdout} = await runCommand(['cors delete', '--help'])\n expect(stdout).toContain('Delete an existing CORS origin from your project')\n })\n\n test('deletes a specific CORS origin', async () => {\n mockApi({\n apiVersion: CORS_API_VERSION,\n uri: '/projects/test-project/cors',\n }).reply(200, [TEST_ORIGINS.EXAMPLE, TEST_ORIGINS.APP_EXAMPLE])\n\n mockApi({\n apiVersion: CORS_API_VERSION,\n method: 'delete',\n uri: '/projects/test-project/cors/1',\n }).reply(204)\n\n const {stdout} = await testCommand(Delete, ['https://example.com'], {mocks: defaultMocks})\n expect(stdout).toBe('Origin deleted\\n')\n })\n\n test('prompts user to select origin when none specified', async () => {\n const {select} = await import('@sanity/cli-core/ux')\n vi.mocked(select).mockResolvedValue(2)\n\n mockApi({\n apiVersion: CORS_API_VERSION,\n uri: '/projects/test-project/cors',\n }).reply(200, [TEST_ORIGINS.EXAMPLE, TEST_ORIGINS.APP_EXAMPLE])\n\n mockApi({\n apiVersion: CORS_API_VERSION,\n method: 'delete',\n uri: '/projects/test-project/cors/2',\n }).reply(204)\n\n const {stdout} = await testCommand(Delete, [], {mocks: defaultMocks})\n expect(stdout).toBe('Origin deleted\\n')\n expect(select).toHaveBeenCalledWith({\n choices: [\n {name: 'https://example.com', value: 1},\n {name: 'https://app.example.com', value: 2},\n ],\n message: 'Select origin to delete',\n })\n })\n\n test('handles case-insensitive origin matching', async () => {\n mockApi({\n apiVersion: CORS_API_VERSION,\n uri: '/projects/test-project/cors',\n }).reply(200, [TEST_ORIGINS.CASE_MIXED])\n\n mockApi({\n apiVersion: CORS_API_VERSION,\n method: 'delete',\n uri: '/projects/test-project/cors/1',\n }).reply(204)\n\n const {stdout} = await testCommand(Delete, ['https://example.com'], {mocks: defaultMocks})\n expect(stdout).toBe('Origin deleted\\n')\n })\n\n test('throws error when specified origin is not found', async () => {\n mockApi({\n apiVersion: CORS_API_VERSION,\n uri: '/projects/test-project/cors',\n }).reply(200, [TEST_ORIGINS.EXAMPLE])\n\n const {error} = await testCommand(Delete, ['https://nonexistent.com'], {mocks: defaultMocks})\n expect(error).toBeInstanceOf(Error)\n expect(error?.message).toEqual('Origin \"https://nonexistent.com\" not found')\n expect(error?.oclif?.exit).toBe(1)\n })\n\n test('throws error when no CORS origins exist', async () => {\n mockApi({\n apiVersion: CORS_API_VERSION,\n uri: '/projects/test-project/cors',\n }).reply(200, [])\n\n const {error} = await testCommand(Delete, ['https://example.com'], {mocks: defaultMocks})\n expect(error).toBeInstanceOf(Error)\n expect(error?.message).toEqual('No CORS origins configured for this project.')\n expect(error?.oclif?.exit).toBe(1)\n })\n\n test.each([\n {desc: 'when fetching origins', message: 'Internal Server Error', statusCode: 500},\n {desc: 'with 404 error when fetching origins', message: 'Project not found', statusCode: 404},\n ])('handles API error $desc', async ({message, statusCode}) => {\n mockApi({\n apiVersion: CORS_API_VERSION,\n uri: '/projects/test-project/cors',\n }).reply(statusCode, {message})\n\n const {error} = await testCommand(Delete, ['https://example.com'], {mocks: defaultMocks})\n expect(error).toBeInstanceOf(Error)\n expect(error?.message).toContain('Failed to fetch CORS origins')\n expect(error?.message).toContain(message)\n expect(error?.oclif?.exit).toBe(1)\n })\n\n test.each([\n {desc: 'when deleting origin', message: 'Failed to delete', statusCode: 500},\n {desc: 'with 404 error when deleting origin', message: 'Origin not found', statusCode: 404},\n ])('handles API error $desc', async ({message, statusCode}) => {\n mockApi({\n apiVersion: CORS_API_VERSION,\n uri: '/projects/test-project/cors',\n }).reply(200, [TEST_ORIGINS.EXAMPLE])\n\n mockApi({\n apiVersion: CORS_API_VERSION,\n method: 'delete',\n uri: '/projects/test-project/cors/1',\n }).reply(statusCode, {message})\n\n const {error} = await testCommand(Delete, ['https://example.com'], {mocks: defaultMocks})\n expect(error).toBeInstanceOf(Error)\n expect(error?.message).toContain('Origin deletion failed')\n expect(error?.message).toContain(message)\n expect(error?.oclif?.exit).toBe(1)\n })\n\n test.each([\n {desc: 'no project ID is found', projectId: undefined},\n {desc: 'project ID is empty string', projectId: ''},\n ])('throws error when $desc', async ({projectId}) => {\n const {error} = await testCommand(Delete, ['https://example.com'], {\n mocks: {\n ...defaultMocks,\n cliConfig: {api: {projectId}},\n },\n })\n expect(error).toBeInstanceOf(Error)\n expect(error?.message).toEqual(NO_PROJECT_ID)\n expect(error?.oclif?.exit).toBe(1)\n })\n\n test('handles network errors when fetching origins', async () => {\n // Don't set up any mock to simulate network failure\n const {error} = await testCommand(Delete, ['https://example.com'], {mocks: defaultMocks})\n expect(error).toBeInstanceOf(Error)\n expect(error?.message).toContain('Failed to fetch CORS origins')\n expect(error?.oclif?.exit).toBe(1)\n })\n\n test.each([\n {\n desc: 'special characters',\n input: 'https://café.example.com',\n origin: TEST_ORIGINS.SPECIAL_CHARS,\n },\n {desc: 'ports', input: 'http://localhost:3000', origin: TEST_ORIGINS.LOCALHOST},\n ])('handles $desc in origin names', async ({input, origin}) => {\n mockApi({\n apiVersion: CORS_API_VERSION,\n uri: '/projects/test-project/cors',\n }).reply(200, [origin])\n\n mockApi({\n apiVersion: CORS_API_VERSION,\n method: 'delete',\n uri: '/projects/test-project/cors/1',\n }).reply(204)\n\n const {stdout} = await testCommand(Delete, [input], {mocks: defaultMocks})\n expect(stdout).toBe('Origin deleted\\n')\n })\n})\n"],"names":["runCommand","mockApi","testCommand","nock","afterEach","describe","expect","test","vi","CORS_API_VERSION","NO_PROJECT_ID","Delete","createCorsOrigin","overrides","allowCredentials","createdAt","deletedAt","projectId","updatedAt","TEST_ORIGINS","APP_EXAMPLE","id","origin","CASE_MIXED","EXAMPLE","LOCALHOST","SPECIAL_CHARS","testProjectId","defaultMocks","cliConfig","api","projectRoot","directory","path","type","token","mock","actual","importActual","select","fn","clearAllMocks","pending","pendingMocks","cleanAll","toEqual","stdout","toContain","apiVersion","uri","reply","method","mocks","toBe","mocked","mockResolvedValue","toHaveBeenCalledWith","choices","name","value","message","error","toBeInstanceOf","Error","oclif","exit","each","desc","statusCode","undefined","input"],"mappings":"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"}
|