@sanity/cli 7.18.0 → 8.0.0
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/README.md +74 -4
- package/dist/SanityHelp.js +1 -35
- package/dist/SanityHelp.js.map +1 -1
- package/dist/actions/assets/assetFileError.js +9 -0
- package/dist/actions/assets/assetFileError.js.map +1 -0
- package/dist/actions/assets/uploadAssetFromFile.js +40 -0
- package/dist/actions/assets/uploadAssetFromFile.js.map +1 -0
- package/dist/actions/assets/uploadAssetWithProgress.js +76 -0
- package/dist/actions/assets/uploadAssetWithProgress.js.map +1 -0
- package/dist/actions/deploy/deployApp.js +39 -44
- package/dist/actions/deploy/deployApp.js.map +1 -1
- package/dist/actions/deploy/deployChecks.js +21 -13
- package/dist/actions/deploy/deployChecks.js.map +1 -1
- package/dist/actions/deploy/deployRunner.js +8 -7
- package/dist/actions/deploy/deployRunner.js.map +1 -1
- package/dist/actions/deploy/deployStudio.js +55 -35
- package/dist/actions/deploy/deployStudio.js.map +1 -1
- package/dist/actions/deploy/deploymentPlan.js +17 -29
- package/dist/actions/deploy/deploymentPlan.js.map +1 -1
- package/dist/actions/deploy/resolveDeployTarget.js +2 -8
- package/dist/actions/deploy/resolveDeployTarget.js.map +1 -1
- package/dist/actions/dev/servers/getDevServerConfig.js +2 -8
- package/dist/actions/dev/servers/getDevServerConfig.js.map +1 -1
- package/dist/actions/dev/servers/startStudioDevServer.js +7 -1
- package/dist/actions/dev/servers/startStudioDevServer.js.map +1 -1
- package/dist/actions/init/bootstrapRemoteTemplate.js +2 -2
- package/dist/actions/init/bootstrapRemoteTemplate.js.map +1 -1
- package/dist/actions/schema/listSchemas.js +1 -1
- package/dist/actions/schema/listSchemas.js.map +1 -1
- package/dist/actions/tokens/constants.js +2 -2
- package/dist/actions/tokens/constants.js.map +1 -1
- package/dist/actions/tokens/expiry.js +35 -0
- package/dist/actions/tokens/expiry.js.map +1 -0
- package/dist/actions/tokens/types.js.map +1 -1
- package/dist/actions/tokens/validateRole.js +5 -2
- package/dist/actions/tokens/validateRole.js.map +1 -1
- package/dist/actions/typegen/count.js +6 -0
- package/dist/actions/typegen/count.js.map +1 -0
- package/dist/actions/typegen/formatPath.js +5 -0
- package/dist/actions/typegen/formatPath.js.map +1 -0
- package/dist/actions/typegen/percent.js +8 -0
- package/dist/actions/typegen/percent.js.map +1 -0
- package/dist/actions/typegen/renderTypegenProgress.js +88 -0
- package/dist/actions/typegen/renderTypegenProgress.js.map +1 -0
- package/dist/actions/undeploy/adapters.js +6 -9
- package/dist/actions/undeploy/adapters.js.map +1 -1
- package/dist/actions/undeploy/runUndeploy.js +12 -4
- package/dist/actions/undeploy/runUndeploy.js.map +1 -1
- package/dist/actions/undeploy/undeployPlan.js +6 -14
- package/dist/actions/undeploy/undeployPlan.js.map +1 -1
- package/dist/commands/assets/upload.js +126 -0
- package/dist/commands/assets/upload.js.map +1 -0
- package/dist/commands/backups/list.js +1 -1
- package/dist/commands/backups/list.js.map +1 -1
- package/dist/commands/datasets/copy.js +1 -1
- package/dist/commands/datasets/copy.js.map +1 -1
- package/dist/commands/login.js +2 -8
- package/dist/commands/login.js.map +1 -1
- package/dist/commands/mcp/configure.js.map +1 -1
- package/dist/commands/migrations/list.js +1 -1
- package/dist/commands/migrations/list.js.map +1 -1
- package/dist/commands/migrations/run.js +67 -3
- package/dist/commands/migrations/run.js.map +1 -1
- package/dist/commands/new.js +4 -3
- package/dist/commands/new.js.map +1 -1
- package/dist/commands/organizations/list.js +1 -1
- package/dist/commands/organizations/list.js.map +1 -1
- package/dist/commands/projects/list.js +39 -12
- package/dist/commands/projects/list.js.map +1 -1
- package/dist/commands/projects/unclaimed.js +39 -33
- package/dist/commands/projects/unclaimed.js.map +1 -1
- package/dist/commands/tokens/create.js +92 -7
- package/dist/commands/tokens/create.js.map +1 -1
- package/dist/commands/tokens/delete.js +2 -2
- package/dist/commands/tokens/delete.js.map +1 -1
- package/dist/commands/tokens/list.js +12 -11
- package/dist/commands/tokens/list.js.map +1 -1
- package/dist/commands/tokens/rotate.js +95 -0
- package/dist/commands/tokens/rotate.js.map +1 -0
- package/dist/commands/typegen/generate.js +222 -0
- package/dist/commands/typegen/generate.js.map +1 -0
- package/dist/commands/users/list.js +8 -8
- package/dist/commands/users/list.js.map +1 -1
- package/dist/exports/_internal.d.ts +29 -0
- package/dist/exports/_internal.js +1 -0
- package/dist/exports/_internal.js.map +1 -1
- package/dist/exports/invokeSanityCli/commandPolicies/mcpPolicy.js +6 -0
- package/dist/exports/invokeSanityCli/commandPolicies/mcpPolicy.js.map +1 -1
- package/dist/exports/invokeSanityCli/help.js +4 -0
- package/dist/exports/invokeSanityCli/help.js.map +1 -1
- package/dist/exports/invokeSanityCli/index.js +2 -0
- package/dist/exports/invokeSanityCli/index.js.map +1 -1
- package/dist/hooks/commandNotFound/topicAliases.js +13 -40
- package/dist/hooks/commandNotFound/topicAliases.js.map +1 -1
- package/dist/hooks/prerun/setupTelemetry.js +3 -2
- package/dist/hooks/prerun/setupTelemetry.js.map +1 -1
- package/dist/server/vite/plugin-typegen.js +4 -16
- package/dist/server/vite/plugin-typegen.js.map +1 -1
- package/dist/services/assets.js +46 -0
- package/dist/services/assets.js.map +1 -0
- package/dist/services/tokens.js +81 -31
- package/dist/services/tokens.js.map +1 -1
- package/dist/topicAliases.js +43 -0
- package/dist/topicAliases.js.map +1 -1
- package/dist/util/parseArguments.js +7 -2
- package/dist/util/parseArguments.js.map +1 -1
- package/dist/util/readTokenFromStdin.js +17 -0
- package/dist/util/readTokenFromStdin.js.map +1 -0
- package/dist/util/responsiveTable.js +97 -0
- package/dist/util/responsiveTable.js.map +1 -0
- package/dist/util/telemetry/commandTelemetry.js +34 -0
- package/dist/util/telemetry/commandTelemetry.js.map +1 -0
- package/dist/util/unclaimedProjects.js +10 -0
- package/dist/util/unclaimedProjects.js.map +1 -1
- package/oclif.config.js +2 -4
- package/oclif.manifest.json +213 -1
- package/package.json +12 -10
- package/templates/shopify/components/inputs/ShopifyMetafield.tsx +20 -0
- package/templates/shopify/docs/features.md +24 -0
- package/templates/shopify/schemaTypes/index.ts +2 -0
- package/templates/shopify/schemaTypes/objects/shopify/shopifyCollectionType.ts +18 -1
- package/templates/shopify/schemaTypes/objects/shopify/shopifyMetafieldType.ts +47 -0
- package/templates/shopify/schemaTypes/objects/shopify/shopifyProductType.ts +17 -0
- package/templates/shopify/utils/formatMetafieldValue.ts +30 -0
- package/templates/shopify-online-storefront/components/inputs/ShopifyMetafield.tsx +22 -0
- package/templates/shopify-online-storefront/schemaTypes/index.ts +4 -0
- package/templates/shopify-online-storefront/schemaTypes/objects/shop.ts +16 -0
- package/templates/shopify-online-storefront/schemaTypes/objects/shopifyCollection.ts +19 -0
- package/templates/shopify-online-storefront/schemaTypes/objects/shopifyMetafield.ts +53 -0
- package/templates/shopify-online-storefront/schemaTypes/objects/shopifyProduct.ts +19 -0
- package/templates/shopify-online-storefront/utils/formatMetafieldValue.ts +30 -0
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../../src/actions/undeploy/runUndeploy.ts"],"sourcesContent":["import {styleText} from 'node:util'\n\nimport {CLIError} from '@oclif/core/errors'\nimport {exitCodes, type Output, subdebug} from '@sanity/cli-core'\nimport {getErrorMessage} from '@sanity/cli-core/errors'\nimport {\n type UndeployAdapter,\n type UndeployTarget,\n type UndeployTargetResolution,\n} from '@sanity/cli-core/undeploy'\nimport {confirm, spinner} from '@sanity/cli-core/ux'\n\nimport {type UndeployCommand} from '../../commands/undeploy.js'\nimport {\n type CheckReporter,\n createCollectingReporter,\n createFailFastReporter,\n} from '../../util/checks.js'\nimport {toStderrOutput} from '../../util/toStderrOutput.js'\nimport {\n describeUndeployTarget,\n renderUndeployPlan,\n undeployLabel,\n type UndeployPlan,\n undeployPlanToJson,\n} from './undeployPlan.js'\n\nconst undeployDebug = subdebug('undeploy')\n\ntype UndeployFlags = UndeployCommand['flags']\n\nexport interface UndeployOptions {\n flags: UndeployFlags\n isUnattended: boolean\n output: Output\n}\n\n/** What a real undeploy produced — the payload `--json` reports. */\ntype UndeployResult =\n | {application: UndeployTarget; undeployed: true}\n | {reason: string; undeployed: false}\n\n/**\n * Runs an undeploy in the mode the flags select: a real undeploy fails fast,\n * confirms, and deletes; `--dry-run` drives the same target resolution\n * read-only and renders a plan instead. `--json` emits the same information as\n * machine-readable JSON.\n */\nexport async function runUndeploy(\n options: UndeployOptions,\n adapter: UndeployAdapter,\n): Promise<void> {\n const {flags, output} = options\n const json = !!flags.json\n // The report-only `summary` lines never enter the payload\n const emitJson = (payload: unknown) =>\n output.log(JSON.stringify(payload, (key, value) => (key === 'summary' ? undefined : value), 2))\n\n // The JSON payload owns stdout, so the run's progress logs go to stderr; only\n // the final JSON.stringify writes to stdout.\n const runOptions = json ? {...options, output: toStderrOutput(output)} : options\n\n try {\n if (flags['dry-run']) {\n const reporter = createCollectingReporter()\n const resolution = await resolveTarget(adapter, reporter)\n const plan: UndeployPlan = {\n checks: reporter.results,\n reason: resolution?.type === 'none' ? resolution.message : null,\n target: resolution?.type === 'found' ? resolution.target : null,\n type: adapter.type,\n }\n if (json) emitJson(undeployPlanToJson(plan))\n else renderUndeployPlan(plan, output)\n // A blocked plan exits like a real (fail-fast) undeploy would.\n const failed = plan.checks.find((check) => check.status === 'fail')\n if (failed) output.error('Undeploy blocked by failing checks.', {exit: failed.exitCode ?? 1})\n return\n }\n\n const result = await undeployApp(runOptions, adapter)\n if (json && result) emitJson(result)\n } catch (error) {\n const failure = normalizeFailure(error, adapter.type)\n // A blocked dry run reaches this catch too (its exit throws) and already\n // printed its plan, so only a real undeploy adds the {undeployed: false} envelope.\n if (json && !flags['dry-run']) {\n emitJson({error: {message: failure.message}, undeployed: false})\n }\n output.error(failure.message, {exit: failure.exit})\n }\n}\n\n/** The real run: resolve the target, confirm, delete, report. */\nasync function undeployApp(\n options: UndeployOptions,\n adapter: UndeployAdapter,\n): Promise<UndeployResult | undefined> {\n const {flags, output} = options\n\n const resolution = await resolveTarget(adapter, createFailFastReporter(output))\n // A resolve failure never lands here: the fail-fast reporter already exited.\n if (!resolution) return undefined\n if (resolution.type === 'none') {\n output.log(`${resolution.message}.`)\n if (resolution.solution) output.log(`${resolution.solution}.`)\n output.log('Nothing to undeploy.')\n return {reason: resolution.message, undeployed: false}\n }\n\n const {target} = resolution\n if (!flags.yes && options.isUnattended) {\n throw new CLIError('Undeploy requires confirmation. Pass --yes to continue.', {\n exit: exitCodes.USAGE_ERROR,\n })\n }\n\n if (!flags.yes) {\n const shouldUndeploy = await confirm({\n default: false,\n message: confirmUndeployMessage(target),\n })\n if (!shouldUndeploy) {\n throw new CLIError('Undeploy cancelled', {exit: exitCodes.USER_ABORT})\n }\n }\n\n const label = undeployLabel(target, adapter.type)\n const spin = spinner(`Undeploying ${label}`).start()\n try {\n await adapter.undeploy(target)\n } catch (error) {\n spin.fail()\n undeployDebug(`Error undeploying ${label}`, error)\n // Labeled here, where the target is known — `normalizeFailure` only sees the adapter type.\n throw new CLIError(`Error undeploying ${label}: ${getErrorMessage(error)}`, {exit: 1})\n }\n spin.succeed()\n\n printUndeployed(target, output)\n return {application: target, undeployed: true}\n}\n\n/**\n * Resolves the undeploy target and reports the verdict as a check, shared by\n * both modes so a real run and a dry run can't diverge.\n */\nasync function resolveTarget(\n adapter: UndeployAdapter,\n reporter: CheckReporter,\n): Promise<UndeployTargetResolution | null> {\n const spin = spinner('Checking application info').start()\n\n let resolution: UndeployTargetResolution\n try {\n resolution = await adapter.resolveTarget()\n } catch (error) {\n spin.fail()\n undeployDebug('Failed to resolve undeploy target', error)\n reporter.report({\n message: `Failed to resolve undeploy target: ${getErrorMessage(error)}`,\n status: 'fail',\n })\n return null\n }\n\n spin[resolution.type === 'found' ? 'succeed' : 'fail']()\n reporter.report(describeUndeployTarget(resolution))\n return resolution\n}\n\nfunction confirmUndeployMessage(target: UndeployTarget): string {\n if (target.deletes === 'config') {\n return `This will delete the deployed config for ${styleText(\n 'yellow',\n target.title ?? 'this app',\n )}.\nAre you ${styleText('red', 'sure')} you want to undeploy?`\n }\n\n if (target.type === 'coreApp') {\n return `This will undeploy the following application:\n\n Title: ${styleText('yellow', target.title || '(untitled application)')}\n ID: ${styleText('yellow', target.id)}\n\nThe application will no longer be available for any of your users if you proceed.\n\nAre you ${styleText('red', 'sure')} you want to undeploy?`\n }\n\n return `This will undeploy ${styleText('yellow', target.url ?? target.id)} and make it unavailable for your users.\nAre you ${styleText('red', 'sure')} you want to undeploy?`\n}\n\nfunction printUndeployed(target: UndeployTarget, output: Output): void {\n if (target.deletes === 'config') {\n output.log(`\\n${styleText('bold', 'Config deleted.')}`)\n return\n }\n\n if (target.type === 'coreApp') {\n output.log(\n `\\n${styleText('bold', '⏱️ Application undeploy scheduled.')} It might be a few minutes until ${\n target.title ? styleText('italic', `'${target.title}'`) : 'your application'\n } is unavailable.`,\n )\n output.log(\n `\\n${styleText('bold', 'Remember to remove `deployment.appId` from your application configuration')} to avoid errors when redeploying.`,\n )\n return\n }\n\n output.log(\n `\\nStudio undeploy scheduled. It might be a few minutes until ${target.url ?? 'your studio'} is unavailable.`,\n )\n}\n\n/** The one failure diagnosis both the stderr message and the `--json` envelope read. */\nfunction normalizeFailure(\n error: unknown,\n type: UndeployAdapter['type'],\n): {exit: number; message: string} {\n // Ctrl+C on the confirmation prompt isn't a real failure\n if (error instanceof Error && error.name === 'ExitPromptError') {\n return {exit: exitCodes.USER_ABORT, message: 'Undeploy cancelled'}\n }\n // A failed check already carries its own message and exit code\n if (error instanceof CLIError) {\n return {exit: error.oclif?.exit ?? 1, message: error.message}\n }\n const label = type === 'coreApp' ? 'application' : 'studio'\n undeployDebug(`Error undeploying ${label}`, error)\n return {exit: 1, message: `Error undeploying ${label}: ${getErrorMessage(error)}`}\n}\n"],"names":["styleText","CLIError","exitCodes","subdebug","getErrorMessage","confirm","spinner","createCollectingReporter","createFailFastReporter","toStderrOutput","describeUndeployTarget","renderUndeployPlan","undeployLabel","undeployPlanToJson","undeployDebug","runUndeploy","options","adapter","flags","output","json","emitJson","payload","log","JSON","stringify","key","value","undefined","runOptions","reporter","resolution","resolveTarget","plan","checks","results","reason","type","message","target","failed","find","check","status","error","exit","exitCode","result","undeployApp","failure","normalizeFailure","undeployed","solution","yes","isUnattended","USAGE_ERROR","shouldUndeploy","default","confirmUndeployMessage","USER_ABORT","label","spin","start","undeploy","fail","succeed","printUndeployed","application","report","deletes","title","id","url","Error","name","oclif"],"mappings":"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"}
|
|
1
|
+
{"version":3,"sources":["../../../src/actions/undeploy/runUndeploy.ts"],"sourcesContent":["import {styleText} from 'node:util'\n\nimport {CLIError} from '@oclif/core/errors'\nimport {exitCodes, type Output, subdebug} from '@sanity/cli-core'\nimport {getErrorMessage} from '@sanity/cli-core/errors'\nimport {\n type UndeployAdapter,\n type UndeployTarget,\n type UndeployTargetResolution,\n} from '@sanity/cli-core/undeploy'\nimport {confirm, spinner} from '@sanity/cli-core/ux'\n\nimport {type UndeployCommand} from '../../commands/undeploy.js'\nimport {\n type CheckReporter,\n createCollectingReporter,\n createFailFastReporter,\n} from '../../util/checks.js'\nimport {toStderrOutput} from '../../util/toStderrOutput.js'\nimport {\n describeUndeployTarget,\n renderUndeployPlan,\n undeployLabel,\n type UndeployPlan,\n undeployPlanToJson,\n} from './undeployPlan.js'\n\nconst undeployDebug = subdebug('undeploy')\n\ntype UndeployFlags = UndeployCommand['flags']\n\nexport interface UndeployOptions {\n flags: UndeployFlags\n isUnattended: boolean\n output: Output\n}\n\n/** What a real undeploy produced — the payload `--json` reports. */\ntype UndeployResult =\n | {application: null; payload: null; reason: string; undeployed: false}\n | {\n application: object | null\n deletes: UndeployTarget['deletes']\n payload: UndeployTarget['payload']\n reason: null\n undeployed: true\n url: string | null\n }\n\n/**\n * Runs an undeploy in the mode the flags select: a real undeploy fails fast,\n * confirms, and deletes; `--dry-run` drives the same target resolution\n * read-only and renders a plan instead. `--json` emits the same information as\n * machine-readable JSON.\n */\nexport async function runUndeploy(\n options: UndeployOptions,\n adapter: UndeployAdapter,\n): Promise<void> {\n const {flags, output} = options\n const json = !!flags.json\n const emitJson = (payload: unknown) => output.log(JSON.stringify(payload, null, 2))\n\n // The JSON payload owns stdout, so the run's progress logs go to stderr; only\n // the final JSON.stringify writes to stdout.\n const runOptions = json ? {...options, output: toStderrOutput(output)} : options\n\n try {\n if (flags['dry-run']) {\n const reporter = createCollectingReporter()\n const resolution = await resolveTarget(adapter, reporter)\n const plan: UndeployPlan = {\n checks: reporter.results,\n reason: resolution?.type === 'none' ? resolution.message : null,\n target: resolution?.type === 'found' ? resolution.target : null,\n type: adapter.type,\n }\n if (json) emitJson(undeployPlanToJson(plan))\n else renderUndeployPlan(plan, output)\n // A blocked plan exits like a real (fail-fast) undeploy would.\n const failed = plan.checks.find((check) => check.status === 'fail')\n if (failed) output.error('Undeploy blocked by failing checks.', {exit: failed.exitCode ?? 1})\n return\n }\n\n const result = await undeployApp(runOptions, adapter)\n if (json && result) emitJson(result)\n } catch (error) {\n const failure = normalizeFailure(error, adapter.type)\n // A blocked dry run reaches this catch too (its exit throws) and already\n // printed its plan, so only a real undeploy adds the {undeployed: false} envelope.\n if (json && !flags['dry-run']) {\n emitJson({\n application: null,\n error: {message: failure.message},\n payload: null,\n reason: failure.message,\n undeployed: false,\n })\n }\n output.error(failure.message, {exit: failure.exit})\n }\n}\n\n/** The real run: resolve the target, confirm, delete, report. */\nasync function undeployApp(\n options: UndeployOptions,\n adapter: UndeployAdapter,\n): Promise<UndeployResult | undefined> {\n const {flags, output} = options\n\n const resolution = await resolveTarget(adapter, createFailFastReporter(output))\n // A resolve failure never lands here: the fail-fast reporter already exited.\n if (!resolution) return undefined\n if (resolution.type === 'none') {\n output.log(`${resolution.message}.`)\n if (resolution.solution) output.log(`${resolution.solution}.`)\n output.log('Nothing to undeploy.')\n return {application: null, payload: null, reason: resolution.message, undeployed: false}\n }\n\n const {target} = resolution\n if (!flags.yes && options.isUnattended) {\n throw new CLIError('Undeploy requires confirmation. Pass --yes to continue.', {\n exit: exitCodes.USAGE_ERROR,\n })\n }\n\n if (!flags.yes) {\n const shouldUndeploy = await confirm({\n default: false,\n message: confirmUndeployMessage(target),\n })\n if (!shouldUndeploy) {\n throw new CLIError('Undeploy cancelled', {exit: exitCodes.USER_ABORT})\n }\n }\n\n const label = undeployLabel(target, adapter.type)\n const spin = spinner(`Undeploying ${label}`).start()\n try {\n await adapter.undeploy(target)\n } catch (error) {\n spin.fail()\n undeployDebug(`Error undeploying ${label}`, error)\n // Labeled here, where the target is known — `normalizeFailure` only sees the adapter type.\n throw new CLIError(`Error undeploying ${label}: ${getErrorMessage(error)}`, {exit: 1})\n }\n spin.succeed()\n\n printUndeployed(target, output)\n return {\n application: target.application ?? null,\n deletes: target.deletes,\n payload: target.payload,\n reason: null,\n undeployed: true,\n url: target.url,\n }\n}\n\n/**\n * Resolves the undeploy target and reports the verdict as a check, shared by\n * both modes so a real run and a dry run can't diverge.\n */\nasync function resolveTarget(\n adapter: UndeployAdapter,\n reporter: CheckReporter,\n): Promise<UndeployTargetResolution | null> {\n const spin = spinner('Checking application info').start()\n\n let resolution: UndeployTargetResolution\n try {\n resolution = await adapter.resolveTarget()\n } catch (error) {\n spin.fail()\n undeployDebug('Failed to resolve undeploy target', error)\n reporter.report({\n message: `Failed to resolve undeploy target: ${getErrorMessage(error)}`,\n status: 'fail',\n })\n return null\n }\n\n spin[resolution.type === 'found' ? 'succeed' : 'fail']()\n reporter.report(describeUndeployTarget(resolution))\n return resolution\n}\n\nfunction confirmUndeployMessage(target: UndeployTarget): string {\n if (target.deletes === 'config') {\n return `This will delete the deployed config for ${styleText(\n 'yellow',\n target.title ?? 'this app',\n )}.\nAre you ${styleText('red', 'sure')} you want to undeploy?`\n }\n\n if (target.type === 'coreApp') {\n return `This will undeploy the following application:\n\n Title: ${styleText('yellow', target.title || '(untitled application)')}\n ID: ${styleText('yellow', target.id)}\n\nThe application will no longer be available for any of your users if you proceed.\n\nAre you ${styleText('red', 'sure')} you want to undeploy?`\n }\n\n return `This will undeploy ${styleText('yellow', target.url ?? target.id)} and make it unavailable for your users.\nAre you ${styleText('red', 'sure')} you want to undeploy?`\n}\n\nfunction printUndeployed(target: UndeployTarget, output: Output): void {\n if (target.deletes === 'config') {\n output.log(`\\n${styleText('bold', 'Config deleted.')}`)\n return\n }\n\n if (target.type === 'coreApp') {\n output.log(\n `\\n${styleText('bold', '⏱️ Application undeploy scheduled.')} It might be a few minutes until ${\n target.title ? styleText('italic', `'${target.title}'`) : 'your application'\n } is unavailable.`,\n )\n output.log(\n `\\n${styleText('bold', 'Remember to remove `deployment.appId` from your application configuration')} to avoid errors when redeploying.`,\n )\n return\n }\n\n output.log(\n `\\nStudio undeploy scheduled. It might be a few minutes until ${target.url ?? 'your studio'} is unavailable.`,\n )\n}\n\n/** The one failure diagnosis both the stderr message and the `--json` envelope read. */\nfunction normalizeFailure(\n error: unknown,\n type: UndeployAdapter['type'],\n): {exit: number; message: string} {\n // Ctrl+C on the confirmation prompt isn't a real failure\n if (error instanceof Error && error.name === 'ExitPromptError') {\n return {exit: exitCodes.USER_ABORT, message: 'Undeploy cancelled'}\n }\n // A failed check already carries its own message and exit code\n if (error instanceof CLIError) {\n return {exit: error.oclif?.exit ?? 1, message: error.message}\n }\n const label = type === 'coreApp' ? 'application' : 'studio'\n undeployDebug(`Error undeploying ${label}`, error)\n return {exit: 1, message: `Error undeploying ${label}: ${getErrorMessage(error)}`}\n}\n"],"names":["styleText","CLIError","exitCodes","subdebug","getErrorMessage","confirm","spinner","createCollectingReporter","createFailFastReporter","toStderrOutput","describeUndeployTarget","renderUndeployPlan","undeployLabel","undeployPlanToJson","undeployDebug","runUndeploy","options","adapter","flags","output","json","emitJson","payload","log","JSON","stringify","runOptions","reporter","resolution","resolveTarget","plan","checks","results","reason","type","message","target","failed","find","check","status","error","exit","exitCode","result","undeployApp","failure","normalizeFailure","application","undeployed","undefined","solution","yes","isUnattended","USAGE_ERROR","shouldUndeploy","default","confirmUndeployMessage","USER_ABORT","label","spin","start","undeploy","fail","succeed","printUndeployed","deletes","url","report","title","id","Error","name","oclif"],"mappings":"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"}
|
|
@@ -21,10 +21,14 @@ export function canUndeploy(plan) {
|
|
|
21
21
|
else if (check.status === 'warn') warnings.push(check.message);
|
|
22
22
|
}
|
|
23
23
|
return {
|
|
24
|
-
|
|
24
|
+
// Only a successful undeploy reports the backend's record.
|
|
25
|
+
application: null,
|
|
25
26
|
canUndeploy: canUndeploy(plan),
|
|
27
|
+
deletes: plan.target?.deletes ?? null,
|
|
26
28
|
errors,
|
|
27
|
-
|
|
29
|
+
payload: plan.target?.payload ?? null,
|
|
30
|
+
reason: plan.reason ?? plan.checks.find((check)=>check.status === 'fail')?.message ?? null,
|
|
31
|
+
url: plan.target?.url ?? null,
|
|
28
32
|
warnings
|
|
29
33
|
};
|
|
30
34
|
}
|
|
@@ -96,10 +100,6 @@ function renderTarget(target, output) {
|
|
|
96
100
|
[
|
|
97
101
|
'URL',
|
|
98
102
|
target.url
|
|
99
|
-
],
|
|
100
|
-
[
|
|
101
|
-
'Deployed',
|
|
102
|
-
formatDeployment(target.activeDeployment)
|
|
103
103
|
]
|
|
104
104
|
];
|
|
105
105
|
output.log('');
|
|
@@ -111,13 +111,5 @@ function renderTarget(target, output) {
|
|
|
111
111
|
for (const line of entry.split('\n'))output.log(` ${line}`);
|
|
112
112
|
}
|
|
113
113
|
}
|
|
114
|
-
function formatDeployment(deployment) {
|
|
115
|
-
if (!deployment) return null;
|
|
116
|
-
const parts = [
|
|
117
|
-
deployment.deployedAt ? `at ${deployment.deployedAt}` : null,
|
|
118
|
-
deployment.deployedBy ? `by ${deployment.deployedBy}` : null
|
|
119
|
-
].filter(Boolean);
|
|
120
|
-
return parts.length > 0 ? parts.join(' ') : null;
|
|
121
|
-
}
|
|
122
114
|
|
|
123
115
|
//# sourceMappingURL=undeployPlan.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../../src/actions/undeploy/undeployPlan.ts"],"sourcesContent":["import {styleText} from 'node:util'\n\nimport {type Output} from '@sanity/cli-core'\nimport {type UndeployTarget, type UndeployTargetResolution} from '@sanity/cli-core/undeploy'\n\nimport {type Check, checkStatusIcon, renderIssues} from '../../util/checks.js'\n\n/** What a `--dry-run` undeploy would do: the real undeploy sequence with the deletion gated off. */\nexport interface UndeployPlan {\n checks: Check[]\n /** Why there is nothing to undeploy; `null` when a target resolved. */\n reason: string | null\n /** What would be deleted; `null` when there is nothing to undeploy. */\n target: UndeployTarget | null\n type: 'coreApp' | 'studio'\n}\n\nexport function canUndeploy(plan: UndeployPlan): boolean {\n return plan.target !== null && plan.checks.every((check) => check.status !== 'fail')\n}\n\n/** What an undeploy deletes, phrased for the human output. */\nexport function undeployLabel(target: UndeployTarget | null, type: 'coreApp' | 'studio'): string {\n if (target?.deletes === 'config') {\n return `installation config${target.title ? ` for \"${target.title}\"` : ''}`\n }\n return type === 'coreApp' ? 'application' : 'studio'\n}\n\n/**\n * A machine-readable projection of the plan: blocking problems mapped to their\n * fix, warnings as messages. Derived from the same checks and target the human\n * report renders, so the two can't drift.\n */\nexport function undeployPlanToJson(plan: UndeployPlan):
|
|
1
|
+
{"version":3,"sources":["../../../src/actions/undeploy/undeployPlan.ts"],"sourcesContent":["import {styleText} from 'node:util'\n\nimport {type Output} from '@sanity/cli-core'\nimport {type UndeployTarget, type UndeployTargetResolution} from '@sanity/cli-core/undeploy'\n\nimport {type Check, checkStatusIcon, renderIssues} from '../../util/checks.js'\n\n/** What a `--dry-run` undeploy would do: the real undeploy sequence with the deletion gated off. */\nexport interface UndeployPlan {\n checks: Check[]\n /** Why there is nothing to undeploy; `null` when a target resolved. */\n reason: string | null\n /** What would be deleted; `null` when there is nothing to undeploy. */\n target: UndeployTarget | null\n type: 'coreApp' | 'studio'\n}\n\nexport function canUndeploy(plan: UndeployPlan): boolean {\n return plan.target !== null && plan.checks.every((check) => check.status !== 'fail')\n}\n\n/** What an undeploy deletes, phrased for the human output. */\nexport function undeployLabel(target: UndeployTarget | null, type: 'coreApp' | 'studio'): string {\n if (target?.deletes === 'config') {\n return `installation config${target.title ? ` for \"${target.title}\"` : ''}`\n }\n return type === 'coreApp' ? 'application' : 'studio'\n}\n\n/**\n * A machine-readable projection of the plan: blocking problems mapped to their\n * fix, warnings as messages. Derived from the same checks and target the human\n * report renders, so the two can't drift.\n */\nexport function undeployPlanToJson(plan: UndeployPlan): UndeployPlanJson {\n const errors: Record<string, string | null> = {}\n const warnings: string[] = []\n for (const check of plan.checks) {\n if (check.status === 'fail') errors[check.message] = check.solution ?? null\n else if (check.status === 'warn') warnings.push(check.message)\n }\n\n return {\n // Only a successful undeploy reports the backend's record.\n application: null,\n canUndeploy: canUndeploy(plan),\n deletes: plan.target?.deletes ?? null,\n errors,\n payload: plan.target?.payload ?? null,\n reason: plan.reason ?? plan.checks.find((check) => check.status === 'fail')?.message ?? null,\n url: plan.target?.url ?? null,\n warnings,\n }\n}\n\ninterface UndeployPlanJson {\n application: null\n canUndeploy: boolean\n deletes: UndeployTarget['deletes'] | null\n errors: Record<string, string | null>\n payload: UndeployTarget['payload'] | null\n reason: string | null\n url: string | null\n warnings: string[]\n}\n\n/**\n * The single diagnosis for each undeploy-target verdict, shared by the dry-run\n * report and the real run so message and fix can't drift between the two.\n */\nexport function describeUndeployTarget(resolution: UndeployTargetResolution): Check {\n if (resolution.type === 'none') {\n return {message: resolution.message, solution: resolution.solution, status: 'skip'}\n }\n\n const {target} = resolution\n if (target.deletes === 'config') {\n return {\n message: target.title\n ? `Undeploys the installation config for \"${target.title}\"`\n : 'Undeploys the installation config',\n status: 'pass',\n }\n }\n if (target.type === 'studio') {\n return {message: `Undeploys studio ${target.url ?? target.id}`, status: 'pass'}\n }\n const name = target.title ? `\"${target.title}\" (${target.id})` : target.id\n return {message: `Undeploys application ${name}`, status: 'pass'}\n}\n\nexport function renderUndeployPlan(plan: UndeployPlan, output: Output): void {\n const problems = plan.checks.filter((check) => check.status === 'fail')\n const warnings = plan.checks.filter((check) => check.status === 'warn')\n\n output.log('\\nDry run — no changes made.\\n')\n\n // Only pass/skip here; problems and warnings render below with their fixes.\n // A passing target check already says what gets undeployed, so an\n // undeployable plan needs no extra verdict line.\n for (const check of plan.checks) {\n if (check.status === 'pass' || check.status === 'skip') {\n const fix = check.solution ? `: ${check.solution}` : ''\n output.log(` ${checkStatusIcon(check.status)} ${check.message}${fix}`)\n }\n }\n\n if (plan.target) renderTarget(plan.target, output)\n\n if (!canUndeploy(plan)) {\n if (problems.length > 0) {\n const label = undeployLabel(plan.target, plan.type)\n output.log(\n styleText(\n 'red',\n `\\n${checkStatusIcon('fail')} ${label[0].toUpperCase()}${label.slice(1)} can not be undeployed.`,\n ),\n )\n } else {\n output.log('\\nNothing to undeploy.')\n }\n }\n\n renderIssues(output, 'Problems to fix:', problems)\n renderIssues(output, 'Warnings:', warnings)\n}\n\nfunction renderTarget(target: UndeployTarget, output: Output): void {\n const rows: [string, string | null][] = [\n ['Title', target.title],\n ['ID', target.id],\n ['URL', target.url],\n ]\n\n output.log('')\n for (const [key, value] of rows) {\n if (value) output.log(` ${key.padEnd(8)} ${styleText('yellow', value)}`)\n }\n\n // Adapter-authored lines about what gets deleted (interfaces, config snapshots, …)\n for (const entry of target.summary ?? []) {\n for (const line of entry.split('\\n')) output.log(` ${line}`)\n }\n}\n"],"names":["styleText","checkStatusIcon","renderIssues","canUndeploy","plan","target","checks","every","check","status","undeployLabel","type","deletes","title","undeployPlanToJson","errors","warnings","message","solution","push","application","payload","reason","find","url","describeUndeployTarget","resolution","id","name","renderUndeployPlan","output","problems","filter","log","fix","renderTarget","length","label","toUpperCase","slice","rows","key","value","padEnd","entry","summary","line","split"],"mappings":"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"}
|
|
@@ -0,0 +1,126 @@
|
|
|
1
|
+
import { basename, resolve } from 'node:path';
|
|
2
|
+
import { Flags } from '@oclif/core';
|
|
3
|
+
import { exitCodes, SanityCommand, subdebug } from '@sanity/cli-core';
|
|
4
|
+
import { AssetFileError } from '../../actions/assets/assetFileError.js';
|
|
5
|
+
import { uploadAssetWithProgress } from '../../actions/assets/uploadAssetWithProgress.js';
|
|
6
|
+
import { promptForProject } from '../../prompts/promptForProject.js';
|
|
7
|
+
import { getDatasetFlag, getProjectIdFlag } from '../../util/sharedFlags.js';
|
|
8
|
+
import { defineCommandTelemetry } from '../../util/telemetry/commandTelemetry.js';
|
|
9
|
+
const uploadAssetDebug = subdebug('assets:upload');
|
|
10
|
+
const flags = {
|
|
11
|
+
...getProjectIdFlag({
|
|
12
|
+
description: 'Project ID to upload the asset to',
|
|
13
|
+
semantics: 'override'
|
|
14
|
+
}),
|
|
15
|
+
...getDatasetFlag({
|
|
16
|
+
description: 'Dataset to upload the asset to',
|
|
17
|
+
semantics: 'override'
|
|
18
|
+
}),
|
|
19
|
+
'content-type': Flags.string({
|
|
20
|
+
description: 'MIME type of the asset, such as image/png or application/pdf',
|
|
21
|
+
helpValue: '<mime-type>'
|
|
22
|
+
}),
|
|
23
|
+
file: Flags.string({
|
|
24
|
+
description: 'Path to the local file to upload',
|
|
25
|
+
helpValue: '<path>',
|
|
26
|
+
required: true
|
|
27
|
+
}),
|
|
28
|
+
filename: Flags.string({
|
|
29
|
+
description: 'Original filename stored on the asset document. Defaults to the local filename',
|
|
30
|
+
helpValue: '<filename>'
|
|
31
|
+
}),
|
|
32
|
+
type: Flags.custom({
|
|
33
|
+
default: 'image',
|
|
34
|
+
description: 'Asset type to create',
|
|
35
|
+
options: [
|
|
36
|
+
'image',
|
|
37
|
+
'file'
|
|
38
|
+
]
|
|
39
|
+
})()
|
|
40
|
+
};
|
|
41
|
+
export class UploadAssetCommand extends SanityCommand {
|
|
42
|
+
static description = 'Upload one local image or file to a Sanity dataset and print the asset document as JSON';
|
|
43
|
+
static examples = [
|
|
44
|
+
{
|
|
45
|
+
command: '<%= config.bin %> <%= command.id %> --file ./hero.png --type image --dataset production',
|
|
46
|
+
description: 'Upload an image using the configured project'
|
|
47
|
+
},
|
|
48
|
+
{
|
|
49
|
+
command: '<%= config.bin %> <%= command.id %> --file ./brief.pdf --type file --content-type application/pdf --project-id abc123 --dataset production',
|
|
50
|
+
description: 'Upload a file with explicit project, dataset, and MIME type'
|
|
51
|
+
}
|
|
52
|
+
];
|
|
53
|
+
static flags = flags;
|
|
54
|
+
static hiddenAliases = [
|
|
55
|
+
'asset:upload'
|
|
56
|
+
];
|
|
57
|
+
static telemetry = defineCommandTelemetry(flags, {
|
|
58
|
+
redact: [
|
|
59
|
+
'file',
|
|
60
|
+
'filename'
|
|
61
|
+
]
|
|
62
|
+
});
|
|
63
|
+
async run() {
|
|
64
|
+
const { flags } = await this.parse(UploadAssetCommand);
|
|
65
|
+
const filePath = resolve(flags.file);
|
|
66
|
+
const cliConfig = await this.tryGetCliConfig();
|
|
67
|
+
const projectId = await this.getProjectId({
|
|
68
|
+
fallback: ()=>promptForProject({})
|
|
69
|
+
});
|
|
70
|
+
const dataset = flags.dataset ?? cliConfig.api?.dataset;
|
|
71
|
+
if (!dataset) {
|
|
72
|
+
this.error('Asset upload failed: Dataset is required. Pass --dataset <name> or configure a dataset in sanity.cli.ts.', {
|
|
73
|
+
exit: exitCodes.USAGE_ERROR
|
|
74
|
+
});
|
|
75
|
+
}
|
|
76
|
+
try {
|
|
77
|
+
const asset = await uploadAssetWithProgress({
|
|
78
|
+
assetType: flags.type,
|
|
79
|
+
contentType: flags['content-type'],
|
|
80
|
+
dataset,
|
|
81
|
+
filename: flags.filename ?? basename(filePath),
|
|
82
|
+
filePath,
|
|
83
|
+
isInteractive: this.resolveIsInteractive(),
|
|
84
|
+
logToStderr: (message)=>this.logToStderr(message),
|
|
85
|
+
projectId
|
|
86
|
+
});
|
|
87
|
+
const fieldType = flags.type === 'image' ? 'image' : 'file';
|
|
88
|
+
this.log(JSON.stringify({
|
|
89
|
+
asset: {
|
|
90
|
+
_id: asset._id,
|
|
91
|
+
_type: asset._type,
|
|
92
|
+
extension: asset.extension,
|
|
93
|
+
mimeType: asset.mimeType,
|
|
94
|
+
originalFilename: asset.originalFilename,
|
|
95
|
+
size: asset.size,
|
|
96
|
+
url: asset.url
|
|
97
|
+
},
|
|
98
|
+
reference: {
|
|
99
|
+
_type: fieldType,
|
|
100
|
+
asset: {
|
|
101
|
+
_ref: asset._id,
|
|
102
|
+
_type: 'reference'
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
}, null, 2));
|
|
106
|
+
} catch (error) {
|
|
107
|
+
if (error instanceof Error && error.message === 'SIGINT') throw error;
|
|
108
|
+
if (error instanceof AssetFileError) {
|
|
109
|
+
if (error.reason === 'not-file') {
|
|
110
|
+
this.error('Asset upload failed: --file must point to a file, not a directory.', {
|
|
111
|
+
exit: exitCodes.USAGE_ERROR
|
|
112
|
+
});
|
|
113
|
+
}
|
|
114
|
+
this.error('Asset upload failed: Cannot read the local file. Check that --file points to a readable file, then retry.', {
|
|
115
|
+
exit: exitCodes.USAGE_ERROR
|
|
116
|
+
});
|
|
117
|
+
}
|
|
118
|
+
uploadAssetDebug('Asset upload failed', error);
|
|
119
|
+
this.error('Asset upload failed. Check authentication, write access, and that the local file is still readable, then retry.', {
|
|
120
|
+
exit: exitCodes.RUNTIME_ERROR
|
|
121
|
+
});
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
//# sourceMappingURL=upload.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../../src/commands/assets/upload.ts"],"sourcesContent":["import {basename, resolve} from 'node:path'\n\nimport {Flags} from '@oclif/core'\nimport {type FlagInput} from '@oclif/core/interfaces'\nimport {exitCodes, SanityCommand, subdebug} from '@sanity/cli-core'\n\nimport {AssetFileError} from '../../actions/assets/assetFileError.js'\nimport {uploadAssetWithProgress} from '../../actions/assets/uploadAssetWithProgress.js'\nimport {promptForProject} from '../../prompts/promptForProject.js'\nimport {type AssetType} from '../../services/assets.js'\nimport {getDatasetFlag, getProjectIdFlag} from '../../util/sharedFlags.js'\nimport {defineCommandTelemetry} from '../../util/telemetry/commandTelemetry.js'\n\nconst uploadAssetDebug = subdebug('assets:upload')\n\nconst flags = {\n ...getProjectIdFlag({\n description: 'Project ID to upload the asset to',\n semantics: 'override',\n }),\n ...getDatasetFlag({description: 'Dataset to upload the asset to', semantics: 'override'}),\n 'content-type': Flags.string({\n description: 'MIME type of the asset, such as image/png or application/pdf',\n helpValue: '<mime-type>',\n }),\n file: Flags.string({\n description: 'Path to the local file to upload',\n helpValue: '<path>',\n required: true,\n }),\n filename: Flags.string({\n description: 'Original filename stored on the asset document. Defaults to the local filename',\n helpValue: '<filename>',\n }),\n type: Flags.custom<AssetType>({\n default: 'image',\n description: 'Asset type to create',\n options: ['image', 'file'],\n })(),\n} satisfies FlagInput\n\nexport class UploadAssetCommand extends SanityCommand<typeof UploadAssetCommand> {\n static override description =\n 'Upload one local image or file to a Sanity dataset and print the asset document as JSON'\n\n static override examples = [\n {\n command:\n '<%= config.bin %> <%= command.id %> --file ./hero.png --type image --dataset production',\n description: 'Upload an image using the configured project',\n },\n {\n command:\n '<%= config.bin %> <%= command.id %> --file ./brief.pdf --type file --content-type application/pdf --project-id abc123 --dataset production',\n description: 'Upload a file with explicit project, dataset, and MIME type',\n },\n ]\n\n static override flags = flags\n\n static override hiddenAliases: string[] = ['asset:upload']\n\n static telemetry = defineCommandTelemetry(flags, {\n redact: ['file', 'filename'],\n })\n\n public async run(): Promise<void> {\n const {flags} = await this.parse(UploadAssetCommand)\n const filePath = resolve(flags.file)\n\n const cliConfig = await this.tryGetCliConfig()\n const projectId = await this.getProjectId({fallback: () => promptForProject({})})\n const dataset = flags.dataset ?? cliConfig.api?.dataset\n if (!dataset) {\n this.error(\n 'Asset upload failed: Dataset is required. Pass --dataset <name> or configure a dataset in sanity.cli.ts.',\n {exit: exitCodes.USAGE_ERROR},\n )\n }\n\n try {\n const asset = await uploadAssetWithProgress({\n assetType: flags.type,\n contentType: flags['content-type'],\n dataset,\n filename: flags.filename ?? basename(filePath),\n filePath,\n isInteractive: this.resolveIsInteractive(),\n logToStderr: (message) => this.logToStderr(message),\n projectId,\n })\n const fieldType = flags.type === 'image' ? 'image' : 'file'\n\n this.log(\n JSON.stringify(\n {\n asset: {\n _id: asset._id,\n _type: asset._type,\n extension: asset.extension,\n mimeType: asset.mimeType,\n originalFilename: asset.originalFilename,\n size: asset.size,\n url: asset.url,\n },\n reference: {\n _type: fieldType,\n asset: {_ref: asset._id, _type: 'reference'},\n },\n },\n null,\n 2,\n ),\n )\n } catch (error) {\n if (error instanceof Error && error.message === 'SIGINT') throw error\n if (error instanceof AssetFileError) {\n if (error.reason === 'not-file') {\n this.error('Asset upload failed: --file must point to a file, not a directory.', {\n exit: exitCodes.USAGE_ERROR,\n })\n }\n this.error(\n 'Asset upload failed: Cannot read the local file. Check that --file points to a readable file, then retry.',\n {exit: exitCodes.USAGE_ERROR},\n )\n }\n uploadAssetDebug('Asset upload failed', error)\n this.error(\n 'Asset upload failed. Check authentication, write access, and that the local file is still readable, then retry.',\n {\n exit: exitCodes.RUNTIME_ERROR,\n },\n )\n }\n }\n}\n"],"names":["basename","resolve","Flags","exitCodes","SanityCommand","subdebug","AssetFileError","uploadAssetWithProgress","promptForProject","getDatasetFlag","getProjectIdFlag","defineCommandTelemetry","uploadAssetDebug","flags","description","semantics","string","helpValue","file","required","filename","type","custom","default","options","UploadAssetCommand","examples","command","hiddenAliases","telemetry","redact","run","parse","filePath","cliConfig","tryGetCliConfig","projectId","getProjectId","fallback","dataset","api","error","exit","USAGE_ERROR","asset","assetType","contentType","isInteractive","resolveIsInteractive","logToStderr","message","fieldType","log","JSON","stringify","_id","_type","extension","mimeType","originalFilename","size","url","reference","_ref","Error","reason","RUNTIME_ERROR"],"mappings":"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"}
|
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
import { Args, Flags } from '@oclif/core';
|
|
2
2
|
import { exitCodes, SanityCommand, subdebug } from '@sanity/cli-core';
|
|
3
3
|
import { select } from '@sanity/cli-core/ux';
|
|
4
|
-
import { Table } from 'console-table-printer';
|
|
5
4
|
import { isAfter } from 'date-fns/isAfter';
|
|
6
5
|
import { isValid } from 'date-fns/isValid';
|
|
7
6
|
import { lightFormat } from 'date-fns/lightFormat';
|
|
@@ -10,6 +9,7 @@ import { assertDatasetExists } from '../../actions/backup/assertDatasetExist.js'
|
|
|
10
9
|
import { promptForProject } from '../../prompts/promptForProject.js';
|
|
11
10
|
import { listBackups } from '../../services/backup.js';
|
|
12
11
|
import { listDatasets } from '../../services/datasets.js';
|
|
12
|
+
import { Table } from '../../util/responsiveTable.js';
|
|
13
13
|
import { getProjectIdFlag } from '../../util/sharedFlags.js';
|
|
14
14
|
const listBackupDebug = subdebug('backup:list');
|
|
15
15
|
const DEFAULT_LIST_BACKUP_LIMIT = 30;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../../src/commands/backups/list.ts"],"sourcesContent":["import {Args, Flags} from '@oclif/core'\nimport {exitCodes, 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} from 'date-fns/isAfter'\nimport {isValid} from 'date-fns/isValid'\nimport {lightFormat} from 'date-fns/lightFormat'\nimport {parse} from 'date-fns/parse'\n\nimport {assertDatasetExists} from '../../actions/backup/assertDatasetExist.js'\nimport {promptForProject} from '../../prompts/promptForProject.js'\nimport {listBackups} from '../../services/backup.js'\nimport {listDatasets} from '../../services/datasets.js'\nimport {getProjectIdFlag} from '../../util/sharedFlags.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 ...getProjectIdFlag({\n description: 'Project ID to list backups for',\n semantics: 'override',\n }),\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 static override hiddenAliases: string[] = ['backup:list']\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 fallback: () =>\n promptForProject({\n requiredPermissions: [{grant: 'read', permission: 'sanity.project.datasets'}],\n }),\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: exitCodes.RUNTIME_ERROR})\n }\n\n if (datasets.length === 0) {\n this.error('No datasets found in this project.', {exit: exitCodes.RUNTIME_ERROR})\n }\n\n if (dataset) {\n assertDatasetExists(datasets, dataset, this.output)\n } else if (this.isUnattended()) {\n this.error('Dataset is required in unattended mode. Pass it as the `<dataset>` argument.', {\n exit: exitCodes.USAGE_ERROR,\n })\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: exitCodes.RUNTIME_ERROR})\n }\n } catch (err) {\n this.error(`Parsing date flags: ${err instanceof Error ? err.message : err}`, {\n exit: exitCodes.RUNTIME_ERROR,\n })\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: exitCodes.RUNTIME_ERROR,\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 this.log(table.render())\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: exitCodes.RUNTIME_ERROR})\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: exitCodes.RUNTIME_ERROR,\n })\n }\n }\n}\n"],"names":["Args","Flags","exitCodes","SanityCommand","subdebug","select","Table","isAfter","isValid","lightFormat","parse","assertDatasetExists","promptForProject","listBackups","listDatasets","getProjectIdFlag","listBackupDebug","DEFAULT_LIST_BACKUP_LIMIT","ListBackupCommand","args","dataset","string","description","required","examples","command","flags","semantics","after","before","limit","integer","char","default","hiddenAliases","run","projectId","getProjectId","fallback","requiredPermissions","grant","permission","datasets","error","message","Error","String","exit","RUNTIME_ERROR","length","output","isUnattended","USAGE_ERROR","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","render","date","flagName","undefined","parsedDate","choices","map","value"],"mappings":"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"}
|
|
1
|
+
{"version":3,"sources":["../../../src/commands/backups/list.ts"],"sourcesContent":["import {Args, Flags} from '@oclif/core'\nimport {exitCodes, SanityCommand, subdebug} from '@sanity/cli-core'\nimport {select} from '@sanity/cli-core/ux'\nimport {type DatasetsResponse} from '@sanity/client'\nimport {isAfter} from 'date-fns/isAfter'\nimport {isValid} from 'date-fns/isValid'\nimport {lightFormat} from 'date-fns/lightFormat'\nimport {parse} from 'date-fns/parse'\n\nimport {assertDatasetExists} from '../../actions/backup/assertDatasetExist.js'\nimport {promptForProject} from '../../prompts/promptForProject.js'\nimport {listBackups} from '../../services/backup.js'\nimport {listDatasets} from '../../services/datasets.js'\nimport {Table} from '../../util/responsiveTable.js'\nimport {getProjectIdFlag} from '../../util/sharedFlags.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 ...getProjectIdFlag({\n description: 'Project ID to list backups for',\n semantics: 'override',\n }),\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 static override hiddenAliases: string[] = ['backup:list']\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 fallback: () =>\n promptForProject({\n requiredPermissions: [{grant: 'read', permission: 'sanity.project.datasets'}],\n }),\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: exitCodes.RUNTIME_ERROR})\n }\n\n if (datasets.length === 0) {\n this.error('No datasets found in this project.', {exit: exitCodes.RUNTIME_ERROR})\n }\n\n if (dataset) {\n assertDatasetExists(datasets, dataset, this.output)\n } else if (this.isUnattended()) {\n this.error('Dataset is required in unattended mode. Pass it as the `<dataset>` argument.', {\n exit: exitCodes.USAGE_ERROR,\n })\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: exitCodes.RUNTIME_ERROR})\n }\n } catch (err) {\n this.error(`Parsing date flags: ${err instanceof Error ? err.message : err}`, {\n exit: exitCodes.RUNTIME_ERROR,\n })\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: exitCodes.RUNTIME_ERROR,\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 this.log(table.render())\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: exitCodes.RUNTIME_ERROR})\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: exitCodes.RUNTIME_ERROR,\n })\n }\n }\n}\n"],"names":["Args","Flags","exitCodes","SanityCommand","subdebug","select","isAfter","isValid","lightFormat","parse","assertDatasetExists","promptForProject","listBackups","listDatasets","Table","getProjectIdFlag","listBackupDebug","DEFAULT_LIST_BACKUP_LIMIT","ListBackupCommand","args","dataset","string","description","required","examples","command","flags","semantics","after","before","limit","integer","char","default","hiddenAliases","run","projectId","getProjectId","fallback","requiredPermissions","grant","permission","datasets","error","message","Error","String","exit","RUNTIME_ERROR","length","output","isUnattended","USAGE_ERROR","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","render","date","flagName","undefined","parsedDate","choices","map","value"],"mappings":"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"}
|
|
@@ -6,7 +6,6 @@ import { subdebug } from '@sanity/cli-core/debug';
|
|
|
6
6
|
import { getCliExecutionContext } from '@sanity/cli-core/executionContext';
|
|
7
7
|
import { SanityCommand } from '@sanity/cli-core/SanityCommand';
|
|
8
8
|
import { spinner } from '@sanity/cli-core/ux';
|
|
9
|
-
import { Table } from 'console-table-printer';
|
|
10
9
|
import { formatDistance } from 'date-fns/formatDistance';
|
|
11
10
|
import { formatDistanceToNow } from 'date-fns/formatDistanceToNow';
|
|
12
11
|
import { parseISO } from 'date-fns/parseISO';
|
|
@@ -16,6 +15,7 @@ import { promptForDatasetName } from '../../prompts/promptForDatasetName.js';
|
|
|
16
15
|
import { promptForProject } from '../../prompts/promptForProject.js';
|
|
17
16
|
import { copyDataset, followCopyJobProgress, listDatasetCopyJobs, listDatasets } from '../../services/datasets.js';
|
|
18
17
|
import { formatCliErrorMessages } from '../../util/formatCliErrorMessages.js';
|
|
18
|
+
import { Table } from '../../util/responsiveTable.js';
|
|
19
19
|
import { getProjectIdFlag } from '../../util/sharedFlags.js';
|
|
20
20
|
const copyDatasetDebug = subdebug('dataset:copy');
|
|
21
21
|
export class CopyDatasetCommand extends SanityCommand {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../../src/commands/datasets/copy.ts"],"sourcesContent":["import {styleText} from 'node:util'\n\nimport {Args, Flags} from '@oclif/core'\nimport {exit} from '@oclif/core/errors'\nimport {exitCodes} from '@sanity/cli-core'\nimport {subdebug} from '@sanity/cli-core/debug'\nimport {getCliExecutionContext} from '@sanity/cli-core/executionContext'\nimport {SanityCommand} from '@sanity/cli-core/SanityCommand'\nimport {spinner} from '@sanity/cli-core/ux'\nimport {Table} from 'console-table-printer'\nimport {formatDistance} from 'date-fns/formatDistance'\nimport {formatDistanceToNow} from 'date-fns/formatDistanceToNow'\nimport {parseISO} from 'date-fns/parseISO'\n\nimport {validateDatasetName} from '../../actions/dataset/validateDatasetName.js'\nimport {promptForDataset} from '../../prompts/promptForDataset.js'\nimport {promptForDatasetName} from '../../prompts/promptForDatasetName.js'\nimport {promptForProject} from '../../prompts/promptForProject.js'\nimport {\n copyDataset,\n type CopyJobProgressEvent,\n type DatasetCopyJob,\n followCopyJobProgress,\n listDatasetCopyJobs,\n listDatasets,\n} from '../../services/datasets.js'\nimport {formatCliErrorMessages} from '../../util/formatCliErrorMessages.js'\nimport {getProjectIdFlag} from '../../util/sharedFlags.js'\n\nconst copyDatasetDebug = subdebug('dataset:copy')\n\nexport class CopyDatasetCommand extends SanityCommand<typeof CopyDatasetCommand> {\n static override args = {\n source: Args.string({\n description: 'Name of the dataset to copy from',\n required: false,\n }),\n target: Args.string({\n description: 'Name of the dataset to copy to',\n required: false,\n }),\n }\n\n static override description = 'Copy a dataset or manage copy jobs'\n\n static override examples = [\n {\n command: '<%= config.bin %> <%= command.id %>',\n description: 'Interactively copy a dataset',\n },\n {\n command: '<%= config.bin %> <%= command.id %> source-dataset',\n description: 'Copy from source-dataset (prompts for target)',\n },\n {\n command: '<%= config.bin %> <%= command.id %> source-dataset target-dataset',\n description: 'Copy from source-dataset to target-dataset',\n },\n {\n command: '<%= config.bin %> <%= command.id %> --skip-history source target',\n description: 'Copy without preserving document history (faster for large datasets)',\n },\n {\n command: '<%= config.bin %> <%= command.id %> --skip-content-releases source target',\n description: 'Copy without content release documents',\n },\n {\n command: '<%= config.bin %> <%= command.id %> --detach source target',\n description: 'Start copy job without waiting for completion',\n },\n {\n command: '<%= config.bin %> <%= command.id %> --attach <job-id>',\n description: 'Attach to a running copy job to follow progress',\n },\n {\n command: '<%= config.bin %> <%= command.id %> --list',\n description: 'List all dataset copy jobs',\n },\n {\n command: '<%= config.bin %> <%= command.id %> --list --offset 2 --limit 10',\n description: 'List copy jobs with pagination',\n },\n ]\n\n static override flags = {\n ...getProjectIdFlag({\n description: 'Project ID to copy dataset in',\n semantics: 'override',\n }),\n attach: Flags.string({\n description: 'Attach to the running copy process to show progress',\n exclusive: ['list', 'detach', 'skip-history'],\n required: false,\n }),\n detach: Flags.boolean({\n description: 'Start the copy without waiting for it to finish',\n exclusive: ['list', 'attach'],\n required: false,\n }),\n limit: Flags.integer({\n dependsOn: ['list'],\n description: 'Maximum number of jobs returned (default 10, max 1000)',\n max: 1000,\n required: false,\n }),\n list: Flags.boolean({\n description: 'Lists all dataset copy jobs',\n exclusive: ['attach', 'detach', 'skip-history'],\n required: false,\n }),\n offset: Flags.integer({\n dependsOn: ['list'],\n description: 'Start position in the list of jobs (default 0)',\n required: false,\n }),\n 'skip-content-releases': Flags.boolean({\n description: \"Don't copy content release documents to the target dataset\",\n exclusive: ['list', 'attach'],\n required: false,\n }),\n 'skip-history': Flags.boolean({\n description: \"Don't preserve document history on copy\",\n exclusive: ['list', 'attach'],\n required: false,\n }),\n }\n\n static override hiddenAliases: string[] = ['dataset:copy']\n\n public async run(): Promise<void> {\n const {args, flags} = await this.parse(CopyDatasetCommand)\n\n if (!flags.list && !flags.attach && this.isUnattended()) {\n const errors: string[] = []\n\n if (!args.source) {\n errors.push('Source dataset is required. Pass it as the `<source>` argument.')\n }\n if (!args.target) {\n errors.push('Target dataset is required. Pass it as the `<target>` argument.')\n }\n\n if (errors.length > 0) {\n this.output.error(formatCliErrorMessages(errors), {exit: exitCodes.USAGE_ERROR})\n }\n }\n\n const projectId = await this.getProjectId({\n fallback: () =>\n promptForProject({\n requiredPermissions: [\n {grant: 'read', permission: 'sanity.project.datasets'},\n {grant: 'create', permission: 'sanity.project.datasets'},\n ],\n }),\n })\n\n // Route to appropriate mode\n if (flags.list) {\n return this.handleListMode(projectId, flags)\n }\n\n if (flags.attach) {\n return this.handleAttachMode(projectId, flags.attach)\n }\n\n return this.handleCopyMode(projectId, args, flags)\n }\n\n private displayCopyJobsTable(jobs: DatasetCopyJob[]): void {\n const table = new Table({\n columns: [\n {alignment: 'left', name: 'id', title: 'Job ID'},\n {alignment: 'left', name: 'sourceDataset', title: 'Source Dataset'},\n {alignment: 'left', name: 'targetDataset', title: 'Target Dataset'},\n {alignment: 'left', name: 'state', title: 'State'},\n {alignment: 'left', name: 'withHistory', title: 'With history'},\n {alignment: 'left', name: 'timeStarted', title: 'Time started'},\n {alignment: 'left', name: 'timeTaken', title: 'Time taken'},\n ],\n title: 'Dataset copy jobs for this project in descending order',\n })\n\n for (const job of jobs) {\n const {createdAt, id, sourceDataset, state, targetDataset, updatedAt, withHistory} = job\n\n let timeStarted = ''\n if (createdAt !== '') {\n timeStarted = formatDistanceToNow(parseISO(createdAt))\n }\n\n let timeTaken = ''\n if (updatedAt !== '') {\n timeTaken = formatDistance(parseISO(updatedAt), parseISO(createdAt))\n }\n\n let color: '' | 'green' | 'red' | 'yellow'\n switch (state) {\n case 'completed': {\n color = 'green'\n break\n }\n case 'failed': {\n color = 'red'\n break\n }\n case 'pending': {\n color = 'yellow'\n break\n }\n default: {\n color = ''\n }\n }\n\n table.addRow(\n {\n id,\n sourceDataset,\n state,\n targetDataset,\n timeStarted: `${timeStarted} ago`,\n timeTaken,\n withHistory,\n },\n {color},\n )\n }\n\n this.output.log(table.render())\n }\n\n private async handleAttachMode(projectId: string, jobId: string): Promise<void> {\n copyDatasetDebug('Attaching to copy job %s', jobId)\n\n if (jobId.trim() === '') {\n return this.output.error('Please supply a valid jobId', {exit: exitCodes.RUNTIME_ERROR})\n }\n\n try {\n await this.subscribeToProgress(projectId, jobId)\n this.output.log(`Job ${styleText('green', jobId)} completed`)\n } catch (error) {\n const message = error instanceof Error ? error.message : String(error)\n copyDatasetDebug('Failed to attach to copy job: %s', message, error)\n return this.output.error(`Failed to attach to copy job: ${message}`, {\n exit: exitCodes.RUNTIME_ERROR,\n })\n }\n }\n\n private async handleCopyMode(\n projectId: string,\n args: {source?: string; target?: string},\n flags: {detach?: boolean; 'skip-content-releases'?: boolean; 'skip-history'?: boolean},\n ): Promise<void> {\n copyDatasetDebug('Starting copy mode')\n\n const skipHistory = Boolean(flags['skip-history'])\n const skipContentReleases = Boolean(flags['skip-content-releases'])\n\n // Surfaced before any prompting so the flag is still actionable: a copy job\n // can't be canceled once started, so mentioning it after the job kicks off\n // leaves nothing to decide.\n if (!skipHistory) {\n this.output.log(\n `Note: You can run this command with flag '--skip-history'. The flag will reduce copy time in larger datasets.`,\n )\n }\n\n // Get and validate source dataset\n let sourceDataset = args.source\n if (sourceDataset) {\n const nameError = validateDatasetName(sourceDataset)\n if (nameError) {\n return this.output.error(nameError, {exit: exitCodes.USAGE_ERROR})\n }\n }\n\n let datasetsResponse\n try {\n datasetsResponse = await listDatasets(projectId)\n } catch (error) {\n const message = error instanceof Error ? error.message : String(error)\n copyDatasetDebug('Failed to fetch datasets: %s', message, error)\n return this.output.error(`Failed to fetch datasets: ${message}`, {\n exit: exitCodes.RUNTIME_ERROR,\n })\n }\n\n const datasetNames = new Set(datasetsResponse.map((ds) => ds.name))\n\n // Prompt for source if not provided\n if (!sourceDataset) {\n sourceDataset = await promptForDataset({\n datasets: datasetsResponse,\n })\n }\n\n if (!datasetNames.has(sourceDataset)) {\n return this.output.error(`Source dataset \"${sourceDataset}\" doesn't exist`, {\n exit: exitCodes.RUNTIME_ERROR,\n })\n }\n\n // Get and validate target dataset\n let targetDataset = args.target\n if (targetDataset) {\n const nameError = validateDatasetName(targetDataset)\n if (nameError) {\n return this.output.error(nameError, {exit: exitCodes.USAGE_ERROR})\n }\n } else {\n targetDataset = await promptForDatasetName({\n message: 'Target dataset name:',\n })\n }\n\n if (datasetNames.has(targetDataset)) {\n return this.output.error(`Target dataset \"${targetDataset}\" already exists`, {\n exit: exitCodes.RUNTIME_ERROR,\n })\n }\n\n // Start the copy job\n try {\n this.output.log(\n `Copying dataset ${styleText('green', sourceDataset)} to ${styleText('green', targetDataset)}...`,\n )\n\n const response = await copyDataset({\n projectId,\n skipContentReleases,\n skipHistory,\n sourceDataset,\n targetDataset,\n })\n\n this.output.log(`Job ${styleText('green', response.jobId)} started`)\n\n if (flags.detach) {\n return\n }\n\n await this.subscribeToProgress(projectId, response.jobId)\n this.output.log(`Job ${styleText('green', response.jobId)} completed`)\n } catch (error) {\n const message = error instanceof Error ? error.message : String(error)\n copyDatasetDebug('Dataset copying failed: %s', message, error)\n return this.output.error(`Dataset copying failed: ${message}`, {\n exit: exitCodes.RUNTIME_ERROR,\n })\n }\n }\n\n private async handleListMode(\n projectId: string,\n flags: {limit?: number; offset?: number},\n ): Promise<void> {\n copyDatasetDebug('Listing dataset copy jobs')\n\n try {\n const jobs = await listDatasetCopyJobs({\n limit: flags.limit,\n offset: flags.offset,\n projectId,\n })\n\n if (jobs.length === 0) {\n this.output.log(\"This project doesn't have any dataset copy jobs\")\n return\n }\n\n this.displayCopyJobsTable(jobs)\n } catch (error) {\n const message = error instanceof Error ? error.message : String(error)\n copyDatasetDebug('Failed to list dataset copy jobs: %s', message, error)\n return this.output.error(`Failed to list dataset copy jobs: ${message}`, {\n exit: exitCodes.RUNTIME_ERROR,\n })\n }\n }\n\n private async subscribeToProgress(projectId: string, jobId: string): Promise<void> {\n const spin = spinner('').start()\n const hasCliExecutionContext = Boolean(getCliExecutionContext())\n\n return new Promise<void>((resolve, reject) => {\n const sigintHandler = () => {\n subscription.unsubscribe()\n spin.fail('Copy interrupted.')\n exit(130)\n }\n\n const subscription = followCopyJobProgress({jobId, projectId}).subscribe({\n complete: () => {\n if (!hasCliExecutionContext) process.off('SIGINT', sigintHandler)\n spin.succeed('Copy finished.')\n resolve()\n },\n error: (err) => {\n if (!hasCliExecutionContext) process.off('SIGINT', sigintHandler)\n spin.fail('Copy failed.')\n reject(err)\n },\n next: (event: CopyJobProgressEvent) => {\n if (typeof event.progress === 'number') {\n spin.text = `Copy in progress: ${event.progress}%`\n }\n },\n })\n\n if (!hasCliExecutionContext) process.once('SIGINT', sigintHandler)\n })\n }\n}\n"],"names":["styleText","Args","Flags","exit","exitCodes","subdebug","getCliExecutionContext","SanityCommand","spinner","Table","formatDistance","formatDistanceToNow","parseISO","validateDatasetName","promptForDataset","promptForDatasetName","promptForProject","copyDataset","followCopyJobProgress","listDatasetCopyJobs","listDatasets","formatCliErrorMessages","getProjectIdFlag","copyDatasetDebug","CopyDatasetCommand","args","source","string","description","required","target","examples","command","flags","semantics","attach","exclusive","detach","boolean","limit","integer","dependsOn","max","list","offset","hiddenAliases","run","parse","isUnattended","errors","push","length","output","error","USAGE_ERROR","projectId","getProjectId","fallback","requiredPermissions","grant","permission","handleListMode","handleAttachMode","handleCopyMode","displayCopyJobsTable","jobs","table","columns","alignment","name","title","job","createdAt","id","sourceDataset","state","targetDataset","updatedAt","withHistory","timeStarted","timeTaken","color","addRow","log","render","jobId","trim","RUNTIME_ERROR","subscribeToProgress","message","Error","String","skipHistory","Boolean","skipContentReleases","nameError","datasetsResponse","datasetNames","Set","map","ds","datasets","has","response","spin","start","hasCliExecutionContext","Promise","resolve","reject","sigintHandler","subscription","unsubscribe","fail","subscribe","complete","process","off","succeed","err","next","event","progress","text","once"],"mappings":"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"}
|
|
1
|
+
{"version":3,"sources":["../../../src/commands/datasets/copy.ts"],"sourcesContent":["import {styleText} from 'node:util'\n\nimport {Args, Flags} from '@oclif/core'\nimport {exit} from '@oclif/core/errors'\nimport {exitCodes} from '@sanity/cli-core'\nimport {subdebug} from '@sanity/cli-core/debug'\nimport {getCliExecutionContext} from '@sanity/cli-core/executionContext'\nimport {SanityCommand} from '@sanity/cli-core/SanityCommand'\nimport {spinner} from '@sanity/cli-core/ux'\nimport {formatDistance} from 'date-fns/formatDistance'\nimport {formatDistanceToNow} from 'date-fns/formatDistanceToNow'\nimport {parseISO} from 'date-fns/parseISO'\n\nimport {validateDatasetName} from '../../actions/dataset/validateDatasetName.js'\nimport {promptForDataset} from '../../prompts/promptForDataset.js'\nimport {promptForDatasetName} from '../../prompts/promptForDatasetName.js'\nimport {promptForProject} from '../../prompts/promptForProject.js'\nimport {\n copyDataset,\n type CopyJobProgressEvent,\n type DatasetCopyJob,\n followCopyJobProgress,\n listDatasetCopyJobs,\n listDatasets,\n} from '../../services/datasets.js'\nimport {formatCliErrorMessages} from '../../util/formatCliErrorMessages.js'\nimport {Table} from '../../util/responsiveTable.js'\nimport {getProjectIdFlag} from '../../util/sharedFlags.js'\n\nconst copyDatasetDebug = subdebug('dataset:copy')\n\nexport class CopyDatasetCommand extends SanityCommand<typeof CopyDatasetCommand> {\n static override args = {\n source: Args.string({\n description: 'Name of the dataset to copy from',\n required: false,\n }),\n target: Args.string({\n description: 'Name of the dataset to copy to',\n required: false,\n }),\n }\n\n static override description = 'Copy a dataset or manage copy jobs'\n\n static override examples = [\n {\n command: '<%= config.bin %> <%= command.id %>',\n description: 'Interactively copy a dataset',\n },\n {\n command: '<%= config.bin %> <%= command.id %> source-dataset',\n description: 'Copy from source-dataset (prompts for target)',\n },\n {\n command: '<%= config.bin %> <%= command.id %> source-dataset target-dataset',\n description: 'Copy from source-dataset to target-dataset',\n },\n {\n command: '<%= config.bin %> <%= command.id %> --skip-history source target',\n description: 'Copy without preserving document history (faster for large datasets)',\n },\n {\n command: '<%= config.bin %> <%= command.id %> --skip-content-releases source target',\n description: 'Copy without content release documents',\n },\n {\n command: '<%= config.bin %> <%= command.id %> --detach source target',\n description: 'Start copy job without waiting for completion',\n },\n {\n command: '<%= config.bin %> <%= command.id %> --attach <job-id>',\n description: 'Attach to a running copy job to follow progress',\n },\n {\n command: '<%= config.bin %> <%= command.id %> --list',\n description: 'List all dataset copy jobs',\n },\n {\n command: '<%= config.bin %> <%= command.id %> --list --offset 2 --limit 10',\n description: 'List copy jobs with pagination',\n },\n ]\n\n static override flags = {\n ...getProjectIdFlag({\n description: 'Project ID to copy dataset in',\n semantics: 'override',\n }),\n attach: Flags.string({\n description: 'Attach to the running copy process to show progress',\n exclusive: ['list', 'detach', 'skip-history'],\n required: false,\n }),\n detach: Flags.boolean({\n description: 'Start the copy without waiting for it to finish',\n exclusive: ['list', 'attach'],\n required: false,\n }),\n limit: Flags.integer({\n dependsOn: ['list'],\n description: 'Maximum number of jobs returned (default 10, max 1000)',\n max: 1000,\n required: false,\n }),\n list: Flags.boolean({\n description: 'Lists all dataset copy jobs',\n exclusive: ['attach', 'detach', 'skip-history'],\n required: false,\n }),\n offset: Flags.integer({\n dependsOn: ['list'],\n description: 'Start position in the list of jobs (default 0)',\n required: false,\n }),\n 'skip-content-releases': Flags.boolean({\n description: \"Don't copy content release documents to the target dataset\",\n exclusive: ['list', 'attach'],\n required: false,\n }),\n 'skip-history': Flags.boolean({\n description: \"Don't preserve document history on copy\",\n exclusive: ['list', 'attach'],\n required: false,\n }),\n }\n\n static override hiddenAliases: string[] = ['dataset:copy']\n\n public async run(): Promise<void> {\n const {args, flags} = await this.parse(CopyDatasetCommand)\n\n if (!flags.list && !flags.attach && this.isUnattended()) {\n const errors: string[] = []\n\n if (!args.source) {\n errors.push('Source dataset is required. Pass it as the `<source>` argument.')\n }\n if (!args.target) {\n errors.push('Target dataset is required. Pass it as the `<target>` argument.')\n }\n\n if (errors.length > 0) {\n this.output.error(formatCliErrorMessages(errors), {exit: exitCodes.USAGE_ERROR})\n }\n }\n\n const projectId = await this.getProjectId({\n fallback: () =>\n promptForProject({\n requiredPermissions: [\n {grant: 'read', permission: 'sanity.project.datasets'},\n {grant: 'create', permission: 'sanity.project.datasets'},\n ],\n }),\n })\n\n // Route to appropriate mode\n if (flags.list) {\n return this.handleListMode(projectId, flags)\n }\n\n if (flags.attach) {\n return this.handleAttachMode(projectId, flags.attach)\n }\n\n return this.handleCopyMode(projectId, args, flags)\n }\n\n private displayCopyJobsTable(jobs: DatasetCopyJob[]): void {\n const table = new Table({\n columns: [\n {alignment: 'left', name: 'id', title: 'Job ID'},\n {alignment: 'left', name: 'sourceDataset', title: 'Source Dataset'},\n {alignment: 'left', name: 'targetDataset', title: 'Target Dataset'},\n {alignment: 'left', name: 'state', title: 'State'},\n {alignment: 'left', name: 'withHistory', title: 'With history'},\n {alignment: 'left', name: 'timeStarted', title: 'Time started'},\n {alignment: 'left', name: 'timeTaken', title: 'Time taken'},\n ],\n title: 'Dataset copy jobs for this project in descending order',\n })\n\n for (const job of jobs) {\n const {createdAt, id, sourceDataset, state, targetDataset, updatedAt, withHistory} = job\n\n let timeStarted = ''\n if (createdAt !== '') {\n timeStarted = formatDistanceToNow(parseISO(createdAt))\n }\n\n let timeTaken = ''\n if (updatedAt !== '') {\n timeTaken = formatDistance(parseISO(updatedAt), parseISO(createdAt))\n }\n\n let color: '' | 'green' | 'red' | 'yellow'\n switch (state) {\n case 'completed': {\n color = 'green'\n break\n }\n case 'failed': {\n color = 'red'\n break\n }\n case 'pending': {\n color = 'yellow'\n break\n }\n default: {\n color = ''\n }\n }\n\n table.addRow(\n {\n id,\n sourceDataset,\n state,\n targetDataset,\n timeStarted: `${timeStarted} ago`,\n timeTaken,\n withHistory,\n },\n {color},\n )\n }\n\n this.output.log(table.render())\n }\n\n private async handleAttachMode(projectId: string, jobId: string): Promise<void> {\n copyDatasetDebug('Attaching to copy job %s', jobId)\n\n if (jobId.trim() === '') {\n return this.output.error('Please supply a valid jobId', {exit: exitCodes.RUNTIME_ERROR})\n }\n\n try {\n await this.subscribeToProgress(projectId, jobId)\n this.output.log(`Job ${styleText('green', jobId)} completed`)\n } catch (error) {\n const message = error instanceof Error ? error.message : String(error)\n copyDatasetDebug('Failed to attach to copy job: %s', message, error)\n return this.output.error(`Failed to attach to copy job: ${message}`, {\n exit: exitCodes.RUNTIME_ERROR,\n })\n }\n }\n\n private async handleCopyMode(\n projectId: string,\n args: {source?: string; target?: string},\n flags: {detach?: boolean; 'skip-content-releases'?: boolean; 'skip-history'?: boolean},\n ): Promise<void> {\n copyDatasetDebug('Starting copy mode')\n\n const skipHistory = Boolean(flags['skip-history'])\n const skipContentReleases = Boolean(flags['skip-content-releases'])\n\n // Surfaced before any prompting so the flag is still actionable: a copy job\n // can't be canceled once started, so mentioning it after the job kicks off\n // leaves nothing to decide.\n if (!skipHistory) {\n this.output.log(\n `Note: You can run this command with flag '--skip-history'. The flag will reduce copy time in larger datasets.`,\n )\n }\n\n // Get and validate source dataset\n let sourceDataset = args.source\n if (sourceDataset) {\n const nameError = validateDatasetName(sourceDataset)\n if (nameError) {\n return this.output.error(nameError, {exit: exitCodes.USAGE_ERROR})\n }\n }\n\n let datasetsResponse\n try {\n datasetsResponse = await listDatasets(projectId)\n } catch (error) {\n const message = error instanceof Error ? error.message : String(error)\n copyDatasetDebug('Failed to fetch datasets: %s', message, error)\n return this.output.error(`Failed to fetch datasets: ${message}`, {\n exit: exitCodes.RUNTIME_ERROR,\n })\n }\n\n const datasetNames = new Set(datasetsResponse.map((ds) => ds.name))\n\n // Prompt for source if not provided\n if (!sourceDataset) {\n sourceDataset = await promptForDataset({\n datasets: datasetsResponse,\n })\n }\n\n if (!datasetNames.has(sourceDataset)) {\n return this.output.error(`Source dataset \"${sourceDataset}\" doesn't exist`, {\n exit: exitCodes.RUNTIME_ERROR,\n })\n }\n\n // Get and validate target dataset\n let targetDataset = args.target\n if (targetDataset) {\n const nameError = validateDatasetName(targetDataset)\n if (nameError) {\n return this.output.error(nameError, {exit: exitCodes.USAGE_ERROR})\n }\n } else {\n targetDataset = await promptForDatasetName({\n message: 'Target dataset name:',\n })\n }\n\n if (datasetNames.has(targetDataset)) {\n return this.output.error(`Target dataset \"${targetDataset}\" already exists`, {\n exit: exitCodes.RUNTIME_ERROR,\n })\n }\n\n // Start the copy job\n try {\n this.output.log(\n `Copying dataset ${styleText('green', sourceDataset)} to ${styleText('green', targetDataset)}...`,\n )\n\n const response = await copyDataset({\n projectId,\n skipContentReleases,\n skipHistory,\n sourceDataset,\n targetDataset,\n })\n\n this.output.log(`Job ${styleText('green', response.jobId)} started`)\n\n if (flags.detach) {\n return\n }\n\n await this.subscribeToProgress(projectId, response.jobId)\n this.output.log(`Job ${styleText('green', response.jobId)} completed`)\n } catch (error) {\n const message = error instanceof Error ? error.message : String(error)\n copyDatasetDebug('Dataset copying failed: %s', message, error)\n return this.output.error(`Dataset copying failed: ${message}`, {\n exit: exitCodes.RUNTIME_ERROR,\n })\n }\n }\n\n private async handleListMode(\n projectId: string,\n flags: {limit?: number; offset?: number},\n ): Promise<void> {\n copyDatasetDebug('Listing dataset copy jobs')\n\n try {\n const jobs = await listDatasetCopyJobs({\n limit: flags.limit,\n offset: flags.offset,\n projectId,\n })\n\n if (jobs.length === 0) {\n this.output.log(\"This project doesn't have any dataset copy jobs\")\n return\n }\n\n this.displayCopyJobsTable(jobs)\n } catch (error) {\n const message = error instanceof Error ? error.message : String(error)\n copyDatasetDebug('Failed to list dataset copy jobs: %s', message, error)\n return this.output.error(`Failed to list dataset copy jobs: ${message}`, {\n exit: exitCodes.RUNTIME_ERROR,\n })\n }\n }\n\n private async subscribeToProgress(projectId: string, jobId: string): Promise<void> {\n const spin = spinner('').start()\n const hasCliExecutionContext = Boolean(getCliExecutionContext())\n\n return new Promise<void>((resolve, reject) => {\n const sigintHandler = () => {\n subscription.unsubscribe()\n spin.fail('Copy interrupted.')\n exit(130)\n }\n\n const subscription = followCopyJobProgress({jobId, projectId}).subscribe({\n complete: () => {\n if (!hasCliExecutionContext) process.off('SIGINT', sigintHandler)\n spin.succeed('Copy finished.')\n resolve()\n },\n error: (err) => {\n if (!hasCliExecutionContext) process.off('SIGINT', sigintHandler)\n spin.fail('Copy failed.')\n reject(err)\n },\n next: (event: CopyJobProgressEvent) => {\n if (typeof event.progress === 'number') {\n spin.text = `Copy in progress: ${event.progress}%`\n }\n },\n })\n\n if (!hasCliExecutionContext) process.once('SIGINT', sigintHandler)\n })\n }\n}\n"],"names":["styleText","Args","Flags","exit","exitCodes","subdebug","getCliExecutionContext","SanityCommand","spinner","formatDistance","formatDistanceToNow","parseISO","validateDatasetName","promptForDataset","promptForDatasetName","promptForProject","copyDataset","followCopyJobProgress","listDatasetCopyJobs","listDatasets","formatCliErrorMessages","Table","getProjectIdFlag","copyDatasetDebug","CopyDatasetCommand","args","source","string","description","required","target","examples","command","flags","semantics","attach","exclusive","detach","boolean","limit","integer","dependsOn","max","list","offset","hiddenAliases","run","parse","isUnattended","errors","push","length","output","error","USAGE_ERROR","projectId","getProjectId","fallback","requiredPermissions","grant","permission","handleListMode","handleAttachMode","handleCopyMode","displayCopyJobsTable","jobs","table","columns","alignment","name","title","job","createdAt","id","sourceDataset","state","targetDataset","updatedAt","withHistory","timeStarted","timeTaken","color","addRow","log","render","jobId","trim","RUNTIME_ERROR","subscribeToProgress","message","Error","String","skipHistory","Boolean","skipContentReleases","nameError","datasetsResponse","datasetNames","Set","map","ds","datasets","has","response","spin","start","hasCliExecutionContext","Promise","resolve","reject","sigintHandler","subscription","unsubscribe","fail","subscribe","complete","process","off","succeed","err","next","event","progress","text","once"],"mappings":"AAAA,SAAQA,SAAS,QAAO,YAAW;AAEnC,SAAQC,IAAI,EAAEC,KAAK,QAAO,cAAa;AACvC,SAAQC,IAAI,QAAO,qBAAoB;AACvC,SAAQC,SAAS,QAAO,mBAAkB;AAC1C,SAAQC,QAAQ,QAAO,yBAAwB;AAC/C,SAAQC,sBAAsB,QAAO,oCAAmC;AACxE,SAAQC,aAAa,QAAO,iCAAgC;AAC5D,SAAQC,OAAO,QAAO,sBAAqB;AAC3C,SAAQC,cAAc,QAAO,0BAAyB;AACtD,SAAQC,mBAAmB,QAAO,+BAA8B;AAChE,SAAQC,QAAQ,QAAO,oBAAmB;AAE1C,SAAQC,mBAAmB,QAAO,+CAA8C;AAChF,SAAQC,gBAAgB,QAAO,oCAAmC;AAClE,SAAQC,oBAAoB,QAAO,wCAAuC;AAC1E,SAAQC,gBAAgB,QAAO,oCAAmC;AAClE,SACEC,WAAW,EAGXC,qBAAqB,EACrBC,mBAAmB,EACnBC,YAAY,QACP,6BAA4B;AACnC,SAAQC,sBAAsB,QAAO,uCAAsC;AAC3E,SAAQC,KAAK,QAAO,gCAA+B;AACnD,SAAQC,gBAAgB,QAAO,4BAA2B;AAE1D,MAAMC,mBAAmBlB,SAAS;AAElC,OAAO,MAAMmB,2BAA2BjB;IACtC,OAAgBkB,OAAO;QACrBC,QAAQzB,KAAK0B,MAAM,CAAC;YAClBC,aAAa;YACbC,UAAU;QACZ;QACAC,QAAQ7B,KAAK0B,MAAM,CAAC;YAClBC,aAAa;YACbC,UAAU;QACZ;IACF,EAAC;IAED,OAAgBD,cAAc,qCAAoC;IAElE,OAAgBG,WAAW;QACzB;YACEC,SAAS;YACTJ,aAAa;QACf;QACA;YACEI,SAAS;YACTJ,aAAa;QACf;QACA;YACEI,SAAS;YACTJ,aAAa;QACf;QACA;YACEI,SAAS;YACTJ,aAAa;QACf;QACA;YACEI,SAAS;YACTJ,aAAa;QACf;QACA;YACEI,SAAS;YACTJ,aAAa;QACf;QACA;YACEI,SAAS;YACTJ,aAAa;QACf;QACA;YACEI,SAAS;YACTJ,aAAa;QACf;QACA;YACEI,SAAS;YACTJ,aAAa;QACf;KACD,CAAA;IAED,OAAgBK,QAAQ;QACtB,GAAGX,iBAAiB;YAClBM,aAAa;YACbM,WAAW;QACb,EAAE;QACFC,QAAQjC,MAAMyB,MAAM,CAAC;YACnBC,aAAa;YACbQ,WAAW;gBAAC;gBAAQ;gBAAU;aAAe;YAC7CP,UAAU;QACZ;QACAQ,QAAQnC,MAAMoC,OAAO,CAAC;YACpBV,aAAa;YACbQ,WAAW;gBAAC;gBAAQ;aAAS;YAC7BP,UAAU;QACZ;QACAU,OAAOrC,MAAMsC,OAAO,CAAC;YACnBC,WAAW;gBAAC;aAAO;YACnBb,aAAa;YACbc,KAAK;YACLb,UAAU;QACZ;QACAc,MAAMzC,MAAMoC,OAAO,CAAC;YAClBV,aAAa;YACbQ,WAAW;gBAAC;gBAAU;gBAAU;aAAe;YAC/CP,UAAU;QACZ;QACAe,QAAQ1C,MAAMsC,OAAO,CAAC;YACpBC,WAAW;gBAAC;aAAO;YACnBb,aAAa;YACbC,UAAU;QACZ;QACA,yBAAyB3B,MAAMoC,OAAO,CAAC;YACrCV,aAAa;YACbQ,WAAW;gBAAC;gBAAQ;aAAS;YAC7BP,UAAU;QACZ;QACA,gBAAgB3B,MAAMoC,OAAO,CAAC;YAC5BV,aAAa;YACbQ,WAAW;gBAAC;gBAAQ;aAAS;YAC7BP,UAAU;QACZ;IACF,EAAC;IAED,OAAgBgB,gBAA0B;QAAC;KAAe,CAAA;IAE1D,MAAaC,MAAqB;QAChC,MAAM,EAACrB,IAAI,EAAEQ,KAAK,EAAC,GAAG,MAAM,IAAI,CAACc,KAAK,CAACvB;QAEvC,IAAI,CAACS,MAAMU,IAAI,IAAI,CAACV,MAAME,MAAM,IAAI,IAAI,CAACa,YAAY,IAAI;YACvD,MAAMC,SAAmB,EAAE;YAE3B,IAAI,CAACxB,KAAKC,MAAM,EAAE;gBAChBuB,OAAOC,IAAI,CAAC;YACd;YACA,IAAI,CAACzB,KAAKK,MAAM,EAAE;gBAChBmB,OAAOC,IAAI,CAAC;YACd;YAEA,IAAID,OAAOE,MAAM,GAAG,GAAG;gBACrB,IAAI,CAACC,MAAM,CAACC,KAAK,CAACjC,uBAAuB6B,SAAS;oBAAC9C,MAAMC,UAAUkD,WAAW;gBAAA;YAChF;QACF;QAEA,MAAMC,YAAY,MAAM,IAAI,CAACC,YAAY,CAAC;YACxCC,UAAU,IACR1C,iBAAiB;oBACf2C,qBAAqB;wBACnB;4BAACC,OAAO;4BAAQC,YAAY;wBAAyB;wBACrD;4BAACD,OAAO;4BAAUC,YAAY;wBAAyB;qBACxD;gBACH;QACJ;QAEA,4BAA4B;QAC5B,IAAI3B,MAAMU,IAAI,EAAE;YACd,OAAO,IAAI,CAACkB,cAAc,CAACN,WAAWtB;QACxC;QAEA,IAAIA,MAAME,MAAM,EAAE;YAChB,OAAO,IAAI,CAAC2B,gBAAgB,CAACP,WAAWtB,MAAME,MAAM;QACtD;QAEA,OAAO,IAAI,CAAC4B,cAAc,CAACR,WAAW9B,MAAMQ;IAC9C;IAEQ+B,qBAAqBC,IAAsB,EAAQ;QACzD,MAAMC,QAAQ,IAAI7C,MAAM;YACtB8C,SAAS;gBACP;oBAACC,WAAW;oBAAQC,MAAM;oBAAMC,OAAO;gBAAQ;gBAC/C;oBAACF,WAAW;oBAAQC,MAAM;oBAAiBC,OAAO;gBAAgB;gBAClE;oBAACF,WAAW;oBAAQC,MAAM;oBAAiBC,OAAO;gBAAgB;gBAClE;oBAACF,WAAW;oBAAQC,MAAM;oBAASC,OAAO;gBAAO;gBACjD;oBAACF,WAAW;oBAAQC,MAAM;oBAAeC,OAAO;gBAAc;gBAC9D;oBAACF,WAAW;oBAAQC,MAAM;oBAAeC,OAAO;gBAAc;gBAC9D;oBAACF,WAAW;oBAAQC,MAAM;oBAAaC,OAAO;gBAAY;aAC3D;YACDA,OAAO;QACT;QAEA,KAAK,MAAMC,OAAON,KAAM;YACtB,MAAM,EAACO,SAAS,EAAEC,EAAE,EAAEC,aAAa,EAAEC,KAAK,EAAEC,aAAa,EAAEC,SAAS,EAAEC,WAAW,EAAC,GAAGP;YAErF,IAAIQ,cAAc;YAClB,IAAIP,cAAc,IAAI;gBACpBO,cAAcrE,oBAAoBC,SAAS6D;YAC7C;YAEA,IAAIQ,YAAY;YAChB,IAAIH,cAAc,IAAI;gBACpBG,YAAYvE,eAAeE,SAASkE,YAAYlE,SAAS6D;YAC3D;YAEA,IAAIS;YACJ,OAAQN;gBACN,KAAK;oBAAa;wBAChBM,QAAQ;wBACR;oBACF;gBACA,KAAK;oBAAU;wBACbA,QAAQ;wBACR;oBACF;gBACA,KAAK;oBAAW;wBACdA,QAAQ;wBACR;oBACF;gBACA;oBAAS;wBACPA,QAAQ;oBACV;YACF;YAEAf,MAAMgB,MAAM,CACV;gBACET;gBACAC;gBACAC;gBACAC;gBACAG,aAAa,GAAGA,YAAY,IAAI,CAAC;gBACjCC;gBACAF;YACF,GACA;gBAACG;YAAK;QAEV;QAEA,IAAI,CAAC7B,MAAM,CAAC+B,GAAG,CAACjB,MAAMkB,MAAM;IAC9B;IAEA,MAActB,iBAAiBP,SAAiB,EAAE8B,KAAa,EAAiB;QAC9E9D,iBAAiB,4BAA4B8D;QAE7C,IAAIA,MAAMC,IAAI,OAAO,IAAI;YACvB,OAAO,IAAI,CAAClC,MAAM,CAACC,KAAK,CAAC,+BAA+B;gBAAClD,MAAMC,UAAUmF,aAAa;YAAA;QACxF;QAEA,IAAI;YACF,MAAM,IAAI,CAACC,mBAAmB,CAACjC,WAAW8B;YAC1C,IAAI,CAACjC,MAAM,CAAC+B,GAAG,CAAC,CAAC,IAAI,EAAEnF,UAAU,SAASqF,OAAO,UAAU,CAAC;QAC9D,EAAE,OAAOhC,OAAO;YACd,MAAMoC,UAAUpC,iBAAiBqC,QAAQrC,MAAMoC,OAAO,GAAGE,OAAOtC;YAChE9B,iBAAiB,oCAAoCkE,SAASpC;YAC9D,OAAO,IAAI,CAACD,MAAM,CAACC,KAAK,CAAC,CAAC,8BAA8B,EAAEoC,SAAS,EAAE;gBACnEtF,MAAMC,UAAUmF,aAAa;YAC/B;QACF;IACF;IAEA,MAAcxB,eACZR,SAAiB,EACjB9B,IAAwC,EACxCQ,KAAsF,EACvE;QACfV,iBAAiB;QAEjB,MAAMqE,cAAcC,QAAQ5D,KAAK,CAAC,eAAe;QACjD,MAAM6D,sBAAsBD,QAAQ5D,KAAK,CAAC,wBAAwB;QAElE,4EAA4E;QAC5E,2EAA2E;QAC3E,4BAA4B;QAC5B,IAAI,CAAC2D,aAAa;YAChB,IAAI,CAACxC,MAAM,CAAC+B,GAAG,CACb,CAAC,6GAA6G,CAAC;QAEnH;QAEA,kCAAkC;QAClC,IAAIT,gBAAgBjD,KAAKC,MAAM;QAC/B,IAAIgD,eAAe;YACjB,MAAMqB,YAAYnF,oBAAoB8D;YACtC,IAAIqB,WAAW;gBACb,OAAO,IAAI,CAAC3C,MAAM,CAACC,KAAK,CAAC0C,WAAW;oBAAC5F,MAAMC,UAAUkD,WAAW;gBAAA;YAClE;QACF;QAEA,IAAI0C;QACJ,IAAI;YACFA,mBAAmB,MAAM7E,aAAaoC;QACxC,EAAE,OAAOF,OAAO;YACd,MAAMoC,UAAUpC,iBAAiBqC,QAAQrC,MAAMoC,OAAO,GAAGE,OAAOtC;YAChE9B,iBAAiB,gCAAgCkE,SAASpC;YAC1D,OAAO,IAAI,CAACD,MAAM,CAACC,KAAK,CAAC,CAAC,0BAA0B,EAAEoC,SAAS,EAAE;gBAC/DtF,MAAMC,UAAUmF,aAAa;YAC/B;QACF;QAEA,MAAMU,eAAe,IAAIC,IAAIF,iBAAiBG,GAAG,CAAC,CAACC,KAAOA,GAAG/B,IAAI;QAEjE,oCAAoC;QACpC,IAAI,CAACK,eAAe;YAClBA,gBAAgB,MAAM7D,iBAAiB;gBACrCwF,UAAUL;YACZ;QACF;QAEA,IAAI,CAACC,aAAaK,GAAG,CAAC5B,gBAAgB;YACpC,OAAO,IAAI,CAACtB,MAAM,CAACC,KAAK,CAAC,CAAC,gBAAgB,EAAEqB,cAAc,eAAe,CAAC,EAAE;gBAC1EvE,MAAMC,UAAUmF,aAAa;YAC/B;QACF;QAEA,kCAAkC;QAClC,IAAIX,gBAAgBnD,KAAKK,MAAM;QAC/B,IAAI8C,eAAe;YACjB,MAAMmB,YAAYnF,oBAAoBgE;YACtC,IAAImB,WAAW;gBACb,OAAO,IAAI,CAAC3C,MAAM,CAACC,KAAK,CAAC0C,WAAW;oBAAC5F,MAAMC,UAAUkD,WAAW;gBAAA;YAClE;QACF,OAAO;YACLsB,gBAAgB,MAAM9D,qBAAqB;gBACzC2E,SAAS;YACX;QACF;QAEA,IAAIQ,aAAaK,GAAG,CAAC1B,gBAAgB;YACnC,OAAO,IAAI,CAACxB,MAAM,CAACC,KAAK,CAAC,CAAC,gBAAgB,EAAEuB,cAAc,gBAAgB,CAAC,EAAE;gBAC3EzE,MAAMC,UAAUmF,aAAa;YAC/B;QACF;QAEA,qBAAqB;QACrB,IAAI;YACF,IAAI,CAACnC,MAAM,CAAC+B,GAAG,CACb,CAAC,gBAAgB,EAAEnF,UAAU,SAAS0E,eAAe,IAAI,EAAE1E,UAAU,SAAS4E,eAAe,GAAG,CAAC;YAGnG,MAAM2B,WAAW,MAAMvF,YAAY;gBACjCuC;gBACAuC;gBACAF;gBACAlB;gBACAE;YACF;YAEA,IAAI,CAACxB,MAAM,CAAC+B,GAAG,CAAC,CAAC,IAAI,EAAEnF,UAAU,SAASuG,SAASlB,KAAK,EAAE,QAAQ,CAAC;YAEnE,IAAIpD,MAAMI,MAAM,EAAE;gBAChB;YACF;YAEA,MAAM,IAAI,CAACmD,mBAAmB,CAACjC,WAAWgD,SAASlB,KAAK;YACxD,IAAI,CAACjC,MAAM,CAAC+B,GAAG,CAAC,CAAC,IAAI,EAAEnF,UAAU,SAASuG,SAASlB,KAAK,EAAE,UAAU,CAAC;QACvE,EAAE,OAAOhC,OAAO;YACd,MAAMoC,UAAUpC,iBAAiBqC,QAAQrC,MAAMoC,OAAO,GAAGE,OAAOtC;YAChE9B,iBAAiB,8BAA8BkE,SAASpC;YACxD,OAAO,IAAI,CAACD,MAAM,CAACC,KAAK,CAAC,CAAC,wBAAwB,EAAEoC,SAAS,EAAE;gBAC7DtF,MAAMC,UAAUmF,aAAa;YAC/B;QACF;IACF;IAEA,MAAc1B,eACZN,SAAiB,EACjBtB,KAAwC,EACzB;QACfV,iBAAiB;QAEjB,IAAI;YACF,MAAM0C,OAAO,MAAM/C,oBAAoB;gBACrCqB,OAAON,MAAMM,KAAK;gBAClBK,QAAQX,MAAMW,MAAM;gBACpBW;YACF;YAEA,IAAIU,KAAKd,MAAM,KAAK,GAAG;gBACrB,IAAI,CAACC,MAAM,CAAC+B,GAAG,CAAC;gBAChB;YACF;YAEA,IAAI,CAACnB,oBAAoB,CAACC;QAC5B,EAAE,OAAOZ,OAAO;YACd,MAAMoC,UAAUpC,iBAAiBqC,QAAQrC,MAAMoC,OAAO,GAAGE,OAAOtC;YAChE9B,iBAAiB,wCAAwCkE,SAASpC;YAClE,OAAO,IAAI,CAACD,MAAM,CAACC,KAAK,CAAC,CAAC,kCAAkC,EAAEoC,SAAS,EAAE;gBACvEtF,MAAMC,UAAUmF,aAAa;YAC/B;QACF;IACF;IAEA,MAAcC,oBAAoBjC,SAAiB,EAAE8B,KAAa,EAAiB;QACjF,MAAMmB,OAAOhG,QAAQ,IAAIiG,KAAK;QAC9B,MAAMC,yBAAyBb,QAAQvF;QAEvC,OAAO,IAAIqG,QAAc,CAACC,SAASC;YACjC,MAAMC,gBAAgB;gBACpBC,aAAaC,WAAW;gBACxBR,KAAKS,IAAI,CAAC;gBACV9G,KAAK;YACP;YAEA,MAAM4G,eAAe9F,sBAAsB;gBAACoE;gBAAO9B;YAAS,GAAG2D,SAAS,CAAC;gBACvEC,UAAU;oBACR,IAAI,CAACT,wBAAwBU,QAAQC,GAAG,CAAC,UAAUP;oBACnDN,KAAKc,OAAO,CAAC;oBACbV;gBACF;gBACAvD,OAAO,CAACkE;oBACN,IAAI,CAACb,wBAAwBU,QAAQC,GAAG,CAAC,UAAUP;oBACnDN,KAAKS,IAAI,CAAC;oBACVJ,OAAOU;gBACT;gBACAC,MAAM,CAACC;oBACL,IAAI,OAAOA,MAAMC,QAAQ,KAAK,UAAU;wBACtClB,KAAKmB,IAAI,GAAG,CAAC,kBAAkB,EAAEF,MAAMC,QAAQ,CAAC,CAAC,CAAC;oBACpD;gBACF;YACF;YAEA,IAAI,CAAChB,wBAAwBU,QAAQQ,IAAI,CAAC,UAAUd;QACtD;IACF;AACF"}
|
package/dist/commands/login.js
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
|
-
import { text } from 'node:stream/consumers';
|
|
2
1
|
import { Flags } from '@oclif/core';
|
|
3
2
|
import { exitCodes, SanityCommand } from '@sanity/cli-core';
|
|
4
3
|
import { login } from '../actions/auth/login/login.js';
|
|
5
4
|
import { LOGIN_PROVIDER_IDS } from '../actions/auth/login/loginInstructions.js';
|
|
5
|
+
import { readTokenFromStdin } from '../util/readTokenFromStdin.js';
|
|
6
6
|
export class LoginCommand extends SanityCommand {
|
|
7
7
|
static description = 'Log in to your Sanity account';
|
|
8
8
|
static examples = [
|
|
@@ -73,7 +73,7 @@ export class LoginCommand extends SanityCommand {
|
|
|
73
73
|
const { flags } = await this.parse(LoginCommand);
|
|
74
74
|
const { 'sso-provider': ssoProvider, 'with-token': withToken, ...loginFlags } = flags;
|
|
75
75
|
try {
|
|
76
|
-
const token = withToken ? await readTokenFromStdin() : undefined;
|
|
76
|
+
const token = withToken ? await readTokenFromStdin('Token is required on standard input. Run `sanity login --with-token < token.txt`.') : undefined;
|
|
77
77
|
await login({
|
|
78
78
|
...loginFlags,
|
|
79
79
|
output: this.output,
|
|
@@ -90,11 +90,5 @@ export class LoginCommand extends SanityCommand {
|
|
|
90
90
|
}
|
|
91
91
|
}
|
|
92
92
|
}
|
|
93
|
-
async function readTokenFromStdin() {
|
|
94
|
-
if (process.stdin.isTTY) {
|
|
95
|
-
throw new Error('Token is required on standard input. Run `sanity login --with-token < token.txt`.');
|
|
96
|
-
}
|
|
97
|
-
return text(process.stdin);
|
|
98
|
-
}
|
|
99
93
|
|
|
100
94
|
//# sourceMappingURL=login.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../src/commands/login.ts"],"sourcesContent":["import {
|
|
1
|
+
{"version":3,"sources":["../../src/commands/login.ts"],"sourcesContent":["import {Command, Flags} from '@oclif/core'\nimport {type FlagInput} from '@oclif/core/interfaces'\nimport {exitCodes, SanityCommand} from '@sanity/cli-core'\n\nimport {login} from '../actions/auth/login/login.js'\nimport {LOGIN_PROVIDER_IDS} from '../actions/auth/login/loginInstructions.js'\nimport {readTokenFromStdin} from '../util/readTokenFromStdin.js'\n\nexport class LoginCommand extends SanityCommand<typeof LoginCommand> {\n static override description = 'Log in to your Sanity account'\n static override examples: Array<Command.Example> = [\n {\n command: '<%= config.bin %> <%= command.id %>',\n description: 'Log in using default settings',\n },\n {\n command: '<%= config.bin %> <%= command.id %> --with-token < token.txt',\n description: 'Log in using a token from standard input',\n },\n {\n command: '<%= config.bin %> <%= command.id %> --provider github --no-open',\n description: 'Login with GitHub provider, but do not open a browser window automatically',\n },\n {\n command: '<%= config.bin %> <%= command.id %> --sso my-organization',\n description: 'Log in using Single Sign-On with the \"my-organization\" slug',\n },\n {\n command:\n '<%= config.bin %> <%= command.id %> --sso my-organization --sso-provider \"Okta SSO\"',\n description: 'Log in using a specific SSO provider within an organization',\n },\n ]\n static override flags = {\n 'with-token': Flags.boolean({\n description: 'Read token from standard input',\n exclusive: ['provider', 'sso'],\n }),\n // eslint-disable-next-line perfectionist/sort-objects -- Keep token login first in generated usage.\n experimental: Flags.boolean({\n default: false,\n hidden: true,\n }),\n open: Flags.boolean({\n allowNo: true,\n default: true,\n description: 'Open a browser window to log in (`--no-open` only prints URL)',\n }),\n provider: Flags.string({\n description: `Log in using a provider ID (${LOGIN_PROVIDER_IDS.join(', ')})`,\n exclusive: ['sso', 'with-token'],\n helpValue: '<providerId>',\n }),\n sso: Flags.string({\n description: 'Log in using Single Sign-On, using the given organization slug',\n exclusive: ['provider', 'with-token'],\n helpValue: '<slug>',\n }),\n 'sso-provider': Flags.string({\n dependsOn: ['sso'],\n description: 'Select a specific SSO provider by name (use with --sso)',\n helpValue: '<name>',\n }),\n } satisfies FlagInput\n\n public async run(): Promise<void> {\n const {flags} = await this.parse(LoginCommand)\n const {'sso-provider': ssoProvider, 'with-token': withToken, ...loginFlags} = flags\n\n try {\n const token = withToken\n ? await readTokenFromStdin(\n 'Token is required on standard input. Run `sanity login --with-token < token.txt`.',\n )\n : undefined\n\n await login({\n ...loginFlags,\n output: this.output,\n ssoProvider,\n telemetry: this.telemetry,\n token,\n })\n this.log('Login successful')\n } catch (error) {\n const message = error instanceof Error ? error.message : String(error)\n this.error(`Login failed: ${message}`, {exit: exitCodes.RUNTIME_ERROR})\n }\n }\n}\n"],"names":["Flags","exitCodes","SanityCommand","login","LOGIN_PROVIDER_IDS","readTokenFromStdin","LoginCommand","description","examples","command","flags","boolean","exclusive","experimental","default","hidden","open","allowNo","provider","string","join","helpValue","sso","dependsOn","run","parse","ssoProvider","withToken","loginFlags","token","undefined","output","telemetry","log","error","message","Error","String","exit","RUNTIME_ERROR"],"mappings":"AAAA,SAAiBA,KAAK,QAAO,cAAa;AAE1C,SAAQC,SAAS,EAAEC,aAAa,QAAO,mBAAkB;AAEzD,SAAQC,KAAK,QAAO,iCAAgC;AACpD,SAAQC,kBAAkB,QAAO,6CAA4C;AAC7E,SAAQC,kBAAkB,QAAO,gCAA+B;AAEhE,OAAO,MAAMC,qBAAqBJ;IAChC,OAAgBK,cAAc,gCAA+B;IAC7D,OAAgBC,WAAmC;QACjD;YACEC,SAAS;YACTF,aAAa;QACf;QACA;YACEE,SAAS;YACTF,aAAa;QACf;QACA;YACEE,SAAS;YACTF,aAAa;QACf;QACA;YACEE,SAAS;YACTF,aAAa;QACf;QACA;YACEE,SACE;YACFF,aAAa;QACf;KACD,CAAA;IACD,OAAgBG,QAAQ;QACtB,cAAcV,MAAMW,OAAO,CAAC;YAC1BJ,aAAa;YACbK,WAAW;gBAAC;gBAAY;aAAM;QAChC;QACA,oGAAoG;QACpGC,cAAcb,MAAMW,OAAO,CAAC;YAC1BG,SAAS;YACTC,QAAQ;QACV;QACAC,MAAMhB,MAAMW,OAAO,CAAC;YAClBM,SAAS;YACTH,SAAS;YACTP,aAAa;QACf;QACAW,UAAUlB,MAAMmB,MAAM,CAAC;YACrBZ,aAAa,CAAC,4BAA4B,EAAEH,mBAAmBgB,IAAI,CAAC,MAAM,CAAC,CAAC;YAC5ER,WAAW;gBAAC;gBAAO;aAAa;YAChCS,WAAW;QACb;QACAC,KAAKtB,MAAMmB,MAAM,CAAC;YAChBZ,aAAa;YACbK,WAAW;gBAAC;gBAAY;aAAa;YACrCS,WAAW;QACb;QACA,gBAAgBrB,MAAMmB,MAAM,CAAC;YAC3BI,WAAW;gBAAC;aAAM;YAClBhB,aAAa;YACbc,WAAW;QACb;IACF,EAAqB;IAErB,MAAaG,MAAqB;QAChC,MAAM,EAACd,KAAK,EAAC,GAAG,MAAM,IAAI,CAACe,KAAK,CAACnB;QACjC,MAAM,EAAC,gBAAgBoB,WAAW,EAAE,cAAcC,SAAS,EAAE,GAAGC,YAAW,GAAGlB;QAE9E,IAAI;YACF,MAAMmB,QAAQF,YACV,MAAMtB,mBACJ,uFAEFyB;YAEJ,MAAM3B,MAAM;gBACV,GAAGyB,UAAU;gBACbG,QAAQ,IAAI,CAACA,MAAM;gBACnBL;gBACAM,WAAW,IAAI,CAACA,SAAS;gBACzBH;YACF;YACA,IAAI,CAACI,GAAG,CAAC;QACX,EAAE,OAAOC,OAAO;YACd,MAAMC,UAAUD,iBAAiBE,QAAQF,MAAMC,OAAO,GAAGE,OAAOH;YAChE,IAAI,CAACA,KAAK,CAAC,CAAC,cAAc,EAAEC,SAAS,EAAE;gBAACG,MAAMrC,UAAUsC,aAAa;YAAA;QACvE;IACF;AACF"}
|