@sanity/cli 6.1.8 → 6.2.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 +105 -103
- package/dist/actions/debug/gatherDebugInfo.js +130 -81
- package/dist/actions/debug/gatherDebugInfo.js.map +1 -1
- package/dist/actions/debug/output.js +25 -0
- package/dist/actions/debug/output.js.map +1 -0
- package/dist/actions/debug/types.js.map +1 -1
- package/dist/actions/mcp/setupMCP.js +5 -5
- package/dist/actions/mcp/setupMCP.js.map +1 -1
- package/dist/actions/schema/getExtractOptions.js.map +1 -1
- package/dist/commands/{backup → backups}/disable.js +3 -0
- package/dist/commands/backups/disable.js.map +1 -0
- package/dist/commands/{backup → backups}/download.js +3 -0
- package/dist/commands/backups/download.js.map +1 -0
- package/dist/commands/{backup → backups}/enable.js +3 -0
- package/dist/commands/backups/enable.js.map +1 -0
- package/dist/commands/{backup → backups}/list.js +3 -0
- package/dist/commands/backups/list.js.map +1 -0
- package/dist/commands/{dataset → datasets}/alias/create.js +3 -0
- package/dist/commands/datasets/alias/create.js.map +1 -0
- package/dist/commands/{dataset → datasets}/alias/delete.js +3 -0
- package/dist/commands/datasets/alias/delete.js.map +1 -0
- package/dist/commands/{dataset → datasets}/alias/link.js +3 -0
- package/dist/commands/datasets/alias/link.js.map +1 -0
- package/dist/commands/{dataset → datasets}/alias/unlink.js +3 -0
- package/dist/commands/datasets/alias/unlink.js.map +1 -0
- package/dist/commands/{dataset → datasets}/copy.js +3 -0
- package/dist/commands/datasets/copy.js.map +1 -0
- package/dist/commands/{dataset → datasets}/create.js +3 -0
- package/dist/commands/datasets/create.js.map +1 -0
- package/dist/commands/{dataset → datasets}/delete.js +3 -0
- package/dist/commands/datasets/delete.js.map +1 -0
- package/dist/commands/{dataset → datasets}/embeddings/disable.js +3 -0
- package/dist/commands/datasets/embeddings/disable.js.map +1 -0
- package/dist/commands/{dataset → datasets}/embeddings/enable.js +3 -0
- package/dist/commands/datasets/embeddings/enable.js.map +1 -0
- package/dist/commands/{dataset → datasets}/embeddings/status.js +3 -0
- package/dist/commands/datasets/embeddings/status.js.map +1 -0
- package/dist/commands/{dataset → datasets}/export.js +3 -0
- package/dist/commands/datasets/export.js.map +1 -0
- package/dist/commands/{dataset → datasets}/import.js +3 -0
- package/dist/commands/datasets/import.js.map +1 -0
- package/dist/commands/{dataset → datasets}/list.js +3 -0
- package/dist/commands/datasets/list.js.map +1 -0
- package/dist/commands/{dataset → datasets}/visibility/get.js +3 -0
- package/dist/commands/datasets/visibility/get.js.map +1 -0
- package/dist/commands/{dataset → datasets}/visibility/set.js +3 -0
- package/dist/commands/datasets/visibility/set.js.map +1 -0
- package/dist/commands/debug.js +189 -74
- package/dist/commands/debug.js.map +1 -1
- package/dist/commands/documents/create.js +3 -0
- package/dist/commands/documents/create.js.map +1 -1
- package/dist/commands/documents/delete.js +3 -0
- package/dist/commands/documents/delete.js.map +1 -1
- package/dist/commands/documents/get.js +3 -0
- package/dist/commands/documents/get.js.map +1 -1
- package/dist/commands/documents/query.js +3 -0
- package/dist/commands/documents/query.js.map +1 -1
- package/dist/commands/documents/validate.js +3 -0
- package/dist/commands/documents/validate.js.map +1 -1
- package/dist/commands/{hook → hooks}/attempt.js +3 -0
- package/dist/commands/hooks/attempt.js.map +1 -0
- package/dist/commands/{hook → hooks}/create.js +3 -0
- package/dist/commands/hooks/create.js.map +1 -0
- package/dist/commands/{hook → hooks}/delete.js +3 -0
- package/dist/commands/hooks/delete.js.map +1 -0
- package/dist/commands/{hook → hooks}/list.js +3 -0
- package/dist/commands/hooks/list.js.map +1 -0
- package/dist/commands/{hook → hooks}/logs.js +3 -0
- package/dist/commands/hooks/logs.js.map +1 -0
- package/dist/commands/init.js +65 -13
- package/dist/commands/init.js.map +1 -1
- package/dist/commands/mcp/configure.js +3 -2
- package/dist/commands/mcp/configure.js.map +1 -1
- package/dist/commands/preview.js +1 -0
- package/dist/commands/preview.js.map +1 -1
- package/dist/commands/projects/create.js +3 -0
- package/dist/commands/projects/create.js.map +1 -1
- package/dist/commands/projects/list.js +3 -0
- package/dist/commands/projects/list.js.map +1 -1
- package/dist/commands/{schema → schemas}/delete.js +3 -0
- package/dist/commands/schemas/delete.js.map +1 -0
- package/dist/commands/{schema → schemas}/deploy.js +3 -0
- package/dist/commands/schemas/deploy.js.map +1 -0
- package/dist/commands/{schema → schemas}/extract.js +3 -0
- package/dist/commands/schemas/extract.js.map +1 -0
- package/dist/commands/{schema → schemas}/list.js +3 -0
- package/dist/commands/schemas/list.js.map +1 -0
- package/dist/commands/{schema → schemas}/validate.js +3 -0
- package/dist/commands/schemas/validate.js.map +1 -0
- package/dist/commands/tokens/add.js +3 -0
- package/dist/commands/tokens/add.js.map +1 -1
- package/dist/commands/tokens/delete.js +3 -0
- package/dist/commands/tokens/delete.js.map +1 -1
- package/dist/commands/tokens/list.js +3 -0
- package/dist/commands/tokens/list.js.map +1 -1
- package/dist/commands/users/invite.js +3 -0
- package/dist/commands/users/invite.js.map +1 -1
- package/dist/commands/users/list.js +3 -0
- package/dist/commands/users/list.js.map +1 -1
- package/dist/hooks/commandNotFound/topicAliases.js +71 -0
- package/dist/hooks/commandNotFound/topicAliases.js.map +1 -0
- package/dist/topicAliases.js +51 -0
- package/dist/topicAliases.js.map +1 -0
- package/oclif.config.js +20 -7
- package/oclif.manifest.json +471 -359
- package/package.json +5 -4
- package/dist/actions/debug/formatters.js +0 -22
- package/dist/actions/debug/formatters.js.map +0 -1
- package/dist/actions/debug/getGlobalConfigLocation.js +0 -7
- package/dist/actions/debug/getGlobalConfigLocation.js.map +0 -1
- package/dist/commands/backup/disable.js.map +0 -1
- package/dist/commands/backup/download.js.map +0 -1
- package/dist/commands/backup/enable.js.map +0 -1
- package/dist/commands/backup/list.js.map +0 -1
- package/dist/commands/dataset/alias/create.js.map +0 -1
- package/dist/commands/dataset/alias/delete.js.map +0 -1
- package/dist/commands/dataset/alias/link.js.map +0 -1
- package/dist/commands/dataset/alias/unlink.js.map +0 -1
- package/dist/commands/dataset/copy.js.map +0 -1
- package/dist/commands/dataset/create.js.map +0 -1
- package/dist/commands/dataset/delete.js.map +0 -1
- package/dist/commands/dataset/embeddings/disable.js.map +0 -1
- package/dist/commands/dataset/embeddings/enable.js.map +0 -1
- package/dist/commands/dataset/embeddings/status.js.map +0 -1
- package/dist/commands/dataset/export.js.map +0 -1
- package/dist/commands/dataset/import.js.map +0 -1
- package/dist/commands/dataset/list.js.map +0 -1
- package/dist/commands/dataset/visibility/get.js.map +0 -1
- package/dist/commands/dataset/visibility/set.js.map +0 -1
- package/dist/commands/hook/attempt.js.map +0 -1
- package/dist/commands/hook/create.js.map +0 -1
- package/dist/commands/hook/delete.js.map +0 -1
- package/dist/commands/hook/list.js.map +0 -1
- package/dist/commands/hook/logs.js.map +0 -1
- package/dist/commands/schema/delete.js.map +0 -1
- package/dist/commands/schema/deploy.js.map +0 -1
- package/dist/commands/schema/extract.js.map +0 -1
- package/dist/commands/schema/list.js.map +0 -1
- package/dist/commands/schema/validate.js.map +0 -1
|
@@ -1,102 +1,151 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { access } from 'node:fs/promises';
|
|
2
|
+
import path from 'node:path';
|
|
3
|
+
import { getCliToken, getStudioConfig, getUserConfig, tryFindStudioConfigPath } from '@sanity/cli-core';
|
|
2
4
|
import { getProjectById } from '../../services/projects.js';
|
|
3
5
|
import { getCliUser, getProjectUser } from '../../services/user.js';
|
|
4
|
-
import {
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
// Gather all info in parallel where possible
|
|
8
|
-
const [auth, globalConfig, projectConfigResult, versions] = await Promise.all([
|
|
9
|
-
gatherAuthInfo(includeSecrets),
|
|
10
|
-
gatherGlobalConfig(),
|
|
11
|
-
cliConfig ? gatherProjectConfig(cliConfig) : undefined,
|
|
12
|
-
gatherVersionsInfo(projectRoot)
|
|
13
|
-
]);
|
|
14
|
-
// Gather user and project info that depend on auth
|
|
15
|
-
const user = await gatherUserInfo(projectConfigResult, auth.hasToken);
|
|
16
|
-
const project = await gatherProjectInfo(projectConfigResult, auth.hasToken, user);
|
|
17
|
-
return {
|
|
18
|
-
auth,
|
|
19
|
-
globalConfig,
|
|
20
|
-
project,
|
|
21
|
-
projectConfig: projectConfigResult,
|
|
22
|
-
user,
|
|
23
|
-
versions
|
|
24
|
-
};
|
|
25
|
-
}
|
|
26
|
-
async function gatherAuthInfo(includeSecrets) {
|
|
6
|
+
import { getCliVersion } from '../../util/getCliVersion.js';
|
|
7
|
+
import { detectCliInstallation } from '../../util/packageManager/installationInfo/index.js';
|
|
8
|
+
export async function gatherUserInfo(projectId) {
|
|
27
9
|
const token = await getCliToken();
|
|
28
|
-
|
|
29
|
-
return {
|
|
30
|
-
authToken: includeSecrets && token ? token : '<redacted>',
|
|
31
|
-
hasToken,
|
|
32
|
-
userType: 'normal'
|
|
33
|
-
};
|
|
34
|
-
}
|
|
35
|
-
function gatherGlobalConfig() {
|
|
36
|
-
return getUserConfig().all;
|
|
37
|
-
}
|
|
38
|
-
function gatherProjectConfig(cliConfig) {
|
|
39
|
-
if (!cliConfig.api?.projectId) {
|
|
40
|
-
return new Error('Missing required "api.projectId" key');
|
|
41
|
-
}
|
|
42
|
-
return cliConfig;
|
|
43
|
-
}
|
|
44
|
-
async function gatherVersionsInfo(projectRoot) {
|
|
45
|
-
if (!projectRoot) {
|
|
46
|
-
return undefined;
|
|
47
|
-
}
|
|
48
|
-
try {
|
|
49
|
-
return await findSanityModulesVersions({
|
|
50
|
-
cwd: projectRoot.directory
|
|
51
|
-
});
|
|
52
|
-
} catch {
|
|
53
|
-
return [];
|
|
54
|
-
}
|
|
55
|
-
}
|
|
56
|
-
async function gatherUserInfo(projectConfig, hasToken) {
|
|
57
|
-
if (!hasToken) {
|
|
10
|
+
if (!token) {
|
|
58
11
|
return new Error('Not logged in');
|
|
59
12
|
}
|
|
60
13
|
try {
|
|
61
|
-
|
|
62
|
-
* If the project config has a project ID, get the user for the project
|
|
63
|
-
* Otherwise, get the user for the global client
|
|
64
|
-
*/ const userInfo = !projectConfig || projectConfig instanceof Error || !projectConfig.api?.projectId ? await getCliUser() : await getProjectUser(projectConfig.api.projectId);
|
|
14
|
+
const userInfo = projectId ? await getProjectUser(projectId) : await getCliUser();
|
|
65
15
|
return {
|
|
66
16
|
email: userInfo.email,
|
|
67
17
|
id: userInfo.id,
|
|
68
|
-
name: userInfo.name
|
|
18
|
+
name: userInfo.name,
|
|
19
|
+
provider: userInfo.provider
|
|
69
20
|
};
|
|
70
21
|
} catch (error) {
|
|
71
22
|
return error instanceof Error ? error : new Error('Failed to fetch user info');
|
|
72
23
|
}
|
|
73
24
|
}
|
|
74
|
-
async function
|
|
75
|
-
|
|
76
|
-
|
|
25
|
+
export async function gatherAuthInfo(includeSecrets) {
|
|
26
|
+
const token = await getCliToken();
|
|
27
|
+
const hasToken = Boolean(token);
|
|
28
|
+
const config = getUserConfig();
|
|
29
|
+
const authType = config.get('authType');
|
|
30
|
+
return {
|
|
31
|
+
authToken: token ? includeSecrets ? token : '<redacted>' : undefined,
|
|
32
|
+
hasToken,
|
|
33
|
+
userType: typeof authType === 'string' ? authType : 'normal'
|
|
34
|
+
};
|
|
35
|
+
}
|
|
36
|
+
export async function gatherCliInfo() {
|
|
37
|
+
const [version, installation] = await Promise.all([
|
|
38
|
+
getCliVersion(),
|
|
39
|
+
detectCliInstallation()
|
|
40
|
+
]);
|
|
41
|
+
const { packageManager, resolvedFrom } = installation.currentExecution;
|
|
42
|
+
let installContext;
|
|
43
|
+
switch(resolvedFrom){
|
|
44
|
+
case 'global':
|
|
45
|
+
{
|
|
46
|
+
installContext = packageManager ? `globally (${packageManager})` : 'globally';
|
|
47
|
+
break;
|
|
48
|
+
}
|
|
49
|
+
case 'local':
|
|
50
|
+
{
|
|
51
|
+
installContext = 'locally';
|
|
52
|
+
break;
|
|
53
|
+
}
|
|
54
|
+
case 'npx':
|
|
55
|
+
{
|
|
56
|
+
installContext = 'via npx';
|
|
57
|
+
break;
|
|
58
|
+
}
|
|
59
|
+
default:
|
|
60
|
+
{
|
|
61
|
+
installContext = 'unknown';
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
return {
|
|
65
|
+
installContext,
|
|
66
|
+
version
|
|
67
|
+
};
|
|
68
|
+
}
|
|
69
|
+
export async function gatherProjectInfo(projectDirectory) {
|
|
70
|
+
if (!projectDirectory) {
|
|
71
|
+
return undefined;
|
|
77
72
|
}
|
|
78
|
-
const
|
|
79
|
-
|
|
80
|
-
|
|
73
|
+
const [cliConfigName, studioConfigFullPath] = await Promise.all([
|
|
74
|
+
findCliConfigFile(projectDirectory),
|
|
75
|
+
tryFindStudioConfigPath(projectDirectory)
|
|
76
|
+
]);
|
|
77
|
+
return {
|
|
78
|
+
cliConfigPath: cliConfigName,
|
|
79
|
+
rootPath: projectDirectory,
|
|
80
|
+
studioConfigPath: studioConfigFullPath ? path.basename(studioConfigFullPath) : undefined
|
|
81
|
+
};
|
|
82
|
+
}
|
|
83
|
+
export async function gatherStudioWorkspaces(projectDirectory) {
|
|
84
|
+
const rawConfig = await getStudioConfig(projectDirectory, {
|
|
85
|
+
resolvePlugins: false
|
|
86
|
+
});
|
|
87
|
+
if (Array.isArray(rawConfig)) {
|
|
88
|
+
return rawConfig.map((ws)=>({
|
|
89
|
+
dataset: ws.dataset,
|
|
90
|
+
name: ws.name,
|
|
91
|
+
projectId: ws.projectId
|
|
92
|
+
}));
|
|
81
93
|
}
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
94
|
+
return [
|
|
95
|
+
{
|
|
96
|
+
dataset: rawConfig.dataset,
|
|
97
|
+
name: rawConfig.name,
|
|
98
|
+
projectId: rawConfig.projectId
|
|
99
|
+
}
|
|
100
|
+
];
|
|
101
|
+
}
|
|
102
|
+
export async function gatherResolvedWorkspaces(projectDirectory, userId) {
|
|
103
|
+
// resolvePlugins: true goes through getStudioWorkspaces() which calls resolveConfig()
|
|
104
|
+
// from the sanity package. resolveConfig() always returns an array of workspaces,
|
|
105
|
+
// so resolvedConfigSchema (z.array(...)) is guaranteed to match.
|
|
106
|
+
const resolvedConfig = await getStudioConfig(projectDirectory, {
|
|
107
|
+
resolvePlugins: true
|
|
108
|
+
});
|
|
109
|
+
const projectMap = await fetchRolesByProject(resolvedConfig, userId);
|
|
110
|
+
return resolvedConfig.map((ws)=>({
|
|
111
|
+
name: ws.name,
|
|
112
|
+
roles: projectMap.get(ws.projectId) ?? [],
|
|
113
|
+
title: ws.title
|
|
114
|
+
}));
|
|
115
|
+
}
|
|
116
|
+
async function findCliConfigFile(directory) {
|
|
117
|
+
for (const name of [
|
|
118
|
+
'sanity.cli.ts',
|
|
119
|
+
'sanity.cli.js'
|
|
120
|
+
]){
|
|
121
|
+
try {
|
|
122
|
+
await access(path.join(directory, name));
|
|
123
|
+
return name;
|
|
124
|
+
} catch {
|
|
125
|
+
// File doesn't exist, try next
|
|
86
126
|
}
|
|
87
|
-
const userId = user instanceof Error || !user ? null : user.id;
|
|
88
|
-
const member = (projectInfo.members || []).find((member)=>member.id === userId);
|
|
89
|
-
return {
|
|
90
|
-
displayName: projectInfo.displayName,
|
|
91
|
-
id: projectId,
|
|
92
|
-
// @ts-expect-error - Incorrect type definition in @sanity/client
|
|
93
|
-
userRoles: member && member.roles ? member.roles.map((role)=>role.name) : [
|
|
94
|
-
'<none>'
|
|
95
|
-
]
|
|
96
|
-
};
|
|
97
|
-
} catch (error) {
|
|
98
|
-
return error instanceof Error ? error : new Error('Failed to fetch project info');
|
|
99
127
|
}
|
|
128
|
+
return undefined;
|
|
129
|
+
}
|
|
130
|
+
async function fetchRolesByProject(workspaces, userId) {
|
|
131
|
+
const projectMap = new Map();
|
|
132
|
+
if (!userId) return projectMap;
|
|
133
|
+
const projectIds = [
|
|
134
|
+
...new Set(workspaces.map((ws)=>ws.projectId))
|
|
135
|
+
];
|
|
136
|
+
await Promise.all(projectIds.map(async (projectId)=>{
|
|
137
|
+
try {
|
|
138
|
+
const project = await getProjectById(projectId);
|
|
139
|
+
if (!project) return;
|
|
140
|
+
const member = (project.members || []).find((m)=>m.id === userId);
|
|
141
|
+
// @ts-expect-error - Incorrect type definition in @sanity/client
|
|
142
|
+
const roles = member?.roles?.map((r)=>r.name) ?? [];
|
|
143
|
+
projectMap.set(projectId, roles);
|
|
144
|
+
} catch {
|
|
145
|
+
// Project not accessible, skip roles
|
|
146
|
+
}
|
|
147
|
+
}));
|
|
148
|
+
return projectMap;
|
|
100
149
|
}
|
|
101
150
|
|
|
102
151
|
//# sourceMappingURL=gatherDebugInfo.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../../src/actions/debug/gatherDebugInfo.ts"],"sourcesContent":["import {
|
|
1
|
+
{"version":3,"sources":["../../../src/actions/debug/gatherDebugInfo.ts"],"sourcesContent":["import {access} from 'node:fs/promises'\nimport path from 'node:path'\n\nimport {\n getCliToken,\n getStudioConfig,\n getUserConfig,\n tryFindStudioConfigPath,\n} from '@sanity/cli-core'\n\nimport {getProjectById} from '../../services/projects.js'\nimport {getCliUser, getProjectUser} from '../../services/user.js'\nimport {getCliVersion} from '../../util/getCliVersion.js'\nimport {detectCliInstallation} from '../../util/packageManager/installationInfo/index.js'\nimport {\n type AuthInfo,\n type CliInfo,\n type ProjectInfo,\n type ResolvedWorkspace,\n type StudioWorkspace,\n type UserInfo,\n} from './types.js'\n\nexport async function gatherUserInfo(projectId: string | undefined): Promise<Error | UserInfo> {\n const token = await getCliToken()\n if (!token) {\n return new Error('Not logged in')\n }\n\n try {\n const userInfo = projectId ? await getProjectUser(projectId) : await getCliUser()\n\n return {\n email: userInfo.email,\n id: userInfo.id,\n name: userInfo.name,\n provider: userInfo.provider,\n }\n } catch (error) {\n return error instanceof Error ? error : new Error('Failed to fetch user info')\n }\n}\n\nexport async function gatherAuthInfo(includeSecrets: boolean): Promise<AuthInfo> {\n const token = await getCliToken()\n const hasToken = Boolean(token)\n const config = getUserConfig()\n const authType = config.get('authType')\n\n return {\n authToken: token ? (includeSecrets ? token : '<redacted>') : undefined,\n hasToken,\n userType: typeof authType === 'string' ? authType : 'normal',\n }\n}\n\nexport async function gatherCliInfo(): Promise<CliInfo> {\n const [version, installation] = await Promise.all([getCliVersion(), detectCliInstallation()])\n\n const {packageManager, resolvedFrom} = installation.currentExecution\n\n let installContext: string\n switch (resolvedFrom) {\n case 'global': {\n installContext = packageManager ? `globally (${packageManager})` : 'globally'\n break\n }\n case 'local': {\n installContext = 'locally'\n break\n }\n case 'npx': {\n installContext = 'via npx'\n break\n }\n default: {\n installContext = 'unknown'\n }\n }\n\n return {installContext, version}\n}\n\nexport async function gatherProjectInfo(\n projectDirectory: string | undefined,\n): Promise<ProjectInfo | undefined> {\n if (!projectDirectory) {\n return undefined\n }\n\n const [cliConfigName, studioConfigFullPath] = await Promise.all([\n findCliConfigFile(projectDirectory),\n tryFindStudioConfigPath(projectDirectory),\n ])\n\n return {\n cliConfigPath: cliConfigName,\n rootPath: projectDirectory,\n studioConfigPath: studioConfigFullPath ? path.basename(studioConfigFullPath) : undefined,\n }\n}\n\nexport async function gatherStudioWorkspaces(projectDirectory: string): Promise<StudioWorkspace[]> {\n const rawConfig = await getStudioConfig(projectDirectory, {resolvePlugins: false})\n\n if (Array.isArray(rawConfig)) {\n return rawConfig.map((ws) => ({\n dataset: ws.dataset,\n name: ws.name,\n projectId: ws.projectId,\n }))\n }\n\n return [\n {\n dataset: rawConfig.dataset,\n name: rawConfig.name,\n projectId: rawConfig.projectId,\n },\n ]\n}\n\nexport async function gatherResolvedWorkspaces(\n projectDirectory: string,\n userId: string | undefined,\n): Promise<ResolvedWorkspace[]> {\n // resolvePlugins: true goes through getStudioWorkspaces() which calls resolveConfig()\n // from the sanity package. resolveConfig() always returns an array of workspaces,\n // so resolvedConfigSchema (z.array(...)) is guaranteed to match.\n const resolvedConfig = await getStudioConfig(projectDirectory, {resolvePlugins: true})\n\n const projectMap = await fetchRolesByProject(resolvedConfig, userId)\n\n return resolvedConfig.map((ws) => ({\n name: ws.name,\n roles: projectMap.get(ws.projectId) ?? [],\n title: ws.title,\n }))\n}\n\nasync function findCliConfigFile(directory: string): Promise<string | undefined> {\n for (const name of ['sanity.cli.ts', 'sanity.cli.js']) {\n try {\n await access(path.join(directory, name))\n return name\n } catch {\n // File doesn't exist, try next\n }\n }\n return undefined\n}\n\nasync function fetchRolesByProject(\n workspaces: {projectId: string}[],\n userId: string | undefined,\n): Promise<Map<string, string[]>> {\n const projectMap = new Map<string, string[]>()\n if (!userId) return projectMap\n\n const projectIds = [...new Set(workspaces.map((ws) => ws.projectId))]\n await Promise.all(\n projectIds.map(async (projectId) => {\n try {\n const project = await getProjectById(projectId)\n if (!project) return\n const member = (project.members || []).find((m) => m.id === userId)\n // @ts-expect-error - Incorrect type definition in @sanity/client\n const roles: string[] = member?.roles?.map((r) => r.name) ?? []\n projectMap.set(projectId, roles)\n } catch {\n // Project not accessible, skip roles\n }\n }),\n )\n return projectMap\n}\n"],"names":["access","path","getCliToken","getStudioConfig","getUserConfig","tryFindStudioConfigPath","getProjectById","getCliUser","getProjectUser","getCliVersion","detectCliInstallation","gatherUserInfo","projectId","token","Error","userInfo","email","id","name","provider","error","gatherAuthInfo","includeSecrets","hasToken","Boolean","config","authType","get","authToken","undefined","userType","gatherCliInfo","version","installation","Promise","all","packageManager","resolvedFrom","currentExecution","installContext","gatherProjectInfo","projectDirectory","cliConfigName","studioConfigFullPath","findCliConfigFile","cliConfigPath","rootPath","studioConfigPath","basename","gatherStudioWorkspaces","rawConfig","resolvePlugins","Array","isArray","map","ws","dataset","gatherResolvedWorkspaces","userId","resolvedConfig","projectMap","fetchRolesByProject","roles","title","directory","join","workspaces","Map","projectIds","Set","project","member","members","find","m","r","set"],"mappings":"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"}
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import { inspect, styleText } from 'node:util';
|
|
2
|
+
export function sectionHeader(title) {
|
|
3
|
+
return styleText('bold', `${title}:`);
|
|
4
|
+
}
|
|
5
|
+
export function formatKeyValue(key, value, options) {
|
|
6
|
+
const indent = options?.indent ?? 2;
|
|
7
|
+
const padTo = options?.padTo ?? 0;
|
|
8
|
+
const paddedKey = `${key}:`.padEnd(padTo > 0 ? padTo + 1 : key.length + 1);
|
|
9
|
+
const formattedValue = formatValue(value);
|
|
10
|
+
return `${' '.repeat(indent)}${styleText('dim', paddedKey)} ${formattedValue}`;
|
|
11
|
+
}
|
|
12
|
+
function formatValue(value) {
|
|
13
|
+
if (Array.isArray(value)) {
|
|
14
|
+
return `[ ${value.map((v)=>JSON.stringify(v)).join(', ')} ]`;
|
|
15
|
+
}
|
|
16
|
+
if (typeof value === 'string') {
|
|
17
|
+
return value;
|
|
18
|
+
}
|
|
19
|
+
return inspect(value, {
|
|
20
|
+
colors: true,
|
|
21
|
+
depth: Infinity
|
|
22
|
+
});
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
//# sourceMappingURL=output.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../../src/actions/debug/output.ts"],"sourcesContent":["import {inspect, styleText} from 'node:util'\n\nexport function sectionHeader(title: string): string {\n return styleText('bold', `${title}:`)\n}\n\nexport function formatKeyValue(\n key: string,\n value: unknown,\n options?: {indent?: number; padTo?: number},\n): string {\n const indent = options?.indent ?? 2\n const padTo = options?.padTo ?? 0\n const paddedKey = `${key}:`.padEnd(padTo > 0 ? padTo + 1 : key.length + 1)\n const formattedValue = formatValue(value)\n return `${' '.repeat(indent)}${styleText('dim', paddedKey)} ${formattedValue}`\n}\n\nfunction formatValue(value: unknown): string {\n if (Array.isArray(value)) {\n return `[ ${value.map((v) => JSON.stringify(v)).join(', ')} ]`\n }\n if (typeof value === 'string') {\n return value\n }\n return inspect(value, {colors: true, depth: Infinity})\n}\n"],"names":["inspect","styleText","sectionHeader","title","formatKeyValue","key","value","options","indent","padTo","paddedKey","padEnd","length","formattedValue","formatValue","repeat","Array","isArray","map","v","JSON","stringify","join","colors","depth","Infinity"],"mappings":"AAAA,SAAQA,OAAO,EAAEC,SAAS,QAAO,YAAW;AAE5C,OAAO,SAASC,cAAcC,KAAa;IACzC,OAAOF,UAAU,QAAQ,GAAGE,MAAM,CAAC,CAAC;AACtC;AAEA,OAAO,SAASC,eACdC,GAAW,EACXC,KAAc,EACdC,OAA2C;IAE3C,MAAMC,SAASD,SAASC,UAAU;IAClC,MAAMC,QAAQF,SAASE,SAAS;IAChC,MAAMC,YAAY,GAAGL,IAAI,CAAC,CAAC,CAACM,MAAM,CAACF,QAAQ,IAAIA,QAAQ,IAAIJ,IAAIO,MAAM,GAAG;IACxE,MAAMC,iBAAiBC,YAAYR;IACnC,OAAO,GAAG,IAAIS,MAAM,CAACP,UAAUP,UAAU,OAAOS,WAAW,CAAC,EAAEG,gBAAgB;AAChF;AAEA,SAASC,YAAYR,KAAc;IACjC,IAAIU,MAAMC,OAAO,CAACX,QAAQ;QACxB,OAAO,CAAC,EAAE,EAAEA,MAAMY,GAAG,CAAC,CAACC,IAAMC,KAAKC,SAAS,CAACF,IAAIG,IAAI,CAAC,MAAM,EAAE,CAAC;IAChE;IACA,IAAI,OAAOhB,UAAU,UAAU;QAC7B,OAAOA;IACT;IACA,OAAON,QAAQM,OAAO;QAACiB,QAAQ;QAAMC,OAAOC;IAAQ;AACtD"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../../src/actions/debug/types.ts"],"sourcesContent":["
|
|
1
|
+
{"version":3,"sources":["../../../src/actions/debug/types.ts"],"sourcesContent":["export interface UserInfo {\n email: string\n id: string\n name: string\n provider: string\n}\n\nexport interface AuthInfo {\n authToken: string | undefined\n hasToken: boolean\n userType: string\n}\n\nexport interface CliInfo {\n installContext: string\n version: string\n}\n\nexport interface ProjectInfo {\n cliConfigPath: string | undefined\n rootPath: string\n studioConfigPath: string | undefined\n}\n\nexport interface StudioWorkspace {\n dataset: string\n name: string | undefined\n projectId: string\n}\n\nexport interface ResolvedWorkspace {\n name: string\n roles: string[]\n title: string\n}\n"],"names":[],"mappings":"AA8BA,WAIC"}
|
|
@@ -12,10 +12,10 @@ const NO_EDITORS_DETECTED_MESSAGE = `Couldn't auto-configure Sanity MCP server f
|
|
|
12
12
|
* Main MCP setup orchestration
|
|
13
13
|
* Opt-out by default: runs automatically unless skip option is set
|
|
14
14
|
*/ export async function setupMCP(options) {
|
|
15
|
-
const { explicit = false,
|
|
15
|
+
const { explicit = false, mode = 'prompt' } = options ?? {};
|
|
16
16
|
// 1. Check for explicit opt-out
|
|
17
|
-
if (skip) {
|
|
18
|
-
|
|
17
|
+
if (mode === 'skip') {
|
|
18
|
+
mcpDebug('Skipping MCP configuration (mode: skip)');
|
|
19
19
|
return {
|
|
20
20
|
alreadyConfiguredEditors: [],
|
|
21
21
|
configuredEditors: [],
|
|
@@ -57,8 +57,8 @@ const NO_EDITORS_DETECTED_MESSAGE = `Couldn't auto-configure Sanity MCP server f
|
|
|
57
57
|
}
|
|
58
58
|
// Non-actionable editors are already configured with valid credentials
|
|
59
59
|
const alreadyConfiguredEditors = editors.filter((e)=>!actionable.includes(e)).map((e)=>e.name);
|
|
60
|
-
// 5.
|
|
61
|
-
const selected = await promptForMCPSetup(actionable);
|
|
60
|
+
// 5. Select editors to configure — prompt interactively or auto-select all
|
|
61
|
+
const selected = mode === 'auto' ? actionable : await promptForMCPSetup(actionable);
|
|
62
62
|
if (!selected || selected.length === 0) {
|
|
63
63
|
// User deselected all editors
|
|
64
64
|
ux.stdout('MCP configuration skipped');
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../../src/actions/mcp/setupMCP.ts"],"sourcesContent":["import {ux} from '@oclif/core'\nimport {subdebug} from '@sanity/cli-core'\nimport {logSymbols} from '@sanity/cli-core/ux'\n\nimport {createMCPToken, MCP_SERVER_URL} from '../../services/mcp.js'\nimport {detectAvailableEditors} from './detectAvailableEditors.js'\nimport {type EditorName} from './editorConfigs.js'\nimport {promptForMCPSetup} from './promptForMCPSetup.js'\nimport {validateEditorTokens} from './validateEditorTokens.js'\nimport {writeMCPConfig} from './writeMCPConfig.js'\n\nconst mcpDebug = subdebug('mcp:setup')\n\nconst NO_EDITORS_DETECTED_MESSAGE = `Couldn't auto-configure Sanity MCP server for your editor. Visit ${MCP_SERVER_URL} for setup instructions.`\n\ninterface MCPSetupOptions {\n /**\n * Whether the user explicitly requested MCP configuration (e.g. `sanity mcp configure`).\n * When true, shows status messages even when there's nothing to do.\n * When false/undefined (e.g. called from `sanity init`), stays quiet.\n */\n explicit?: boolean\n\n /**\n *
|
|
1
|
+
{"version":3,"sources":["../../../src/actions/mcp/setupMCP.ts"],"sourcesContent":["import {ux} from '@oclif/core'\nimport {subdebug} from '@sanity/cli-core'\nimport {logSymbols} from '@sanity/cli-core/ux'\n\nimport {createMCPToken, MCP_SERVER_URL} from '../../services/mcp.js'\nimport {detectAvailableEditors} from './detectAvailableEditors.js'\nimport {type EditorName} from './editorConfigs.js'\nimport {promptForMCPSetup} from './promptForMCPSetup.js'\nimport {validateEditorTokens} from './validateEditorTokens.js'\nimport {writeMCPConfig} from './writeMCPConfig.js'\n\nconst mcpDebug = subdebug('mcp:setup')\n\nconst NO_EDITORS_DETECTED_MESSAGE = `Couldn't auto-configure Sanity MCP server for your editor. Visit ${MCP_SERVER_URL} for setup instructions.`\n\ninterface MCPSetupOptions {\n /**\n * Whether the user explicitly requested MCP configuration (e.g. `sanity mcp configure`).\n * When true, shows status messages even when there's nothing to do.\n * When false/undefined (e.g. called from `sanity init`), stays quiet.\n */\n explicit?: boolean\n\n /**\n * Controls how MCP setup behaves:\n * - 'prompt': Ask the user which editors to configure (default)\n * - 'auto': Auto-configure all detected editors without prompting\n * - 'skip': Skip MCP configuration entirely\n */\n mode?: 'auto' | 'prompt' | 'skip'\n}\n\ninterface MCPSetupResult {\n /** Editors that were already configured with valid credentials (nothing to do) */\n alreadyConfiguredEditors: EditorName[]\n configuredEditors: EditorName[]\n detectedEditors: EditorName[]\n skipped: boolean\n\n error?: Error\n}\n\n/**\n * Main MCP setup orchestration\n * Opt-out by default: runs automatically unless skip option is set\n */\nexport async function setupMCP(options?: MCPSetupOptions): Promise<MCPSetupResult> {\n const {explicit = false, mode = 'prompt'} = options ?? {}\n\n // 1. Check for explicit opt-out\n if (mode === 'skip') {\n mcpDebug('Skipping MCP configuration (mode: skip)')\n return {\n alreadyConfiguredEditors: [],\n configuredEditors: [],\n detectedEditors: [],\n skipped: true,\n }\n }\n\n // 2. Detect available editors (filters out unparseable configs)\n const editors = await detectAvailableEditors()\n const detectedEditors = editors.map((e) => e.name)\n\n mcpDebug('Detected %d editors: %s', detectedEditors.length, detectedEditors)\n\n if (editors.length === 0) {\n if (explicit) {\n ux.warn(NO_EDITORS_DETECTED_MESSAGE)\n }\n return {\n alreadyConfiguredEditors: [],\n configuredEditors: [],\n detectedEditors,\n skipped: true,\n }\n }\n\n // 3. Validate existing tokens against the Sanity API\n await validateEditorTokens(editors)\n\n // 4. Check if there's anything actionable\n const actionable = editors.filter((e) => !e.configured || e.authStatus !== 'valid')\n\n if (actionable.length === 0) {\n mcpDebug('All editors configured with valid credentials')\n const alreadyConfiguredEditors = editors\n .filter((e) => e.configured && e.authStatus === 'valid')\n .map((e) => e.name)\n if (explicit) {\n ux.stdout(`${logSymbols.success} All detected editors are already configured`)\n }\n return {\n alreadyConfiguredEditors,\n configuredEditors: [],\n detectedEditors,\n skipped: true,\n }\n }\n\n // Non-actionable editors are already configured with valid credentials\n const alreadyConfiguredEditors = editors.filter((e) => !actionable.includes(e)).map((e) => e.name)\n\n // 5. Select editors to configure — prompt interactively or auto-select all\n const selected = mode === 'auto' ? actionable : await promptForMCPSetup(actionable)\n\n if (!selected || selected.length === 0) {\n // User deselected all editors\n ux.stdout('MCP configuration skipped')\n return {\n alreadyConfiguredEditors,\n configuredEditors: [],\n detectedEditors,\n skipped: true,\n }\n }\n\n // 6. Get a token — reuse a valid existing one or create a new one\n let token: string | undefined\n\n // Look for an existing valid token we can reuse\n const validEditor = editors.find((e) => e.authStatus === 'valid' && e.existingToken)\n if (validEditor?.existingToken) {\n mcpDebug('Reusing valid token from %s', validEditor.name)\n token = validEditor.existingToken\n }\n\n // Fall back to creating a new token\n if (!token) {\n try {\n token = await createMCPToken()\n } catch (error) {\n const err = error instanceof Error ? error : new Error(String(error))\n mcpDebug('Error creating MCP token', error)\n ux.warn(`Could not configure MCP: ${err.message}`)\n ux.warn('You can set up MCP manually later using https://mcp.sanity.io')\n return {\n alreadyConfiguredEditors,\n configuredEditors: [],\n detectedEditors,\n error: err,\n skipped: false,\n }\n }\n }\n\n // 7. Write configs for each selected editor\n const configuredEditors: EditorName[] = []\n try {\n for (const editor of selected) {\n await writeMCPConfig(editor, token)\n configuredEditors.push(editor.name)\n }\n } catch (error) {\n const err = error instanceof Error ? error : new Error(String(error))\n mcpDebug('Error writing MCP config', error)\n ux.warn(`Could not configure MCP: ${err.message}`)\n ux.warn('You can set up MCP manually later using https://mcp.sanity.io')\n return {\n alreadyConfiguredEditors,\n configuredEditors,\n detectedEditors,\n error: err,\n skipped: false,\n }\n }\n\n ux.stdout(`${logSymbols.success} MCP configured for ${configuredEditors.join(', ')}`)\n\n return {\n alreadyConfiguredEditors,\n configuredEditors,\n detectedEditors,\n skipped: false,\n }\n}\n"],"names":["ux","subdebug","logSymbols","createMCPToken","MCP_SERVER_URL","detectAvailableEditors","promptForMCPSetup","validateEditorTokens","writeMCPConfig","mcpDebug","NO_EDITORS_DETECTED_MESSAGE","setupMCP","options","explicit","mode","alreadyConfiguredEditors","configuredEditors","detectedEditors","skipped","editors","map","e","name","length","warn","actionable","filter","configured","authStatus","stdout","success","includes","selected","token","validEditor","find","existingToken","error","err","Error","String","message","editor","push","join"],"mappings":"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"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../../src/actions/schema/getExtractOptions.ts"],"sourcesContent":["import {existsSync, statSync} from 'node:fs'\nimport {extname, join, resolve} from 'node:path'\n\nimport {type CliConfig, ProjectRootResult} from '@sanity/cli-core'\n\nimport {type ExtractSchemaCommand} from '../../commands/
|
|
1
|
+
{"version":3,"sources":["../../../src/actions/schema/getExtractOptions.ts"],"sourcesContent":["import {existsSync, statSync} from 'node:fs'\nimport {extname, join, resolve} from 'node:path'\n\nimport {type CliConfig, ProjectRootResult} from '@sanity/cli-core'\n\nimport {type ExtractSchemaCommand} from '../../commands/schemas/extract.js'\n\nexport interface ExtractOptions {\n configPath: string\n enforceRequiredFields: boolean\n format: string\n outputPath: string\n watchPatterns: string[]\n workspace: string | undefined\n}\n\ninterface GetExtractionOptions {\n flags: ExtractSchemaCommand['flags']\n projectRoot: ProjectRootResult\n schemaExtraction: CliConfig['schemaExtraction']\n}\n\nexport function getExtractOptions({\n flags,\n projectRoot,\n schemaExtraction,\n}: GetExtractionOptions): ExtractOptions {\n const pathFlag = flags.path ?? schemaExtraction?.path\n let outputPath: string\n if (pathFlag) {\n const resolved = resolve(join(projectRoot.directory, pathFlag))\n const isExistingDirectory = existsSync(resolved) && statSync(resolved).isDirectory()\n\n outputPath =\n isExistingDirectory || !extname(resolved) ? join(resolved, 'schema.json') : resolved\n } else {\n outputPath = resolve(join(projectRoot.directory, 'schema.json'))\n }\n\n return {\n configPath: projectRoot.path,\n enforceRequiredFields:\n flags['enforce-required-fields'] ?? schemaExtraction?.enforceRequiredFields ?? false,\n format: flags.format ?? 'groq-type-nodes',\n outputPath,\n watchPatterns: flags['watch-patterns'] ?? schemaExtraction?.watchPatterns ?? [],\n workspace: flags.workspace ?? schemaExtraction?.workspace,\n }\n}\n"],"names":["existsSync","statSync","extname","join","resolve","getExtractOptions","flags","projectRoot","schemaExtraction","pathFlag","path","outputPath","resolved","directory","isExistingDirectory","isDirectory","configPath","enforceRequiredFields","format","watchPatterns","workspace"],"mappings":"AAAA,SAAQA,UAAU,EAAEC,QAAQ,QAAO,UAAS;AAC5C,SAAQC,OAAO,EAAEC,IAAI,EAAEC,OAAO,QAAO,YAAW;AAqBhD,OAAO,SAASC,kBAAkB,EAChCC,KAAK,EACLC,WAAW,EACXC,gBAAgB,EACK;IACrB,MAAMC,WAAWH,MAAMI,IAAI,IAAIF,kBAAkBE;IACjD,IAAIC;IACJ,IAAIF,UAAU;QACZ,MAAMG,WAAWR,QAAQD,KAAKI,YAAYM,SAAS,EAAEJ;QACrD,MAAMK,sBAAsBd,WAAWY,aAAaX,SAASW,UAAUG,WAAW;QAElFJ,aACEG,uBAAuB,CAACZ,QAAQU,YAAYT,KAAKS,UAAU,iBAAiBA;IAChF,OAAO;QACLD,aAAaP,QAAQD,KAAKI,YAAYM,SAAS,EAAE;IACnD;IAEA,OAAO;QACLG,YAAYT,YAAYG,IAAI;QAC5BO,uBACEX,KAAK,CAAC,0BAA0B,IAAIE,kBAAkBS,yBAAyB;QACjFC,QAAQZ,MAAMY,MAAM,IAAI;QACxBP;QACAQ,eAAeb,KAAK,CAAC,iBAAiB,IAAIE,kBAAkBW,iBAAiB,EAAE;QAC/EC,WAAWd,MAAMc,SAAS,IAAIZ,kBAAkBY;IAClD;AACF"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../../src/commands/backups/disable.ts"],"sourcesContent":["import {styleText} from 'node:util'\n\nimport {Args} from '@oclif/core'\nimport {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 {setBackup} from '../../services/backup.js'\nimport {listDatasets} from '../../services/datasets.js'\nimport {getProjectIdFlag} from '../../util/sharedFlags.js'\n\nconst disableBackupDebug = subdebug('backup:disable')\n\nexport class DisableBackupCommand extends SanityCommand<typeof DisableBackupCommand> {\n static override args = {\n dataset: Args.string({\n description: 'Dataset name to disable backup for',\n required: false,\n }),\n }\n\n static override description = 'Disable backup for a dataset.'\n\n static override examples = [\n {\n command: '<%= config.bin %> <%= command.id %>',\n description: 'Interactively disable backup for a dataset',\n },\n {\n command: '<%= config.bin %> <%= command.id %> production',\n description: 'Disable backup for the production dataset',\n },\n ]\n\n static override flags = {\n ...getProjectIdFlag({\n description: 'Project ID to disable backups for',\n semantics: 'override',\n }),\n }\n\n static override hiddenAliases: string[] = ['backup:disable']\n\n public async run(): Promise<void> {\n const {args} = await this.parse(DisableBackupCommand)\n let {dataset} = args\n\n const projectId = await this.getProjectId({\n fallback: () =>\n promptForProject({\n requiredPermissions: [\n {grant: 'read', permission: 'sanity.project.datasets'},\n {grant: 'update', permission: 'sanity.project.datasets'},\n ],\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 disableBackupDebug(`Failed to list datasets: ${message}`, error)\n this.error(`Failed to list datasets: ${message}`, {exit: 1})\n }\n\n if (datasets.length === 0) {\n this.error('No datasets found in this project.', {exit: 1})\n }\n\n if (dataset) {\n assertDatasetExists(datasets, dataset)\n } else {\n dataset = await this.promptForDataset(datasets)\n }\n\n try {\n await setBackup({dataset, projectId, status: false})\n\n this.log(`${styleText('green', `Disabled daily backups for dataset ${dataset}.\\n`)}`)\n this.log(\n `${styleText('yellow', 'Note: Existing backups will be retained according to your retention policy.\\n')}`,\n )\n\n disableBackupDebug(`Successfully disabled backup for dataset ${dataset}`)\n } catch (error) {\n const message = error instanceof Error ? error.message : String(error)\n disableBackupDebug(`Failed to disable backup for dataset`, error)\n this.error(`Disabling dataset backup failed: ${message}`, {exit: 1})\n }\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 const err = error as Error\n disableBackupDebug(`Error fetching datasets`, err)\n this.error(`Failed to fetch datasets:\\n${err.message}`, {exit: 1})\n }\n }\n}\n"],"names":["styleText","Args","SanityCommand","subdebug","select","assertDatasetExists","promptForProject","setBackup","listDatasets","getProjectIdFlag","disableBackupDebug","DisableBackupCommand","args","dataset","string","description","required","examples","command","flags","semantics","hiddenAliases","run","parse","projectId","getProjectId","fallback","requiredPermissions","grant","permission","datasets","error","message","Error","String","exit","length","promptForDataset","status","log","choices","map","name","value","err"],"mappings":"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"}
|
|
@@ -73,6 +73,9 @@ export class DownloadBackupCommand extends SanityCommand {
|
|
|
73
73
|
description: 'Allows overwriting of existing backup file'
|
|
74
74
|
})
|
|
75
75
|
};
|
|
76
|
+
static hiddenAliases = [
|
|
77
|
+
'backup:download'
|
|
78
|
+
];
|
|
76
79
|
async run() {
|
|
77
80
|
const { args } = await this.parse(DownloadBackupCommand);
|
|
78
81
|
let { dataset } = args;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../../src/commands/backups/download.ts"],"sourcesContent":["import {createWriteStream} from 'node:fs'\nimport {access, mkdir, mkdtemp} from 'node:fs/promises'\nimport {tmpdir} from 'node:os'\nimport path from 'node:path'\nimport {finished} from 'node:stream/promises'\nimport {styleText} from 'node:util'\n\nimport {Args, Flags} from '@oclif/core'\nimport {SanityCommand} from '@sanity/cli-core'\nimport {boxen, confirm, input, select} from '@sanity/cli-core/ux'\nimport {type DatasetsResponse} from '@sanity/client'\nimport pMap from 'p-map'\nimport prettyMs from 'pretty-ms'\n\nimport {archiveDir} from '../../actions/backup/archiveDir.js'\nimport {assertDatasetExists} from '../../actions/backup/assertDatasetExist.js'\nimport {backupDownloadDebug} from '../../actions/backup/backupDownloadDebug.js'\nimport {cleanupTmpDir} from '../../actions/backup/cleanupTmpDir.js'\nimport {downloadAsset} from '../../actions/backup/downloadAsset.js'\nimport {downloadDocument} from '../../actions/backup/downloadDocument.js'\nimport {type File, PaginatedGetBackupStream} from '../../actions/backup/fetchNextBackupPage.js'\nimport {newProgress} from '../../actions/backup/progressSpinner.js'\nimport {validateDatasetName} from '../../actions/dataset/validateDatasetName.js'\nimport {promptForDataset} from '../../prompts/promptForDataset.js'\nimport {promptForProject} from '../../prompts/promptForProject.js'\nimport {type BackupItem, listBackups} from '../../services/backup.js'\nimport {listDatasets} from '../../services/datasets.js'\nimport {humanFileSize} from '../../util/humanFileSize.js'\nimport {isPathDirName} from '../../util/isPathDirName.js'\nimport {getProjectIdFlag} from '../../util/sharedFlags.js'\n\nconst DEFAULT_DOWNLOAD_CONCURRENCY = 10\nconst MAX_DOWNLOAD_CONCURRENCY = 24\n\ninterface DownloadBackupOptions {\n backupId: string\n concurrency: number\n datasetName: string\n outDir: string\n outFileName: string\n overwrite: boolean\n projectId: string\n}\n\nexport class DownloadBackupCommand extends SanityCommand<typeof DownloadBackupCommand> {\n static override args = {\n dataset: Args.string({\n description: 'Dataset name to download backup from',\n required: false,\n }),\n }\n\n static override description = 'Download a dataset backup to a local file.'\n\n static override examples = [\n {\n command: '<%= config.bin %> <%= command.id %>',\n description: 'Interactively download a backup',\n },\n {\n command: '<%= config.bin %> <%= command.id %> production --backup-id 2024-01-01-backup-1',\n description: 'Download a specific backup for the production dataset',\n },\n {\n command:\n '<%= config.bin %> <%= command.id %> production --backup-id 2024-01-01-backup-2 --out /path/to/file',\n description: 'Download backup to a specific file',\n },\n {\n command:\n '<%= config.bin %> <%= command.id %> production --backup-id 2024-01-01-backup-3 --out /path/to/file --overwrite',\n description: 'Download backup and overwrite existing file',\n },\n ]\n\n static override flags = {\n ...getProjectIdFlag({\n description: 'Project ID to download backup from',\n semantics: 'override',\n }),\n 'backup-id': Flags.string({\n description: 'The backup ID to download',\n }),\n concurrency: Flags.integer({\n default: DEFAULT_DOWNLOAD_CONCURRENCY,\n description: `Concurrent number of backup item downloads (max: ${MAX_DOWNLOAD_CONCURRENCY})`,\n }),\n out: Flags.string({\n description: 'The file or directory path the backup should download to',\n }),\n overwrite: Flags.boolean({\n default: false,\n description: 'Allows overwriting of existing backup file',\n }),\n }\n\n static override hiddenAliases: string[] = ['backup:download']\n\n public async run(): Promise<void> {\n const {args} = await this.parse(DownloadBackupCommand)\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 backupDownloadDebug(`Failed to list datasets: ${message}`, error)\n this.error(`Failed to list datasets: ${message}`, {exit: 1})\n }\n\n if (datasets.length === 0) {\n this.error('No datasets found in this project.', {exit: 1})\n }\n\n if (dataset) {\n assertDatasetExists(datasets, dataset)\n } else {\n dataset = await promptForDataset({allowCreation: false, datasets})\n }\n\n const opts = await this.prepareBackupOptions(projectId, dataset)\n const outFilePath = path.join(opts.outDir, opts.outFileName)\n\n this.log(\n boxen(\n `Downloading backup for:\n\n${styleText('bold', 'projectId')}: ${styleText('cyan', opts.projectId)}\n${styleText('bold', 'dataset')}: ${styleText('cyan', opts.datasetName)}\n${styleText('bold', 'backupId')}: ${styleText('cyan', opts.backupId)}`,\n {\n borderColor: 'cyan',\n borderStyle: 'round',\n padding: 1,\n },\n ),\n )\n this.log('')\n this.log(`Downloading backup to \"${styleText('cyan', outFilePath)}\"`)\n\n const start = Date.now()\n const progressSpinner = newProgress('Setting up backup environment...')\n\n // Create a unique temporary directory to store files before bundling them into the archive at outputPath.\n // Temporary directories are normally deleted at the end of backup process, any unexpected exit may leave them\n // behind, hence it is important to create a unique directory for each attempt.\n const tmpOutDir = await mkdtemp(path.join(tmpdir(), `sanity-backup-`))\n\n // Create required directories if they don't exist.\n for (const dir of [\n opts.outDir,\n path.join(tmpOutDir, 'images'),\n path.join(tmpOutDir, 'files'),\n ]) {\n await mkdir(dir, {recursive: true})\n }\n\n backupDownloadDebug('Writing to temporary directory %s', tmpOutDir)\n const tmpOutDocumentsFile = path.join(tmpOutDir, 'data.ndjson')\n\n const docOutStream = createWriteStream(tmpOutDocumentsFile)\n\n try {\n const backupFileStream = new PaginatedGetBackupStream(\n opts.projectId,\n opts.datasetName,\n opts.backupId,\n )\n\n const files: File[] = []\n let i = 0\n for await (const file of backupFileStream) {\n files.push(file)\n i++\n progressSpinner.set({\n current: i,\n step: `Reading backup files...`,\n total: backupFileStream.totalFiles,\n update: true,\n })\n }\n\n let totalItemsDownloaded = 0\n await pMap(\n files,\n async (file: File) => {\n if (file.type === 'file' || file.type === 'image') {\n await downloadAsset(file.url, file.name, file.type, tmpOutDir)\n } else {\n const doc = await downloadDocument(file.url)\n docOutStream.write(`${doc}\\n`)\n }\n\n totalItemsDownloaded += 1\n progressSpinner.set({\n current: totalItemsDownloaded,\n step: `Downloading documents and assets...`,\n total: backupFileStream.totalFiles,\n update: true,\n })\n },\n {concurrency: opts.concurrency},\n )\n } catch (error) {\n progressSpinner.fail()\n const message = error instanceof Error ? error.message : String(error)\n backupDownloadDebug(`Downloading dataset backup failed: ${message}`, error)\n this.error(`Downloading dataset backup failed: ${message}`, {exit: 1})\n }\n\n docOutStream.end()\n await finished(docOutStream)\n\n progressSpinner.set({step: `Archiving files into a tarball...`, update: true})\n try {\n await archiveDir(tmpOutDir, outFilePath, (processedBytes: number) => {\n progressSpinner.update({\n step: `Archiving files into a tarball, ${humanFileSize(processedBytes)} bytes written...`,\n })\n })\n } catch (err) {\n progressSpinner.fail()\n const message = err instanceof Error ? err.message : String(err)\n backupDownloadDebug(`Archiving backup failed: ${message}`, err)\n this.error(`Archiving backup failed: ${message}`, {exit: 1})\n }\n\n progressSpinner.set({\n step: `Cleaning up temporary files at ${styleText('cyan', `${tmpOutDir}`)}`,\n })\n await cleanupTmpDir(tmpOutDir)\n\n progressSpinner.set({\n step: `Backup download complete [${prettyMs(Date.now() - start)}]`,\n })\n progressSpinner.succeed()\n }\n\n private async getOutputPath(defaultOutFileName: string): Promise<string> {\n if (this.flags.out !== undefined) {\n // Rewrite the output path to an absolute path, if it is not already.\n return path.resolve(this.flags.out)\n }\n\n const workDir = process.cwd()\n const inputResult = await input({\n default: path.join(workDir, defaultOutFileName),\n message: 'Output path:',\n })\n return path.resolve(inputResult)\n }\n\n private async prepareBackupOptions(\n projectId: string,\n datasetName: string,\n ): Promise<DownloadBackupOptions> {\n const err = validateDatasetName(datasetName)\n if (err) {\n this.error(err, {exit: 1})\n }\n\n const backupId = String(\n this.flags['backup-id'] || (await this.promptForBackupId(projectId, datasetName)),\n )\n\n if (\n 'concurrency' in this.flags &&\n (this.flags.concurrency < 1 || this.flags.concurrency > MAX_DOWNLOAD_CONCURRENCY)\n ) {\n this.error(`concurrency should be in 1 to ${MAX_DOWNLOAD_CONCURRENCY} range`, {exit: 1})\n }\n\n const defaultOutFileName = `${datasetName}-backup-${backupId}.tar.gz`\n let out = await this.getOutputPath(defaultOutFileName)\n\n // If path is a directory name, then add a default file name to the path.\n if (isPathDirName(out)) {\n out = path.join(out, defaultOutFileName)\n }\n\n const exists = await access(out).then(\n () => true,\n () => false,\n )\n // If the file already exists, ask for confirmation if it should be overwritten.\n if (!this.flags.overwrite && exists) {\n const shouldOverwrite = await confirm({\n default: false,\n message: `File \"${out}\" already exists, would you like to overwrite it?`,\n })\n\n // If the user does not want to overwrite the file, cancel the operation.\n if (!shouldOverwrite) {\n this.error('Operation cancelled.', {exit: 1})\n }\n }\n\n return {\n backupId,\n concurrency: this.flags.concurrency || DEFAULT_DOWNLOAD_CONCURRENCY,\n datasetName,\n outDir: path.dirname(out),\n outFileName: path.basename(out),\n overwrite: this.flags.overwrite,\n projectId,\n }\n }\n\n private async promptForBackupId(projectId: string, datasetName: string): Promise<string> {\n const maxBackupIdsShown = 100\n\n try {\n const response = await listBackups({\n datasetName,\n limit: maxBackupIdsShown,\n projectId,\n })\n\n if (!response?.backups?.length) {\n this.error('No backups found', {exit: 1})\n }\n\n const backupIdChoices = response.backups.map((backup: BackupItem) => ({\n name: backup.id,\n value: backup.id,\n }))\n\n const hint =\n backupIdChoices.length === maxBackupIdsShown\n ? ` (only last ${maxBackupIdsShown} shown)`\n : ''\n\n return select({\n choices: backupIdChoices,\n message: `Select backup ID to use${hint}`,\n })\n } catch (err) {\n const message = err instanceof Error ? err.message : String(err)\n backupDownloadDebug(`Failed to fetch backups for dataset ${datasetName}: ${message}`, err)\n this.error(`Failed to fetch backups for dataset ${datasetName}: ${message}`, {exit: 1})\n }\n }\n}\n"],"names":["createWriteStream","access","mkdir","mkdtemp","tmpdir","path","finished","styleText","Args","Flags","SanityCommand","boxen","confirm","input","select","pMap","prettyMs","archiveDir","assertDatasetExists","backupDownloadDebug","cleanupTmpDir","downloadAsset","downloadDocument","PaginatedGetBackupStream","newProgress","validateDatasetName","promptForDataset","promptForProject","listBackups","listDatasets","humanFileSize","isPathDirName","getProjectIdFlag","DEFAULT_DOWNLOAD_CONCURRENCY","MAX_DOWNLOAD_CONCURRENCY","DownloadBackupCommand","args","dataset","string","description","required","examples","command","flags","semantics","concurrency","integer","default","out","overwrite","boolean","hiddenAliases","run","parse","projectId","getProjectId","fallback","requiredPermissions","grant","permission","datasets","error","message","Error","String","exit","length","allowCreation","opts","prepareBackupOptions","outFilePath","join","outDir","outFileName","log","datasetName","backupId","borderColor","borderStyle","padding","start","Date","now","progressSpinner","tmpOutDir","dir","recursive","tmpOutDocumentsFile","docOutStream","backupFileStream","files","i","file","push","set","current","step","total","totalFiles","update","totalItemsDownloaded","type","url","name","doc","write","fail","end","processedBytes","err","succeed","getOutputPath","defaultOutFileName","undefined","resolve","workDir","process","cwd","inputResult","promptForBackupId","exists","then","shouldOverwrite","dirname","basename","maxBackupIdsShown","response","limit","backups","backupIdChoices","map","backup","id","value","hint","choices"],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../../src/commands/backups/enable.ts"],"sourcesContent":["import {styleText} from 'node:util'\n\nimport {Args} from '@oclif/core'\nimport {SanityCommand, subdebug} from '@sanity/cli-core'\nimport {type DatasetsResponse} from '@sanity/client'\n\nimport {assertDatasetExists} from '../../actions/backup/assertDatasetExist.js'\nimport {NEW_DATASET_VALUE, promptForDataset} from '../../prompts/promptForDataset.js'\nimport {promptForDatasetName} from '../../prompts/promptForDatasetName.js'\nimport {promptForProject} from '../../prompts/promptForProject.js'\nimport {setBackup} from '../../services/backup.js'\nimport {createDataset, listDatasets} from '../../services/datasets.js'\nimport {getProjectIdFlag} from '../../util/sharedFlags.js'\n\nconst enableBackupDebug = subdebug('backup:enable')\n\nexport class EnableBackupCommand extends SanityCommand<typeof EnableBackupCommand> {\n static override args = {\n dataset: Args.string({\n description: 'Dataset name to enable backup for',\n required: false,\n }),\n }\n\n static override description = 'Enable backup for a dataset.'\n\n static override examples = [\n {\n command: '<%= config.bin %> <%= command.id %>',\n description: 'Interactively enable backup for a dataset',\n },\n {\n command: '<%= config.bin %> <%= command.id %> production',\n description: 'Enable backup for the production dataset',\n },\n ]\n\n static override flags = {\n ...getProjectIdFlag({\n description: 'Project ID to enable backups for',\n semantics: 'override',\n }),\n }\n\n static override hiddenAliases: string[] = ['backup:enable']\n\n public async run(): Promise<void> {\n const {args} = await this.parse(EnableBackupCommand)\n let {dataset} = args\n\n const projectId = await this.getProjectId({\n fallback: () =>\n promptForProject({\n requiredPermissions: [\n {grant: 'read', permission: 'sanity.project.datasets'},\n {grant: 'update', permission: 'sanity.project.datasets'},\n ],\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 enableBackupDebug(`Failed to list datasets: ${message}`, error)\n this.error(`Failed to list datasets: ${message}`, {exit: 1})\n }\n\n const hasProduction = datasets.some((dataset) => dataset.name === 'production')\n\n if (datasets.length === 0) {\n this.error('No datasets found in this project.', {exit: 1})\n }\n\n if (dataset) {\n assertDatasetExists(datasets, dataset)\n } else {\n dataset = await promptForDataset({allowCreation: true, datasets})\n\n if (dataset === NEW_DATASET_VALUE) {\n const newDatasetName = await promptForDatasetName({\n default: hasProduction ? undefined : 'production',\n })\n\n try {\n await createDataset({\n datasetName: newDatasetName,\n projectId,\n })\n dataset = newDatasetName\n } catch (error) {\n const message = error instanceof Error ? error.message : String(error)\n enableBackupDebug(`Failed to create dataset ${newDatasetName}: ${message}`, error)\n this.error(`Failed to create dataset ${newDatasetName}: ${message}`, {exit: 1})\n }\n }\n }\n\n try {\n await setBackup({dataset, projectId, status: true})\n\n this.log(\n `${styleText(\n 'green',\n `Enabled backups for dataset ${dataset}.\\nPlease note that it may take up to 24 hours before the first backup is created.\\n`,\n )}`,\n )\n\n this.log(\n `${styleText('bold', `Retention policies may apply depending on your plan and agreement.\\n`)}`,\n )\n\n enableBackupDebug(`Successfully enabled backup for dataset ${dataset}`)\n } catch (error) {\n const message = error instanceof Error ? error.message : String(error)\n enableBackupDebug(`Failed to enable backup for dataset`, error)\n this.error(`Enabling dataset backup failed: ${message}`, {exit: 1})\n }\n }\n}\n"],"names":["styleText","Args","SanityCommand","subdebug","assertDatasetExists","NEW_DATASET_VALUE","promptForDataset","promptForDatasetName","promptForProject","setBackup","createDataset","listDatasets","getProjectIdFlag","enableBackupDebug","EnableBackupCommand","args","dataset","string","description","required","examples","command","flags","semantics","hiddenAliases","run","parse","projectId","getProjectId","fallback","requiredPermissions","grant","permission","datasets","error","message","Error","String","exit","hasProduction","some","name","length","allowCreation","newDatasetName","default","undefined","datasetName","status","log"],"mappings":"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"}
|
|
@@ -56,6 +56,9 @@ export class ListBackupCommand extends SanityCommand {
|
|
|
56
56
|
description: 'Maximum number of backups returned'
|
|
57
57
|
})
|
|
58
58
|
};
|
|
59
|
+
static hiddenAliases = [
|
|
60
|
+
'backup:list'
|
|
61
|
+
];
|
|
59
62
|
async run() {
|
|
60
63
|
const { args, flags } = await this.parse(ListBackupCommand);
|
|
61
64
|
let { dataset } = args;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../../src/commands/backups/list.ts"],"sourcesContent":["import {Args, Flags} from '@oclif/core'\nimport {SanityCommand, subdebug} from '@sanity/cli-core'\nimport {select} from '@sanity/cli-core/ux'\nimport {type DatasetsResponse} from '@sanity/client'\nimport {Table} from 'console-table-printer'\nimport {isAfter} 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: 1})\n }\n\n if (datasets.length === 0) {\n this.error('No datasets found in this project.', {exit: 1})\n }\n\n if (dataset) {\n assertDatasetExists(datasets, dataset)\n } else {\n dataset = await this.promptForDataset(datasets)\n }\n\n // Validate date flags\n if (flags.before || flags.after) {\n try {\n const parsedBefore = this.processDateFlag(flags.before, 'before')\n const parsedAfter = this.processDateFlag(flags.after, 'after')\n\n if (parsedAfter && parsedBefore && isAfter(parsedAfter, parsedBefore)) {\n this.error('--after date must be before --before', {exit: 1})\n }\n } catch (err) {\n this.error(`Parsing date flags: ${err instanceof Error ? err.message : err}`, {exit: 1})\n }\n }\n\n // Validate limit flag\n if (flags.limit < 1 || flags.limit > Number.MAX_SAFE_INTEGER) {\n this.error(`Parsing --limit: must be an integer between 1 and ${Number.MAX_SAFE_INTEGER}`, {\n exit: 1,\n })\n }\n\n const query: ListBackupRequestQueryParams = {\n limit: flags.limit.toString(),\n }\n\n if (flags.after) {\n query.after = flags.after\n }\n\n if (flags.before) {\n query.before = flags.before\n }\n\n try {\n const response = await listBackups({\n after: flags.after,\n before: flags.before,\n datasetName: dataset,\n limit: flags.limit,\n projectId,\n })\n\n if (response.backups.length === 0) {\n this.log('No backups found.')\n return\n }\n\n const table = new Table({\n columns: [\n {alignment: 'left', name: 'resource', title: 'RESOURCE'},\n {alignment: 'left', name: 'createdAt', title: 'CREATED AT'},\n {alignment: 'left', name: 'backupId', title: 'BACKUP ID'},\n ],\n })\n\n for (const backup of response.backups) {\n const {createdAt, id} = backup\n table.addRow({\n backupId: id,\n createdAt: lightFormat(Date.parse(createdAt), 'yyyy-MM-dd HH:mm:ss'),\n resource: 'Dataset',\n })\n }\n\n table.printTable()\n\n listBackupDebug(\n `Successfully listed ${response.backups.length} backups for dataset ${dataset}`,\n )\n } catch (error) {\n const message = error instanceof Error ? error.message : String(error)\n listBackupDebug(`Failed to list backups for dataset ${dataset}:`, error)\n this.error(`List dataset backup failed: ${message}`, {exit: 1})\n }\n }\n\n private processDateFlag(date: string | undefined, flagName: string): Date | undefined {\n if (!date) return undefined\n const parsedDate = parse(date, 'yyyy-MM-dd', new Date())\n if (isValid(parsedDate)) {\n return parsedDate\n }\n\n throw new Error(`Invalid date format for '--${flagName}' flag. Use YYYY-MM-DD`)\n }\n\n private async promptForDataset(datasets: DatasetsResponse): Promise<string> {\n try {\n const choices = datasets.map((dataset) => ({\n name: dataset.name,\n value: dataset.name,\n }))\n\n return select({\n choices,\n message: 'Select the dataset name:',\n })\n } catch (error) {\n listBackupDebug(`Error selecting dataset`, error)\n this.error(`Failed to select dataset:\\n${error instanceof Error ? error.message : error}`, {\n exit: 1,\n })\n }\n }\n}\n"],"names":["Args","Flags","SanityCommand","subdebug","select","Table","isAfter","isValid","lightFormat","parse","assertDatasetExists","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","length","promptForDataset","parsedBefore","processDateFlag","parsedAfter","err","Number","MAX_SAFE_INTEGER","query","toString","response","datasetName","backups","log","table","columns","alignment","name","title","backup","createdAt","id","addRow","backupId","Date","resource","printTable","date","flagName","undefined","parsedDate","choices","map","value"],"mappings":"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"}
|