@sanity/cli 8.25.0 → 8.27.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.
Files changed (127) hide show
  1. package/README.md +3 -2
  2. package/dist/actions/build/shouldAutoUpdate.js +1 -26
  3. package/dist/actions/build/shouldAutoUpdate.js.map +1 -1
  4. package/dist/actions/context/mcpEndpoint.js +165 -0
  5. package/dist/actions/context/mcpEndpoint.js.map +1 -0
  6. package/dist/actions/context/resolveOrganizationId.js +8 -1
  7. package/dist/actions/context/resolveOrganizationId.js.map +1 -1
  8. package/dist/actions/deploy/deployApp.js +10 -3
  9. package/dist/actions/deploy/deployApp.js.map +1 -1
  10. package/dist/actions/deploy/deployStudio.js +11 -4
  11. package/dist/actions/deploy/deployStudio.js.map +1 -1
  12. package/dist/actions/init/initAction.js +3 -3
  13. package/dist/actions/init/initAction.js.map +1 -1
  14. package/dist/actions/init/plan/verifyCoupon.js +0 -1
  15. package/dist/actions/init/plan/verifyCoupon.js.map +1 -1
  16. package/dist/actions/init/scaffoldTemplate.js +1 -1
  17. package/dist/actions/init/scaffoldTemplate.js.map +1 -1
  18. package/dist/commands/api.js +10 -0
  19. package/dist/commands/api.js.map +1 -1
  20. package/dist/commands/backups/download.js +6 -0
  21. package/dist/commands/backups/download.js.map +1 -1
  22. package/dist/commands/context/create.js +8 -22
  23. package/dist/commands/context/create.js.map +1 -1
  24. package/dist/commands/context/delete.js +8 -1
  25. package/dist/commands/context/delete.js.map +1 -1
  26. package/dist/commands/context/issues/list.js +0 -5
  27. package/dist/commands/context/issues/list.js.map +1 -1
  28. package/dist/commands/context/list.js +6 -14
  29. package/dist/commands/context/list.js.map +1 -1
  30. package/dist/commands/context/mcp/create.js +152 -0
  31. package/dist/commands/context/mcp/create.js.map +1 -0
  32. package/dist/commands/context/mcp/delete.js +82 -0
  33. package/dist/commands/context/mcp/delete.js.map +1 -0
  34. package/dist/commands/context/mcp/get.js +42 -0
  35. package/dist/commands/context/mcp/get.js.map +1 -0
  36. package/dist/commands/context/mcp/list.js +87 -0
  37. package/dist/commands/context/mcp/list.js.map +1 -0
  38. package/dist/commands/context/mcp/status.js +98 -0
  39. package/dist/commands/context/mcp/status.js.map +1 -0
  40. package/dist/commands/context/mcp/update.js +225 -0
  41. package/dist/commands/context/mcp/update.js.map +1 -0
  42. package/dist/commands/context/query.js +6 -14
  43. package/dist/commands/context/query.js.map +1 -1
  44. package/dist/commands/context/update.js +12 -1
  45. package/dist/commands/context/update.js.map +1 -1
  46. package/dist/commands/datasets/create.js +6 -0
  47. package/dist/commands/datasets/create.js.map +1 -1
  48. package/dist/commands/datasets/embeddings/enable.js +6 -0
  49. package/dist/commands/datasets/embeddings/enable.js.map +1 -1
  50. package/dist/commands/datasets/import.js +6 -0
  51. package/dist/commands/datasets/import.js.map +1 -1
  52. package/dist/commands/deploy.js +7 -0
  53. package/dist/commands/deploy.js.map +1 -1
  54. package/dist/commands/documents/validate.js +6 -0
  55. package/dist/commands/documents/validate.js.map +1 -1
  56. package/dist/commands/exec.js +4 -2
  57. package/dist/commands/exec.js.map +1 -1
  58. package/dist/commands/init.js +13 -0
  59. package/dist/commands/init.js.map +1 -1
  60. package/dist/commands/manifest/extract.js +6 -0
  61. package/dist/commands/manifest/extract.js.map +1 -1
  62. package/dist/commands/media/create-aspect.js +7 -0
  63. package/dist/commands/media/create-aspect.js.map +1 -1
  64. package/dist/commands/migrations/run.js +6 -0
  65. package/dist/commands/migrations/run.js.map +1 -1
  66. package/dist/commands/organizations/create.js +6 -0
  67. package/dist/commands/organizations/create.js.map +1 -1
  68. package/dist/commands/organizations/update.js +6 -0
  69. package/dist/commands/organizations/update.js.map +1 -1
  70. package/dist/commands/schemas/delete.js +6 -0
  71. package/dist/commands/schemas/delete.js.map +1 -1
  72. package/dist/commands/schemas/deploy.js +6 -0
  73. package/dist/commands/schemas/deploy.js.map +1 -1
  74. package/dist/commands/schemas/extract.js +28 -3
  75. package/dist/commands/schemas/extract.js.map +1 -1
  76. package/dist/commands/schemas/list.js +6 -0
  77. package/dist/commands/schemas/list.js.map +1 -1
  78. package/dist/commands/schemas/validate.js +6 -0
  79. package/dist/commands/schemas/validate.js.map +1 -1
  80. package/dist/commands/tokens/rotate.js +6 -0
  81. package/dist/commands/tokens/rotate.js.map +1 -1
  82. package/dist/commands/typegen/generate.js +25 -1
  83. package/dist/commands/typegen/generate.js.map +1 -1
  84. package/dist/exports/invokeSanityCli/commandPolicies/mcpPolicy.js +6 -0
  85. package/dist/exports/invokeSanityCli/commandPolicies/mcpPolicy.js.map +1 -1
  86. package/dist/generated/apiRoutes.js +22 -0
  87. package/dist/generated/apiRoutes.js.map +1 -1
  88. package/dist/hooks/prerun/setupTelemetry.js +2 -3
  89. package/dist/hooks/prerun/setupTelemetry.js.map +1 -1
  90. package/dist/prompts/promptForText.js +12 -0
  91. package/dist/prompts/promptForText.js.map +1 -0
  92. package/dist/services/context.js +174 -1
  93. package/dist/services/context.js.map +1 -1
  94. package/dist/services/telemetry.js +4 -2
  95. package/dist/services/telemetry.js.map +1 -1
  96. package/dist/telemetry/cli.telemetry.js +1 -1
  97. package/dist/telemetry/cli.telemetry.js.map +1 -1
  98. package/dist/telemetry/init.telemetry.js +1 -1
  99. package/dist/telemetry/init.telemetry.js.map +1 -1
  100. package/dist/telemetry/typegen.telemetry.js +12 -0
  101. package/dist/telemetry/typegen.telemetry.js.map +1 -0
  102. package/dist/util/parseArguments.js +12 -40
  103. package/dist/util/parseArguments.js.map +1 -1
  104. package/dist/util/sharedFlags.js.map +1 -1
  105. package/dist/util/telemetry/cliCommandIdentity.js +222 -0
  106. package/dist/util/telemetry/cliCommandIdentity.js.map +1 -0
  107. package/dist/util/telemetry/cliTelemetryFlags.js +186 -0
  108. package/dist/util/telemetry/cliTelemetryFlags.js.map +1 -0
  109. package/dist/util/telemetry/commandTelemetry.js +56 -11
  110. package/dist/util/telemetry/commandTelemetry.js.map +1 -1
  111. package/dist/util/telemetry/createTelemetryStore.js +13 -5
  112. package/dist/util/telemetry/createTelemetryStore.js.map +1 -1
  113. package/dist/util/telemetry/createTraceId.js +3 -3
  114. package/dist/util/telemetry/createTraceId.js.map +1 -1
  115. package/dist/util/telemetry/flushTelemetryFiles.js +14 -4
  116. package/dist/util/telemetry/flushTelemetryFiles.js.map +1 -1
  117. package/dist/util/telemetry/logger.js +1 -1
  118. package/dist/util/telemetry/logger.js.map +1 -1
  119. package/dist/util/telemetry/readNDJSON.js +1 -1
  120. package/dist/util/telemetry/readNDJSON.js.map +1 -1
  121. package/dist/util/telemetry/sanitizeTelemetryEvent.js +304 -0
  122. package/dist/util/telemetry/sanitizeTelemetryEvent.js.map +1 -0
  123. package/dist/util/telemetry/trace.js +4 -7
  124. package/dist/util/telemetry/trace.js.map +1 -1
  125. package/oclif.config.js +1 -1
  126. package/oclif.manifest.json +2380 -1526
  127. package/package.json +8 -11
@@ -1,5 +1,11 @@
1
- import { getGlobalCliClient } from '@sanity/cli-core';
1
+ import { getGlobalCliClient, subdebug } from '@sanity/cli-core';
2
+ import { EXPERIMENTAL_API_WARNING, isHttpError } from '@sanity/client';
2
3
  const CONTEXT_API_VERSION = 'v2026-08-25';
4
+ const BLUEPRINTS_API_VERSION = 'vX';
5
+ const BLUEPRINTS_LOOKUP_TIMEOUT_MS = 5000;
6
+ // The version in the URLs the Context API returns for agents to connect to MCP endpoints
7
+ const MCP_CONNECT_API_VERSION = 'v2026-11-10';
8
+ const blueprintOwnershipDebug = subdebug('context:blueprint-ownership');
3
9
  /**
4
10
  * Knowledge-base-scoped methods on `client.context` operate on the client's
5
11
  * configured `resource`; collection-level methods
@@ -64,6 +70,102 @@ const CONTEXT_API_VERSION = 'v2026-08-25';
64
70
  const client = await getContextClient();
65
71
  return client.context.knowledgeBases.edit(knowledgeBaseId, params);
66
72
  }
73
+ /**
74
+ * Who manages a resource, for refusal messages: the stack by name when the caller can read it
75
+ */ export function describeBlueprintManager(stackName) {
76
+ return stackName ? `The blueprint stack "${stackName}"` : 'A blueprint';
77
+ }
78
+ /**
79
+ * Whether a live blueprint stack manages the knowledge base
80
+ */ export function getKnowledgeBaseBlueprintOwnership(knowledgeBaseId) {
81
+ return getBlueprintOwnership('sanity.context.knowledge-base', async ()=>{
82
+ const { organizationId, publicId } = await getKnowledgeBase(knowledgeBaseId);
83
+ return {
84
+ organizationId,
85
+ publicId
86
+ };
87
+ });
88
+ }
89
+ /**
90
+ * Whether a live blueprint stack manages the MCP endpoint
91
+ */ export function getMcpEndpointBlueprintOwnership(organizationId, { id }) {
92
+ return getBlueprintOwnership('sanity.context.mcp', async ()=>({
93
+ organizationId,
94
+ publicId: id
95
+ }));
96
+ }
97
+ /**
98
+ * Looks the resource up in the Blueprints resource search. Fails open: a
99
+ * failed lookup answers not managed, so a Blueprints outage never blocks a
100
+ * command. A resource that doesn't exist isn't managed either.
101
+ */ async function getBlueprintOwnership(type, resolveResource) {
102
+ try {
103
+ const resource = await resolveResource();
104
+ if (!resource) return {
105
+ managed: false
106
+ };
107
+ const { organizationId, publicId } = resource;
108
+ const client = await getGlobalCliClient({
109
+ apiVersion: BLUEPRINTS_API_VERSION,
110
+ // The Blueprints API has no stable version yet, and the warning would interrupt command output.
111
+ // The client splits the warning header on commas, so its second clause is matched separately.
112
+ ignoreWarnings: [
113
+ EXPERIMENTAL_API_WARNING,
114
+ 'which will change without warning'
115
+ ],
116
+ // The lookup fails open, so an unreachable Blueprints API must not hold the command for the client's defaults.
117
+ maxRetries: 0,
118
+ requireUser: true,
119
+ timeout: BLUEPRINTS_LOOKUP_TIMEOUT_MS
120
+ });
121
+ const headers = {
122
+ 'x-sanity-scope-id': organizationId,
123
+ 'x-sanity-scope-type': 'organization'
124
+ };
125
+ const matches = await client.request({
126
+ headers,
127
+ query: {
128
+ id: publicId,
129
+ type
130
+ },
131
+ url: '/blueprints/resources/search'
132
+ });
133
+ const stackId = matches.find((match)=>typeof match.stackId === 'string')?.stackId;
134
+ if (!stackId) return {
135
+ managed: false
136
+ };
137
+ const stackName = await client.request({
138
+ headers,
139
+ url: `/blueprints/stacks/${encodeURIComponent(stackId)}`
140
+ }).then((stack)=>stack.name ?? null).catch(()=>null);
141
+ return {
142
+ managed: true,
143
+ stackName
144
+ };
145
+ } catch (error) {
146
+ if (isHttpError(error)) {
147
+ if (error.statusCode === 403 && errorCode(error) === 'RESOURCE_MANAGED') {
148
+ return {
149
+ managed: true,
150
+ stackName: null
151
+ };
152
+ }
153
+ if (error.statusCode === 404 && errorCode(error) === 'RESOURCE_NOT_MANAGED') {
154
+ return {
155
+ managed: false
156
+ };
157
+ }
158
+ }
159
+ blueprintOwnershipDebug('Blueprint ownership lookup failed, treating as not managed', error);
160
+ return {
161
+ managed: false
162
+ };
163
+ }
164
+ }
165
+ function errorCode(error) {
166
+ const { body } = error.response;
167
+ return body && typeof body === 'object' && 'errorCode' in body ? body.errorCode : undefined;
168
+ }
67
169
  /**
68
170
  * Delete a knowledge base and its generated content
69
171
  */ export async function deleteKnowledgeBase(knowledgeBaseId) {
@@ -221,5 +323,76 @@ const CONTEXT_API_VERSION = 'v2026-08-25';
221
323
  const client = await getContextClient(undefined, organizationId);
222
324
  return client.context.fetch(query, params);
223
325
  }
