@sanity/cli 8.0.2 → 8.1.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 +4 -4
- package/dist/SanityHelp.js.map +1 -1
- package/dist/actions/backup/assertDatasetExist.js +2 -1
- package/dist/actions/backup/assertDatasetExist.js.map +1 -1
- package/dist/actions/build/buildStudio.js +3 -2
- package/dist/actions/build/buildStudio.js.map +1 -1
- package/dist/actions/build/eventListenerFactory.js +7 -6
- package/dist/actions/build/eventListenerFactory.js.map +1 -1
- package/dist/actions/build/shouldAutoUpdate.js +2 -1
- package/dist/actions/build/shouldAutoUpdate.js.map +1 -1
- package/dist/actions/cors/filterAndValidateOrigin.js +3 -2
- package/dist/actions/cors/filterAndValidateOrigin.js.map +1 -1
- package/dist/actions/deploy/createUserApplication.js +2 -1
- package/dist/actions/deploy/createUserApplication.js.map +1 -1
- package/dist/actions/deploy/deployRunner.js +3 -2
- package/dist/actions/deploy/deployRunner.js.map +1 -1
- package/dist/actions/deploy/deployStudio.js +3 -3
- package/dist/actions/deploy/deployStudio.js.map +1 -1
- package/dist/actions/deploy/findUserApplication.js +8 -7
- package/dist/actions/deploy/findUserApplication.js.map +1 -1
- package/dist/actions/dev/servers/startAppDevServer.js +2 -1
- package/dist/actions/dev/servers/startAppDevServer.js.map +1 -1
- package/dist/actions/dev/servers/startStudioDevServer.js +3 -3
- package/dist/actions/dev/servers/startStudioDevServer.js.map +1 -1
- package/dist/actions/documents/validate.js +5 -2
- package/dist/actions/documents/validate.js.map +1 -1
- package/dist/actions/init/env/createOrAppendEnvVars.js +2 -1
- package/dist/actions/init/env/createOrAppendEnvVars.js.map +1 -1
- package/dist/actions/init/env/parseAndUpdateEnvVars.js +2 -2
- package/dist/actions/init/env/parseAndUpdateEnvVars.js.map +1 -1
- package/dist/actions/init/sdkAppDependencies.js +2 -2
- package/dist/actions/init/sdkAppDependencies.js.map +1 -1
- package/dist/actions/init/studioDependencies.js +2 -2
- package/dist/actions/init/studioDependencies.js.map +1 -1
- package/dist/actions/scaffold/scaffoldProject.js +11 -6
- package/dist/actions/scaffold/scaffoldProject.js.map +1 -1
- package/dist/actions/schema/extractSchema.js +2 -2
- package/dist/actions/schema/extractSchema.js.map +1 -1
- package/dist/actions/schema/extractSchemaWatcher.js +2 -1
- package/dist/actions/schema/extractSchemaWatcher.js.map +1 -1
- package/dist/actions/undeploy/runUndeploy.js +2 -2
- package/dist/actions/undeploy/runUndeploy.js.map +1 -1
- package/dist/commands/backups/list.js +5 -8
- package/dist/commands/backups/list.js.map +1 -1
- package/dist/commands/datasets/copy.js +5 -7
- package/dist/commands/datasets/copy.js.map +1 -1
- package/dist/commands/docs/search.js +2 -2
- package/dist/commands/docs/search.js.map +1 -1
- package/dist/commands/mcp/configure.js +2 -2
- package/dist/commands/mcp/configure.js.map +1 -1
- package/dist/commands/new.js +1 -2
- package/dist/commands/new.js.map +1 -1
- package/dist/commands/typegen/generate.js +6 -6
- package/dist/commands/typegen/generate.js.map +1 -1
- package/dist/services/assets.js +1 -1
- package/dist/services/assets.js.map +1 -1
- package/dist/services/mintProject.js +27 -3
- package/dist/services/mintProject.js.map +1 -1
- package/dist/util/appId.js +2 -1
- package/dist/util/appId.js.map +1 -1
- package/dist/util/dates.js +125 -0
- package/dist/util/dates.js.map +1 -0
- package/dist/util/getCliVersion.js +1 -11
- package/dist/util/getCliVersion.js.map +1 -1
- package/oclif.manifest.json +2 -2
- package/package.json +5 -8
|
@@ -61,13 +61,19 @@ export async function getUnavailableScaffoldTarget(workDir) {
|
|
|
61
61
|
const { unavailableTarget } = await inspectScaffoldTargets(workDir);
|
|
62
62
|
return unavailableTarget;
|
|
63
63
|
}
|
|
64
|
-
function
|
|
64
|
+
function createStudioEnv(dataset, projectId, token) {
|
|
65
65
|
return {
|
|
66
66
|
SANITY_AUTH_TOKEN: token,
|
|
67
67
|
SANITY_DATASET: dataset,
|
|
68
68
|
SANITY_PROJECT_ID: projectId
|
|
69
69
|
};
|
|
70
70
|
}
|
|
71
|
+
function createFrontendEnv(dataset, projectId) {
|
|
72
|
+
return {
|
|
73
|
+
NEXT_PUBLIC_SANITY_DATASET: dataset,
|
|
74
|
+
NEXT_PUBLIC_SANITY_PROJECT_ID: projectId
|
|
75
|
+
};
|
|
76
|
+
}
|
|
71
77
|
export async function scaffoldProject({ cancelSignal, dataset, displayName, output, packageManager, projectId, telemetry, token, workDir }) {
|
|
72
78
|
cancelSignal?.throwIfAborted();
|
|
73
79
|
const { detectedFramework: detected, unavailableTarget } = await inspectScaffoldTargets(workDir);
|
|
@@ -129,8 +135,7 @@ export async function scaffoldProject({ cancelSignal, dataset, displayName, outp
|
|
|
129
135
|
throw err;
|
|
130
136
|
}
|
|
131
137
|
cancelSignal?.throwIfAborted();
|
|
132
|
-
|
|
133
|
-
writeScaffoldEnv(path.join(studioPath, '.env.local'), scaffoldEnv, `Studio scaffold completed, but writing ./${STUDIO_ENV_FILE} failed`);
|
|
138
|
+
writeScaffoldEnv(path.join(studioPath, '.env.local'), createStudioEnv(dataset, projectId, token), `Studio scaffold completed, but writing ./${STUDIO_ENV_FILE} failed`, 'it holds a live project token');
|
|
134
139
|
if (detected) {
|
|
135
140
|
debug('Detected %s in %s, leaving the frontend alone', detected.name, workDir);
|
|
136
141
|
return {
|
|
@@ -158,7 +163,7 @@ export async function scaffoldProject({ cancelSignal, dataset, displayName, outp
|
|
|
158
163
|
throw err;
|
|
159
164
|
}
|
|
160
165
|
const frontendPath = path.join(workDir, FRONTEND_DIR);
|
|
161
|
-
writeScaffoldEnv(path.join(frontendPath, '.env.local'),
|
|
166
|
+
writeScaffoldEnv(path.join(frontendPath, '.env.local'), createFrontendEnv(dataset, projectId), `Website scaffold completed, but writing ./${FRONTEND_ENV_FILE} failed`, 'it holds the project ID and dataset');
|
|
162
167
|
let frontendDependenciesInstalled = resolvedPackageManager !== 'manual';
|
|
163
168
|
let frontendDependencyError;
|
|
164
169
|
if (frontendDependenciesInstalled) {
|
|
@@ -185,9 +190,9 @@ export async function scaffoldProject({ cancelSignal, dataset, displayName, outp
|
|
|
185
190
|
studioPath
|
|
186
191
|
};
|
|
187
192
|
}
|
|
188
|
-
function writeScaffoldEnv(envPath, values, errorContext) {
|
|
193
|
+
function writeScaffoldEnv(envPath, values, errorContext, gitignoreReason) {
|
|
189
194
|
const lines = [
|
|
190
|
-
|
|
195
|
+
`# Added by \`sanity new\`. Keep this file out of git: ${gitignoreReason}.`,
|
|
191
196
|
...Object.entries(values).map(([key, value])=>`${key}="${value}"`)
|
|
192
197
|
];
|
|
193
198
|
try {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../../src/actions/scaffold/scaffoldProject.ts"],"sourcesContent":["import {writeFileSync} from 'node:fs'\nimport {lstat} from 'node:fs/promises'\nimport path from 'node:path'\n\nimport {type CLITelemetryStore, type Output, subdebug} from '@sanity/cli-core'\nimport {getErrorMessage} from '@sanity/cli-core/errors'\nimport {type Framework, frameworks} from '@vercel/frameworks'\n\nimport {CLIInitStepCompleted} from '../../telemetry/init.telemetry.js'\nimport {detectFrameworkRecord} from '../../util/detectFramework.js'\nimport {dirIsEmptyOrNonExistent} from '../../util/dirIsEmptyOrNonExistent.js'\nimport {getProjectDefaults} from '../../util/getProjectDefaults.js'\nimport {type PackageManager} from '../../util/packageManager/packageManagerChoice.js'\nimport {initStudio} from '../init/initStudio.js'\nimport {resolvePackageManager} from '../init/resolvePackageManager.js'\nimport {type InitOptions} from '../init/types.js'\nimport {createFrontend, FrontendScaffoldError, installFrontendDeps} from './createFrontend.js'\n\nconst debug = subdebug('scaffold')\n\nexport const STUDIO_DIR = 'sanity'\nexport const FRONTEND_DIR = 'web'\nexport const STUDIO_ENV_FILE = `${STUDIO_DIR}/.env.local`\nexport const FRONTEND_ENV_FILE = `${FRONTEND_DIR}/.env.local`\n\nconst STUDIO_PACKAGE_NAME = 'sanity-studio'\nconst STUDIO_PACKAGE_MANAGERS = new Set<PackageManager>(['npm', 'pnpm', 'yarn'])\n\nexport async function isStudioScaffoldTargetAvailable(workDir: string): Promise<boolean> {\n return isScaffoldTargetAvailable(workDir, STUDIO_DIR)\n}\n\nasync function isScaffoldTargetAvailable(workDir: string, targetDir: string): Promise<boolean> {\n const targetPath = path.join(workDir, targetDir)\n try {\n if (!(await lstat(targetPath)).isDirectory()) {\n return false\n }\n } catch (err) {\n if ((err as NodeJS.ErrnoException).code !== 'ENOENT') {\n throw err\n }\n }\n\n return dirIsEmptyOrNonExistent(targetPath)\n}\n\nasync function inspectScaffoldTargets(workDir: string) {\n if (!(await isStudioScaffoldTargetAvailable(workDir))) {\n return {unavailableTarget: STUDIO_DIR}\n }\n\n const detectedFramework = await detectFrameworkRecord({\n frameworkList: frameworks as readonly Framework[],\n rootPath: workDir,\n })\n if (!detectedFramework && !(await isScaffoldTargetAvailable(workDir, FRONTEND_DIR))) {\n return {unavailableTarget: FRONTEND_DIR}\n }\n\n return {detectedFramework}\n}\n\nexport async function getUnavailableScaffoldTarget(workDir: string): Promise<string | undefined> {\n const {unavailableTarget} = await inspectScaffoldTargets(workDir)\n return unavailableTarget\n}\n\nexport interface ScaffoldResult {\n studioPath: string\n\n detectedFramework?: string\n frontendCreationError?: string\n frontendDependenciesInstalled?: boolean\n frontendDependencyError?: string\n frontendPackageManager?: PackageManager\n frontendPath?: string\n}\n\nfunction createScaffoldEnv(dataset: string, projectId: string, token: string) {\n return {\n SANITY_AUTH_TOKEN: token,\n SANITY_DATASET: dataset,\n SANITY_PROJECT_ID: projectId,\n }\n}\n\nexport async function scaffoldProject({\n cancelSignal,\n dataset,\n displayName,\n output,\n packageManager,\n projectId,\n telemetry,\n token,\n workDir,\n}: {\n cancelSignal?: AbortSignal\n dataset: string\n displayName: string\n output: Output\n packageManager?: PackageManager\n projectId: string\n telemetry: CLITelemetryStore\n token: string\n workDir: string\n}): Promise<ScaffoldResult> {\n cancelSignal?.throwIfAborted()\n const {detectedFramework: detected, unavailableTarget} = await inspectScaffoldTargets(workDir)\n const studioPath = path.join(workDir, STUDIO_DIR)\n if (unavailableTarget) {\n throw new Error(`./${unavailableTarget} is not an empty directory`)\n }\n\n const resolvedPackageManager = await resolvePackageManager({\n interactive: false,\n output,\n packageManager: packageManager as PackageManager,\n targetDir: workDir,\n })\n\n const options: InitOptions = {\n autoUpdates: true,\n bare: false,\n dataset,\n datasetDefault: true,\n fromCreate: false,\n git: false,\n mcpMode: 'skip',\n outputPath: studioPath,\n packageManager: STUDIO_PACKAGE_MANAGERS.has(resolvedPackageManager)\n ? (resolvedPackageManager as InitOptions['packageManager'])\n : undefined,\n project: projectId,\n skillsMode: 'skip',\n template: 'clean',\n typescript: true,\n unattended: true,\n }\n\n const trace = telemetry.trace(CLIInitStepCompleted)\n trace.start()\n try {\n // To avoid changing Studio initialization internals, we're deferring passing `cancelSignal`\n // until post `sanity new` launch.\n await initStudio({\n datasetName: dataset,\n defaults: await getProjectDefaults({isPlugin: false, workDir}),\n displayName,\n isFirstProject: false,\n mcpConfigured: [],\n options,\n organizationId: undefined,\n output,\n outputPath: studioPath,\n preclaim: true,\n projectId,\n remoteTemplateInfo: undefined,\n sluggedName: STUDIO_PACKAGE_NAME,\n trace,\n workbench: false,\n workDir,\n })\n trace.complete()\n } catch (err) {\n trace.error(err instanceof Error ? err : new Error(String(err)))\n throw err\n }\n\n cancelSignal?.throwIfAborted()\n const scaffoldEnv = createScaffoldEnv(dataset, projectId, token)\n writeScaffoldEnv(\n path.join(studioPath, '.env.local'),\n scaffoldEnv,\n `Studio scaffold completed, but writing ./${STUDIO_ENV_FILE} failed`,\n )\n\n if (detected) {\n debug('Detected %s in %s, leaving the frontend alone', detected.name, workDir)\n return {\n detectedFramework: detected.name,\n studioPath,\n }\n }\n\n try {\n await createFrontend({\n cancelSignal,\n dirName: FRONTEND_DIR,\n output,\n packageManager: resolvedPackageManager,\n workDir,\n })\n cancelSignal?.throwIfAborted()\n } catch (err) {\n if (err instanceof FrontendScaffoldError) {\n return {\n frontendCreationError: err.message,\n frontendPackageManager: resolvedPackageManager,\n studioPath,\n }\n }\n throw err\n }\n\n const frontendPath = path.join(workDir, FRONTEND_DIR)\n writeScaffoldEnv(\n path.join(frontendPath, '.env.local'),\n scaffoldEnv,\n `Website scaffold completed, but writing ./${FRONTEND_ENV_FILE} failed`,\n )\n\n let frontendDependenciesInstalled = resolvedPackageManager !== 'manual'\n let frontendDependencyError: string | undefined\n if (frontendDependenciesInstalled) {\n try {\n await installFrontendDeps({\n cancelSignal,\n dirName: FRONTEND_DIR,\n output,\n packageManager: resolvedPackageManager,\n workDir,\n })\n cancelSignal?.throwIfAborted()\n } catch (err) {\n if (!(err instanceof FrontendScaffoldError)) throw err\n frontendDependenciesInstalled = false\n frontendDependencyError = err.message\n }\n }\n\n return {\n frontendDependenciesInstalled,\n frontendDependencyError,\n frontendPackageManager: resolvedPackageManager,\n frontendPath,\n studioPath,\n }\n}\n\nfunction writeScaffoldEnv(\n envPath: string,\n values: Record<string, string>,\n errorContext: string,\n): void {\n const lines = [\n '# Added by `sanity new`. Keep this file out of git: it holds a live project token.',\n ...Object.entries(values).map(([key, value]) => `${key}=\"${value}\"`),\n ]\n try {\n writeFileSync(envPath, `${lines.join('\\n')}\\n`, 'utf8')\n } catch (err) {\n throw new Error(`${errorContext}: ${getErrorMessage(err)}`, {cause: err})\n }\n}\n"],"names":["writeFileSync","lstat","path","subdebug","getErrorMessage","frameworks","CLIInitStepCompleted","detectFrameworkRecord","dirIsEmptyOrNonExistent","getProjectDefaults","initStudio","resolvePackageManager","createFrontend","FrontendScaffoldError","installFrontendDeps","debug","STUDIO_DIR","FRONTEND_DIR","STUDIO_ENV_FILE","FRONTEND_ENV_FILE","STUDIO_PACKAGE_NAME","STUDIO_PACKAGE_MANAGERS","Set","isStudioScaffoldTargetAvailable","workDir","isScaffoldTargetAvailable","targetDir","targetPath","join","isDirectory","err","code","inspectScaffoldTargets","unavailableTarget","detectedFramework","frameworkList","rootPath","getUnavailableScaffoldTarget","createScaffoldEnv","dataset","projectId","token","SANITY_AUTH_TOKEN","SANITY_DATASET","SANITY_PROJECT_ID","scaffoldProject","cancelSignal","displayName","output","packageManager","telemetry","throwIfAborted","detected","studioPath","Error","resolvedPackageManager","interactive","options","autoUpdates","bare","datasetDefault","fromCreate","git","mcpMode","outputPath","has","undefined","project","skillsMode","template","typescript","unattended","trace","start","datasetName","defaults","isPlugin","isFirstProject","mcpConfigured","organizationId","preclaim","remoteTemplateInfo","sluggedName","workbench","complete","error","String","scaffoldEnv","writeScaffoldEnv","name","dirName","frontendCreationError","message","frontendPackageManager","frontendPath","frontendDependenciesInstalled","frontendDependencyError","envPath","values","errorContext","lines","Object","entries","map","key","value","cause"],"mappings":"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"}
|
|
1
|
+
{"version":3,"sources":["../../../src/actions/scaffold/scaffoldProject.ts"],"sourcesContent":["import {writeFileSync} from 'node:fs'\nimport {lstat} from 'node:fs/promises'\nimport path from 'node:path'\n\nimport {type CLITelemetryStore, type Output, subdebug} from '@sanity/cli-core'\nimport {getErrorMessage} from '@sanity/cli-core/errors'\nimport {type Framework, frameworks} from '@vercel/frameworks'\n\nimport {CLIInitStepCompleted} from '../../telemetry/init.telemetry.js'\nimport {detectFrameworkRecord} from '../../util/detectFramework.js'\nimport {dirIsEmptyOrNonExistent} from '../../util/dirIsEmptyOrNonExistent.js'\nimport {getProjectDefaults} from '../../util/getProjectDefaults.js'\nimport {type PackageManager} from '../../util/packageManager/packageManagerChoice.js'\nimport {initStudio} from '../init/initStudio.js'\nimport {resolvePackageManager} from '../init/resolvePackageManager.js'\nimport {type InitOptions} from '../init/types.js'\nimport {createFrontend, FrontendScaffoldError, installFrontendDeps} from './createFrontend.js'\n\nconst debug = subdebug('scaffold')\n\nexport const STUDIO_DIR = 'sanity'\nexport const FRONTEND_DIR = 'web'\nexport const STUDIO_ENV_FILE = `${STUDIO_DIR}/.env.local`\nexport const FRONTEND_ENV_FILE = `${FRONTEND_DIR}/.env.local`\n\nconst STUDIO_PACKAGE_NAME = 'sanity-studio'\nconst STUDIO_PACKAGE_MANAGERS = new Set<PackageManager>(['npm', 'pnpm', 'yarn'])\n\nexport async function isStudioScaffoldTargetAvailable(workDir: string): Promise<boolean> {\n return isScaffoldTargetAvailable(workDir, STUDIO_DIR)\n}\n\nasync function isScaffoldTargetAvailable(workDir: string, targetDir: string): Promise<boolean> {\n const targetPath = path.join(workDir, targetDir)\n try {\n if (!(await lstat(targetPath)).isDirectory()) {\n return false\n }\n } catch (err) {\n if ((err as NodeJS.ErrnoException).code !== 'ENOENT') {\n throw err\n }\n }\n\n return dirIsEmptyOrNonExistent(targetPath)\n}\n\nasync function inspectScaffoldTargets(workDir: string) {\n if (!(await isStudioScaffoldTargetAvailable(workDir))) {\n return {unavailableTarget: STUDIO_DIR}\n }\n\n const detectedFramework = await detectFrameworkRecord({\n frameworkList: frameworks as readonly Framework[],\n rootPath: workDir,\n })\n if (!detectedFramework && !(await isScaffoldTargetAvailable(workDir, FRONTEND_DIR))) {\n return {unavailableTarget: FRONTEND_DIR}\n }\n\n return {detectedFramework}\n}\n\nexport async function getUnavailableScaffoldTarget(workDir: string): Promise<string | undefined> {\n const {unavailableTarget} = await inspectScaffoldTargets(workDir)\n return unavailableTarget\n}\n\nexport interface ScaffoldResult {\n studioPath: string\n\n detectedFramework?: string\n frontendCreationError?: string\n frontendDependenciesInstalled?: boolean\n frontendDependencyError?: string\n frontendPackageManager?: PackageManager\n frontendPath?: string\n}\n\nfunction createStudioEnv(dataset: string, projectId: string, token: string) {\n return {\n SANITY_AUTH_TOKEN: token,\n SANITY_DATASET: dataset,\n SANITY_PROJECT_ID: projectId,\n }\n}\n\nfunction createFrontendEnv(dataset: string, projectId: string) {\n return {\n NEXT_PUBLIC_SANITY_DATASET: dataset,\n NEXT_PUBLIC_SANITY_PROJECT_ID: projectId,\n }\n}\n\nexport async function scaffoldProject({\n cancelSignal,\n dataset,\n displayName,\n output,\n packageManager,\n projectId,\n telemetry,\n token,\n workDir,\n}: {\n cancelSignal?: AbortSignal\n dataset: string\n displayName: string\n output: Output\n packageManager?: PackageManager\n projectId: string\n telemetry: CLITelemetryStore\n token: string\n workDir: string\n}): Promise<ScaffoldResult> {\n cancelSignal?.throwIfAborted()\n const {detectedFramework: detected, unavailableTarget} = await inspectScaffoldTargets(workDir)\n const studioPath = path.join(workDir, STUDIO_DIR)\n if (unavailableTarget) {\n throw new Error(`./${unavailableTarget} is not an empty directory`)\n }\n\n const resolvedPackageManager = await resolvePackageManager({\n interactive: false,\n output,\n packageManager: packageManager as PackageManager,\n targetDir: workDir,\n })\n\n const options: InitOptions = {\n autoUpdates: true,\n bare: false,\n dataset,\n datasetDefault: true,\n fromCreate: false,\n git: false,\n mcpMode: 'skip',\n outputPath: studioPath,\n packageManager: STUDIO_PACKAGE_MANAGERS.has(resolvedPackageManager)\n ? (resolvedPackageManager as InitOptions['packageManager'])\n : undefined,\n project: projectId,\n skillsMode: 'skip',\n template: 'clean',\n typescript: true,\n unattended: true,\n }\n\n const trace = telemetry.trace(CLIInitStepCompleted)\n trace.start()\n try {\n // To avoid changing Studio initialization internals, we're deferring passing `cancelSignal`\n // until post `sanity new` launch.\n await initStudio({\n datasetName: dataset,\n defaults: await getProjectDefaults({isPlugin: false, workDir}),\n displayName,\n isFirstProject: false,\n mcpConfigured: [],\n options,\n organizationId: undefined,\n output,\n outputPath: studioPath,\n preclaim: true,\n projectId,\n remoteTemplateInfo: undefined,\n sluggedName: STUDIO_PACKAGE_NAME,\n trace,\n workbench: false,\n workDir,\n })\n trace.complete()\n } catch (err) {\n trace.error(err instanceof Error ? err : new Error(String(err)))\n throw err\n }\n\n cancelSignal?.throwIfAborted()\n writeScaffoldEnv(\n path.join(studioPath, '.env.local'),\n createStudioEnv(dataset, projectId, token),\n `Studio scaffold completed, but writing ./${STUDIO_ENV_FILE} failed`,\n 'it holds a live project token',\n )\n\n if (detected) {\n debug('Detected %s in %s, leaving the frontend alone', detected.name, workDir)\n return {\n detectedFramework: detected.name,\n studioPath,\n }\n }\n\n try {\n await createFrontend({\n cancelSignal,\n dirName: FRONTEND_DIR,\n output,\n packageManager: resolvedPackageManager,\n workDir,\n })\n cancelSignal?.throwIfAborted()\n } catch (err) {\n if (err instanceof FrontendScaffoldError) {\n return {\n frontendCreationError: err.message,\n frontendPackageManager: resolvedPackageManager,\n studioPath,\n }\n }\n throw err\n }\n\n const frontendPath = path.join(workDir, FRONTEND_DIR)\n writeScaffoldEnv(\n path.join(frontendPath, '.env.local'),\n createFrontendEnv(dataset, projectId),\n `Website scaffold completed, but writing ./${FRONTEND_ENV_FILE} failed`,\n 'it holds the project ID and dataset',\n )\n\n let frontendDependenciesInstalled = resolvedPackageManager !== 'manual'\n let frontendDependencyError: string | undefined\n if (frontendDependenciesInstalled) {\n try {\n await installFrontendDeps({\n cancelSignal,\n dirName: FRONTEND_DIR,\n output,\n packageManager: resolvedPackageManager,\n workDir,\n })\n cancelSignal?.throwIfAborted()\n } catch (err) {\n if (!(err instanceof FrontendScaffoldError)) throw err\n frontendDependenciesInstalled = false\n frontendDependencyError = err.message\n }\n }\n\n return {\n frontendDependenciesInstalled,\n frontendDependencyError,\n frontendPackageManager: resolvedPackageManager,\n frontendPath,\n studioPath,\n }\n}\n\nfunction writeScaffoldEnv(\n envPath: string,\n values: Record<string, string>,\n errorContext: string,\n gitignoreReason: string,\n): void {\n const lines = [\n `# Added by \\`sanity new\\`. Keep this file out of git: ${gitignoreReason}.`,\n ...Object.entries(values).map(([key, value]) => `${key}=\"${value}\"`),\n ]\n try {\n writeFileSync(envPath, `${lines.join('\\n')}\\n`, 'utf8')\n } catch (err) {\n throw new Error(`${errorContext}: ${getErrorMessage(err)}`, {cause: err})\n }\n}\n"],"names":["writeFileSync","lstat","path","subdebug","getErrorMessage","frameworks","CLIInitStepCompleted","detectFrameworkRecord","dirIsEmptyOrNonExistent","getProjectDefaults","initStudio","resolvePackageManager","createFrontend","FrontendScaffoldError","installFrontendDeps","debug","STUDIO_DIR","FRONTEND_DIR","STUDIO_ENV_FILE","FRONTEND_ENV_FILE","STUDIO_PACKAGE_NAME","STUDIO_PACKAGE_MANAGERS","Set","isStudioScaffoldTargetAvailable","workDir","isScaffoldTargetAvailable","targetDir","targetPath","join","isDirectory","err","code","inspectScaffoldTargets","unavailableTarget","detectedFramework","frameworkList","rootPath","getUnavailableScaffoldTarget","createStudioEnv","dataset","projectId","token","SANITY_AUTH_TOKEN","SANITY_DATASET","SANITY_PROJECT_ID","createFrontendEnv","NEXT_PUBLIC_SANITY_DATASET","NEXT_PUBLIC_SANITY_PROJECT_ID","scaffoldProject","cancelSignal","displayName","output","packageManager","telemetry","throwIfAborted","detected","studioPath","Error","resolvedPackageManager","interactive","options","autoUpdates","bare","datasetDefault","fromCreate","git","mcpMode","outputPath","has","undefined","project","skillsMode","template","typescript","unattended","trace","start","datasetName","defaults","isPlugin","isFirstProject","mcpConfigured","organizationId","preclaim","remoteTemplateInfo","sluggedName","workbench","complete","error","String","writeScaffoldEnv","name","dirName","frontendCreationError","message","frontendPackageManager","frontendPath","frontendDependenciesInstalled","frontendDependencyError","envPath","values","errorContext","gitignoreReason","lines","Object","entries","map","key","value","cause"],"mappings":"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"}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { exit } from '@oclif/core/errors';
|
|
2
2
|
import { formatSchemaValidation, runSchemaExtraction, SchemaExtractedTrace, SchemaExtractionError } from '@sanity/cli-build/_internal/extract';
|
|
3
|
-
import { getCliTelemetry } from '@sanity/cli-core';
|
|
3
|
+
import { exitCodes, getCliTelemetry } from '@sanity/cli-core';
|
|
4
4
|
import { spinner } from '@sanity/cli-core/ux';
|
|
5
5
|
import { schemasExtractDebug } from './utils/debug.js';
|
|
6
6
|
export async function extractSchema(options) {
|
|
@@ -32,7 +32,7 @@ export async function extractSchema(options) {
|
|
|
32
32
|
exit(1);
|
|
33
33
|
}
|
|
34
34
|
output.error(err instanceof Error ? err.message : 'Failed to extract schema', {
|
|
35
|
-
exit:
|
|
35
|
+
exit: exitCodes.RUNTIME_ERROR
|
|
36
36
|
});
|
|
37
37
|
}
|
|
38
38
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../../src/actions/schema/extractSchema.ts"],"sourcesContent":["import {exit} from '@oclif/core/errors'\nimport {\n type ExtractOptions,\n formatSchemaValidation,\n runSchemaExtraction,\n SchemaExtractedTrace,\n SchemaExtractionError,\n} from '@sanity/cli-build/_internal/extract'\nimport {getCliTelemetry, type Output} from '@sanity/cli-core'\nimport {spinner} from '@sanity/cli-core/ux'\n\nimport {schemasExtractDebug} from './utils/debug.js'\n\ninterface ExtractSchemaActionOptions {\n extractOptions: ExtractOptions\n output: Output\n}\n\nexport async function extractSchema(options: ExtractSchemaActionOptions): Promise<void> {\n const {extractOptions, output} = options\n const {enforceRequiredFields, format, outputPath} = extractOptions\n\n const spin = spinner(\n enforceRequiredFields ? 'Extracting schema with enforced required fields' : 'Extracting schema',\n ).start()\n\n const trace = getCliTelemetry().trace(SchemaExtractedTrace)\n trace.start()\n\n try {\n spin.text = `Writing schema to ${outputPath}`\n\n const schema = await runSchemaExtraction(extractOptions)\n\n trace.log({\n enforceRequiredFields,\n schemaAllTypesCount: schema.length,\n schemaDocumentTypesCount: schema.filter((type) => type.type === 'document').length,\n schemaFormat: format,\n schemaTypesCount: schema.filter((type) => type.type === 'type').length,\n })\n\n spin.succeed(\n enforceRequiredFields\n ? `Extracted schema to ${outputPath} with enforced required fields`\n : `Extracted schema to ${outputPath}`,\n )\n\n trace.complete()\n } catch (err) {\n trace.error(err)\n schemasExtractDebug('Failed to extract schema', err)\n spin.fail(\n enforceRequiredFields\n ? 'Failed to extract schema with enforced required fields'\n : 'Failed to extract schema',\n )\n\n // Display validation errors if available and exit\n if (err instanceof SchemaExtractionError && err.validation && err.validation.length > 0) {\n output.log('')\n output.log(formatSchemaValidation(err.validation))\n exit(1)\n }\n\n output.error(err instanceof Error ? err.message : 'Failed to extract schema', {exit:
|
|
1
|
+
{"version":3,"sources":["../../../src/actions/schema/extractSchema.ts"],"sourcesContent":["import {exit} from '@oclif/core/errors'\nimport {\n type ExtractOptions,\n formatSchemaValidation,\n runSchemaExtraction,\n SchemaExtractedTrace,\n SchemaExtractionError,\n} from '@sanity/cli-build/_internal/extract'\nimport {exitCodes, getCliTelemetry, type Output} from '@sanity/cli-core'\nimport {spinner} from '@sanity/cli-core/ux'\n\nimport {schemasExtractDebug} from './utils/debug.js'\n\ninterface ExtractSchemaActionOptions {\n extractOptions: ExtractOptions\n output: Output\n}\n\nexport async function extractSchema(options: ExtractSchemaActionOptions): Promise<void> {\n const {extractOptions, output} = options\n const {enforceRequiredFields, format, outputPath} = extractOptions\n\n const spin = spinner(\n enforceRequiredFields ? 'Extracting schema with enforced required fields' : 'Extracting schema',\n ).start()\n\n const trace = getCliTelemetry().trace(SchemaExtractedTrace)\n trace.start()\n\n try {\n spin.text = `Writing schema to ${outputPath}`\n\n const schema = await runSchemaExtraction(extractOptions)\n\n trace.log({\n enforceRequiredFields,\n schemaAllTypesCount: schema.length,\n schemaDocumentTypesCount: schema.filter((type) => type.type === 'document').length,\n schemaFormat: format,\n schemaTypesCount: schema.filter((type) => type.type === 'type').length,\n })\n\n spin.succeed(\n enforceRequiredFields\n ? `Extracted schema to ${outputPath} with enforced required fields`\n : `Extracted schema to ${outputPath}`,\n )\n\n trace.complete()\n } catch (err) {\n trace.error(err)\n schemasExtractDebug('Failed to extract schema', err)\n spin.fail(\n enforceRequiredFields\n ? 'Failed to extract schema with enforced required fields'\n : 'Failed to extract schema',\n )\n\n // Display validation errors if available and exit\n if (err instanceof SchemaExtractionError && err.validation && err.validation.length > 0) {\n output.log('')\n output.log(formatSchemaValidation(err.validation))\n exit(1)\n }\n\n output.error(err instanceof Error ? err.message : 'Failed to extract schema', {\n exit: exitCodes.RUNTIME_ERROR,\n })\n }\n}\n"],"names":["exit","formatSchemaValidation","runSchemaExtraction","SchemaExtractedTrace","SchemaExtractionError","exitCodes","getCliTelemetry","spinner","schemasExtractDebug","extractSchema","options","extractOptions","output","enforceRequiredFields","format","outputPath","spin","start","trace","text","schema","log","schemaAllTypesCount","length","schemaDocumentTypesCount","filter","type","schemaFormat","schemaTypesCount","succeed","complete","err","error","fail","validation","Error","message","RUNTIME_ERROR"],"mappings":"AAAA,SAAQA,IAAI,QAAO,qBAAoB;AACvC,SAEEC,sBAAsB,EACtBC,mBAAmB,EACnBC,oBAAoB,EACpBC,qBAAqB,QAChB,sCAAqC;AAC5C,SAAQC,SAAS,EAAEC,eAAe,QAAoB,mBAAkB;AACxE,SAAQC,OAAO,QAAO,sBAAqB;AAE3C,SAAQC,mBAAmB,QAAO,mBAAkB;AAOpD,OAAO,eAAeC,cAAcC,OAAmC;IACrE,MAAM,EAACC,cAAc,EAAEC,MAAM,EAAC,GAAGF;IACjC,MAAM,EAACG,qBAAqB,EAAEC,MAAM,EAAEC,UAAU,EAAC,GAAGJ;IAEpD,MAAMK,OAAOT,QACXM,wBAAwB,oDAAoD,qBAC5EI,KAAK;IAEP,MAAMC,QAAQZ,kBAAkBY,KAAK,CAACf;IACtCe,MAAMD,KAAK;IAEX,IAAI;QACFD,KAAKG,IAAI,GAAG,CAAC,kBAAkB,EAAEJ,YAAY;QAE7C,MAAMK,SAAS,MAAMlB,oBAAoBS;QAEzCO,MAAMG,GAAG,CAAC;YACRR;YACAS,qBAAqBF,OAAOG,MAAM;YAClCC,0BAA0BJ,OAAOK,MAAM,CAAC,CAACC,OAASA,KAAKA,IAAI,KAAK,YAAYH,MAAM;YAClFI,cAAcb;YACdc,kBAAkBR,OAAOK,MAAM,CAAC,CAACC,OAASA,KAAKA,IAAI,KAAK,QAAQH,MAAM;QACxE;QAEAP,KAAKa,OAAO,CACVhB,wBACI,CAAC,oBAAoB,EAAEE,WAAW,8BAA8B,CAAC,GACjE,CAAC,oBAAoB,EAAEA,YAAY;QAGzCG,MAAMY,QAAQ;IAChB,EAAE,OAAOC,KAAK;QACZb,MAAMc,KAAK,CAACD;QACZvB,oBAAoB,4BAA4BuB;QAChDf,KAAKiB,IAAI,CACPpB,wBACI,2DACA;QAGN,kDAAkD;QAClD,IAAIkB,eAAe3B,yBAAyB2B,IAAIG,UAAU,IAAIH,IAAIG,UAAU,CAACX,MAAM,GAAG,GAAG;YACvFX,OAAOS,GAAG,CAAC;YACXT,OAAOS,GAAG,CAACpB,uBAAuB8B,IAAIG,UAAU;YAChDlC,KAAK;QACP;QAEAY,OAAOoB,KAAK,CAACD,eAAeI,QAAQJ,IAAIK,OAAO,GAAG,4BAA4B;YAC5EpC,MAAMK,UAAUgC,aAAa;QAC/B;IACF;AACF"}
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { dirname, isAbsolute, join, relative } from 'node:path';
|
|
2
2
|
import { createSchemaPatternMatcher, formatSchemaValidation, runSchemaExtraction, SchemaExtractionError } from '@sanity/cli-build/_internal/extract';
|
|
3
|
+
import { exitCodes } from '@sanity/cli-core';
|
|
3
4
|
import { spinner } from '@sanity/cli-core/ux';
|
|
4
5
|
import { watch as chokidarWatch } from 'chokidar';
|
|
5
6
|
import debounce from 'lodash-es/debounce.js';
|
|
@@ -81,7 +82,7 @@ import { schemasExtractDebug } from './utils/debug.js';
|
|
|
81
82
|
output.log(formatSchemaValidation(err.validation));
|
|
82
83
|
} else if (err instanceof Error) {
|
|
83
84
|
output.error(err.message, {
|
|
84
|
-
exit:
|
|
85
|
+
exit: exitCodes.RUNTIME_ERROR
|
|
85
86
|
});
|
|
86
87
|
}
|
|
87
88
|
return false;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../../src/actions/schema/extractSchemaWatcher.ts"],"sourcesContent":["import {dirname, isAbsolute, join, relative} from 'node:path'\n\nimport {\n createSchemaPatternMatcher,\n type ExtractOptions,\n formatSchemaValidation,\n runSchemaExtraction,\n SchemaExtractionError,\n} from '@sanity/cli-build/_internal/extract'\nimport {type Output} from '@sanity/cli-core'\nimport {spinner} from '@sanity/cli-core/ux'\nimport {watch as chokidarWatch, type FSWatcher} from 'chokidar'\nimport debounce from 'lodash-es/debounce.js'\n\nimport {schemasExtractDebug} from './utils/debug.js'\n\n/** Default glob patterns to watch for schema changes */\nexport const DEFAULT_WATCH_PATTERNS = [\n 'sanity.config.{js,jsx,ts,tsx,mjs}',\n 'schema*/**/*.{js,jsx,ts,tsx,mjs}',\n]\n\n/** Default patterns to ignore when watching */\nconst IGNORED_PATTERNS = [\n '**/node_modules/**',\n '**/.git/**',\n '**/dist/**',\n '**/lib/**',\n '**/.sanity/**',\n]\n\ninterface ExtractSchemaWatcherOptions {\n extractOptions: ExtractOptions\n output: Output\n watchPatterns: string[]\n\n onExtraction?: (result: {duration: number; success: boolean}) => void\n}\n\ninterface ExtractSchemaWatcher {\n close: () => Promise<void>\n watcher: FSWatcher\n}\n\n/** State for tracking extraction status */\ninterface WatchState {\n isExtracting: boolean\n pendingExtraction: boolean\n}\n\n/** Return type for createExtractionRunner */\ninterface ExtractionRunner {\n runExtraction: () => Promise<void>\n state: WatchState\n}\n\n/**\n * Creates an extraction runner with concurrency control.\n * If extraction is already running, queues one more extraction to run after completion.\n * Multiple queued requests are coalesced into a single pending extraction.\n */\nfunction createExtractionRunner(onExtract: () => Promise<void>): ExtractionRunner {\n const state: WatchState = {\n isExtracting: false,\n pendingExtraction: false,\n }\n\n async function runExtraction(): Promise<void> {\n if (state.isExtracting) {\n state.pendingExtraction = true\n return\n }\n\n state.isExtracting = true\n state.pendingExtraction = false\n\n try {\n await onExtract()\n } finally {\n state.isExtracting = false\n\n // If a change came in during extraction, run again\n if (state.pendingExtraction) {\n state.pendingExtraction = false\n await runExtraction()\n }\n }\n }\n\n return {runExtraction, state}\n}\n\n/**\n * Starts a schema watcher that extracts schema on file changes.\n * Returns a watcher instance and a stop function.\n */\nexport async function startExtractSchemaWatcher(\n options: ExtractSchemaWatcherOptions,\n): Promise<ExtractSchemaWatcher> {\n const {extractOptions, onExtraction, output, watchPatterns} = options\n\n const {configPath, enforceRequiredFields, outputPath} = extractOptions\n const workDir = dirname(configPath)\n\n // Helper function to run extraction with spinner and error handling\n const runExtraction = async (): Promise<boolean> => {\n const spin = spinner(\n enforceRequiredFields\n ? 'Extracting schema with enforced required fields'\n : 'Extracting schema...',\n ).start()\n const extractionStartTime = Date.now()\n\n try {\n await runSchemaExtraction(extractOptions)\n\n spin.succeed(\n enforceRequiredFields\n ? `Extracted schema to ${outputPath} with enforced required fields`\n : `Extracted schema to ${outputPath}`,\n )\n\n const duration = Date.now() - extractionStartTime\n onExtraction?.({duration, success: true})\n\n return true\n } catch (err) {\n const duration = Date.now() - extractionStartTime\n onExtraction?.({duration, success: false})\n\n schemasExtractDebug('Failed to extract schema', err)\n spin.fail('Extraction failed')\n\n // Display validation errors if available\n if (err instanceof SchemaExtractionError && err.validation && err.validation.length > 0) {\n output.log('')\n output.log(formatSchemaValidation(err.validation))\n } else if (err instanceof Error) {\n output.error(err.message, {exit:
|
|
1
|
+
{"version":3,"sources":["../../../src/actions/schema/extractSchemaWatcher.ts"],"sourcesContent":["import {dirname, isAbsolute, join, relative} from 'node:path'\n\nimport {\n createSchemaPatternMatcher,\n type ExtractOptions,\n formatSchemaValidation,\n runSchemaExtraction,\n SchemaExtractionError,\n} from '@sanity/cli-build/_internal/extract'\nimport {exitCodes, type Output} from '@sanity/cli-core'\nimport {spinner} from '@sanity/cli-core/ux'\nimport {watch as chokidarWatch, type FSWatcher} from 'chokidar'\nimport debounce from 'lodash-es/debounce.js'\n\nimport {schemasExtractDebug} from './utils/debug.js'\n\n/** Default glob patterns to watch for schema changes */\nexport const DEFAULT_WATCH_PATTERNS = [\n 'sanity.config.{js,jsx,ts,tsx,mjs}',\n 'schema*/**/*.{js,jsx,ts,tsx,mjs}',\n]\n\n/** Default patterns to ignore when watching */\nconst IGNORED_PATTERNS = [\n '**/node_modules/**',\n '**/.git/**',\n '**/dist/**',\n '**/lib/**',\n '**/.sanity/**',\n]\n\ninterface ExtractSchemaWatcherOptions {\n extractOptions: ExtractOptions\n output: Output\n watchPatterns: string[]\n\n onExtraction?: (result: {duration: number; success: boolean}) => void\n}\n\ninterface ExtractSchemaWatcher {\n close: () => Promise<void>\n watcher: FSWatcher\n}\n\n/** State for tracking extraction status */\ninterface WatchState {\n isExtracting: boolean\n pendingExtraction: boolean\n}\n\n/** Return type for createExtractionRunner */\ninterface ExtractionRunner {\n runExtraction: () => Promise<void>\n state: WatchState\n}\n\n/**\n * Creates an extraction runner with concurrency control.\n * If extraction is already running, queues one more extraction to run after completion.\n * Multiple queued requests are coalesced into a single pending extraction.\n */\nfunction createExtractionRunner(onExtract: () => Promise<void>): ExtractionRunner {\n const state: WatchState = {\n isExtracting: false,\n pendingExtraction: false,\n }\n\n async function runExtraction(): Promise<void> {\n if (state.isExtracting) {\n state.pendingExtraction = true\n return\n }\n\n state.isExtracting = true\n state.pendingExtraction = false\n\n try {\n await onExtract()\n } finally {\n state.isExtracting = false\n\n // If a change came in during extraction, run again\n if (state.pendingExtraction) {\n state.pendingExtraction = false\n await runExtraction()\n }\n }\n }\n\n return {runExtraction, state}\n}\n\n/**\n * Starts a schema watcher that extracts schema on file changes.\n * Returns a watcher instance and a stop function.\n */\nexport async function startExtractSchemaWatcher(\n options: ExtractSchemaWatcherOptions,\n): Promise<ExtractSchemaWatcher> {\n const {extractOptions, onExtraction, output, watchPatterns} = options\n\n const {configPath, enforceRequiredFields, outputPath} = extractOptions\n const workDir = dirname(configPath)\n\n // Helper function to run extraction with spinner and error handling\n const runExtraction = async (): Promise<boolean> => {\n const spin = spinner(\n enforceRequiredFields\n ? 'Extracting schema with enforced required fields'\n : 'Extracting schema...',\n ).start()\n const extractionStartTime = Date.now()\n\n try {\n await runSchemaExtraction(extractOptions)\n\n spin.succeed(\n enforceRequiredFields\n ? `Extracted schema to ${outputPath} with enforced required fields`\n : `Extracted schema to ${outputPath}`,\n )\n\n const duration = Date.now() - extractionStartTime\n onExtraction?.({duration, success: true})\n\n return true\n } catch (err) {\n const duration = Date.now() - extractionStartTime\n onExtraction?.({duration, success: false})\n\n schemasExtractDebug('Failed to extract schema', err)\n spin.fail('Extraction failed')\n\n // Display validation errors if available\n if (err instanceof SchemaExtractionError && err.validation && err.validation.length > 0) {\n output.log('')\n output.log(formatSchemaValidation(err.validation))\n } else if (err instanceof Error) {\n output.error(err.message, {exit: exitCodes.RUNTIME_ERROR})\n }\n\n return false\n }\n }\n\n // Run initial extraction\n await runExtraction()\n\n const absoluteWatchPatterns = watchPatterns.map((pattern) => join(workDir, pattern))\n\n // Create extraction runner with concurrency control\n const {runExtraction: runConcurrentExtraction} = createExtractionRunner(async () => {\n await runExtraction()\n })\n\n // Debounced extraction trigger (1 second delay)\n const debouncedExtract = debounce(() => {\n void runConcurrentExtraction()\n }, 1000)\n\n const {isMatch} = createSchemaPatternMatcher(watchPatterns)\n\n const watcher: FSWatcher = chokidarWatch(absoluteWatchPatterns, {\n cwd: workDir,\n ignored: IGNORED_PATTERNS,\n ignoreInitial: true,\n })\n\n watcher.on('all', (event, filePath) => {\n if (!isMatch(filePath, workDir)) {\n return\n }\n\n const timestamp = new Date().toLocaleTimeString()\n const relativePath = isAbsolute(filePath) ? relative(workDir, filePath) : filePath\n output.log(`[${timestamp}] ${event}: ${relativePath}`)\n debouncedExtract()\n })\n\n watcher.on('error', (err) => {\n output.error(`Watcher error: ${err instanceof Error ? err.message : String(err)}`)\n })\n\n return {\n close: () => watcher.close(),\n watcher,\n }\n}\n"],"names":["dirname","isAbsolute","join","relative","createSchemaPatternMatcher","formatSchemaValidation","runSchemaExtraction","SchemaExtractionError","exitCodes","spinner","watch","chokidarWatch","debounce","schemasExtractDebug","DEFAULT_WATCH_PATTERNS","IGNORED_PATTERNS","createExtractionRunner","onExtract","state","isExtracting","pendingExtraction","runExtraction","startExtractSchemaWatcher","options","extractOptions","onExtraction","output","watchPatterns","configPath","enforceRequiredFields","outputPath","workDir","spin","start","extractionStartTime","Date","now","succeed","duration","success","err","fail","validation","length","log","Error","error","message","exit","RUNTIME_ERROR","absoluteWatchPatterns","map","pattern","runConcurrentExtraction","debouncedExtract","isMatch","watcher","cwd","ignored","ignoreInitial","on","event","filePath","timestamp","toLocaleTimeString","relativePath","String","close"],"mappings":"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"}
|
|
@@ -105,7 +105,7 @@ const undeployDebug = subdebug('undeploy');
|
|
|
105
105
|
undeployDebug(`Error undeploying ${label}`, error);
|
|
106
106
|
// Labeled here, where the target is known — `normalizeFailure` only sees the adapter type.
|
|
107
107
|
throw new CLIError(`Error undeploying ${label}: ${getErrorMessage(error)}`, {
|
|
108
|
-
exit:
|
|
108
|
+
exit: exitCodes.RUNTIME_ERROR
|
|
109
109
|
});
|
|
110
110
|
}
|
|
111
111
|
spin.succeed();
|
|
@@ -188,7 +188,7 @@ function printUndeployed(target, output) {
|
|
|
188
188
|
const label = type === 'coreApp' ? 'application' : 'studio';
|
|
189
189
|
undeployDebug(`Error undeploying ${label}`, error);
|
|
190
190
|
return {
|
|
191
|
-
exit:
|
|
191
|
+
exit: exitCodes.RUNTIME_ERROR,
|
|
192
192
|
message: `Error undeploying ${label}: ${getErrorMessage(error)}`
|
|
193
193
|
};
|
|
194
194
|
}
|
|
@@ -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: 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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"}
|
|
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)}`, {\n exit: exitCodes.RUNTIME_ERROR,\n })\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 {\n exit: exitCodes.RUNTIME_ERROR,\n message: `Error undeploying ${label}: ${getErrorMessage(error)}`,\n }\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","RUNTIME_ERROR","succeed","printUndeployed","deletes","url","report","title","id","Error","name","oclif"],"mappings":"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"}
|
|
@@ -1,14 +1,11 @@
|
|
|
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 { isAfter } from 'date-fns/isAfter';
|
|
5
|
-
import { isValid } from 'date-fns/isValid';
|
|
6
|
-
import { lightFormat } from 'date-fns/lightFormat';
|
|
7
|
-
import { parse } from 'date-fns/parse';
|
|
8
4
|
import { assertDatasetExists } from '../../actions/backup/assertDatasetExist.js';
|
|
9
5
|
import { promptForProject } from '../../prompts/promptForProject.js';
|
|
10
6
|
import { listBackups } from '../../services/backup.js';
|
|
11
7
|
import { listDatasets } from '../../services/datasets.js';
|
|
8
|
+
import { formatDateTime, parseDateOnly } from '../../util/dates.js';
|
|
12
9
|
import { Table } from '../../util/responsiveTable.js';
|
|
13
10
|
import { getProjectIdFlag } from '../../util/sharedFlags.js';
|
|
14
11
|
const listBackupDebug = subdebug('backup:list');
|
|
@@ -101,7 +98,7 @@ export class ListBackupCommand extends SanityCommand {
|
|
|
101
98
|
try {
|
|
102
99
|
const parsedBefore = this.processDateFlag(flags.before, 'before');
|
|
103
100
|
const parsedAfter = this.processDateFlag(flags.after, 'after');
|
|
104
|
-
if (parsedAfter && parsedBefore &&
|
|
101
|
+
if (parsedAfter && parsedBefore && parsedAfter.getTime() > parsedBefore.getTime()) {
|
|
105
102
|
this.error('--after date must be before --before', {
|
|
106
103
|
exit: exitCodes.RUNTIME_ERROR
|
|
107
104
|
});
|
|
@@ -162,7 +159,7 @@ export class ListBackupCommand extends SanityCommand {
|
|
|
162
159
|
const { createdAt, id } = backup;
|
|
163
160
|
table.addRow({
|
|
164
161
|
backupId: id,
|
|
165
|
-
createdAt:
|
|
162
|
+
createdAt: formatDateTime(createdAt),
|
|
166
163
|
resource: 'Dataset'
|
|
167
164
|
});
|
|
168
165
|
}
|
|
@@ -178,8 +175,8 @@ export class ListBackupCommand extends SanityCommand {
|
|
|
178
175
|
}
|
|
179
176
|
processDateFlag(date, flagName) {
|
|
180
177
|
if (!date) return undefined;
|
|
181
|
-
const parsedDate =
|
|
182
|
-
if (
|
|
178
|
+
const parsedDate = parseDateOnly(date);
|
|
179
|
+
if (parsedDate) {
|
|
183
180
|
return parsedDate;
|
|
184
181
|
}
|
|
185
182
|
throw new Error(`Invalid date format for '--${flagName}' flag. Use YYYY-MM-DD`);
|
|
@@ -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 {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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"}
|
|
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'\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 {formatDateTime, parseDateOnly} from '../../util/dates.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 && parsedAfter.getTime() > parsedBefore.getTime()) {\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: formatDateTime(createdAt),\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 = parseDateOnly(date)\n if (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","assertDatasetExists","promptForProject","listBackups","listDatasets","formatDateTime","parseDateOnly","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","parse","projectId","getProjectId","fallback","requiredPermissions","grant","permission","datasets","error","message","Error","String","exit","RUNTIME_ERROR","length","output","isUnattended","USAGE_ERROR","promptForDataset","parsedBefore","processDateFlag","parsedAfter","getTime","err","Number","MAX_SAFE_INTEGER","query","toString","response","datasetName","backups","log","table","columns","alignment","name","title","backup","createdAt","id","addRow","backupId","resource","render","date","flagName","undefined","parsedDate","choices","map","value"],"mappings":"AAAA,SAAQA,IAAI,EAAEC,KAAK,QAAO,cAAa;AACvC,SAAQC,SAAS,EAAEC,aAAa,EAAEC,QAAQ,QAAO,mBAAkB;AACnE,SAAQC,MAAM,QAAO,sBAAqB;AAG1C,SAAQC,mBAAmB,QAAO,6CAA4C;AAC9E,SAAQC,gBAAgB,QAAO,oCAAmC;AAClE,SAAQC,WAAW,QAAO,2BAA0B;AACpD,SAAQC,YAAY,QAAO,6BAA4B;AACvD,SAAQC,cAAc,EAAEC,aAAa,QAAO,sBAAqB;AACjE,SAAQC,KAAK,QAAO,gCAA+B;AACnD,SAAQC,gBAAgB,QAAO,4BAA2B;AAE1D,MAAMC,kBAAkBV,SAAS;AAEjC,MAAMW,4BAA4B;AAQlC,OAAO,MAAMC,0BAA0Bb;IACrC,OAAgBc,OAAO;QACrBC,SAASlB,KAAKmB,MAAM,CAAC;YACnBC,aAAa;YACbC,UAAU;QACZ;IACF,EAAC;IAED,OAAgBD,cAAc,uCAAsC;IAEpE,OAAgBE,WAAW;QACzB;YACEC,SAAS;YACTH,aAAa;QACf;QACA;YACEG,SAAS;YACTH,aAAa;QACf;QACA;YACEG,SAAS;YACTH,aAAa;QACf;QACA;YACEG,SAAS;YACTH,aAAa;QACf;KACD,CAAA;IAED,OAAgBI,QAAQ;QACtB,GAAGX,iBAAiB;YAClBO,aAAa;YACbK,WAAW;QACb,EAAE;QACFC,OAAOzB,MAAMkB,MAAM,CAAC;YAClBC,aAAa;QACf;QACAO,QAAQ1B,MAAMkB,MAAM,CAAC;YACnBC,aAAa;QACf;QACAQ,OAAO3B,MAAM4B,OAAO,CAAC;YACnBC,MAAM;YACNC,SAAShB;YACTK,aAAa;QACf;IACF,EAAC;IAED,OAAgBY,gBAA0B;QAAC;KAAc,CAAA;IAEzD,MAAaC,MAAqB;QAChC,MAAM,EAAChB,IAAI,EAAEO,KAAK,EAAC,GAAG,MAAM,IAAI,CAACU,KAAK,CAAClB;QACvC,IAAI,EAACE,OAAO,EAAC,GAAGD;QAEhB,MAAMkB,YAAY,MAAM,IAAI,CAACC,YAAY,CAAC;YACxCC,UAAU,IACR9B,iBAAiB;oBACf+B,qBAAqB;wBAAC;4BAACC,OAAO;4BAAQC,YAAY;wBAAyB;qBAAE;gBAC/E;QACJ;QAEA,IAAIC;QAEJ,IAAI;YACFA,WAAW,MAAMhC,aAAa0B;QAChC,EAAE,OAAOO,OAAO;YACd,MAAMC,UAAUD,iBAAiBE,QAAQF,MAAMC,OAAO,GAAGE,OAAOH;YAChE5B,gBAAgB,CAAC,yBAAyB,EAAE6B,SAAS,EAAED;YACvD,IAAI,CAACA,KAAK,CAAC,CAAC,yBAAyB,EAAEC,SAAS,EAAE;gBAACG,MAAM5C,UAAU6C,aAAa;YAAA;QAClF;QAEA,IAAIN,SAASO,MAAM,KAAK,GAAG;YACzB,IAAI,CAACN,KAAK,CAAC,sCAAsC;gBAACI,MAAM5C,UAAU6C,aAAa;YAAA;QACjF;QAEA,IAAI7B,SAAS;YACXZ,oBAAoBmC,UAAUvB,SAAS,IAAI,CAAC+B,MAAM;QACpD,OAAO,IAAI,IAAI,CAACC,YAAY,IAAI;YAC9B,IAAI,CAACR,KAAK,CAAC,gFAAgF;gBACzFI,MAAM5C,UAAUiD,WAAW;YAC7B;QACF,OAAO;YACLjC,UAAU,MAAM,IAAI,CAACkC,gBAAgB,CAACX;QACxC;QAEA,sBAAsB;QACtB,IAAIjB,MAAMG,MAAM,IAAIH,MAAME,KAAK,EAAE;YAC/B,IAAI;gBACF,MAAM2B,eAAe,IAAI,CAACC,eAAe,CAAC9B,MAAMG,MAAM,EAAE;gBACxD,MAAM4B,cAAc,IAAI,CAACD,eAAe,CAAC9B,MAAME,KAAK,EAAE;gBAEtD,IAAI6B,eAAeF,gBAAgBE,YAAYC,OAAO,KAAKH,aAAaG,OAAO,IAAI;oBACjF,IAAI,CAACd,KAAK,CAAC,wCAAwC;wBAACI,MAAM5C,UAAU6C,aAAa;oBAAA;gBACnF;YACF,EAAE,OAAOU,KAAK;gBACZ,IAAI,CAACf,KAAK,CAAC,CAAC,oBAAoB,EAAEe,eAAeb,QAAQa,IAAId,OAAO,GAAGc,KAAK,EAAE;oBAC5EX,MAAM5C,UAAU6C,aAAa;gBAC/B;YACF;QACF;QAEA,sBAAsB;QACtB,IAAIvB,MAAMI,KAAK,GAAG,KAAKJ,MAAMI,KAAK,GAAG8B,OAAOC,gBAAgB,EAAE;YAC5D,IAAI,CAACjB,KAAK,CAAC,CAAC,kDAAkD,EAAEgB,OAAOC,gBAAgB,EAAE,EAAE;gBACzFb,MAAM5C,UAAU6C,aAAa;YAC/B;QACF;QAEA,MAAMa,QAAsC;YAC1ChC,OAAOJ,MAAMI,KAAK,CAACiC,QAAQ;QAC7B;QAEA,IAAIrC,MAAME,KAAK,EAAE;YACfkC,MAAMlC,KAAK,GAAGF,MAAME,KAAK;QAC3B;QAEA,IAAIF,MAAMG,MAAM,EAAE;YAChBiC,MAAMjC,MAAM,GAAGH,MAAMG,MAAM;QAC7B;QAEA,IAAI;YACF,MAAMmC,WAAW,MAAMtD,YAAY;gBACjCkB,OAAOF,MAAME,KAAK;gBAClBC,QAAQH,MAAMG,MAAM;gBACpBoC,aAAa7C;gBACbU,OAAOJ,MAAMI,KAAK;gBAClBO;YACF;YAEA,IAAI2B,SAASE,OAAO,CAAChB,MAAM,KAAK,GAAG;gBACjC,IAAI,CAACiB,GAAG,CAAC;gBACT;YACF;YAEA,MAAMC,QAAQ,IAAItD,MAAM;gBACtBuD,SAAS;oBACP;wBAACC,WAAW;wBAAQC,MAAM;wBAAYC,OAAO;oBAAU;oBACvD;wBAACF,WAAW;wBAAQC,MAAM;wBAAaC,OAAO;oBAAY;oBAC1D;wBAACF,WAAW;wBAAQC,MAAM;wBAAYC,OAAO;oBAAW;iBACzD;YACH;YAEA,KAAK,MAAMC,UAAUT,SAASE,OAAO,CAAE;gBACrC,MAAM,EAACQ,SAAS,EAAEC,EAAE,EAAC,GAAGF;gBACxBL,MAAMQ,MAAM,CAAC;oBACXC,UAAUF;oBACVD,WAAW9D,eAAe8D;oBAC1BI,UAAU;gBACZ;YACF;YAEA,IAAI,CAACX,GAAG,CAACC,MAAMW,MAAM;YAErB/D,gBACE,CAAC,oBAAoB,EAAEgD,SAASE,OAAO,CAAChB,MAAM,CAAC,qBAAqB,EAAE9B,SAAS;QAEnF,EAAE,OAAOwB,OAAO;YACd,MAAMC,UAAUD,iBAAiBE,QAAQF,MAAMC,OAAO,GAAGE,OAAOH;YAChE5B,gBAAgB,CAAC,mCAAmC,EAAEI,QAAQ,CAAC,CAAC,EAAEwB;YAClE,IAAI,CAACA,KAAK,CAAC,CAAC,4BAA4B,EAAEC,SAAS,EAAE;gBAACG,MAAM5C,UAAU6C,aAAa;YAAA;QACrF;IACF;IAEQO,gBAAgBwB,IAAwB,EAAEC,QAAgB,EAAoB;QACpF,IAAI,CAACD,MAAM,OAAOE;QAClB,MAAMC,aAAatE,cAAcmE;QACjC,IAAIG,YAAY;YACd,OAAOA;QACT;QAEA,MAAM,IAAIrC,MAAM,CAAC,2BAA2B,EAAEmC,SAAS,sBAAsB,CAAC;IAChF;IAEA,MAAc3B,iBAAiBX,QAA0B,EAAmB;QAC1E,IAAI;YACF,MAAMyC,UAAUzC,SAAS0C,GAAG,CAAC,CAACjE,UAAa,CAAA;oBACzCmD,MAAMnD,QAAQmD,IAAI;oBAClBe,OAAOlE,QAAQmD,IAAI;gBACrB,CAAA;YAEA,OAAOhE,OAAO;gBACZ6E;gBACAvC,SAAS;YACX;QACF,EAAE,OAAOD,OAAO;YACd5B,gBAAgB,CAAC,uBAAuB,CAAC,EAAE4B;YAC3C,IAAI,CAACA,KAAK,CAAC,CAAC,2BAA2B,EAAEA,iBAAiBE,QAAQF,MAAMC,OAAO,GAAGD,OAAO,EAAE;gBACzFI,MAAM5C,UAAU6C,aAAa;YAC/B;QACF;IACF;AACF"}
|
|
@@ -6,14 +6,12 @@ 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 { formatDistance } from 'date-fns/formatDistance';
|
|
10
|
-
import { formatDistanceToNow } from 'date-fns/formatDistanceToNow';
|
|
11
|
-
import { parseISO } from 'date-fns/parseISO';
|
|
12
9
|
import { validateDatasetName } from '../../actions/dataset/validateDatasetName.js';
|
|
13
10
|
import { promptForDataset } from '../../prompts/promptForDataset.js';
|
|
14
11
|
import { promptForDatasetName } from '../../prompts/promptForDatasetName.js';
|
|
15
12
|
import { promptForProject } from '../../prompts/promptForProject.js';
|
|
16
13
|
import { copyDataset, followCopyJobProgress, listDatasetCopyJobs, listDatasets } from '../../services/datasets.js';
|
|
14
|
+
import { formatDuration, formatTimeAgo } from '../../util/dates.js';
|
|
17
15
|
import { formatCliErrorMessages } from '../../util/formatCliErrorMessages.js';
|
|
18
16
|
import { Table } from '../../util/responsiveTable.js';
|
|
19
17
|
import { getProjectIdFlag } from '../../util/sharedFlags.js';
|
|
@@ -218,11 +216,11 @@ export class CopyDatasetCommand extends SanityCommand {
|
|
|
218
216
|
const { createdAt, id, sourceDataset, state, targetDataset, updatedAt, withHistory } = job;
|
|
219
217
|
let timeStarted = '';
|
|
220
218
|
if (createdAt !== '') {
|
|
221
|
-
timeStarted =
|
|
219
|
+
timeStarted = formatTimeAgo(new Date(createdAt));
|
|
222
220
|
}
|
|
223
221
|
let timeTaken = '';
|
|
224
|
-
if (updatedAt !== '') {
|
|
225
|
-
timeTaken =
|
|
222
|
+
if (createdAt !== '' && updatedAt !== '') {
|
|
223
|
+
timeTaken = formatDuration(new Date(updatedAt).getTime() - new Date(createdAt).getTime());
|
|
226
224
|
}
|
|
227
225
|
let color;
|
|
228
226
|
switch(state){
|
|
@@ -251,7 +249,7 @@ export class CopyDatasetCommand extends SanityCommand {
|
|
|
251
249
|
sourceDataset,
|
|
252
250
|
state,
|
|
253
251
|
targetDataset,
|
|
254
|
-
timeStarted
|
|
252
|
+
timeStarted,
|
|
255
253
|
timeTaken,
|
|
256
254
|
withHistory
|
|
257
255
|
}, {
|