326
+ /**
327
+ * List an organization's MCP endpoints, oldest first
328
+ */ export async function listMcpEndpoints(organizationId) {
329
+ const client = await getContextClient(undefined, organizationId);
330
+ const docs = await client.context.mcpEndpoints.list();
331
+ const connectClient = client.withConfig({
332
+ apiVersion: MCP_CONNECT_API_VERSION
333
+ });
334
+ return docs.map((doc)=>toMcpEndpoint(connectClient, doc));
335
+ }
336
+ /**
337
+ * Get an MCP endpoint by name or ID, or `null` when the organization has none.
338
+ * Update and delete accept either, so the lookups guarding them must too.
339
+ */ export async function getMcpEndpoint(organizationId, nameOrId) {
340
+ const client = await getContextClient(undefined, organizationId);
341
+ const doc = await client.context.fetch('*[_type == "sanity.context.mcp" && organizationId == $org && (name == $nameOrId || publicId == $nameOrId)][0]', {
342
+ nameOrId,
343
+ org: organizationId
344
+ });
345
+ return doc && toMcpEndpoint(client.withConfig({
346
+ apiVersion: MCP_CONNECT_API_VERSION
347
+ }), doc);
348
+ }
349
+ /**
350
+ * The stored configuration in the shape create and edit return, so every command outputs the
351
+ * same fields. The URL isn't stored, so it is built from the same route and API version.
352
+ */ function toMcpEndpoint(connectClient, doc) {
353
+ return {
354
+ createdAt: doc._createdAt,
355
+ groqFilter: doc.groqFilter ?? null,
356
+ id: doc.publicId,
357
+ instructions: doc.instructions ?? null,
358
+ name: doc.name,
359
+ organizationId: doc.organizationId,
360
+ sources: doc.sources,
361
+ title: doc.title,
362
+ updatedAt: doc._updatedAt,
363
+ url: connectClient.getUrl(`/context/organizations/${encodeURIComponent(doc.organizationId)}/mcp/${encodeURIComponent(doc.name)}`)
364
+ };
365
+ }
366
+ /**
367
+ * Create an MCP endpoint
368
+ */ export async function createMcpEndpoint(organizationId, params) {
369
+ const client = await getContextClient(undefined, organizationId);
370
+ return client.context.mcpEndpoints.create(params);
371
+ }
372
+ /**
373
+ * Update an MCP endpoint's configuration
374
+ */ export async function updateMcpEndpoint(organizationId, name, params) {
375
+ const client = await getContextClient(undefined, organizationId);
376
+ return client.context.mcpEndpoints.edit({
377
+ ...params,
378
+ name
379
+ });
380
+ }
381
+ /**
382
+ * Delete an MCP endpoint
383
+ */ export async function deleteMcpEndpoint(organizationId, name) {
384
+ const client = await getContextClient(undefined, organizationId);
385
+ await client.context.mcpEndpoints.delete({
386
+ name
387
+ });
388
+ }
389
+ /**
390
+ * Check whether an agent connecting with the current credentials can use an MCP endpoint
391
+ */ export async function getMcpEndpointStatus(organizationId, name) {
392
+ const client = await getContextClient(undefined, organizationId);
393
+ return client.context.mcpEndpoints.status({
394
+ name
395
+ });
396
+ }
224
397
 
225
398
  //# sourceMappingURL=context.js.map
@@ -1 +1 @@
1
- {"version":3,"sources":["../../src/services/context.ts"],"sourcesContent":["import {getGlobalCliClient} from '@sanity/cli-core'\nimport {type Context} from '@sanity/client'\n\nconst CONTEXT_API_VERSION = 'v2026-08-25'\n\n/**\n * Knowledge-base-scoped methods on `client.context` operate on the client's\n * configured `resource`; collection-level methods\n * (`client.context.knowledgeBases.*`) are addressed per call and need none.\n * GROQ-backed reads also need `context.organizationId`.\n */\nasync function getContextClient(knowledgeBaseId?: string, organizationId?: string) {\n return getGlobalCliClient({\n apiVersion: CONTEXT_API_VERSION,\n requireUser: true,\n ...(knowledgeBaseId === undefined\n ? {}\n : {resource: {id: knowledgeBaseId, type: 'knowledge-base' as const}}),\n ...(organizationId === undefined ? {} : {context: {organizationId}}),\n })\n}\n\n/**\n * GROQ-backed reads (issues, entries) query the organization's\n * document store, where documents are keyed by the knowledge base's public ID.\n * Fetching the knowledge base resolves both, whichever of its IDs was passed.\n */\nasync function getContextStoreClient(knowledgeBaseId: string) {\n const {organizationId, publicId} = await getKnowledgeBase(knowledgeBaseId)\n return getContextClient(publicId, organizationId)\n}\n\n/**\n * Create a knowledge base in an organization\n */\nexport async function createKnowledgeBase(\n params: Context.CreateKnowledgeBaseParams,\n): Promise<Context.KnowledgeBase> {\n const client = await getContextClient()\n return client.context.knowledgeBases.create(params)\n}\n\n/**\n * List all knowledge bases in an organization (drains pagination)\n */\nexport async function listKnowledgeBases(organizationId: string): Promise<Context.KnowledgeBase[]> {\n const client = await getContextClient()\n\n const knowledgeBases: Context.KnowledgeBase[] = []\n let cursor: string | undefined\n do {\n const page = await client.context.knowledgeBases.list({cursor, organizationId})\n knowledgeBases.push(...page.data)\n cursor = page.nextCursor ?? undefined\n } while (cursor !== undefined)\n\n return knowledgeBases\n}\n\n/**\n * Get a single knowledge base by ID\n */\nexport async function getKnowledgeBase(knowledgeBaseId: string): Promise<Context.KnowledgeBase> {\n const client = await getContextClient()\n return client.context.knowledgeBases.get(knowledgeBaseId)\n}\n\n/**\n * Update a knowledge base's configuration\n */\nexport async function updateKnowledgeBase(\n knowledgeBaseId: string,\n params: Context.EditKnowledgeBaseParams,\n): Promise<Context.KnowledgeBase> {\n const client = await getContextClient()\n return client.context.knowledgeBases.edit(knowledgeBaseId, params)\n}\n\n/**\n * Delete a knowledge base and its generated content\n */\nexport async function deleteKnowledgeBase(knowledgeBaseId: string): Promise<void> {\n const client = await getContextClient()\n await client.context.knowledgeBases.delete(knowledgeBaseId)\n}\n\n/**\n * Import content into a knowledge base. File imports are staged and uploaded\n * to signed storage by the client.\n */\nexport async function createImport(\n knowledgeBaseId: string,\n params: Context.CreateFileImportParams | Context.CreateImportParams,\n): Promise<Context.JobAccepted> {\n const client = await getContextClient(knowledgeBaseId)\n return client.context.imports.create(params)\n}\n\n/**\n * List all imports for a knowledge base (drains pagination)\n */\nexport async function listImports(knowledgeBaseId: string): Promise<Context.Import[]> {\n const client = await getContextClient(knowledgeBaseId)\n\n const imports: Context.Import[] = []\n let cursor: string | undefined\n do {\n const page = await client.context.imports.list({cursor})\n imports.push(...page.data)\n cursor = page.nextCursor ?? undefined\n } while (cursor !== undefined)\n\n return imports\n}\n\n/**\n * Get a single import by ID\n */\nexport async function getImport(\n knowledgeBaseId: string,\n importId: string,\n): Promise<Context.ImportDetail> {\n const client = await getContextClient(knowledgeBaseId)\n return client.context.imports.get({importId})\n}\n\n/**\n * Get a short-lived signed URL for an import's original uploaded bytes\n */\nexport async function downloadImport(\n knowledgeBaseId: string,\n importId: string,\n): Promise<Context.ImportDownloadResponse> {\n const client = await getContextClient(knowledgeBaseId)\n return client.context.imports.download({importId})\n}\n\n/**\n * Delete an import from a knowledge base\n */\nexport async function deleteImport(knowledgeBaseId: string, importId: string): Promise<void> {\n const client = await getContextClient(knowledgeBaseId)\n await client.context.imports.delete({importId})\n}\n\n/**\n * Start a build of a knowledge base\n */\nexport async function buildKnowledgeBase(knowledgeBaseId: string): Promise<Context.JobAccepted> {\n const client = await getContextClient(knowledgeBaseId)\n return client.context.build()\n}\n\n/**\n * Cancel the running build of a knowledge base, if any\n */\nexport async function cancelKnowledgeBaseBuild(\n knowledgeBaseId: string,\n): Promise<{cancelled: boolean}> {\n const client = await getContextClient(knowledgeBaseId)\n return client.context.cancelBuild()\n}\n\n/**\n * Run an incremental refresh: re-check sources and apply what changed\n */\nexport async function refreshKnowledgeBase(\n knowledgeBaseId: string,\n): Promise<{jobId: string; started: boolean}> {\n const client = await getContextClient(knowledgeBaseId)\n return client.context.refresh()\n}\n\n/**\n * Get an async job (build, import) by ID\n */\nexport async function getJob(knowledgeBaseId: string, jobId: string): Promise<Context.Job> {\n const client = await getContextClient(knowledgeBaseId)\n return client.context.jobs.get({jobId})\n}\n\n/**\n * List all issues for a knowledge base, optionally narrowed to one status\n */\nexport async function listIssues(\n knowledgeBaseId: string,\n status?: Context.IssueDoc['status'],\n): Promise<Context.IssueDoc[]> {\n const client = await getContextStoreClient(knowledgeBaseId)\n return client.context.issues.list({status})\n}\n\n/**\n * Get a single issue by ID, or `null` when it does not exist\n */\nexport async function getIssue(\n knowledgeBaseId: string,\n issueId: string,\n): Promise<Context.IssueDoc | null> {\n const client = await getContextStoreClient(knowledgeBaseId)\n return client.context.issues.get({issueId})\n}\n\n/**\n * Resolve a conflict issue by choosing one of its sides (by index). Choosing\n * a side other than the entry's current content starts a rewrite job.\n */\nexport async function resolveIssue(\n knowledgeBaseId: string,\n issueId: string,\n resolution: number,\n): Promise<Context.ResolveIssueResponse> {\n const client = await getContextClient(knowledgeBaseId)\n return client.context.issues.resolve({issueId, resolution})\n}\n\n/**\n * Dismiss an issue\n */\nexport async function dismissIssue(\n knowledgeBaseId: string,\n issueId: string,\n): Promise<Context.DismissIssueResponse> {\n const client = await getContextClient(knowledgeBaseId)\n return client.context.issues.dismiss({issueId})\n}\n\n/**\n * Reopen a resolved conflict issue\n */\nexport async function reopenIssue(\n knowledgeBaseId: string,\n issueId: string,\n): Promise<Context.ReopenIssueResponse> {\n const client = await getContextClient(knowledgeBaseId)\n return client.context.issues.reopen({issueId})\n}\n\n/**\n * Apply accepted issues to a knowledge base in one batch\n */\nexport async function applyIssues(\n knowledgeBaseId: string,\n issueIds: string[],\n): Promise<Context.ApplyIssuesResponse> {\n const client = await getContextClient(knowledgeBaseId)\n return client.context.issues.apply({issueIds})\n}\n\n/**\n * List all entries for a knowledge base, path-ordered, without bodies\n */\nexport async function listEntries(knowledgeBaseId: string): Promise<Context.Entry[]> {\n const client = await getContextStoreClient(knowledgeBaseId)\n return client.context.entries.list()\n}\n\n/**\n * Get a single entry with its body and citations by outline path, or `null`\n * when no entry sits at that path\n */\nexport async function getEntry(\n knowledgeBaseId: string,\n path: string,\n): Promise<Context.EntryDoc | null> {\n const client = await getContextStoreClient(knowledgeBaseId)\n return client.context.entries.get({path})\n}\n\n/**\n * Rebuild one entry from its sources by outline path\n */\nexport async function rebuildEntry(\n knowledgeBaseId: string,\n path: string,\n): Promise<Context.RebuildEntryResponse> {\n const client = await getContextClient(knowledgeBaseId)\n return client.context.entries.rebuild({path})\n}\n\n/**\n * Run a GROQ query against an organization's Context documents\n */\nexport async function queryContext(\n organizationId: string,\n query: string,\n params?: Record<string, unknown>,\n): Promise<unknown> {\n const client = await getContextClient(undefined, organizationId)\n return client.context.fetch(query, params)\n}\n"],"names":["getGlobalCliClient","CONTEXT_API_VERSION","getContextClient","knowledgeBaseId","organizationId","apiVersion","requireUser","undefined","resource","id","type","context","getContextStoreClient","publicId","getKnowledgeBase","createKnowledgeBase","params","client","knowledgeBases","create","listKnowledgeBases","cursor","page","list","push","data","nextCursor","get","updateKnowledgeBase","edit","deleteKnowledgeBase","delete","createImport","imports","listImports","getImport","importId","downloadImport","download","deleteImport","buildKnowledgeBase","build","cancelKnowledgeBaseBuild","cancelBuild","refreshKnowledgeBase","refresh","getJob","jobId","jobs","listIssues","status","issues","getIssue","issueId","resolveIssue","resolution","resolve","dismissIssue","dismiss","reopenIssue","reopen","applyIssues","issueIds","apply","listEntries","entries","getEntry","path","rebuildEntry","rebuild","queryContext","query","fetch"],"mappings":"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"}
1
+ {"version":3,"sources":["../../src/services/context.ts"],"sourcesContent":["import {getGlobalCliClient, subdebug} from '@sanity/cli-core'\nimport {type Context, EXPERIMENTAL_API_WARNING, isHttpError} from '@sanity/client'\n\nconst CONTEXT_API_VERSION = 'v2026-08-25'\nconst BLUEPRINTS_API_VERSION = 'vX'\nconst BLUEPRINTS_LOOKUP_TIMEOUT_MS = 5000\n// The version in the URLs the Context API returns for agents to connect to MCP endpoints\nconst MCP_CONNECT_API_VERSION = 'v2026-11-10'\n\nconst blueprintOwnershipDebug = subdebug('context:blueprint-ownership')\n\n/**\n * Knowledge-base-scoped methods on `client.context` operate on the client's\n * configured `resource`; collection-level methods\n * (`client.context.knowledgeBases.*`) are addressed per call and need none.\n * GROQ-backed reads also need `context.organizationId`.\n */\nasync function getContextClient(knowledgeBaseId?: string, organizationId?: string) {\n return getGlobalCliClient({\n apiVersion: CONTEXT_API_VERSION,\n requireUser: true,\n ...(knowledgeBaseId === undefined\n ? {}\n : {resource: {id: knowledgeBaseId, type: 'knowledge-base' as const}}),\n ...(organizationId === undefined ? {} : {context: {organizationId}}),\n })\n}\n\n/**\n * GROQ-backed reads (issues, entries) query the organization's\n * document store, where documents are keyed by the knowledge base's public ID.\n * Fetching the knowledge base resolves both, whichever of its IDs was passed.\n */\nasync function getContextStoreClient(knowledgeBaseId: string) {\n const {organizationId, publicId} = await getKnowledgeBase(knowledgeBaseId)\n return getContextClient(publicId, organizationId)\n}\n\n/**\n * Create a knowledge base in an organization\n */\nexport async function createKnowledgeBase(\n params: Context.CreateKnowledgeBaseParams,\n): Promise<Context.KnowledgeBase> {\n const client = await getContextClient()\n return client.context.knowledgeBases.create(params)\n}\n\n/**\n * List all knowledge bases in an organization (drains pagination)\n */\nexport async function listKnowledgeBases(organizationId: string): Promise<Context.KnowledgeBase[]> {\n const client = await getContextClient()\n\n const knowledgeBases: Context.KnowledgeBase[] = []\n let cursor: string | undefined\n do {\n const page = await client.context.knowledgeBases.list({\n cursor,\n organizationId,\n })\n knowledgeBases.push(...page.data)\n cursor = page.nextCursor ?? undefined\n } while (cursor !== undefined)\n\n return knowledgeBases\n}\n\n/**\n * Get a single knowledge base by ID\n */\nexport async function getKnowledgeBase(knowledgeBaseId: string): Promise<Context.KnowledgeBase> {\n const client = await getContextClient()\n return client.context.knowledgeBases.get(knowledgeBaseId)\n}\n\n/**\n * Update a knowledge base's configuration\n */\nexport async function updateKnowledgeBase(\n knowledgeBaseId: string,\n params: Context.EditKnowledgeBaseParams,\n): Promise<Context.KnowledgeBase> {\n const client = await getContextClient()\n return client.context.knowledgeBases.edit(knowledgeBaseId, params)\n}\n\n/**\n * `stackName` is `null` when the caller can't read Blueprints: the search then\n * only says whether a blueprint manages the resource.\n */\nexport type BlueprintOwnership = {managed: false} | {managed: true; stackName: string | null}\n\n/**\n * Who manages a resource, for refusal messages: the stack by name when the caller can read it\n */\nexport function describeBlueprintManager(stackName: string | null): string {\n return stackName ? `The blueprint stack \"${stackName}\"` : 'A blueprint'\n}\n\n/**\n * Whether a live blueprint stack manages the knowledge base\n */\nexport function getKnowledgeBaseBlueprintOwnership(\n knowledgeBaseId: string,\n): Promise<BlueprintOwnership> {\n return getBlueprintOwnership('sanity.context.knowledge-base', async () => {\n const {organizationId, publicId} = await getKnowledgeBase(knowledgeBaseId)\n return {organizationId, publicId}\n })\n}\n\n/**\n * Whether a live blueprint stack manages the MCP endpoint\n */\nexport function getMcpEndpointBlueprintOwnership(\n organizationId: string,\n {id}: Pick<Context.McpEndpoint, 'id'>,\n): Promise<BlueprintOwnership> {\n return getBlueprintOwnership('sanity.context.mcp', async () => ({organizationId, publicId: id}))\n}\n\n/**\n * Looks the resource up in the Blueprints resource search. Fails open: a\n * failed lookup answers not managed, so a Blueprints outage never blocks a\n * command. A resource that doesn't exist isn't managed either.\n */\nasync function getBlueprintOwnership(\n type: string,\n resolveResource: () => Promise<{organizationId: string; publicId: string} | null>,\n): Promise<BlueprintOwnership> {\n try {\n const resource = await resolveResource()\n if (!resource) return {managed: false}\n const {organizationId, publicId} = resource\n const client = await getGlobalCliClient({\n apiVersion: BLUEPRINTS_API_VERSION,\n // The Blueprints API has no stable version yet, and the warning would interrupt command output.\n // The client splits the warning header on commas, so its second clause is matched separately.\n ignoreWarnings: [EXPERIMENTAL_API_WARNING, 'which will change without warning'],\n // The lookup fails open, so an unreachable Blueprints API must not hold the command for the client's defaults.\n maxRetries: 0,\n requireUser: true,\n timeout: BLUEPRINTS_LOOKUP_TIMEOUT_MS,\n })\n const headers = {\n 'x-sanity-scope-id': organizationId,\n 'x-sanity-scope-type': 'organization',\n }\n const matches = await client.request<{stackId?: string}[]>({\n headers,\n query: {id: publicId, type},\n url: '/blueprints/resources/search',\n })\n const stackId = matches.find((match) => typeof match.stackId === 'string')?.stackId\n if (!stackId) return {managed: false}\n const stackName = await client\n .request<{name?: string}>({\n headers,\n url: `/blueprints/stacks/${encodeURIComponent(stackId)}`,\n })\n .then((stack) => stack.name ?? null)\n .catch(() => null)\n return {managed: true, stackName}\n } catch (error) {\n if (isHttpError(error)) {\n if (error.statusCode === 403 && errorCode(error) === 'RESOURCE_MANAGED') {\n return {managed: true, stackName: null}\n }\n if (error.statusCode === 404 && errorCode(error) === 'RESOURCE_NOT_MANAGED') {\n return {managed: false}\n }\n }\n blueprintOwnershipDebug('Blueprint ownership lookup failed, treating as not managed', error)\n return {managed: false}\n }\n}\n\nfunction errorCode(error: {response: {body: unknown}}): unknown {\n const {body} = error.response\n return body && typeof body === 'object' && 'errorCode' in body ? body.errorCode : undefined\n}\n\n/**\n * Delete a knowledge base and its generated content\n */\nexport async function deleteKnowledgeBase(knowledgeBaseId: string): Promise<void> {\n const client = await getContextClient()\n await client.context.knowledgeBases.delete(knowledgeBaseId)\n}\n\n/**\n * Import content into a knowledge base. File imports are staged and uploaded\n * to signed storage by the client.\n */\nexport async function createImport(\n knowledgeBaseId: string,\n params: Context.CreateFileImportParams | Context.CreateImportParams,\n): Promise<Context.JobAccepted> {\n const client = await getContextClient(knowledgeBaseId)\n return client.context.imports.create(params)\n}\n\n/**\n * List all imports for a knowledge base (drains pagination)\n */\nexport async function listImports(knowledgeBaseId: string): Promise<Context.Import[]> {\n const client = await getContextClient(knowledgeBaseId)\n\n const imports: Context.Import[] = []\n let cursor: string | undefined\n do {\n const page = await client.context.imports.list({cursor})\n imports.push(...page.data)\n cursor = page.nextCursor ?? undefined\n } while (cursor !== undefined)\n\n return imports\n}\n\n/**\n * Get a single import by ID\n */\nexport async function getImport(\n knowledgeBaseId: string,\n importId: string,\n): Promise<Context.ImportDetail> {\n const client = await getContextClient(knowledgeBaseId)\n return client.context.imports.get({importId})\n}\n\n/**\n * Get a short-lived signed URL for an import's original uploaded bytes\n */\nexport async function downloadImport(\n knowledgeBaseId: string,\n importId: string,\n): Promise<Context.ImportDownloadResponse> {\n const client = await getContextClient(knowledgeBaseId)\n return client.context.imports.download({importId})\n}\n\n/**\n * Delete an import from a knowledge base\n */\nexport async function deleteImport(knowledgeBaseId: string, importId: string): Promise<void> {\n const client = await getContextClient(knowledgeBaseId)\n await client.context.imports.delete({importId})\n}\n\n/**\n * Start a build of a knowledge base\n */\nexport async function buildKnowledgeBase(knowledgeBaseId: string): Promise<Context.JobAccepted> {\n const client = await getContextClient(knowledgeBaseId)\n return client.context.build()\n}\n\n/**\n * Cancel the running build of a knowledge base, if any\n */\nexport async function cancelKnowledgeBaseBuild(\n knowledgeBaseId: string,\n): Promise<{cancelled: boolean}> {\n const client = await getContextClient(knowledgeBaseId)\n return client.context.cancelBuild()\n}\n\n/**\n * Run an incremental refresh: re-check sources and apply what changed\n */\nexport async function refreshKnowledgeBase(\n knowledgeBaseId: string,\n): Promise<{jobId: string; started: boolean}> {\n const client = await getContextClient(knowledgeBaseId)\n return client.context.refresh()\n}\n\n/**\n * Get an async job (build, import) by ID\n */\nexport async function getJob(knowledgeBaseId: string, jobId: string): Promise<Context.Job> {\n const client = await getContextClient(knowledgeBaseId)\n return client.context.jobs.get({jobId})\n}\n\n/**\n * List all issues for a knowledge base, optionally narrowed to one status\n */\nexport async function listIssues(\n knowledgeBaseId: string,\n status?: Context.IssueDoc['status'],\n): Promise<Context.IssueDoc[]> {\n const client = await getContextStoreClient(knowledgeBaseId)\n return client.context.issues.list({status})\n}\n\n/**\n * Get a single issue by ID, or `null` when it does not exist\n */\nexport async function getIssue(\n knowledgeBaseId: string,\n issueId: string,\n): Promise<Context.IssueDoc | null> {\n const client = await getContextStoreClient(knowledgeBaseId)\n return client.context.issues.get({issueId})\n}\n\n/**\n * Resolve a conflict issue by choosing one of its sides (by index). Choosing\n * a side other than the entry's current content starts a rewrite job.\n */\nexport async function resolveIssue(\n knowledgeBaseId: string,\n issueId: string,\n resolution: number,\n): Promise<Context.ResolveIssueResponse> {\n const client = await getContextClient(knowledgeBaseId)\n return client.context.issues.resolve({issueId, resolution})\n}\n\n/**\n * Dismiss an issue\n */\nexport async function dismissIssue(\n knowledgeBaseId: string,\n issueId: string,\n): Promise<Context.DismissIssueResponse> {\n const client = await getContextClient(knowledgeBaseId)\n return client.context.issues.dismiss({issueId})\n}\n\n/**\n * Reopen a resolved conflict issue\n */\nexport async function reopenIssue(\n knowledgeBaseId: string,\n issueId: string,\n): Promise<Context.ReopenIssueResponse> {\n const client = await getContextClient(knowledgeBaseId)\n return client.context.issues.reopen({issueId})\n}\n\n/**\n * Apply accepted issues to a knowledge base in one batch\n */\nexport async function applyIssues(\n knowledgeBaseId: string,\n issueIds: string[],\n): Promise<Context.ApplyIssuesResponse> {\n const client = await getContextClient(knowledgeBaseId)\n return client.context.issues.apply({issueIds})\n}\n\n/**\n * List all entries for a knowledge base, path-ordered, without bodies\n */\nexport async function listEntries(knowledgeBaseId: string): Promise<Context.Entry[]> {\n const client = await getContextStoreClient(knowledgeBaseId)\n return client.context.entries.list()\n}\n\n/**\n * Get a single entry with its body and citations by outline path, or `null`\n * when no entry sits at that path\n */\nexport async function getEntry(\n knowledgeBaseId: string,\n path: string,\n): Promise<Context.EntryDoc | null> {\n const client = await getContextStoreClient(knowledgeBaseId)\n return client.context.entries.get({path})\n}\n\n/**\n * Rebuild one entry from its sources by outline path\n */\nexport async function rebuildEntry(\n knowledgeBaseId: string,\n path: string,\n): Promise<Context.RebuildEntryResponse> {\n const client = await getContextClient(knowledgeBaseId)\n return client.context.entries.rebuild({path})\n}\n\n/**\n * Run a GROQ query against an organization's Context documents\n */\nexport async function queryContext(\n organizationId: string,\n query: string,\n params?: Record<string, unknown>,\n): Promise<unknown> {\n const client = await getContextClient(undefined, organizationId)\n return client.context.fetch(query, params)\n}\n\n/**\n * List an organization's MCP endpoints, oldest first\n */\nexport async function listMcpEndpoints(organizationId: string): Promise<Context.McpEndpoint[]> {\n const client = await getContextClient(undefined, organizationId)\n const docs = await client.context.mcpEndpoints.list()\n const connectClient = client.withConfig({apiVersion: MCP_CONNECT_API_VERSION})\n return docs.map((doc) => toMcpEndpoint(connectClient, doc))\n}\n\n/**\n * Get an MCP endpoint by name or ID, or `null` when the organization has none.\n * Update and delete accept either, so the lookups guarding them must too.\n */\nexport async function getMcpEndpoint(\n organizationId: string,\n nameOrId: string,\n): Promise<Context.McpEndpoint | null> {\n const client = await getContextClient(undefined, organizationId)\n const doc = await client.context.fetch<Context.McpDoc | null>(\n '*[_type == \"sanity.context.mcp\" && organizationId == $org && (name == $nameOrId || publicId == $nameOrId)][0]',\n {nameOrId, org: organizationId},\n )\n return doc && toMcpEndpoint(client.withConfig({apiVersion: MCP_CONNECT_API_VERSION}), doc)\n}\n\n/**\n * The stored configuration in the shape create and edit return, so every command outputs the\n * same fields. The URL isn't stored, so it is built from the same route and API version.\n */\nfunction toMcpEndpoint(\n connectClient: {getUrl: (uri: string) => string},\n doc: Context.McpDoc,\n): Context.McpEndpoint {\n return {\n createdAt: doc._createdAt,\n groqFilter: doc.groqFilter ?? null,\n id: doc.publicId,\n instructions: doc.instructions ?? null,\n name: doc.name,\n organizationId: doc.organizationId,\n sources: doc.sources,\n title: doc.title,\n updatedAt: doc._updatedAt,\n url: connectClient.getUrl(\n `/context/organizations/${encodeURIComponent(doc.organizationId)}/mcp/${encodeURIComponent(doc.name)}`,\n ),\n }\n}\n\n/**\n * Create an MCP endpoint\n */\nexport async function createMcpEndpoint(\n organizationId: string,\n params: Context.CreateMcpEndpointParams,\n): Promise<Context.McpEndpoint> {\n const client = await getContextClient(undefined, organizationId)\n return client.context.mcpEndpoints.create(params)\n}\n\n/**\n * Update an MCP endpoint's configuration\n */\nexport async function updateMcpEndpoint(\n organizationId: string,\n name: string,\n params: Context.EditMcpEndpointParams,\n): Promise<Context.McpEndpoint> {\n const client = await getContextClient(undefined, organizationId)\n return client.context.mcpEndpoints.edit({...params, name})\n}\n\n/**\n * Delete an MCP endpoint\n */\nexport async function deleteMcpEndpoint(organizationId: string, name: string): Promise<void> {\n const client = await getContextClient(undefined, organizationId)\n await client.context.mcpEndpoints.delete({name})\n}\n\n/**\n * Check whether an agent connecting with the current credentials can use an MCP endpoint\n */\nexport async function getMcpEndpointStatus(\n organizationId: string,\n name: string,\n): Promise<Context.McpEndpointStatus> {\n const client = await getContextClient(undefined, organizationId)\n return client.context.mcpEndpoints.status({name})\n}\n"],"names":["getGlobalCliClient","subdebug","EXPERIMENTAL_API_WARNING","isHttpError","CONTEXT_API_VERSION","BLUEPRINTS_API_VERSION","BLUEPRINTS_LOOKUP_TIMEOUT_MS","MCP_CONNECT_API_VERSION","blueprintOwnershipDebug","getContextClient","knowledgeBaseId","organizationId","apiVersion","requireUser","undefined","resource","id","type","context","getContextStoreClient","publicId","getKnowledgeBase","createKnowledgeBase","params","client","knowledgeBases","create","listKnowledgeBases","cursor","page","list","push","data","nextCursor","get","updateKnowledgeBase","edit","describeBlueprintManager","stackName","getKnowledgeBaseBlueprintOwnership","getBlueprintOwnership","getMcpEndpointBlueprintOwnership","resolveResource","managed","ignoreWarnings","maxRetries","timeout","headers","matches","request","query","url","stackId","find","match","encodeURIComponent","then","stack","name","catch","error","statusCode","errorCode","body","response","deleteKnowledgeBase","delete","createImport","imports","listImports","getImport","importId","downloadImport","download","deleteImport","buildKnowledgeBase","build","cancelKnowledgeBaseBuild","cancelBuild","refreshKnowledgeBase","refresh","getJob","jobId","jobs","listIssues","status","issues","getIssue","issueId","resolveIssue","resolution","resolve","dismissIssue","dismiss","reopenIssue","reopen","applyIssues","issueIds","apply","listEntries","entries","getEntry","path","rebuildEntry","rebuild","queryContext","fetch","listMcpEndpoints","docs","mcpEndpoints","connectClient","withConfig","map","doc","toMcpEndpoint","getMcpEndpoint","nameOrId","org","createdAt","_createdAt","groqFilter","instructions","sources","title","updatedAt","_updatedAt","getUrl","createMcpEndpoint","updateMcpEndpoint","deleteMcpEndpoint","getMcpEndpointStatus"],"mappings":"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"}
@@ -3,6 +3,7 @@ import { getCliToken, getGlobalCliClient, getUserConfig } from '@sanity/cli-core
3
3
  import { getCliExecutionContext } from '@sanity/cli-core/executionContext';
4
4
  import { telemetryDebug } from '../actions/telemetry/telemetryDebug.js';
5
5
  import { createExpiringConfig } from '../util/createExpiringConfig.js';
6
+ import { sanitizeTelemetryEvent } from '../util/telemetry/sanitizeTelemetryEvent.js';
6
7
  export const TELEMETRY_API_VERSION = 'v2026-01-22';
7
8
  export const VALID_API_STATUSES = [
8
9
  'granted',
@@ -14,10 +15,11 @@ export async function sendEvents(batch) {
14
15
  apiVersion: TELEMETRY_API_VERSION,
15
16
  requireUser: true
16
17
  });
17
- const projectId = process.env.SANITY_TELEMETRY_PROJECT_ID;
18
+ const configuredProjectId = process.env.SANITY_TELEMETRY_PROJECT_ID;
19
+ const projectId = configuredProjectId && /^[a-z0-9]{8}$/.test(configuredProjectId) ? configuredProjectId : undefined;
18
20
  return client.request({
19
21
  body: {
20
- batch,
22
+ batch: batch.map((event)=>sanitizeTelemetryEvent(event)),
21
23
  projectId
22
24
  },
23
25
  json: true,
@@ -1 +1 @@
1
- {"version":3,"sources":["../../src/services/telemetry.ts"],"sourcesContent":["import {createHash} from 'node:crypto'\n\nimport {getCliToken, getGlobalCliClient, getUserConfig} from '@sanity/cli-core'\nimport {getCliExecutionContext} from '@sanity/cli-core/executionContext'\nimport {type TelemetryEvent} from '@sanity/telemetry'\n\nimport {telemetryDebug} from '../actions/telemetry/telemetryDebug.js'\nimport {createExpiringConfig} from '../util/createExpiringConfig.js'\n\nexport const TELEMETRY_API_VERSION = 'v2026-01-22'\n\nexport const VALID_API_STATUSES = ['granted', 'denied', 'unset'] as const\nexport type ValidApiConsentStatus = (typeof VALID_API_STATUSES)[number]\n\nexport async function sendEvents(batch: TelemetryEvent[]) {\n const client = await getGlobalCliClient({\n apiVersion: TELEMETRY_API_VERSION,\n requireUser: true,\n })\n\n const projectId = process.env.SANITY_TELEMETRY_PROJECT_ID\n\n return client.request({\n body: {batch, projectId},\n json: true,\n method: 'POST',\n url: '/intake/batch',\n })\n}\n\nasync function getTelemetryConsent(): Promise<{\n status: ValidApiConsentStatus\n}> {\n const client = await getGlobalCliClient({\n apiVersion: TELEMETRY_API_VERSION,\n requireUser: false,\n })\n\n return client.request({tag: 'telemetry-consent', url: '/intake/telemetry-status'})\n}\n\n/**\n * Check if the given status is a valid consent status\n *\n * @param status - The status to check\n * @returns True if the status is valid, false otherwise\n * @internal\n */\nexport function isValidApiConsentStatus(status: string): status is ValidApiConsentStatus {\n return VALID_API_STATUSES.includes(status as ValidApiConsentStatus)\n}\n\n/**\n * Check if the given response is a valid API consent response\n *\n * @param response - The response to check\n * @returns True if the response is valid, false otherwise\n * @internal\n */\nfunction isValidApiConsentResponse(response: unknown): response is {status: ValidApiConsentStatus} {\n return (\n typeof response === 'object' &&\n response !== null &&\n 'status' in response &&\n typeof response.status === 'string' &&\n isValidApiConsentStatus(response.status)\n )\n}\n\nexport const TELEMETRY_CONSENT_CONFIG_KEY = 'telemetryConsent'\nconst FIVE_MINUTES = 1000 * 60 * 5\n\n/**\n * Get a token-scoped cache key for telemetry consent. This ensures that switching\n * users (via login/logout) always results in a cache miss, preventing one user\n * from inheriting another user's cached consent status.\n *\n * @param token - The current auth token, or undefined if not logged in\n * @returns A cache key scoped to the token\n */\nexport function getTelemetryConsentCacheKey(token: string | undefined): string {\n if (!token) {\n return TELEMETRY_CONSENT_CONFIG_KEY\n }\n\n const hash = createHash('sha256').update(token).digest('hex').slice(0, 12)\n return `${TELEMETRY_CONSENT_CONFIG_KEY}:${hash}`\n}\n\n/**\n * Fetch the telemetry consent status for the current user\n * @returns The telemetry consent status\n *\n * @internal\n */\nexport async function fetchTelemetryConsent(): Promise<{\n status: ValidApiConsentStatus\n}> {\n // Programmatic invocations must not use the host user's disk-backed consent\n // cache. The request is already authenticated with the invocation token.\n if (getCliExecutionContext()) return getTelemetryConsent()\n\n const token = await getCliToken()\n const cacheKey = getTelemetryConsentCacheKey(token)\n\n // NOTE: createExpiringConfig is instantiated on every call, so in-flight request\n // deduplication (via currentFetch) does not work across concurrent calls to\n // fetchTelemetryConsent(). Two concurrent callers will make two HTTP requests.\n // Consider moving to module-level instance if this becomes a bottleneck.\n const telemetryConsentConfig = createExpiringConfig<{\n status: ValidApiConsentStatus\n }>({\n fetchValue: () => getTelemetryConsent(),\n key: cacheKey,\n onCacheHit() {\n telemetryDebug('Retrieved telemetry consent status from cache')\n },\n onFetch() {\n telemetryDebug('Fetching telemetry consent status...')\n },\n onRevalidate() {\n telemetryDebug('Revalidating cached telemetry consent status...')\n },\n store: getUserConfig(),\n ttl: FIVE_MINUTES,\n validateValue: isValidApiConsentResponse,\n })\n\n return telemetryConsentConfig.get()\n}\n"],"names":["createHash","getCliToken","getGlobalCliClient","getUserConfig","getCliExecutionContext","telemetryDebug","createExpiringConfig","TELEMETRY_API_VERSION","VALID_API_STATUSES","sendEvents","batch","client","apiVersion","requireUser","projectId","process","env","SANITY_TELEMETRY_PROJECT_ID","request","body","json","method","url","getTelemetryConsent","tag","isValidApiConsentStatus","status","includes","isValidApiConsentResponse","response","TELEMETRY_CONSENT_CONFIG_KEY","FIVE_MINUTES","getTelemetryConsentCacheKey","token","hash","update","digest","slice","fetchTelemetryConsent","cacheKey","telemetryConsentConfig","fetchValue","key","onCacheHit","onFetch","onRevalidate","store","ttl","validateValue","get"],"mappings":"AAAA,SAAQA,UAAU,QAAO,cAAa;AAEtC,SAAQC,WAAW,EAAEC,kBAAkB,EAAEC,aAAa,QAAO,mBAAkB;AAC/E,SAAQC,sBAAsB,QAAO,oCAAmC;AAGxE,SAAQC,cAAc,QAAO,yCAAwC;AACrE,SAAQC,oBAAoB,QAAO,kCAAiC;AAEpE,OAAO,MAAMC,wBAAwB,cAAa;AAElD,OAAO,MAAMC,qBAAqB;IAAC;IAAW;IAAU;CAAQ,CAAS;AAGzE,OAAO,eAAeC,WAAWC,KAAuB;IACtD,MAAMC,SAAS,MAAMT,mBAAmB;QACtCU,YAAYL;QACZM,aAAa;IACf;IAEA,MAAMC,YAAYC,QAAQC,GAAG,CAACC,2BAA2B;IAEzD,OAAON,OAAOO,OAAO,CAAC;QACpBC,MAAM;YAACT;YAAOI;QAAS;QACvBM,MAAM;QACNC,QAAQ;QACRC,KAAK;IACP;AACF;AAEA,eAAeC;IAGb,MAAMZ,SAAS,MAAMT,mBAAmB;QACtCU,YAAYL;QACZM,aAAa;IACf;IAEA,OAAOF,OAAOO,OAAO,CAAC;QAACM,KAAK;QAAqBF,KAAK;IAA0B;AAClF;AAEA;;;;;;CAMC,GACD,OAAO,SAASG,wBAAwBC,MAAc;IACpD,OAAOlB,mBAAmBmB,QAAQ,CAACD;AACrC;AAEA;;;;;;CAMC,GACD,SAASE,0BAA0BC,QAAiB;IAClD,OACE,OAAOA,aAAa,YACpBA,aAAa,QACb,YAAYA,YACZ,OAAOA,SAASH,MAAM,KAAK,YAC3BD,wBAAwBI,SAASH,MAAM;AAE3C;AAEA,OAAO,MAAMI,+BAA+B,mBAAkB;AAC9D,MAAMC,eAAe,OAAO,KAAK;AAEjC;;;;;;;CAOC,GACD,OAAO,SAASC,4BAA4BC,KAAyB;IACnE,IAAI,CAACA,OAAO;QACV,OAAOH;IACT;IAEA,MAAMI,OAAOlC,WAAW,UAAUmC,MAAM,CAACF,OAAOG,MAAM,CAAC,OAAOC,KAAK,CAAC,GAAG;IACvE,OAAO,GAAGP,6BAA6B,CAAC,EAAEI,MAAM;AAClD;AAEA;;;;;CAKC,GACD,OAAO,eAAeI;IAGpB,4EAA4E;IAC5E,yEAAyE;IACzE,IAAIlC,0BAA0B,OAAOmB;IAErC,MAAMU,QAAQ,MAAMhC;IACpB,MAAMsC,WAAWP,4BAA4BC;IAE7C,iFAAiF;IACjF,4EAA4E;IAC5E,+EAA+E;IAC/E,yEAAyE;IACzE,MAAMO,yBAAyBlC,qBAE5B;QACDmC,YAAY,IAAMlB;QAClBmB,KAAKH;QACLI;YACEtC,eAAe;QACjB;QACAuC;YACEvC,eAAe;QACjB;QACAwC;YACExC,eAAe;QACjB;QACAyC,OAAO3C;QACP4C,KAAKhB;QACLiB,eAAepB;IACjB;IAEA,OAAOY,uBAAuBS,GAAG;AACnC"}
1
+ {"version":3,"sources":["../../src/services/telemetry.ts"],"sourcesContent":["import {createHash} from 'node:crypto'\n\nimport {getCliToken, getGlobalCliClient, getUserConfig} from '@sanity/cli-core'\nimport {getCliExecutionContext} from '@sanity/cli-core/executionContext'\nimport {type TelemetryEvent} from '@sanity/telemetry'\n\nimport {telemetryDebug} from '../actions/telemetry/telemetryDebug.js'\nimport {createExpiringConfig} from '../util/createExpiringConfig.js'\nimport {sanitizeTelemetryEvent} from '../util/telemetry/sanitizeTelemetryEvent.js'\n\nexport const TELEMETRY_API_VERSION = 'v2026-01-22'\n\nexport const VALID_API_STATUSES = ['granted', 'denied', 'unset'] as const\nexport type ValidApiConsentStatus = (typeof VALID_API_STATUSES)[number]\n\nexport async function sendEvents(batch: TelemetryEvent[]) {\n const client = await getGlobalCliClient({\n apiVersion: TELEMETRY_API_VERSION,\n requireUser: true,\n })\n\n const configuredProjectId = process.env.SANITY_TELEMETRY_PROJECT_ID\n const projectId =\n configuredProjectId && /^[a-z0-9]{8}$/.test(configuredProjectId)\n ? configuredProjectId\n : undefined\n\n return client.request({\n body: {batch: batch.map((event) => sanitizeTelemetryEvent(event)), projectId},\n json: true,\n method: 'POST',\n url: '/intake/batch',\n })\n}\n\nasync function getTelemetryConsent(): Promise<{\n status: ValidApiConsentStatus\n}> {\n const client = await getGlobalCliClient({\n apiVersion: TELEMETRY_API_VERSION,\n requireUser: false,\n })\n\n return client.request({tag: 'telemetry-consent', url: '/intake/telemetry-status'})\n}\n\n/**\n * Check if the given status is a valid consent status\n *\n * @param status - The status to check\n * @returns True if the status is valid, false otherwise\n * @internal\n */\nexport function isValidApiConsentStatus(status: string): status is ValidApiConsentStatus {\n return VALID_API_STATUSES.includes(status as ValidApiConsentStatus)\n}\n\n/**\n * Check if the given response is a valid API consent response\n *\n * @param response - The response to check\n * @returns True if the response is valid, false otherwise\n * @internal\n */\nfunction isValidApiConsentResponse(response: unknown): response is {status: ValidApiConsentStatus} {\n return (\n typeof response === 'object' &&\n response !== null &&\n 'status' in response &&\n typeof response.status === 'string' &&\n isValidApiConsentStatus(response.status)\n )\n}\n\nexport const TELEMETRY_CONSENT_CONFIG_KEY = 'telemetryConsent'\nconst FIVE_MINUTES = 1000 * 60 * 5\n\n/**\n * Get a token-scoped cache key for telemetry consent. This ensures that switching\n * users (via login/logout) always results in a cache miss, preventing one user\n * from inheriting another user's cached consent status.\n *\n * @param token - The current auth token, or undefined if not logged in\n * @returns A cache key scoped to the token\n */\nexport function getTelemetryConsentCacheKey(token: string | undefined): string {\n if (!token) {\n return TELEMETRY_CONSENT_CONFIG_KEY\n }\n\n const hash = createHash('sha256').update(token).digest('hex').slice(0, 12)\n return `${TELEMETRY_CONSENT_CONFIG_KEY}:${hash}`\n}\n\n/**\n * Fetch the telemetry consent status for the current user\n * @returns The telemetry consent status\n *\n * @internal\n */\nexport async function fetchTelemetryConsent(): Promise<{\n status: ValidApiConsentStatus\n}> {\n // Programmatic invocations must not use the host user's disk-backed consent\n // cache. The request is already authenticated with the invocation token.\n if (getCliExecutionContext()) return getTelemetryConsent()\n\n const token = await getCliToken()\n const cacheKey = getTelemetryConsentCacheKey(token)\n\n // NOTE: createExpiringConfig is instantiated on every call, so in-flight request\n // deduplication (via currentFetch) does not work across concurrent calls to\n // fetchTelemetryConsent(). Two concurrent callers will make two HTTP requests.\n // Consider moving to module-level instance if this becomes a bottleneck.\n const telemetryConsentConfig = createExpiringConfig<{\n status: ValidApiConsentStatus\n }>({\n fetchValue: () => getTelemetryConsent(),\n key: cacheKey,\n onCacheHit() {\n telemetryDebug('Retrieved telemetry consent status from cache')\n },\n onFetch() {\n telemetryDebug('Fetching telemetry consent status...')\n },\n onRevalidate() {\n telemetryDebug('Revalidating cached telemetry consent status...')\n },\n store: getUserConfig(),\n ttl: FIVE_MINUTES,\n validateValue: isValidApiConsentResponse,\n })\n\n return telemetryConsentConfig.get()\n}\n"],"names":["createHash","getCliToken","getGlobalCliClient","getUserConfig","getCliExecutionContext","telemetryDebug","createExpiringConfig","sanitizeTelemetryEvent","TELEMETRY_API_VERSION","VALID_API_STATUSES","sendEvents","batch","client","apiVersion","requireUser","configuredProjectId","process","env","SANITY_TELEMETRY_PROJECT_ID","projectId","test","undefined","request","body","map","event","json","method","url","getTelemetryConsent","tag","isValidApiConsentStatus","status","includes","isValidApiConsentResponse","response","TELEMETRY_CONSENT_CONFIG_KEY","FIVE_MINUTES","getTelemetryConsentCacheKey","token","hash","update","digest","slice","fetchTelemetryConsent","cacheKey","telemetryConsentConfig","fetchValue","key","onCacheHit","onFetch","onRevalidate","store","ttl","validateValue","get"],"mappings":"AAAA,SAAQA,UAAU,QAAO,cAAa;AAEtC,SAAQC,WAAW,EAAEC,kBAAkB,EAAEC,aAAa,QAAO,mBAAkB;AAC/E,SAAQC,sBAAsB,QAAO,oCAAmC;AAGxE,SAAQC,cAAc,QAAO,yCAAwC;AACrE,SAAQC,oBAAoB,QAAO,kCAAiC;AACpE,SAAQC,sBAAsB,QAAO,8CAA6C;AAElF,OAAO,MAAMC,wBAAwB,cAAa;AAElD,OAAO,MAAMC,qBAAqB;IAAC;IAAW;IAAU;CAAQ,CAAS;AAGzE,OAAO,eAAeC,WAAWC,KAAuB;IACtD,MAAMC,SAAS,MAAMV,mBAAmB;QACtCW,YAAYL;QACZM,aAAa;IACf;IAEA,MAAMC,sBAAsBC,QAAQC,GAAG,CAACC,2BAA2B;IACnE,MAAMC,YACJJ,uBAAuB,gBAAgBK,IAAI,CAACL,uBACxCA,sBACAM;IAEN,OAAOT,OAAOU,OAAO,CAAC;QACpBC,MAAM;YAACZ,OAAOA,MAAMa,GAAG,CAAC,CAACC,QAAUlB,uBAAuBkB;YAASN;QAAS;QAC5EO,MAAM;QACNC,QAAQ;QACRC,KAAK;IACP;AACF;AAEA,eAAeC;IAGb,MAAMjB,SAAS,MAAMV,mBAAmB;QACtCW,YAAYL;QACZM,aAAa;IACf;IAEA,OAAOF,OAAOU,OAAO,CAAC;QAACQ,KAAK;QAAqBF,KAAK;IAA0B;AAClF;AAEA;;;;;;CAMC,GACD,OAAO,SAASG,wBAAwBC,MAAc;IACpD,OAAOvB,mBAAmBwB,QAAQ,CAACD;AACrC;AAEA;;;;;;CAMC,GACD,SAASE,0BAA0BC,QAAiB;IAClD,OACE,OAAOA,aAAa,YACpBA,aAAa,QACb,YAAYA,YACZ,OAAOA,SAASH,MAAM,KAAK,YAC3BD,wBAAwBI,SAASH,MAAM;AAE3C;AAEA,OAAO,MAAMI,+BAA+B,mBAAkB;AAC9D,MAAMC,eAAe,OAAO,KAAK;AAEjC;;;;;;;CAOC,GACD,OAAO,SAASC,4BAA4BC,KAAyB;IACnE,IAAI,CAACA,OAAO;QACV,OAAOH;IACT;IAEA,MAAMI,OAAOxC,WAAW,UAAUyC,MAAM,CAACF,OAAOG,MAAM,CAAC,OAAOC,KAAK,CAAC,GAAG;IACvE,OAAO,GAAGP,6BAA6B,CAAC,EAAEI,MAAM;AAClD;AAEA;;;;;CAKC,GACD,OAAO,eAAeI;IAGpB,4EAA4E;IAC5E,yEAAyE;IACzE,IAAIxC,0BAA0B,OAAOyB;IAErC,MAAMU,QAAQ,MAAMtC;IACpB,MAAM4C,WAAWP,4BAA4BC;IAE7C,iFAAiF;IACjF,4EAA4E;IAC5E,+EAA+E;IAC/E,yEAAyE;IACzE,MAAMO,yBAAyBxC,qBAE5B;QACDyC,YAAY,IAAMlB;QAClBmB,KAAKH;QACLI;YACE5C,eAAe;QACjB;QACA6C;YACE7C,eAAe;QACjB;QACA8C;YACE9C,eAAe;QACjB;QACA+C,OAAOjD;QACPkD,KAAKhB;QACLiB,eAAepB;IACjB;IAEA,OAAOY,uBAAuBS,GAAG;AACnC"}
@@ -2,7 +2,7 @@ import { defineTrace } from '@sanity/telemetry';
2
2
  export const CliCommandTelemetry = defineTrace({
3
3
  description: 'A CLI command was executed',
4
4
  name: 'CLI Command Executed',
5
- version: 1
5
+ version: 2
6
6
  });
7
7
 
8
8
  //# sourceMappingURL=cli.telemetry.js.map
@@ -1 +1 @@
1
- {"version":3,"sources":["../../src/telemetry/cli.telemetry.ts"],"sourcesContent":["import {defineTrace} from '@sanity/telemetry'\n\nexport interface CLITraceData {\n /**\n * Command arguments, eg any arguments after `sanity <command>` (no flags)\n */\n commandArguments: string[]\n\n coreOptions: {\n debug?: boolean\n help?: boolean\n version?: boolean\n }\n\n /**\n * Arguments after the ended argument list (--)\n */\n extraArguments: string[]\n /**\n * Command flags, without the core options (help, debug, version etc)\n */\n groupOrCommand: string\n}\n\nexport const CliCommandTelemetry = defineTrace<CLITraceData>({\n description: 'A CLI command was executed',\n name: 'CLI Command Executed',\n version: 1,\n})\n"],"names":["defineTrace","CliCommandTelemetry","description","name","version"],"mappings":"AAAA,SAAQA,WAAW,QAAO,oBAAmB;AAwB7C,OAAO,MAAMC,sBAAsBD,YAA0B;IAC3DE,aAAa;IACbC,MAAM;IACNC,SAAS;AACX,GAAE"}
1
+ {"version":3,"sources":["../../src/telemetry/cli.telemetry.ts"],"sourcesContent":["import {defineTrace} from '@sanity/telemetry'\n\nexport interface CLITraceData {\n /**\n * Resolved subcommand names, excluding positional user input\n */\n commandArguments: string[]\n\n coreOptions: {\n debug?: boolean\n help?: boolean\n version?: boolean\n }\n\n /**\n * Recognized flags, with sensitive values and forwarded arguments omitted\n */\n extraArguments: string[]\n /**\n * Command flags, without the core options (help, debug, version etc)\n */\n groupOrCommand: string\n}\n\nexport const CliCommandTelemetry = defineTrace<CLITraceData>({\n description: 'A CLI command was executed',\n name: 'CLI Command Executed',\n version: 2,\n})\n"],"names":["defineTrace","CliCommandTelemetry","description","name","version"],"mappings":"AAAA,SAAQA,WAAW,QAAO,oBAAmB;AAwB7C,OAAO,MAAMC,sBAAsBD,YAA0B;IAC3DE,aAAa;IACbC,MAAM;IACNC,SAAS;AACX,GAAE"}
@@ -2,7 +2,7 @@ import { defineTrace } from '@sanity/telemetry';
2
2
  export const CLIInitStepCompleted = defineTrace({
3
3
  description: 'User completed a step in the CLI init flow',
4
4
  name: 'CLI Init Step Completed',
5
- version: 1
5
+ version: 2
6
6
  });
7
7
 
8
8
  //# sourceMappingURL=init.telemetry.js.map
@@ -1 +1 @@
1
- {"version":3,"sources":["../../src/telemetry/init.telemetry.ts"],"sourcesContent":["import {defineTrace} from '@sanity/telemetry'\n\nimport {type EditorName} from '../actions/mcp/editorConfigs.js'\n\ninterface StartStep {\n flags: Record<string, boolean | number | string | undefined>\n step: 'start'\n}\n\ninterface LoginStep {\n step: 'login'\n\n alreadyLoggedIn?: boolean\n}\n\ninterface FetchJourneyConfigStep {\n datasetName: string\n displayName: string\n isFirstProject: boolean\n projectId: string\n step: 'fetchJourneyConfig'\n}\n\ninterface CreateOrSelectProjectStep {\n projectId: string\n selectedOption: 'create' | 'none' | 'select'\n step: 'createOrSelectProject'\n}\n\ninterface CreateOrSelectDatasetStep {\n datasetName: string\n selectedOption: 'create' | 'none' | 'select' | 'skip'\n step: 'createOrSelectDataset'\n\n visibility?: 'private' | 'public'\n}\n\ninterface UseDefaultPlanCoupon {\n selectedOption: 'no' | 'yes'\n step: 'useDefaultPlanCoupon'\n\n coupon?: string\n}\n\ninterface UseDefaultPlanId {\n selectedOption: 'no' | 'yes'\n step: 'useDefaultPlanId'\n\n planId?: string\n}\n\ninterface UseDetectedFrameworkStep {\n selectedOption: 'no' | 'yes'\n step: 'useDetectedFramework'\n\n detectedFramework?: string\n}\n\ninterface UseTypeScriptStep {\n selectedOption: 'no' | 'yes'\n step: 'useTypeScript'\n}\n\ninterface SelectTemplateStep {\n selectedOption: string\n step: 'selectProjectTemplate'\n}\ninterface ImportTemplateDatasetStep {\n selectedOption: 'no' | 'yes'\n step: 'importTemplateDataset'\n}\n\ninterface SendCommunityInviteStep {\n selectedOption: 'no' | 'yes'\n step: 'sendCommunityInvite'\n}\n\ninterface SelectPackageManagerStep {\n selectedOption: string\n step: 'selectPackageManager'\n}\n\ninterface MCPSetupStep {\n configuredEditors: EditorName[]\n detectedEditors: EditorName[]\n skipped: boolean\n step: 'mcpSetup'\n}\n\ninterface SkillsSetupStep {\n /** `--agent` values that received the Sanity skills bundle */\n installedAgents: string[]\n skipped: boolean\n step: 'skillsSetup'\n}\n\ninterface ConfigureAppProjectStep {\n selectedOption: 'create' | 'existing' | 'skip'\n step: 'configureAppProject'\n}\n\nexport type InitStepResult =\n | ConfigureAppProjectStep\n | CreateOrSelectDatasetStep\n | CreateOrSelectProjectStep\n | FetchJourneyConfigStep\n | ImportTemplateDatasetStep\n | LoginStep\n | MCPSetupStep\n | SelectPackageManagerStep\n | SelectTemplateStep\n | SendCommunityInviteStep\n | SkillsSetupStep\n | StartStep\n | UseDefaultPlanCoupon\n | UseDefaultPlanId\n | UseDetectedFrameworkStep\n | UseTypeScriptStep\n\nexport const CLIInitStepCompleted = defineTrace<InitStepResult>({\n description: 'User completed a step in the CLI init flow',\n name: 'CLI Init Step Completed',\n version: 1,\n})\n"],"names":["defineTrace","CLIInitStepCompleted","description","name","version"],"mappings":"AAAA,SAAQA,WAAW,QAAO,oBAAmB;AAuH7C,OAAO,MAAMC,uBAAuBD,YAA4B;IAC9DE,aAAa;IACbC,MAAM;IACNC,SAAS;AACX,GAAE"}
1
+ {"version":3,"sources":["../../src/telemetry/init.telemetry.ts"],"sourcesContent":["import {defineTrace} from '@sanity/telemetry'\n\nimport {type EditorName} from '../actions/mcp/editorConfigs.js'\n\ninterface StartStep {\n flags: Record<string, boolean | number | string | undefined>\n step: 'start'\n}\n\ninterface LoginStep {\n step: 'login'\n\n alreadyLoggedIn?: boolean\n}\n\ninterface FetchJourneyConfigStep {\n datasetName: string\n displayName: string\n isFirstProject: boolean\n projectId: string\n step: 'fetchJourneyConfig'\n}\n\ninterface CreateOrSelectProjectStep {\n projectId: string\n selectedOption: 'create' | 'none' | 'select'\n step: 'createOrSelectProject'\n}\n\ninterface CreateOrSelectDatasetStep {\n datasetName: string\n selectedOption: 'create' | 'none' | 'select' | 'skip'\n step: 'createOrSelectDataset'\n\n visibility?: 'private' | 'public'\n}\n\ninterface UseDefaultPlanCoupon {\n selectedOption: 'no' | 'yes'\n step: 'useDefaultPlanCoupon'\n}\n\ninterface UseDefaultPlanId {\n selectedOption: 'no' | 'yes'\n step: 'useDefaultPlanId'\n\n planId?: string\n}\n\ninterface UseDetectedFrameworkStep {\n selectedOption: 'no' | 'yes'\n step: 'useDetectedFramework'\n\n detectedFramework?: string\n}\n\ninterface UseTypeScriptStep {\n selectedOption: 'no' | 'yes'\n step: 'useTypeScript'\n}\n\ninterface SelectTemplateStep {\n selectedOption: string\n step: 'selectProjectTemplate'\n}\ninterface ImportTemplateDatasetStep {\n selectedOption: 'no' | 'yes'\n step: 'importTemplateDataset'\n}\n\ninterface SendCommunityInviteStep {\n selectedOption: 'no' | 'yes'\n step: 'sendCommunityInvite'\n}\n\ninterface SelectPackageManagerStep {\n selectedOption: string\n step: 'selectPackageManager'\n}\n\ninterface MCPSetupStep {\n configuredEditors: EditorName[]\n detectedEditors: EditorName[]\n skipped: boolean\n step: 'mcpSetup'\n}\n\ninterface SkillsSetupStep {\n /** `--agent` values that received the Sanity skills bundle */\n installedAgents: string[]\n skipped: boolean\n step: 'skillsSetup'\n}\n\ninterface ConfigureAppProjectStep {\n selectedOption: 'create' | 'existing' | 'skip'\n step: 'configureAppProject'\n}\n\nexport type InitStepResult =\n | ConfigureAppProjectStep\n | CreateOrSelectDatasetStep\n | CreateOrSelectProjectStep\n | FetchJourneyConfigStep\n | ImportTemplateDatasetStep\n | LoginStep\n | MCPSetupStep\n | SelectPackageManagerStep\n | SelectTemplateStep\n | SendCommunityInviteStep\n | SkillsSetupStep\n | StartStep\n | UseDefaultPlanCoupon\n | UseDefaultPlanId\n | UseDetectedFrameworkStep\n | UseTypeScriptStep\n\nexport const CLIInitStepCompleted = defineTrace<InitStepResult>({\n description: 'User completed a step in the CLI init flow',\n name: 'CLI Init Step Completed',\n version: 2,\n})\n"],"names":["defineTrace","CLIInitStepCompleted","description","name","version"],"mappings":"AAAA,SAAQA,WAAW,QAAO,oBAAmB;AAqH7C,OAAO,MAAMC,uBAAuBD,YAA4B;IAC9DE,aAAa;IACbC,MAAM;IACNC,SAAS;AACX,GAAE"}
@@ -0,0 +1,12 @@
1
+ import { defineTrace } from '@sanity/telemetry';
2
+ /**
3
+ * Logged once per `sanity typegen generate` run that uses `typegen.resources`. The codegen
4
+ * `Types Generated` trace looks the same for that path and for a single-schema config, so this
5
+ * trace is what tells the two apart.
6
+ */ export const TypegenResourcesTrace = defineTrace({
7
+ description: 'User ran typegen generate with typegen.resources in the CLI config',
8
+ name: 'Typegen Resources Used',
9
+ version: 1
10
+ });
11
+
12
+ //# sourceMappingURL=typegen.telemetry.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../src/telemetry/typegen.telemetry.ts"],"sourcesContent":["import {defineTrace} from '@sanity/telemetry'\n\ninterface TypegenResourcesTraceData {\n /** Resources without a `schema` file, which use the schema bound to their dataset. */\n boundSchemaCount: number\n mode: 'generate' | 'watch'\n resourceCount: number\n /**\n * Each resource's `projectId.dataset`. `resourceCount` is kept alongside this because a key\n * the telemetry sanitizer rejects is dropped from the list, leaving the count as the one\n * number that is always right.\n */\n resourceKeys: string[]\n}\n\n/**\n * Logged once per `sanity typegen generate` run that uses `typegen.resources`. The codegen\n * `Types Generated` trace looks the same for that path and for a single-schema config, so this\n * trace is what tells the two apart.\n */\nexport const TypegenResourcesTrace = defineTrace<TypegenResourcesTraceData>({\n description: 'User ran typegen generate with typegen.resources in the CLI config',\n name: 'Typegen Resources Used',\n version: 1,\n})\n"],"names":["defineTrace","TypegenResourcesTrace","description","name","version"],"mappings":"AAAA,SAAQA,WAAW,QAAO,oBAAmB;AAe7C;;;;CAIC,GACD,OAAO,MAAMC,wBAAwBD,YAAuC;IAC1EE,aAAa;IACbC,MAAM;IACNC,SAAS;AACX,GAAE"}
@@ -1,46 +1,18 @@
1
- import minimist from 'minimist';
2
- import { redactTelemetryArguments } from './telemetry/commandTelemetry.js';
3
- function collectOptionArguments(argv, commandTelemetry) {
4
- return redactTelemetryArguments(argv, commandTelemetry).filter((argument)=>argument.startsWith('-'));
5
- }
6
- /**
7
- * Parse the arguments from the command line
8
- *
9
- * @param argv - The arguments from the command line
10
- * @param commandTelemetry - Resolved telemetry behavior for the command
11
- * @returns The parsed arguments
12
- */ export function parseArguments(argv = process.argv, commandTelemetry) {
13
- const args = argv.slice(2);
14
- const { '--': extraArguments, _, version, ...extOptions } = minimist(args, {
15
- '--': true,
16
- boolean: [
17
- 'version'
18
- ],
19
- string: [
20
- '_'
21
- ]
22
- });
23
- const [groupOrCommand, ...argsWithoutOptions] = _;
24
- const finalExtraArguments = [
25
- ...extraArguments || [],
26
- ...collectOptionArguments(argv, commandTelemetry)
27
- ];
28
- // oclif allows to run `sanity help` or `sanity help <command>`
29
- // It does not fire the hooks on `--help` so this is okay to track
30
- const hasHelp = args.includes('help');
31
- // We only have global debug via env var
32
- const hasDebug = !!process.env.DEBUG;
1
+ import { collectTelemetryFlags } from './telemetry/commandTelemetry.js';
2
+ /** Collect telemetry without retaining positional input or forwarded arguments. */ export function parseArguments(argv, command) {
3
+ const [groupOrCommand = 'unknown', ...argsWithoutOptions] = command?.id?.split(':') ?? [];
33
4
  return {
34
- groupOrCommand,
35
5
  argsWithoutOptions,
36
- argv,
37
- extOptions,
38
- extraArguments: finalExtraArguments,
39
6
  coreOptions: {
40
- debug: hasDebug,
41
- help: hasHelp,
42
- version
43
- }
7
+ debug: Boolean(process.env.DEBUG),
8
+ help: command?.id === 'help',
9
+ version: command?.id === 'version'
10
+ },
11
+ extraArguments: collectTelemetryFlags(argv.slice(2), {
12
+ ...command?.baseFlags,
13
+ ...command?.flags
14
+ }, command?.telemetry),
15
+ groupOrCommand
44
16
  };
45
17
  }
46
18
 
@@ -1 +1 @@
1
- {"version":3,"sources":["../../src/util/parseArguments.ts"],"sourcesContent":["import minimist from 'minimist'\n\nimport {type CommandTelemetry, redactTelemetryArguments} from './telemetry/commandTelemetry.js'\n\ninterface ParsedArguments<F = Record<string, string>> {\n /**\n * Command arguments, eg any arguments after `sanity <command>` (no flags)\n */\n argsWithoutOptions: string[]\n\n // Raw, forwarded arguments, for commands that want to be more explicit about parsing\n argv: string[]\n\n /**\n * Options mostly relevant for the core CLI runner\n */\n coreOptions: {\n debug: boolean\n\n help: boolean\n\n version: boolean\n }\n\n /**\n * Command flags, without the core options (help, debug, version etc)\n */\n extOptions: F\n\n /**\n * Arguments after the ended argument list (--)\n */\n extraArguments: string[]\n\n /**\n * Group or command name, eg `dataset` (`sanity dataset`) or `import` (`sanity dataset import`)\n */\n groupOrCommand: string\n}\n\nfunction collectOptionArguments(argv: string[], commandTelemetry?: CommandTelemetry): string[] {\n return redactTelemetryArguments(argv, commandTelemetry).filter((argument) =>\n argument.startsWith('-'),\n )\n}\n\n/**\n * Parse the arguments from the command line\n *\n * @param argv - The arguments from the command line\n * @param commandTelemetry - Resolved telemetry behavior for the command\n * @returns The parsed arguments\n */\nexport function parseArguments(\n argv = process.argv,\n commandTelemetry?: CommandTelemetry,\n): ParsedArguments {\n const args = argv.slice(2)\n const {\n '--': extraArguments,\n _,\n version,\n ...extOptions\n } = minimist(args, {\n '--': true,\n boolean: ['version'],\n string: ['_'],\n })\n\n const [groupOrCommand, ...argsWithoutOptions] = _\n\n const finalExtraArguments = [\n ...(extraArguments || []),\n ...collectOptionArguments(argv, commandTelemetry),\n ]\n\n // oclif allows to run `sanity help` or `sanity help <command>`\n // It does not fire the hooks on `--help` so this is okay to track\n const hasHelp = args.includes('help')\n\n // We only have global debug via env var\n const hasDebug = !!process.env.DEBUG\n\n return {\n groupOrCommand,\n\n argsWithoutOptions,\n argv,\n extOptions,\n extraArguments: finalExtraArguments,\n\n coreOptions: {\n debug: hasDebug,\n help: hasHelp,\n version,\n },\n }\n}\n"],"names":["minimist","redactTelemetryArguments","collectOptionArguments","argv","commandTelemetry","filter","argument","startsWith","parseArguments","process","args","slice","extraArguments","_","version","extOptions","boolean","string","groupOrCommand","argsWithoutOptions","finalExtraArguments","hasHelp","includes","hasDebug","env","DEBUG","coreOptions","debug","help"],"mappings":"AAAA,OAAOA,cAAc,WAAU;AAE/B,SAA+BC,wBAAwB,QAAO,kCAAiC;AAsC/F,SAASC,uBAAuBC,IAAc,EAAEC,gBAAmC;IACjF,OAAOH,yBAAyBE,MAAMC,kBAAkBC,MAAM,CAAC,CAACC,WAC9DA,SAASC,UAAU,CAAC;AAExB;AAEA;;;;;;CAMC,GACD,OAAO,SAASC,eACdL,OAAOM,QAAQN,IAAI,EACnBC,gBAAmC;IAEnC,MAAMM,OAAOP,KAAKQ,KAAK,CAAC;IACxB,MAAM,EACJ,MAAMC,cAAc,EACpBC,CAAC,EACDC,OAAO,EACP,GAAGC,YACJ,GAAGf,SAASU,MAAM;QACjB,MAAM;QACNM,SAAS;YAAC;SAAU;QACpBC,QAAQ;YAAC;SAAI;IACf;IAEA,MAAM,CAACC,gBAAgB,GAAGC,mBAAmB,GAAGN;IAEhD,MAAMO,sBAAsB;WACtBR,kBAAkB,EAAE;WACrBV,uBAAuBC,MAAMC;KACjC;IAED,+DAA+D;IAC/D,kEAAkE;IAClE,MAAMiB,UAAUX,KAAKY,QAAQ,CAAC;IAE9B,wCAAwC;IACxC,MAAMC,WAAW,CAAC,CAACd,QAAQe,GAAG,CAACC,KAAK;IAEpC,OAAO;QACLP;QAEAC;QACAhB;QACAY;QACAH,gBAAgBQ;QAEhBM,aAAa;YACXC,OAAOJ;YACPK,MAAMP;YACNP;QACF;IACF;AACF"}
1
+ {"version":3,"sources":["../../src/util/parseArguments.ts"],"sourcesContent":["import {type Interfaces} from '@oclif/core'\n\nimport {collectTelemetryFlags, type CommandTelemetry} from './telemetry/commandTelemetry.js'\n\ninterface TelemetryCommand {\n baseFlags?: Interfaces.FlagInput\n flags?: Interfaces.FlagInput\n id?: string\n telemetry?: CommandTelemetry\n}\n\n/** Collect telemetry without retaining positional input or forwarded arguments. */\nexport function parseArguments(argv: string[], command?: TelemetryCommand) {\n const [groupOrCommand = 'unknown', ...argsWithoutOptions] = command?.id?.split(':') ?? []\n\n return {\n argsWithoutOptions,\n coreOptions: {\n debug: Boolean(process.env.DEBUG),\n help: command?.id === 'help',\n version: command?.id === 'version',\n },\n extraArguments: collectTelemetryFlags(\n argv.slice(2),\n {...command?.baseFlags, ...command?.flags},\n command?.telemetry,\n ),\n groupOrCommand,\n }\n}\n"],"names":["collectTelemetryFlags","parseArguments","argv","command","groupOrCommand","argsWithoutOptions","id","split","coreOptions","debug","Boolean","process","env","DEBUG","help","version","extraArguments","slice","baseFlags","flags","telemetry"],"mappings":"AAEA,SAAQA,qBAAqB,QAA8B,kCAAiC;AAS5F,iFAAiF,GACjF,OAAO,SAASC,eAAeC,IAAc,EAAEC,OAA0B;IACvE,MAAM,CAACC,iBAAiB,SAAS,EAAE,GAAGC,mBAAmB,GAAGF,SAASG,IAAIC,MAAM,QAAQ,EAAE;IAEzF,OAAO;QACLF;QACAG,aAAa;YACXC,OAAOC,QAAQC,QAAQC,GAAG,CAACC,KAAK;YAChCC,MAAMX,SAASG,OAAO;YACtBS,SAASZ,SAASG,OAAO;QAC3B;QACAU,gBAAgBhB,sBACdE,KAAKe,KAAK,CAAC,IACX;YAAC,GAAGd,SAASe,SAAS;YAAE,GAAGf,SAASgB,KAAK;QAAA,GACzChB,SAASiB;QAEXhB;IACF;AACF"}
@@ -1 +1 @@
1
- {"version":3,"sources":["../../src/util/sharedFlags.ts"],"sourcesContent":["import {Flags} from '@oclif/core'\n\n/**\n * Controls how the flag relates to CLI configuration:\n *\n * - `'override'` — The command falls back to CLI config (sanity.cli.ts) when the flag is not\n * provided. The flag description automatically gets an \" (overrides CLI configuration)\" suffix,\n * and `helpGroup` defaults to `'OVERRIDE'`.\n *\n * - `'specify'` — The command does NOT fall back to CLI config; the flag is simply how the user\n * provides the value. No suffix is appended, and no default `helpGroup` is set.\n */\ntype FlagSemantics = 'override' | 'specify'\n\n/**\n * Options accepted by the shared flag getters.\n * Locked properties (char, parse, name, helpValue) are excluded to ensure\n * consistent behavior across all commands.\n */\ninterface SharedFlagOptions {\n /**\n * Controls description suffix and default helpGroup.\n * @see {@link FlagSemantics}\n */\n semantics: FlagSemantics\n\n dependsOn?: string[]\n description?: string\n env?: string\n exclusive?: string[]\n helpGroup?: string\n hidden?: boolean\n required?: boolean\n}\n\nconst OVERRIDE_SUFFIX = ' (overrides CLI configuration)'\n\n/**\n * Returns a `--project` / `-p` flag definition with a deprecated `--project-id` alias.\n *\n * Locked: flag name (`project`), char (`p`), `helpValue` (`<id>`), and parse (trims + validates non-empty).\n */\nexport function getProjectIdFlag(options: SharedFlagOptions) {\n const {description: baseDescription, helpGroup, semantics, ...rest} = options\n const isOverride = semantics === 'override'\n const description = (baseDescription ?? 'Project ID to use') + (isOverride ? OVERRIDE_SUFFIX : '')\n\n return {\n project: Flags.string({\n aliases: ['project-id'],\n deprecateAliases: true,\n description,\n helpGroup: helpGroup ?? (isOverride ? 'OVERRIDE' : undefined),\n helpValue: '<id>',\n ...rest,\n char: 'p',\n parse: async (input: string) => {\n const trimmed = input.trim()\n if (trimmed === '') {\n throw new Error('`--project` cannot be empty if provided')\n }\n return trimmed\n },\n }),\n }\n}\n\n/**\n * Returns an `--organization` flag definition.\n *\n * Locked: flag name (`organization`), `helpValue` (`<id>`), and parse (trims + validates non-empty).\n */\nexport function getOrganizationFlag(options: SharedFlagOptions) {\n const {description: baseDescription, helpGroup, semantics, ...rest} = options\n const isOverride = semantics === 'override'\n const description =\n (baseDescription ?? 'Organization ID to use') + (isOverride ? OVERRIDE_SUFFIX : '')\n\n return {\n organization: Flags.string({\n description,\n helpGroup: helpGroup ?? (isOverride ? 'OVERRIDE' : undefined),\n helpValue: '<id>',\n ...rest,\n parse: async (input: string) => {\n const trimmed = input.trim()\n if (trimmed === '') {\n throw new Error('`--organization` cannot be empty if provided')\n }\n return trimmed\n },\n }),\n }\n}\n\n/**\n * Returns a `--dataset` / `-d` flag definition.\n *\n * Locked: flag name (`dataset`), char (`d`), `helpValue` (`<name>`), and parse (trims + validates non-empty).\n */\nexport function getDatasetFlag(options: SharedFlagOptions) {\n const {description: baseDescription, helpGroup, semantics, ...rest} = options\n const isOverride = semantics === 'override'\n const description = (baseDescription ?? 'Dataset to use') + (isOverride ? OVERRIDE_SUFFIX : '')\n\n return {\n dataset: Flags.string({\n description,\n helpGroup: helpGroup ?? (isOverride ? 'OVERRIDE' : undefined),\n helpValue: '<name>',\n ...rest,\n char: 'd',\n parse: async (input: string) => {\n const trimmed = input.trim()\n if (trimmed === '') {\n throw new Error('`--dataset` cannot be empty if provided')\n }\n return trimmed\n },\n }),\n }\n}\n"],"names":["Flags","OVERRIDE_SUFFIX","getProjectIdFlag","options","description","baseDescription","helpGroup","semantics","rest","isOverride","project","string","aliases","deprecateAliases","undefined","helpValue","char","parse","input","trimmed","trim","Error","getOrganizationFlag","organization","getDatasetFlag","dataset"],"mappings":"AAAA,SAAQA,KAAK,QAAO,cAAa;AAmCjC,MAAMC,kBAAkB;AAExB;;;;CAIC,GACD,OAAO,SAASC,iBAAiBC,OAA0B;IACzD,MAAM,EAACC,aAAaC,eAAe,EAAEC,SAAS,EAAEC,SAAS,EAAE,GAAGC,MAAK,GAAGL;IACtE,MAAMM,aAAaF,cAAc;IACjC,MAAMH,cAAc,AAACC,CAAAA,mBAAmB,mBAAkB,IAAMI,CAAAA,aAAaR,kBAAkB,EAAC;IAEhG,OAAO;QACLS,SAASV,MAAMW,MAAM,CAAC;YACpBC,SAAS;gBAAC;aAAa;YACvBC,kBAAkB;YAClBT;YACAE,WAAWA,aAAcG,CAAAA,aAAa,aAAaK,SAAQ;YAC3DC,WAAW;YACX,GAAGP,IAAI;YACPQ,MAAM;YACNC,OAAO,OAAOC;gBACZ,MAAMC,UAAUD,MAAME,IAAI;gBAC1B,IAAID,YAAY,IAAI;oBAClB,MAAM,IAAIE,MAAM;gBAClB;gBACA,OAAOF;YACT;QACF;IACF;AACF;AAEA;;;;CAIC,GACD,OAAO,SAASG,oBAAoBnB,OAA0B;IAC5D,MAAM,EAACC,aAAaC,eAAe,EAAEC,SAAS,EAAEC,SAAS,EAAE,GAAGC,MAAK,GAAGL;IACtE,MAAMM,aAAaF,cAAc;IACjC,MAAMH,cACJ,AAACC,CAAAA,mBAAmB,wBAAuB,IAAMI,CAAAA,aAAaR,kBAAkB,EAAC;IAEnF,OAAO;QACLsB,cAAcvB,MAAMW,MAAM,CAAC;YACzBP;YACAE,WAAWA,aAAcG,CAAAA,aAAa,aAAaK,SAAQ;YAC3DC,WAAW;YACX,GAAGP,IAAI;YACPS,OAAO,OAAOC;gBACZ,MAAMC,UAAUD,MAAME,IAAI;gBAC1B,IAAID,YAAY,IAAI;oBAClB,MAAM,IAAIE,MAAM;gBAClB;gBACA,OAAOF;YACT;QACF;IACF;AACF;AAEA;;;;CAIC,GACD,OAAO,SAASK,eAAerB,OAA0B;IACvD,MAAM,EAACC,aAAaC,eAAe,EAAEC,SAAS,EAAEC,SAAS,EAAE,GAAGC,MAAK,GAAGL;IACtE,MAAMM,aAAaF,cAAc;IACjC,MAAMH,cAAc,AAACC,CAAAA,mBAAmB,gBAAe,IAAMI,CAAAA,aAAaR,kBAAkB,EAAC;IAE7F,OAAO;QACLwB,SAASzB,MAAMW,MAAM,CAAC;YACpBP;YACAE,WAAWA,aAAcG,CAAAA,aAAa,aAAaK,SAAQ;YAC3DC,WAAW;YACX,GAAGP,IAAI;YACPQ,MAAM;YACNC,OAAO,OAAOC;gBACZ,MAAMC,UAAUD,MAAME,IAAI;gBAC1B,IAAID,YAAY,IAAI;oBAClB,MAAM,IAAIE,MAAM;gBAClB;gBACA,OAAOF;YACT;QACF;IACF;AACF"}
1
+ {"version":3,"sources":["../../src/util/sharedFlags.ts"],"sourcesContent":["import {Flags} from '@oclif/core'\n\n/**\n * Controls how the flag relates to CLI configuration:\n *\n * - `'override'` — The command falls back to CLI config (sanity.cli.ts) when the flag is not\n * provided. The flag description automatically gets an \" (overrides CLI configuration)\" suffix,\n * and `helpGroup` defaults to `'OVERRIDE'`.\n *\n * - `'specify'` — The command does NOT fall back to CLI config; the flag is simply how the user\n * provides the value. No suffix is appended, and no default `helpGroup` is set.\n */\ntype FlagSemantics = 'override' | 'specify'\n\n/**\n * Options accepted by the shared flag getters.\n * Locked properties (char, parse, name, helpValue) are excluded to ensure\n * consistent behavior across all commands.\n */\ninterface SharedFlagOptions {\n /**\n * Controls description suffix and default helpGroup.\n * @see {@link FlagSemantics}\n */\n semantics: FlagSemantics\n\n atLeastOne?: string[]\n dependsOn?: string[]\n description?: string\n env?: string\n exclusive?: string[]\n helpGroup?: string\n hidden?: boolean\n required?: boolean\n}\n\nconst OVERRIDE_SUFFIX = ' (overrides CLI configuration)'\n\n/**\n * Returns a `--project` / `-p` flag definition with a deprecated `--project-id` alias.\n *\n * Locked: flag name (`project`), char (`p`), `helpValue` (`<id>`), and parse (trims + validates non-empty).\n */\nexport function getProjectIdFlag(options: SharedFlagOptions) {\n const {description: baseDescription, helpGroup, semantics, ...rest} = options\n const isOverride = semantics === 'override'\n const description = (baseDescription ?? 'Project ID to use') + (isOverride ? OVERRIDE_SUFFIX : '')\n\n return {\n project: Flags.string({\n aliases: ['project-id'],\n deprecateAliases: true,\n description,\n helpGroup: helpGroup ?? (isOverride ? 'OVERRIDE' : undefined),\n helpValue: '<id>',\n ...rest,\n char: 'p',\n parse: async (input: string) => {\n const trimmed = input.trim()\n if (trimmed === '') {\n throw new Error('`--project` cannot be empty if provided')\n }\n return trimmed\n },\n }),\n }\n}\n\n/**\n * Returns an `--organization` flag definition.\n *\n * Locked: flag name (`organization`), `helpValue` (`<id>`), and parse (trims + validates non-empty).\n */\nexport function getOrganizationFlag(options: SharedFlagOptions) {\n const {description: baseDescription, helpGroup, semantics, ...rest} = options\n const isOverride = semantics === 'override'\n const description =\n (baseDescription ?? 'Organization ID to use') + (isOverride ? OVERRIDE_SUFFIX : '')\n\n return {\n organization: Flags.string({\n description,\n helpGroup: helpGroup ?? (isOverride ? 'OVERRIDE' : undefined),\n helpValue: '<id>',\n ...rest,\n parse: async (input: string) => {\n const trimmed = input.trim()\n if (trimmed === '') {\n throw new Error('`--organization` cannot be empty if provided')\n }\n return trimmed\n },\n }),\n }\n}\n\n/**\n * Returns a `--dataset` / `-d` flag definition.\n *\n * Locked: flag name (`dataset`), char (`d`), `helpValue` (`<name>`), and parse (trims + validates non-empty).\n */\nexport function getDatasetFlag(options: SharedFlagOptions) {\n const {description: baseDescription, helpGroup, semantics, ...rest} = options\n const isOverride = semantics === 'override'\n const description = (baseDescription ?? 'Dataset to use') + (isOverride ? OVERRIDE_SUFFIX : '')\n\n return {\n dataset: Flags.string({\n description,\n helpGroup: helpGroup ?? (isOverride ? 'OVERRIDE' : undefined),\n helpValue: '<name>',\n ...rest,\n char: 'd',\n parse: async (input: string) => {\n const trimmed = input.trim()\n if (trimmed === '') {\n throw new Error('`--dataset` cannot be empty if provided')\n }\n return trimmed\n },\n }),\n }\n}\n"],"names":["Flags","OVERRIDE_SUFFIX","getProjectIdFlag","options","description","baseDescription","helpGroup","semantics","rest","isOverride","project","string","aliases","deprecateAliases","undefined","helpValue","char","parse","input","trimmed","trim","Error","getOrganizationFlag","organization","getDatasetFlag","dataset"],"mappings":"AAAA,SAAQA,KAAK,QAAO,cAAa;AAoCjC,MAAMC,kBAAkB;AAExB;;;;CAIC,GACD,OAAO,SAASC,iBAAiBC,OAA0B;IACzD,MAAM,EAACC,aAAaC,eAAe,EAAEC,SAAS,EAAEC,SAAS,EAAE,GAAGC,MAAK,GAAGL;IACtE,MAAMM,aAAaF,cAAc;IACjC,MAAMH,cAAc,AAACC,CAAAA,mBAAmB,mBAAkB,IAAMI,CAAAA,aAAaR,kBAAkB,EAAC;IAEhG,OAAO;QACLS,SAASV,MAAMW,MAAM,CAAC;YACpBC,SAAS;gBAAC;aAAa;YACvBC,kBAAkB;YAClBT;YACAE,WAAWA,aAAcG,CAAAA,aAAa,aAAaK,SAAQ;YAC3DC,WAAW;YACX,GAAGP,IAAI;YACPQ,MAAM;YACNC,OAAO,OAAOC;gBACZ,MAAMC,UAAUD,MAAME,IAAI;gBAC1B,IAAID,YAAY,IAAI;oBAClB,MAAM,IAAIE,MAAM;gBAClB;gBACA,OAAOF;YACT;QACF;IACF;AACF;AAEA;;;;CAIC,GACD,OAAO,SAASG,oBAAoBnB,OAA0B;IAC5D,MAAM,EAACC,aAAaC,eAAe,EAAEC,SAAS,EAAEC,SAAS,EAAE,GAAGC,MAAK,GAAGL;IACtE,MAAMM,aAAaF,cAAc;IACjC,MAAMH,cACJ,AAACC,CAAAA,mBAAmB,wBAAuB,IAAMI,CAAAA,aAAaR,kBAAkB,EAAC;IAEnF,OAAO;QACLsB,cAAcvB,MAAMW,MAAM,CAAC;YACzBP;YACAE,WAAWA,aAAcG,CAAAA,aAAa,aAAaK,SAAQ;YAC3DC,WAAW;YACX,GAAGP,IAAI;YACPS,OAAO,OAAOC;gBACZ,MAAMC,UAAUD,MAAME,IAAI;gBAC1B,IAAID,YAAY,IAAI;oBAClB,MAAM,IAAIE,MAAM;gBAClB;gBACA,OAAOF;YACT;QACF;IACF;AACF;AAEA;;;;CAIC,GACD,OAAO,SAASK,eAAerB,OAA0B;IACvD,MAAM,EAACC,aAAaC,eAAe,EAAEC,SAAS,EAAEC,SAAS,EAAE,GAAGC,MAAK,GAAGL;IACtE,MAAMM,aAAaF,cAAc;IACjC,MAAMH,cAAc,AAACC,CAAAA,mBAAmB,gBAAe,IAAMI,CAAAA,aAAaR,kBAAkB,EAAC;IAE7F,OAAO;QACLwB,SAASzB,MAAMW,MAAM,CAAC;YACpBP;YACAE,WAAWA,aAAcG,CAAAA,aAAa,aAAaK,SAAQ;YAC3DC,WAAW;YACX,GAAGP,IAAI;YACPQ,MAAM;YACNC,OAAO,OAAOC;gBACZ,MAAMC,UAAUD,MAAME,IAAI;gBAC1B,IAAID,YAAY,IAAI;oBAClB,MAAM,IAAIE,MAAM;gBAClB;gBACA,OAAOF;YACT;QACF;IACF;AACF"}