@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,10 +1,10 @@
1
- import { typeid } from 'typeid-js';
1
+ import { randomUUID } from 'node:crypto';
2
2
  /**
3
- * Creates a unique trace ID using typeid
3
+ * Creates a unique ID for correlating telemetry events within a trace.
4
4
  *
5
5
  * @internal
6
6
  */ export function createTraceId() {
7
- return typeid('trace').toString();
7
+ return `trace_${randomUUID()}`;
8
8
  }
9
9
 
10
10
  //# sourceMappingURL=createTraceId.js.map
@@ -1 +1 @@
1
- {"version":3,"sources":["../../../src/util/telemetry/createTraceId.ts"],"sourcesContent":["import {typeid} from 'typeid-js'\n\ntype TraceId = string & {__type: 'TraceId'}\n\n/**\n * Creates a unique trace ID using typeid\n *\n * @internal\n */\nexport function createTraceId(): TraceId {\n return typeid('trace').toString() as TraceId\n}\n"],"names":["typeid","createTraceId","toString"],"mappings":"AAAA,SAAQA,MAAM,QAAO,YAAW;AAIhC;;;;CAIC,GACD,OAAO,SAASC;IACd,OAAOD,OAAO,SAASE,QAAQ;AACjC"}
1
+ {"version":3,"sources":["../../../src/util/telemetry/createTraceId.ts"],"sourcesContent":["import {randomUUID} from 'node:crypto'\n\ntype TraceId = string & {__type: 'TraceId'}\n\n/**\n * Creates a unique ID for correlating telemetry events within a trace.\n *\n * @internal\n */\nexport function createTraceId(): TraceId {\n return `trace_${randomUUID()}` as TraceId\n}\n"],"names":["randomUUID","createTraceId"],"mappings":"AAAA,SAAQA,UAAU,QAAO,cAAa;AAItC;;;;CAIC,GACD,OAAO,SAASC;IACd,OAAO,CAAC,MAAM,EAAED,cAAc;AAChC"}
@@ -3,6 +3,7 @@ import { catchError, defer, from, lastValueFrom, map, mergeMap, of, reduce, swit
3
3
  import { cleanupOldTelemetryFiles } from './cleanupOldTelemetryFiles.js';
4
4
  import { findTelemetryFiles } from './findTelemetryFiles.js';
5
5
  import { readNDJSON } from './readNDJSON.js';
6
+ import { sanitizeTelemetryEvent, telemetryError } from './sanitizeTelemetryEvent.js';
6
7
  import { telemetryStoreDebug } from './telemetryStoreDebug.js';
7
8
  /**
8
9
  * Standalone, stateless function to flush telemetry files.
@@ -29,7 +30,7 @@ import { telemetryStoreDebug } from './telemetryStoreDebug.js';
29
30
  })).pipe(tap(()=>{
30
31
  telemetryStoreDebug(`Deleted file ${reason}: %s`, filePath);
31
32
  }), catchError((error)=>{
32
- telemetryStoreDebug('Error deleting file %s: %o', filePath, error);
33
+ telemetryStoreDebug('Error deleting file %s: %o', filePath, telemetryError(error));
33
34
  // of() is not same as of(undefined) in rxjs
34
35
  // eslint-disable-next-line unicorn/no-useless-undefined
35
36
  return of(undefined);
@@ -59,7 +60,7 @@ import { telemetryStoreDebug } from './telemetryStoreDebug.js';
59
60
  telemetryStoreDebug('File %s no longer exists, skipping', filePath);
60
61
  return of([]);
61
62
  }
62
- telemetryStoreDebug('Error reading file %s: %o', filePath, error);
63
+ telemetryStoreDebug('Error reading file %s: %o', filePath, telemetryError(error));
63
64
  return of([]);
64
65
  }), map((events)=>({
65
66
  events,
@@ -100,7 +101,16 @@ import { telemetryStoreDebug } from './telemetryStoreDebug.js';
100
101
  }
101
102
  // Send events and then delete all files (including empty ones)
102
103
  telemetryStoreDebug('Sending %d events to backend', allEvents.length);
103
- return defer(()=>from(options.sendEvents(allEvents))).pipe(tap(()=>{
104
+ return defer(()=>from(options.sendEvents(allEvents.flatMap((event)=>{
105
+ try {
106
+ return [
107
+ sanitizeTelemetryEvent(event)
108
+ ];
109
+ } catch {
110
+ telemetryStoreDebug('Skipping malformed telemetry event');
111
+ return [];
112
+ }
113
+ })))).pipe(tap(()=>{
104
114
  telemetryStoreDebug('Successfully sent events, deleting %d files', filesToDelete.length);
105
115
  }), switchMap(()=>deleteFiles([
106
116
  ...filesToDelete,
@@ -109,7 +119,7 @@ import { telemetryStoreDebug } from './telemetryStoreDebug.js';
109
119
  }), tap(()=>{
110
120
  telemetryStoreDebug('Standalone flush operation completed successfully');
111
121
  }), map(()=>undefined), catchError((error)=>{
112
- telemetryStoreDebug('Error during standalone flush operation: %o', error);
122
+ telemetryStoreDebug('Error during standalone flush operation: %o', telemetryError(error));
113
123
  throw error;
114
124
  }));
115
125
  return lastValueFrom(flush$);
@@ -1 +1 @@
1
- {"version":3,"sources":["../../../src/util/telemetry/flushTelemetryFiles.ts"],"sourcesContent":["import {rm} from 'node:fs/promises'\n\nimport {type ConsentInformation} from '@sanity/cli-core'\nimport {type TelemetryEvent} from '@sanity/telemetry'\nimport {\n catchError,\n defer,\n from,\n lastValueFrom,\n map,\n mergeMap,\n of,\n reduce,\n switchMap,\n tap,\n} from 'rxjs'\n\nimport {cleanupOldTelemetryFiles} from './cleanupOldTelemetryFiles.js'\nimport {findTelemetryFiles} from './findTelemetryFiles.js'\nimport {readNDJSON} from './readNDJSON.js'\nimport {telemetryStoreDebug} from './telemetryStoreDebug.js'\n\ninterface FlushTelemetryFilesOptions {\n resolveConsent: () => Promise<ConsentInformation>\n sendEvents: (events: TelemetryEvent[]) => Promise<void>\n}\n\n/**\n * Standalone, stateless function to flush telemetry files.\n *\n * This function can be used independently of the telemetry store, making it\n * suitable for use in child processes or other contexts where store state\n * is not available.\n *\n * @param options - Configuration for consent resolution and event sending\n * @returns Promise that resolves when flush operation is complete\n *\n * @internal\n */\nexport async function flushTelemetryFiles(options: FlushTelemetryFilesOptions): Promise<void> {\n telemetryStoreDebug('Starting standalone flush operation')\n\n // Helper function for deleting files with consistent error handling\n const deleteFiles = (files: string[], reason: string) => {\n if (files.length === 0) {\n // of() is not same as of(undefined) in rxjs\n // eslint-disable-next-line unicorn/no-useless-undefined\n return of(undefined)\n }\n\n return from(files).pipe(\n mergeMap((filePath) =>\n from(rm(filePath, {force: true})).pipe(\n tap(() => {\n telemetryStoreDebug(`Deleted file ${reason}: %s`, filePath)\n }),\n catchError((error) => {\n telemetryStoreDebug('Error deleting file %s: %o', filePath, error)\n // of() is not same as of(undefined) in rxjs\n // eslint-disable-next-line unicorn/no-useless-undefined\n return of(undefined)\n }),\n ),\n ),\n // Collect all deletions into a single completion value\n reduce(() => undefined as void, undefined as void),\n )\n }\n\n const flush$ = defer(() => from(options.resolveConsent())).pipe(\n tap((currentConsent) => {\n telemetryStoreDebug('Current consent status for flush: %s', currentConsent.status)\n }),\n switchMap((currentConsent) => {\n // First cleanup old files, then process current files\n return defer(() => from(cleanupOldTelemetryFiles())).pipe(\n switchMap(() => defer(() => from(findTelemetryFiles()))),\n switchMap((filePaths) => {\n if (filePaths.length === 0) {\n telemetryStoreDebug('No telemetry files found, nothing to flush')\n return of({allEvents: [], consent: currentConsent, emptyFiles: [], filesToDelete: []})\n }\n\n telemetryStoreDebug('Found %d telemetry files to process', filePaths.length)\n\n return from(filePaths).pipe(\n mergeMap((filePath) => {\n return defer(() => from(readNDJSON<TelemetryEvent>(filePath))).pipe(\n tap((events) => {\n telemetryStoreDebug('Read %d events from %s', events.length, filePath)\n }),\n catchError((error) => {\n if ((error as {code?: string}).code === 'ENOENT') {\n telemetryStoreDebug('File %s no longer exists, skipping', filePath)\n return of([])\n }\n telemetryStoreDebug('Error reading file %s: %o', filePath, error)\n return of([])\n }),\n map((events) => ({events, filePath})),\n )\n }),\n reduce(\n (\n acc: {allEvents: TelemetryEvent[]; emptyFiles: string[]; filesToDelete: string[]},\n current,\n ) => {\n if (current.events.length > 0) {\n acc.allEvents.push(...current.events)\n acc.filesToDelete.push(current.filePath)\n } else {\n acc.emptyFiles.push(current.filePath)\n }\n return acc\n },\n {allEvents: [], emptyFiles: [], filesToDelete: []},\n ),\n map((result) => ({...result, consent: currentConsent})),\n )\n }),\n )\n }),\n switchMap(({allEvents, consent, emptyFiles, filesToDelete}) => {\n telemetryStoreDebug(\n 'Found %d total events to flush from %d files (%d empty)',\n allEvents.length,\n filesToDelete.length,\n emptyFiles.length,\n )\n\n // Always clean up empty files regardless of consent or event count\n const cleanupEmpty$ =\n emptyFiles.length > 0 ? deleteFiles(emptyFiles, 'empty files') : of(undefined as void)\n\n if (consent.status !== 'granted' || allEvents.length === 0) {\n if (consent.status === 'granted') {\n telemetryStoreDebug('No events to send, cleaning up files')\n return cleanupEmpty$\n } else {\n telemetryStoreDebug(\n 'Consent not granted (%s), cleaning up %d files without sending events',\n consent.status,\n filesToDelete.length,\n )\n return deleteFiles(\n [...filesToDelete, ...emptyFiles],\n `without sending (consent: ${consent.status})`,\n )\n }\n }\n\n // Send events and then delete all files (including empty ones)\n telemetryStoreDebug('Sending %d events to backend', allEvents.length)\n\n return defer(() => from(options.sendEvents(allEvents))).pipe(\n tap(() => {\n telemetryStoreDebug('Successfully sent events, deleting %d files', filesToDelete.length)\n }),\n switchMap(() => deleteFiles([...filesToDelete, ...emptyFiles], 'after successful send')),\n )\n }),\n tap(() => {\n telemetryStoreDebug('Standalone flush operation completed successfully')\n }),\n map(() => undefined as void),\n catchError((error) => {\n telemetryStoreDebug('Error during standalone flush operation: %o', error)\n throw error\n }),\n )\n\n return lastValueFrom(flush$)\n}\n"],"names":["rm","catchError","defer","from","lastValueFrom","map","mergeMap","of","reduce","switchMap","tap","cleanupOldTelemetryFiles","findTelemetryFiles","readNDJSON","telemetryStoreDebug","flushTelemetryFiles","options","deleteFiles","files","reason","length","undefined","pipe","filePath","force","error","flush$","resolveConsent","currentConsent","status","filePaths","allEvents","consent","emptyFiles","filesToDelete","events","code","acc","current","push","result","cleanupEmpty$","sendEvents"],"mappings":"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"}
1
+ {"version":3,"sources":["../../../src/util/telemetry/flushTelemetryFiles.ts"],"sourcesContent":["import {rm} from 'node:fs/promises'\n\nimport {type ConsentInformation} from '@sanity/cli-core'\nimport {type TelemetryEvent} from '@sanity/telemetry'\nimport {\n catchError,\n defer,\n from,\n lastValueFrom,\n map,\n mergeMap,\n of,\n reduce,\n switchMap,\n tap,\n} from 'rxjs'\n\nimport {cleanupOldTelemetryFiles} from './cleanupOldTelemetryFiles.js'\nimport {findTelemetryFiles} from './findTelemetryFiles.js'\nimport {readNDJSON} from './readNDJSON.js'\nimport {sanitizeTelemetryEvent, telemetryError} from './sanitizeTelemetryEvent.js'\nimport {telemetryStoreDebug} from './telemetryStoreDebug.js'\n\ninterface FlushTelemetryFilesOptions {\n resolveConsent: () => Promise<ConsentInformation>\n sendEvents: (events: TelemetryEvent[]) => Promise<void>\n}\n\n/**\n * Standalone, stateless function to flush telemetry files.\n *\n * This function can be used independently of the telemetry store, making it\n * suitable for use in child processes or other contexts where store state\n * is not available.\n *\n * @param options - Configuration for consent resolution and event sending\n * @returns Promise that resolves when flush operation is complete\n *\n * @internal\n */\nexport async function flushTelemetryFiles(options: FlushTelemetryFilesOptions): Promise<void> {\n telemetryStoreDebug('Starting standalone flush operation')\n\n // Helper function for deleting files with consistent error handling\n const deleteFiles = (files: string[], reason: string) => {\n if (files.length === 0) {\n // of() is not same as of(undefined) in rxjs\n // eslint-disable-next-line unicorn/no-useless-undefined\n return of(undefined)\n }\n\n return from(files).pipe(\n mergeMap((filePath) =>\n from(rm(filePath, {force: true})).pipe(\n tap(() => {\n telemetryStoreDebug(`Deleted file ${reason}: %s`, filePath)\n }),\n catchError((error) => {\n telemetryStoreDebug('Error deleting file %s: %o', filePath, telemetryError(error))\n // of() is not same as of(undefined) in rxjs\n // eslint-disable-next-line unicorn/no-useless-undefined\n return of(undefined)\n }),\n ),\n ),\n // Collect all deletions into a single completion value\n reduce(() => undefined as void, undefined as void),\n )\n }\n\n const flush$ = defer(() => from(options.resolveConsent())).pipe(\n tap((currentConsent) => {\n telemetryStoreDebug('Current consent status for flush: %s', currentConsent.status)\n }),\n switchMap((currentConsent) => {\n // First cleanup old files, then process current files\n return defer(() => from(cleanupOldTelemetryFiles())).pipe(\n switchMap(() => defer(() => from(findTelemetryFiles()))),\n switchMap((filePaths) => {\n if (filePaths.length === 0) {\n telemetryStoreDebug('No telemetry files found, nothing to flush')\n return of({allEvents: [], consent: currentConsent, emptyFiles: [], filesToDelete: []})\n }\n\n telemetryStoreDebug('Found %d telemetry files to process', filePaths.length)\n\n return from(filePaths).pipe(\n mergeMap((filePath) => {\n return defer(() => from(readNDJSON<TelemetryEvent>(filePath))).pipe(\n tap((events) => {\n telemetryStoreDebug('Read %d events from %s', events.length, filePath)\n }),\n catchError((error) => {\n if ((error as {code?: string}).code === 'ENOENT') {\n telemetryStoreDebug('File %s no longer exists, skipping', filePath)\n return of([])\n }\n telemetryStoreDebug('Error reading file %s: %o', filePath, telemetryError(error))\n return of([])\n }),\n map((events) => ({events, filePath})),\n )\n }),\n reduce(\n (\n acc: {allEvents: TelemetryEvent[]; emptyFiles: string[]; filesToDelete: string[]},\n current,\n ) => {\n if (current.events.length > 0) {\n acc.allEvents.push(...current.events)\n acc.filesToDelete.push(current.filePath)\n } else {\n acc.emptyFiles.push(current.filePath)\n }\n return acc\n },\n {allEvents: [], emptyFiles: [], filesToDelete: []},\n ),\n map((result) => ({...result, consent: currentConsent})),\n )\n }),\n )\n }),\n switchMap(({allEvents, consent, emptyFiles, filesToDelete}) => {\n telemetryStoreDebug(\n 'Found %d total events to flush from %d files (%d empty)',\n allEvents.length,\n filesToDelete.length,\n emptyFiles.length,\n )\n\n // Always clean up empty files regardless of consent or event count\n const cleanupEmpty$ =\n emptyFiles.length > 0 ? deleteFiles(emptyFiles, 'empty files') : of(undefined as void)\n\n if (consent.status !== 'granted' || allEvents.length === 0) {\n if (consent.status === 'granted') {\n telemetryStoreDebug('No events to send, cleaning up files')\n return cleanupEmpty$\n } else {\n telemetryStoreDebug(\n 'Consent not granted (%s), cleaning up %d files without sending events',\n consent.status,\n filesToDelete.length,\n )\n return deleteFiles(\n [...filesToDelete, ...emptyFiles],\n `without sending (consent: ${consent.status})`,\n )\n }\n }\n\n // Send events and then delete all files (including empty ones)\n telemetryStoreDebug('Sending %d events to backend', allEvents.length)\n\n return defer(() =>\n from(\n options.sendEvents(\n allEvents.flatMap((event) => {\n try {\n return [sanitizeTelemetryEvent(event)]\n } catch {\n telemetryStoreDebug('Skipping malformed telemetry event')\n return []\n }\n }),\n ),\n ),\n ).pipe(\n tap(() => {\n telemetryStoreDebug('Successfully sent events, deleting %d files', filesToDelete.length)\n }),\n switchMap(() => deleteFiles([...filesToDelete, ...emptyFiles], 'after successful send')),\n )\n }),\n tap(() => {\n telemetryStoreDebug('Standalone flush operation completed successfully')\n }),\n map(() => undefined as void),\n catchError((error) => {\n telemetryStoreDebug('Error during standalone flush operation: %o', telemetryError(error))\n throw error\n }),\n )\n\n return lastValueFrom(flush$)\n}\n"],"names":["rm","catchError","defer","from","lastValueFrom","map","mergeMap","of","reduce","switchMap","tap","cleanupOldTelemetryFiles","findTelemetryFiles","readNDJSON","sanitizeTelemetryEvent","telemetryError","telemetryStoreDebug","flushTelemetryFiles","options","deleteFiles","files","reason","length","undefined","pipe","filePath","force","error","flush$","resolveConsent","currentConsent","status","filePaths","allEvents","consent","emptyFiles","filesToDelete","events","code","acc","current","push","result","cleanupEmpty$","sendEvents","flatMap","event"],"mappings":"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"}
@@ -40,7 +40,7 @@ const logSampleTracker = new Map();
40
40
  return createTrace(event, context, sessionId, emit, createLogger);
41
41
  };
42
42
  const updateUserProperties = (properties)=>{
43
- telemetryStoreDebug('Updating user properties: %o', properties);
43
+ telemetryStoreDebug('Updating user properties');
44
44
  const userPropsEvent = {
45
45
  createdAt: new Date().toISOString(),
46
46
  properties,
@@ -1 +1 @@
1
- {"version":3,"sources":["../../../src/util/telemetry/logger.ts"],"sourcesContent":["import {\n type DeferredEvent,\n type DefinedTelemetryLog,\n type DefinedTelemetryTrace,\n type TelemetryEvent,\n type TelemetryLogger,\n type TelemetryTrace,\n} from '@sanity/telemetry'\n\nimport {telemetryStoreDebug} from './telemetryStoreDebug.js'\nimport {createTrace} from './trace.js'\n\n// Process-wide sample rate tracking for log events, shared across all logger instances\n// to deduplicate events regardless of which logger emits them.\n// Use resetLogSampleTracker() in tests to prevent state leaking between test runs.\nconst logSampleTracker = new Map<string, number>()\n\n/** @public Reset sample rate tracking state. Intended for use in tests only. */\nexport function resetLogSampleTracker(): void {\n logSampleTracker.clear()\n}\n\n/**\n * Creates a telemetry logger that emits events via the provided emit function\n * @internal\n */\nexport function createLogger<UserProperties>(\n sessionId: string,\n emit: (event: TelemetryEvent) => void,\n): TelemetryLogger<UserProperties> {\n telemetryStoreDebug('Creating logger for session: %s', sessionId)\n\n const log = <Data>(event: DefinedTelemetryLog<Data>, data?: Data) => {\n telemetryStoreDebug('Logging event: %s', event.name)\n\n // Handle sampling if maxSampleRate is specified\n if (event.maxSampleRate && event.maxSampleRate > 0) {\n const now = Date.now()\n const lastEmit = logSampleTracker.get(event.name) || 0\n\n if (now - lastEmit < event.maxSampleRate) {\n telemetryStoreDebug(\n 'Skipping event %s due to sampling (maxSampleRate: %d)',\n event.name,\n event.maxSampleRate,\n )\n return // Skip due to sampling\n }\n\n logSampleTracker.set(event.name, now)\n telemetryStoreDebug('Event %s passed sampling check', event.name)\n }\n\n const logEvent: TelemetryEvent = {\n createdAt: new Date().toISOString(),\n data: data ?? null,\n name: event.name,\n sessionId,\n type: 'log',\n version: event.version,\n }\n\n emit(logEvent)\n }\n\n const trace = <Data, Context = unknown>(\n event: DefinedTelemetryTrace<Data, Context>,\n context?: Context,\n ): TelemetryTrace<UserProperties, Data> => {\n telemetryStoreDebug('Creating trace: %s', event.name)\n return createTrace(event, context, sessionId, emit, createLogger)\n }\n\n const updateUserProperties = (properties: UserProperties) => {\n telemetryStoreDebug('Updating user properties: %o', properties)\n const userPropsEvent: TelemetryEvent = {\n createdAt: new Date().toISOString(),\n properties,\n sessionId,\n type: 'userProperties',\n }\n\n emit(userPropsEvent)\n }\n\n const resume = (events: DeferredEvent[]) => {\n for (const event of events) {\n emit({\n createdAt: event.createdAt,\n data: event.data ?? null,\n name: event.event.name,\n sessionId,\n type: 'log',\n version: event.event.version,\n })\n }\n }\n\n return {\n log,\n resume,\n trace,\n updateUserProperties,\n }\n}\n"],"names":["telemetryStoreDebug","createTrace","logSampleTracker","Map","resetLogSampleTracker","clear","createLogger","sessionId","emit","log","event","data","name","maxSampleRate","now","Date","lastEmit","get","set","logEvent","createdAt","toISOString","type","version","trace","context","updateUserProperties","properties","userPropsEvent","resume","events"],"mappings":"AASA,SAAQA,mBAAmB,QAAO,2BAA0B;AAC5D,SAAQC,WAAW,QAAO,aAAY;AAEtC,uFAAuF;AACvF,+DAA+D;AAC/D,mFAAmF;AACnF,MAAMC,mBAAmB,IAAIC;AAE7B,8EAA8E,GAC9E,OAAO,SAASC;IACdF,iBAAiBG,KAAK;AACxB;AAEA;;;CAGC,GACD,OAAO,SAASC,aACdC,SAAiB,EACjBC,IAAqC;IAErCR,oBAAoB,mCAAmCO;IAEvD,MAAME,MAAM,CAAOC,OAAkCC;QACnDX,oBAAoB,qBAAqBU,MAAME,IAAI;QAEnD,gDAAgD;QAChD,IAAIF,MAAMG,aAAa,IAAIH,MAAMG,aAAa,GAAG,GAAG;YAClD,MAAMC,MAAMC,KAAKD,GAAG;YACpB,MAAME,WAAWd,iBAAiBe,GAAG,CAACP,MAAME,IAAI,KAAK;YAErD,IAAIE,MAAME,WAAWN,MAAMG,aAAa,EAAE;gBACxCb,oBACE,yDACAU,MAAME,IAAI,EACVF,MAAMG,aAAa;gBAErB,QAAO,uBAAuB;YAChC;YAEAX,iBAAiBgB,GAAG,CAACR,MAAME,IAAI,EAAEE;YACjCd,oBAAoB,kCAAkCU,MAAME,IAAI;QAClE;QAEA,MAAMO,WAA2B;YAC/BC,WAAW,IAAIL,OAAOM,WAAW;YACjCV,MAAMA,QAAQ;YACdC,MAAMF,MAAME,IAAI;YAChBL;YACAe,MAAM;YACNC,SAASb,MAAMa,OAAO;QACxB;QAEAf,KAAKW;IACP;IAEA,MAAMK,QAAQ,CACZd,OACAe;QAEAzB,oBAAoB,sBAAsBU,MAAME,IAAI;QACpD,OAAOX,YAAYS,OAAOe,SAASlB,WAAWC,MAAMF;IACtD;IAEA,MAAMoB,uBAAuB,CAACC;QAC5B3B,oBAAoB,gCAAgC2B;QACpD,MAAMC,iBAAiC;YACrCR,WAAW,IAAIL,OAAOM,WAAW;YACjCM;YACApB;YACAe,MAAM;QACR;QAEAd,KAAKoB;IACP;IAEA,MAAMC,SAAS,CAACC;QACd,KAAK,MAAMpB,SAASoB,OAAQ;YAC1BtB,KAAK;gBACHY,WAAWV,MAAMU,SAAS;gBAC1BT,MAAMD,MAAMC,IAAI,IAAI;gBACpBC,MAAMF,MAAMA,KAAK,CAACE,IAAI;gBACtBL;gBACAe,MAAM;gBACNC,SAASb,MAAMA,KAAK,CAACa,OAAO;YAC9B;QACF;IACF;IAEA,OAAO;QACLd;QACAoB;QACAL;QACAE;IACF;AACF"}
1
+ {"version":3,"sources":["../../../src/util/telemetry/logger.ts"],"sourcesContent":["import {\n type DeferredEvent,\n type DefinedTelemetryLog,\n type DefinedTelemetryTrace,\n type TelemetryEvent,\n type TelemetryLogger,\n type TelemetryTrace,\n} from '@sanity/telemetry'\n\nimport {telemetryStoreDebug} from './telemetryStoreDebug.js'\nimport {createTrace} from './trace.js'\n\n// Process-wide sample rate tracking for log events, shared across all logger instances\n// to deduplicate events regardless of which logger emits them.\n// Use resetLogSampleTracker() in tests to prevent state leaking between test runs.\nconst logSampleTracker = new Map<string, number>()\n\n/** @public Reset sample rate tracking state. Intended for use in tests only. */\nexport function resetLogSampleTracker(): void {\n logSampleTracker.clear()\n}\n\n/**\n * Creates a telemetry logger that emits events via the provided emit function\n * @internal\n */\nexport function createLogger<UserProperties>(\n sessionId: string,\n emit: (event: TelemetryEvent) => void,\n): TelemetryLogger<UserProperties> {\n telemetryStoreDebug('Creating logger for session: %s', sessionId)\n\n const log = <Data>(event: DefinedTelemetryLog<Data>, data?: Data) => {\n telemetryStoreDebug('Logging event: %s', event.name)\n\n // Handle sampling if maxSampleRate is specified\n if (event.maxSampleRate && event.maxSampleRate > 0) {\n const now = Date.now()\n const lastEmit = logSampleTracker.get(event.name) || 0\n\n if (now - lastEmit < event.maxSampleRate) {\n telemetryStoreDebug(\n 'Skipping event %s due to sampling (maxSampleRate: %d)',\n event.name,\n event.maxSampleRate,\n )\n return // Skip due to sampling\n }\n\n logSampleTracker.set(event.name, now)\n telemetryStoreDebug('Event %s passed sampling check', event.name)\n }\n\n const logEvent: TelemetryEvent = {\n createdAt: new Date().toISOString(),\n data: data ?? null,\n name: event.name,\n sessionId,\n type: 'log',\n version: event.version,\n }\n\n emit(logEvent)\n }\n\n const trace = <Data, Context = unknown>(\n event: DefinedTelemetryTrace<Data, Context>,\n context?: Context,\n ): TelemetryTrace<UserProperties, Data> => {\n telemetryStoreDebug('Creating trace: %s', event.name)\n return createTrace(event, context, sessionId, emit, createLogger)\n }\n\n const updateUserProperties = (properties: UserProperties) => {\n telemetryStoreDebug('Updating user properties')\n const userPropsEvent: TelemetryEvent = {\n createdAt: new Date().toISOString(),\n properties,\n sessionId,\n type: 'userProperties',\n }\n\n emit(userPropsEvent)\n }\n\n const resume = (events: DeferredEvent[]) => {\n for (const event of events) {\n emit({\n createdAt: event.createdAt,\n data: event.data ?? null,\n name: event.event.name,\n sessionId,\n type: 'log',\n version: event.event.version,\n })\n }\n }\n\n return {\n log,\n resume,\n trace,\n updateUserProperties,\n }\n}\n"],"names":["telemetryStoreDebug","createTrace","logSampleTracker","Map","resetLogSampleTracker","clear","createLogger","sessionId","emit","log","event","data","name","maxSampleRate","now","Date","lastEmit","get","set","logEvent","createdAt","toISOString","type","version","trace","context","updateUserProperties","properties","userPropsEvent","resume","events"],"mappings":"AASA,SAAQA,mBAAmB,QAAO,2BAA0B;AAC5D,SAAQC,WAAW,QAAO,aAAY;AAEtC,uFAAuF;AACvF,+DAA+D;AAC/D,mFAAmF;AACnF,MAAMC,mBAAmB,IAAIC;AAE7B,8EAA8E,GAC9E,OAAO,SAASC;IACdF,iBAAiBG,KAAK;AACxB;AAEA;;;CAGC,GACD,OAAO,SAASC,aACdC,SAAiB,EACjBC,IAAqC;IAErCR,oBAAoB,mCAAmCO;IAEvD,MAAME,MAAM,CAAOC,OAAkCC;QACnDX,oBAAoB,qBAAqBU,MAAME,IAAI;QAEnD,gDAAgD;QAChD,IAAIF,MAAMG,aAAa,IAAIH,MAAMG,aAAa,GAAG,GAAG;YAClD,MAAMC,MAAMC,KAAKD,GAAG;YACpB,MAAME,WAAWd,iBAAiBe,GAAG,CAACP,MAAME,IAAI,KAAK;YAErD,IAAIE,MAAME,WAAWN,MAAMG,aAAa,EAAE;gBACxCb,oBACE,yDACAU,MAAME,IAAI,EACVF,MAAMG,aAAa;gBAErB,QAAO,uBAAuB;YAChC;YAEAX,iBAAiBgB,GAAG,CAACR,MAAME,IAAI,EAAEE;YACjCd,oBAAoB,kCAAkCU,MAAME,IAAI;QAClE;QAEA,MAAMO,WAA2B;YAC/BC,WAAW,IAAIL,OAAOM,WAAW;YACjCV,MAAMA,QAAQ;YACdC,MAAMF,MAAME,IAAI;YAChBL;YACAe,MAAM;YACNC,SAASb,MAAMa,OAAO;QACxB;QAEAf,KAAKW;IACP;IAEA,MAAMK,QAAQ,CACZd,OACAe;QAEAzB,oBAAoB,sBAAsBU,MAAME,IAAI;QACpD,OAAOX,YAAYS,OAAOe,SAASlB,WAAWC,MAAMF;IACtD;IAEA,MAAMoB,uBAAuB,CAACC;QAC5B3B,oBAAoB;QACpB,MAAM4B,iBAAiC;YACrCR,WAAW,IAAIL,OAAOM,WAAW;YACjCM;YACApB;YACAe,MAAM;QACR;QAEAd,KAAKoB;IACP;IAEA,MAAMC,SAAS,CAACC;QACd,KAAK,MAAMpB,SAASoB,OAAQ;YAC1BtB,KAAK;gBACHY,WAAWV,MAAMU,SAAS;gBAC1BT,MAAMD,MAAMC,IAAI,IAAI;gBACpBC,MAAMF,MAAMA,KAAK,CAACE,IAAI;gBACtBL;gBACAe,MAAM;gBACNC,SAASb,MAAMA,KAAK,CAACa,OAAO;YAC9B;QACF;IACF;IAEA,OAAO;QACLd;QACAoB;QACAL;QACAE;IACF;AACF"}
@@ -19,7 +19,7 @@ import { telemetryStoreDebug } from './telemetryStoreDebug.js';
19
19
  try {
20
20
  results.push(JSON.parse(line));
21
21
  } catch {
22
- telemetryStoreDebug('Skipping malformed JSON line in %s: %s', filePath, line.slice(0, 100));
22
+ telemetryStoreDebug('Skipping malformed JSON line in telemetry file');
23
23
  }
24
24
  }
25
25
  return results;
@@ -1 +1 @@
1
- {"version":3,"sources":["../../../src/util/telemetry/readNDJSON.ts"],"sourcesContent":["import {readFile} from 'node:fs/promises'\n\nimport {telemetryStoreDebug} from './telemetryStoreDebug.js'\n\n/**\n * Reads and parses an NDJSON (newline-delimited JSON) file containing telemetry events.\n * Malformed lines are skipped individually rather than failing the entire file.\n *\n * @param filePath - Path to the NDJSON file\n * @returns Promise resolving to array of parsed telemetry events\n *\n * @internal\n */\nexport async function readNDJSON<T>(filePath: string): Promise<T[]> {\n const content = await readFile(filePath, 'utf8')\n\n if (!content.trim()) {\n return []\n }\n\n const results: T[] = []\n for (const line of content.trim().split('\\n')) {\n if (!line) continue\n try {\n results.push(JSON.parse(line) as T)\n } catch {\n telemetryStoreDebug('Skipping malformed JSON line in %s: %s', filePath, line.slice(0, 100))\n }\n }\n return results\n}\n"],"names":["readFile","telemetryStoreDebug","readNDJSON","filePath","content","trim","results","line","split","push","JSON","parse","slice"],"mappings":"AAAA,SAAQA,QAAQ,QAAO,mBAAkB;AAEzC,SAAQC,mBAAmB,QAAO,2BAA0B;AAE5D;;;;;;;;CAQC,GACD,OAAO,eAAeC,WAAcC,QAAgB;IAClD,MAAMC,UAAU,MAAMJ,SAASG,UAAU;IAEzC,IAAI,CAACC,QAAQC,IAAI,IAAI;QACnB,OAAO,EAAE;IACX;IAEA,MAAMC,UAAe,EAAE;IACvB,KAAK,MAAMC,QAAQH,QAAQC,IAAI,GAAGG,KAAK,CAAC,MAAO;QAC7C,IAAI,CAACD,MAAM;QACX,IAAI;YACFD,QAAQG,IAAI,CAACC,KAAKC,KAAK,CAACJ;QAC1B,EAAE,OAAM;YACNN,oBAAoB,0CAA0CE,UAAUI,KAAKK,KAAK,CAAC,GAAG;QACxF;IACF;IACA,OAAON;AACT"}
1
+ {"version":3,"sources":["../../../src/util/telemetry/readNDJSON.ts"],"sourcesContent":["import {readFile} from 'node:fs/promises'\n\nimport {telemetryStoreDebug} from './telemetryStoreDebug.js'\n\n/**\n * Reads and parses an NDJSON (newline-delimited JSON) file containing telemetry events.\n * Malformed lines are skipped individually rather than failing the entire file.\n *\n * @param filePath - Path to the NDJSON file\n * @returns Promise resolving to array of parsed telemetry events\n *\n * @internal\n */\nexport async function readNDJSON<T>(filePath: string): Promise<T[]> {\n const content = await readFile(filePath, 'utf8')\n\n if (!content.trim()) {\n return []\n }\n\n const results: T[] = []\n for (const line of content.trim().split('\\n')) {\n if (!line) continue\n try {\n results.push(JSON.parse(line) as T)\n } catch {\n telemetryStoreDebug('Skipping malformed JSON line in telemetry file')\n }\n }\n return results\n}\n"],"names":["readFile","telemetryStoreDebug","readNDJSON","filePath","content","trim","results","line","split","push","JSON","parse"],"mappings":"AAAA,SAAQA,QAAQ,QAAO,mBAAkB;AAEzC,SAAQC,mBAAmB,QAAO,2BAA0B;AAE5D;;;;;;;;CAQC,GACD,OAAO,eAAeC,WAAcC,QAAgB;IAClD,MAAMC,UAAU,MAAMJ,SAASG,UAAU;IAEzC,IAAI,CAACC,QAAQC,IAAI,IAAI;QACnB,OAAO,EAAE;IACX;IAEA,MAAMC,UAAe,EAAE;IACvB,KAAK,MAAMC,QAAQH,QAAQC,IAAI,GAAGG,KAAK,CAAC,MAAO;QAC7C,IAAI,CAACD,MAAM;QACX,IAAI;YACFD,QAAQG,IAAI,CAACC,KAAKC,KAAK,CAACJ;QAC1B,EAAE,OAAM;YACNN,oBAAoB;QACtB;IACF;IACA,OAAOK;AACT"}
@@ -0,0 +1,304 @@
1
+ import { commandIdentity } from './cliCommandIdentity.js';
2
+ import { sanitizeCliFlagRecord, sanitizeCliFlags } from './cliTelemetryFlags.js';
3
+ const record = (value)=>value !== null && typeof value === 'object' && !Array.isArray(value) ? value : {};
4
+ const boolean = (value)=>typeof value === 'boolean' ? value : undefined;
5
+ const number = (value)=>typeof value === 'number' && Number.isFinite(value) && value >= 0 ? value : undefined;
6
+ const matching = (pattern)=>(value)=>typeof value === 'string' && pattern.test(value) ? value : undefined;
7
+ const oneOf = (...values)=>(value)=>typeof value === 'string' && values.includes(value) ? value : undefined;
8
+ const listOf = (rule)=>(value)=>Array.isArray(value) ? value.map((item)=>rule(item)).filter((item)=>item !== undefined) : undefined;
9
+ const version = matching(/^v?\d{1,4}\.\d{1,4}\.\d{1,4}$/);
10
+ const cliProjectId = (value)=>typeof value === 'string' && /^[a-z0-9]{8}$/.test(value) ? value : undefined;
11
+ // `projectId.dataset`, the key a resource's generated types are registered under. Both halves
12
+ // are constrained: a project id is eight lowercase alphanumerics, and a dataset name allows
13
+ // only lowercase alphanumerics, dashes and underscores.
14
+ const cliResourceKey = (value)=>typeof value === 'string' && /^[a-z0-9]{8}\.[a-z0-9][a-z0-9_-]{0,63}$/.test(value) ? value : undefined;
15
+ const sessionId = matching(/^session_[0-7][0-9a-hjkmnp-tv-z]{25}$/);
16
+ const traceId = matching(/^trace_(?:[0-7][0-9a-hjkmnp-tv-z]{25}|[0-9a-f]{8}-[0-9a-f]{4}-4[0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12})$/);
17
+ const editor = oneOf('Antigravity', 'Claude Code', 'Cline', 'Codex CLI', 'Cursor', 'Gemini CLI', 'GitHub Copilot CLI', 'MCPorter', 'OpenCode', 'VS Code', 'VS Code Insiders', 'Zed');
18
+ const agent = oneOf('antigravity', 'claude-code', 'cline', 'codex', 'cursor', 'gemini-cli', 'github-copilot', 'opencode');
19
+ const yesNo = oneOf('yes', 'no');
20
+ const errorName = oneOf('AbortError', 'AggregateError', 'ClientError', 'CLIError', 'Error', 'RangeError', 'ReferenceError', 'ServerError', 'SyntaxError', 'TypeError', 'URIError');
21
+ function project(input, schema) {
22
+ const source = record(input);
23
+ const result = {};
24
+ for (const [key, rule] of Object.entries(schema)){
25
+ if (!Object.hasOwn(source, key)) continue;
26
+ const value = rule(source[key]);
27
+ if (value !== undefined) result[key] = value;
28
+ }
29
+ return result;
30
+ }
31
+ const userProperties = {
32
+ cliVersion: version,
33
+ cpuArchitecture: oneOf('arm', 'arm64', 'ia32', 'loong64', 'mips', 'mipsel', 'ppc', 'ppc64', 'riscv64', 's390', 's390x', 'x64'),
34
+ machinePlatform: oneOf('aix', 'android', 'darwin', 'freebsd', 'linux', 'openbsd', 'sunos', 'win32'),
35
+ projectId: cliProjectId,
36
+ runtime: oneOf('node', 'bun', 'deno'),
37
+ runtimeVersion: version
38
+ };
39
+ const initSteps = {
40
+ configureAppProject: {
41
+ selectedOption: oneOf('create', 'existing', 'skip')
42
+ },
43
+ createOrSelectDataset: {
44
+ selectedOption: oneOf('create', 'none', 'select', 'skip'),
45
+ visibility: oneOf('public', 'private')
46
+ },
47
+ createOrSelectProject: {
48
+ projectId: cliProjectId,
49
+ selectedOption: oneOf('create', 'none', 'select')
50
+ },
51
+ fetchJourneyConfig: {
52
+ isFirstProject: boolean,
53
+ projectId: cliProjectId
54
+ },
55
+ importTemplateDataset: {
56
+ selectedOption: yesNo
57
+ },
58
+ login: {
59
+ alreadyLoggedIn: boolean
60
+ },
61
+ mcpSetup: {
62
+ configuredEditors: listOf(editor),
63
+ detectedEditors: listOf(editor),
64
+ skipped: boolean
65
+ },
66
+ selectPackageManager: {
67
+ selectedOption: oneOf('npm', 'yarn', 'pnpm', 'bun')
68
+ },
69
+ selectProjectTemplate: {},
70
+ sendCommunityInvite: {
71
+ selectedOption: yesNo
72
+ },
73
+ skillsSetup: {
74
+ installedAgents: listOf(agent),
75
+ skipped: boolean
76
+ },
77
+ start: {
78
+ flags: sanitizeCliFlagRecord
79
+ },
80
+ useDefaultPlanCoupon: {
81
+ selectedOption: yesNo
82
+ },
83
+ useDefaultPlanId: {
84
+ selectedOption: yesNo
85
+ },
86
+ useDetectedFramework: {
87
+ selectedOption: yesNo
88
+ },
89
+ useTypeScript: {
90
+ selectedOption: yesNo
91
+ }
92
+ };
93
+ const eventSchemas = {
94
+ 'App Build Completed': {
95
+ fields: {
96
+ outputSize: number
97
+ },
98
+ versions: [
99
+ 0
100
+ ]
101
+ },
102
+ 'CLI Command Executed': {
103
+ fields: {},
104
+ versions: [
105
+ 1,
106
+ 2
107
+ ]
108
+ },
109
+ 'CLI Init Step Completed': {
110
+ fields: {},
111
+ versions: [
112
+ 1,
113
+ 2
114
+ ]
115
+ },
116
+ 'CLI Login Step Completed': {
117
+ fields: {
118
+ step: oneOf('selectProvider', 'waitForToken')
119
+ },
120
+ versions: [
121
+ 1
122
+ ]
123
+ },
124
+ 'CLI MCP Configure Completed': {
125
+ fields: {
126
+ configuredEditors: listOf(editor),
127
+ detectedEditors: listOf(editor)
128
+ },
129
+ versions: [
130
+ 1
131
+ ]
132
+ },
133
+ 'CLI Skills Install Completed': {
134
+ fields: {
135
+ detectedEditors: listOf(editor),
136
+ installedAgents: listOf(agent)
137
+ },
138
+ versions: [
139
+ 1
140
+ ]
141
+ },
142
+ 'Schema Built': {
143
+ fields: {
144
+ extractManifest: boolean
145
+ },
146
+ versions: [
147
+ 0
148
+ ]
149
+ },
150
+ 'Schema Deployed': {
151
+ fields: {
152
+ extractManifest: boolean,
153
+ schemaRequired: boolean
154
+ },
155
+ versions: [
156
+ 1
157
+ ]
158
+ },
159
+ 'Schema Extracted': {
160
+ fields: {
161
+ enforceRequiredFields: boolean,
162
+ schemaAllTypesCount: number,
163
+ schemaDocumentTypesCount: number,
164
+ schemaFormat: oneOf('groq-type-nodes'),
165
+ schemaTypesCount: number
166
+ },
167
+ versions: [
168
+ 0
169
+ ]
170
+ },
171
+ 'Schema Extraction Watch Mode': {
172
+ fields: {
173
+ averageExtractionDuration: number,
174
+ enforceRequiredFields: boolean,
175
+ extractionFailedCount: number,
176
+ extractionSuccessfulCount: number,
177
+ schemaFormat: oneOf('groq-type-nodes'),
178
+ step: oneOf('started', 'stopped'),
179
+ watcherDuration: number
180
+ },
181
+ versions: [
182
+ 0
183
+ ]
184
+ },
185
+ 'Studio Build Completed': {
186
+ fields: {
187
+ outputSize: number
188
+ },
189
+ versions: [
190
+ 0
191
+ ]
192
+ },
193
+ 'Typegen Resources Used': {
194
+ fields: {
195
+ boundSchemaCount: number,
196
+ mode: oneOf('generate', 'watch'),
197
+ resourceCount: number,
198
+ resourceKeys: listOf(cliResourceKey)
199
+ },
200
+ versions: [
201
+ 1
202
+ ]
203
+ },
204
+ 'Typegen Watch Mode Started': {
205
+ fields: {
206
+ averageGenerationDuration: number,
207
+ generationFailedCount: number,
208
+ generationSuccessfulCount: number,
209
+ step: oneOf('started', 'stopped'),
210
+ watcherDuration: number
211
+ },
212
+ versions: [
213
+ 0
214
+ ]
215
+ },
216
+ 'Types Generated': {
217
+ fields: {
218
+ configMethod: oneOf('legacy', 'cli'),
219
+ configOverloadClientMethods: boolean,
220
+ emptyUnionTypeNodesGenerated: number,
221
+ filesWithErrors: number,
222
+ outputSize: number,
223
+ queriesCount: number,
224
+ queryFilesCount: number,
225
+ schemaTypesCount: number,
226
+ typeNodesGenerated: number,
227
+ unknownTypeNodesGenerated: number,
228
+ unknownTypeNodesRatio: number
229
+ },
230
+ versions: [
231
+ 0
232
+ ]
233
+ }
234
+ };
235
+ function commandContext(input) {
236
+ const fields = record(input);
237
+ const result = project(fields, {
238
+ emptyCommand: boolean
239
+ });
240
+ if ('groupOrCommand' in fields || 'commandArguments' in fields) {
241
+ Object.assign(result, commandIdentity(fields.groupOrCommand, fields.commandArguments));
242
+ }
243
+ if ('extraArguments' in fields) result.extraArguments = sanitizeCliFlags(fields.extraArguments);
244
+ if ('coreOptions' in fields) result.coreOptions = project(fields.coreOptions, {
245
+ debug: boolean,
246
+ help: boolean,
247
+ version: boolean
248
+ });
249
+ return result;
250
+ }
251
+ export function sanitizeTelemetryEvent(event) {
252
+ const fields = record(event);
253
+ const result = {
254
+ ...project(fields, {
255
+ sessionId,
256
+ traceId,
257
+ version: number
258
+ }),
259
+ createdAt: new Date(event.createdAt).toISOString(),
260
+ redactionVersion: 1,
261
+ source: 'cli',
262
+ type: oneOf('log', 'userProperties', 'trace.start', 'trace.log', 'trace.complete', 'trace.error')(fields.type) ?? 'log'
263
+ };
264
+ if (fields.type === 'userProperties') {
265
+ result.properties = project(fields.properties, userProperties);
266
+ return result;
267
+ }
268
+ const name = typeof fields.name === 'string' ? fields.name : '';
269
+ const schema = Object.hasOwn(eventSchemas, name) ? eventSchemas[name] : undefined;
270
+ result.name = schema ? name : 'CLI Other Event';
271
+ result.data = {};
272
+ if (schema && schema.versions.includes(fields.version)) {
273
+ result.data = project(fields.data, schema.fields);
274
+ result.context = project(fields.context, schema.fields);
275
+ if (name === 'CLI Command Executed') {
276
+ result.context = commandContext(fields.context);
277
+ result.data = commandContext(fields.data);
278
+ }
279
+ if (name === 'CLI Init Step Completed') {
280
+ const data = record(fields.data);
281
+ if (typeof data.step === 'string' && Object.hasOwn(initSteps, data.step)) {
282
+ result.data = {
283
+ step: data.step,
284
+ ...project(data, initSteps[data.step])
285
+ };
286
+ }
287
+ }
288
+ }
289
+ if (fields.type === 'trace.error') {
290
+ result.data = {
291
+ message: 'CLI operation failed',
292
+ name: errorName(record(fields.data).name) ?? 'Error'
293
+ };
294
+ }
295
+ return result;
296
+ }
297
+ export function telemetryError(error) {
298
+ return {
299
+ message: 'CLI operation failed',
300
+ name: errorName(record(error).name) ?? 'Error'
301
+ };
302
+ }
303
+
304
+ //# sourceMappingURL=sanitizeTelemetryEvent.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../../src/util/telemetry/sanitizeTelemetryEvent.ts"],"sourcesContent":["import {type TelemetryEvent} from '@sanity/telemetry'\n\nimport {commandIdentity} from './cliCommandIdentity.js'\nimport {sanitizeCliFlagRecord, sanitizeCliFlags} from './cliTelemetryFlags.js'\n\ntype Fields = Record<string, unknown>\ntype Rule = (value: unknown) => unknown\ntype Schema = Record<string, Rule>\n\nconst record = (value: unknown): Fields =>\n value !== null && typeof value === 'object' && !Array.isArray(value) ? (value as Fields) : {}\nconst boolean: Rule = (value) => (typeof value === 'boolean' ? value : undefined)\nconst number: Rule = (value) =>\n typeof value === 'number' && Number.isFinite(value) && value >= 0 ? value : undefined\nconst matching =\n (pattern: RegExp): Rule =>\n (value) =>\n typeof value === 'string' && pattern.test(value) ? value : undefined\nconst oneOf =\n (...values: string[]) =>\n (value: unknown): string | undefined =>\n typeof value === 'string' && values.includes(value) ? value : undefined\nconst listOf =\n (rule: Rule): Rule =>\n (value) =>\n Array.isArray(value)\n ? value.map((item) => rule(item)).filter((item) => item !== undefined)\n : undefined\nconst version = matching(/^v?\\d{1,4}\\.\\d{1,4}\\.\\d{1,4}$/)\nconst cliProjectId = (value: unknown): string | undefined =>\n typeof value === 'string' && /^[a-z0-9]{8}$/.test(value) ? value : undefined\n// `projectId.dataset`, the key a resource's generated types are registered under. Both halves\n// are constrained: a project id is eight lowercase alphanumerics, and a dataset name allows\n// only lowercase alphanumerics, dashes and underscores.\nconst cliResourceKey = (value: unknown): string | undefined =>\n typeof value === 'string' && /^[a-z0-9]{8}\\.[a-z0-9][a-z0-9_-]{0,63}$/.test(value)\n ? value\n : undefined\nconst sessionId = matching(/^session_[0-7][0-9a-hjkmnp-tv-z]{25}$/)\nconst traceId = matching(\n /^trace_(?:[0-7][0-9a-hjkmnp-tv-z]{25}|[0-9a-f]{8}-[0-9a-f]{4}-4[0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12})$/,\n)\nconst editor = oneOf(\n 'Antigravity',\n 'Claude Code',\n 'Cline',\n 'Codex CLI',\n 'Cursor',\n 'Gemini CLI',\n 'GitHub Copilot CLI',\n 'MCPorter',\n 'OpenCode',\n 'VS Code',\n 'VS Code Insiders',\n 'Zed',\n)\nconst agent = oneOf(\n 'antigravity',\n 'claude-code',\n 'cline',\n 'codex',\n 'cursor',\n 'gemini-cli',\n 'github-copilot',\n 'opencode',\n)\nconst yesNo = oneOf('yes', 'no')\nconst errorName = oneOf(\n 'AbortError',\n 'AggregateError',\n 'ClientError',\n 'CLIError',\n 'Error',\n 'RangeError',\n 'ReferenceError',\n 'ServerError',\n 'SyntaxError',\n 'TypeError',\n 'URIError',\n)\n\nfunction project(input: unknown, schema: Schema): Fields {\n const source = record(input)\n const result: Fields = {}\n for (const [key, rule] of Object.entries(schema)) {\n if (!Object.hasOwn(source, key)) continue\n const value = rule(source[key])\n if (value !== undefined) result[key] = value\n }\n return result\n}\n\nconst userProperties: Schema = {\n cliVersion: version,\n cpuArchitecture: oneOf(\n 'arm',\n 'arm64',\n 'ia32',\n 'loong64',\n 'mips',\n 'mipsel',\n 'ppc',\n 'ppc64',\n 'riscv64',\n 's390',\n 's390x',\n 'x64',\n ),\n machinePlatform: oneOf(\n 'aix',\n 'android',\n 'darwin',\n 'freebsd',\n 'linux',\n 'openbsd',\n 'sunos',\n 'win32',\n ),\n projectId: cliProjectId,\n runtime: oneOf('node', 'bun', 'deno'),\n runtimeVersion: version,\n}\nconst initSteps: Record<string, Schema> = {\n configureAppProject: {selectedOption: oneOf('create', 'existing', 'skip')},\n createOrSelectDataset: {\n selectedOption: oneOf('create', 'none', 'select', 'skip'),\n visibility: oneOf('public', 'private'),\n },\n createOrSelectProject: {\n projectId: cliProjectId,\n selectedOption: oneOf('create', 'none', 'select'),\n },\n fetchJourneyConfig: {isFirstProject: boolean, projectId: cliProjectId},\n importTemplateDataset: {selectedOption: yesNo},\n login: {alreadyLoggedIn: boolean},\n mcpSetup: {\n configuredEditors: listOf(editor),\n detectedEditors: listOf(editor),\n skipped: boolean,\n },\n selectPackageManager: {selectedOption: oneOf('npm', 'yarn', 'pnpm', 'bun')},\n selectProjectTemplate: {},\n sendCommunityInvite: {selectedOption: yesNo},\n skillsSetup: {installedAgents: listOf(agent), skipped: boolean},\n start: {flags: sanitizeCliFlagRecord},\n useDefaultPlanCoupon: {selectedOption: yesNo},\n useDefaultPlanId: {selectedOption: yesNo},\n useDetectedFramework: {selectedOption: yesNo},\n useTypeScript: {selectedOption: yesNo},\n}\nconst eventSchemas: Record<string, {fields: Schema; versions: number[]}> = {\n 'App Build Completed': {fields: {outputSize: number}, versions: [0]},\n 'CLI Command Executed': {fields: {}, versions: [1, 2]},\n 'CLI Init Step Completed': {fields: {}, versions: [1, 2]},\n 'CLI Login Step Completed': {\n fields: {step: oneOf('selectProvider', 'waitForToken')},\n versions: [1],\n },\n 'CLI MCP Configure Completed': {\n fields: {\n configuredEditors: listOf(editor),\n detectedEditors: listOf(editor),\n },\n versions: [1],\n },\n 'CLI Skills Install Completed': {\n fields: {detectedEditors: listOf(editor), installedAgents: listOf(agent)},\n versions: [1],\n },\n 'Schema Built': {fields: {extractManifest: boolean}, versions: [0]},\n 'Schema Deployed': {\n fields: {extractManifest: boolean, schemaRequired: boolean},\n versions: [1],\n },\n 'Schema Extracted': {\n fields: {\n enforceRequiredFields: boolean,\n schemaAllTypesCount: number,\n schemaDocumentTypesCount: number,\n schemaFormat: oneOf('groq-type-nodes'),\n schemaTypesCount: number,\n },\n versions: [0],\n },\n 'Schema Extraction Watch Mode': {\n fields: {\n averageExtractionDuration: number,\n enforceRequiredFields: boolean,\n extractionFailedCount: number,\n extractionSuccessfulCount: number,\n schemaFormat: oneOf('groq-type-nodes'),\n step: oneOf('started', 'stopped'),\n watcherDuration: number,\n },\n versions: [0],\n },\n 'Studio Build Completed': {fields: {outputSize: number}, versions: [0]},\n 'Typegen Resources Used': {\n fields: {\n boundSchemaCount: number,\n mode: oneOf('generate', 'watch'),\n resourceCount: number,\n resourceKeys: listOf(cliResourceKey),\n },\n versions: [1],\n },\n 'Typegen Watch Mode Started': {\n fields: {\n averageGenerationDuration: number,\n generationFailedCount: number,\n generationSuccessfulCount: number,\n step: oneOf('started', 'stopped'),\n watcherDuration: number,\n },\n versions: [0],\n },\n 'Types Generated': {\n fields: {\n configMethod: oneOf('legacy', 'cli'),\n configOverloadClientMethods: boolean,\n emptyUnionTypeNodesGenerated: number,\n filesWithErrors: number,\n outputSize: number,\n queriesCount: number,\n queryFilesCount: number,\n schemaTypesCount: number,\n typeNodesGenerated: number,\n unknownTypeNodesGenerated: number,\n unknownTypeNodesRatio: number,\n },\n versions: [0],\n },\n}\n\nfunction commandContext(input: unknown): Fields {\n const fields = record(input)\n const result = project(fields, {emptyCommand: boolean})\n if ('groupOrCommand' in fields || 'commandArguments' in fields) {\n Object.assign(result, commandIdentity(fields.groupOrCommand, fields.commandArguments))\n }\n if ('extraArguments' in fields) result.extraArguments = sanitizeCliFlags(fields.extraArguments)\n if ('coreOptions' in fields)\n result.coreOptions = project(fields.coreOptions, {\n debug: boolean,\n help: boolean,\n version: boolean,\n })\n return result\n}\n\nexport function sanitizeTelemetryEvent(event: TelemetryEvent): TelemetryEvent {\n const fields = record(event)\n const result: Fields = {\n ...project(fields, {sessionId, traceId, version: number}),\n createdAt: new Date(event.createdAt).toISOString(),\n redactionVersion: 1,\n source: 'cli',\n type:\n oneOf(\n 'log',\n 'userProperties',\n 'trace.start',\n 'trace.log',\n 'trace.complete',\n 'trace.error',\n )(fields.type) ?? 'log',\n }\n if (fields.type === 'userProperties') {\n result.properties = project(fields.properties, userProperties)\n return result as TelemetryEvent\n }\n const name = typeof fields.name === 'string' ? fields.name : ''\n const schema = Object.hasOwn(eventSchemas, name) ? eventSchemas[name] : undefined\n result.name = schema ? name : 'CLI Other Event'\n result.data = {}\n if (schema && schema.versions.includes(fields.version as number)) {\n result.data = project(fields.data, schema.fields)\n result.context = project(fields.context, schema.fields)\n if (name === 'CLI Command Executed') {\n result.context = commandContext(fields.context)\n result.data = commandContext(fields.data)\n }\n if (name === 'CLI Init Step Completed') {\n const data = record(fields.data)\n if (typeof data.step === 'string' && Object.hasOwn(initSteps, data.step)) {\n result.data = {\n step: data.step,\n ...project(data, initSteps[data.step]),\n }\n }\n }\n }\n if (fields.type === 'trace.error') {\n result.data = {\n message: 'CLI operation failed',\n name: errorName(record(fields.data).name) ?? 'Error',\n }\n }\n return result as TelemetryEvent\n}\n\nexport function telemetryError(error: unknown) {\n return {message: 'CLI operation failed', name: errorName(record(error).name) ?? 'Error'}\n}\n"],"names":["commandIdentity","sanitizeCliFlagRecord","sanitizeCliFlags","record","value","Array","isArray","boolean","undefined","number","Number","isFinite","matching","pattern","test","oneOf","values","includes","listOf","rule","map","item","filter","version","cliProjectId","cliResourceKey","sessionId","traceId","editor","agent","yesNo","errorName","project","input","schema","source","result","key","Object","entries","hasOwn","userProperties","cliVersion","cpuArchitecture","machinePlatform","projectId","runtime","runtimeVersion","initSteps","configureAppProject","selectedOption","createOrSelectDataset","visibility","createOrSelectProject","fetchJourneyConfig","isFirstProject","importTemplateDataset","login","alreadyLoggedIn","mcpSetup","configuredEditors","detectedEditors","skipped","selectPackageManager","selectProjectTemplate","sendCommunityInvite","skillsSetup","installedAgents","start","flags","useDefaultPlanCoupon","useDefaultPlanId","useDetectedFramework","useTypeScript","eventSchemas","fields","outputSize","versions","step","extractManifest","schemaRequired","enforceRequiredFields","schemaAllTypesCount","schemaDocumentTypesCount","schemaFormat","schemaTypesCount","averageExtractionDuration","extractionFailedCount","extractionSuccessfulCount","watcherDuration","boundSchemaCount","mode","resourceCount","resourceKeys","averageGenerationDuration","generationFailedCount","generationSuccessfulCount","configMethod","configOverloadClientMethods","emptyUnionTypeNodesGenerated","filesWithErrors","queriesCount","queryFilesCount","typeNodesGenerated","unknownTypeNodesGenerated","unknownTypeNodesRatio","commandContext","emptyCommand","assign","groupOrCommand","commandArguments","extraArguments","coreOptions","debug","help","sanitizeTelemetryEvent","event","createdAt","Date","toISOString","redactionVersion","type","properties","name","data","context","message","telemetryError","error"],"mappings":"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"}
@@ -1,4 +1,5 @@
1
1
  import { createTraceId } from './createTraceId.js';
2
+ import { telemetryError } from './sanitizeTelemetryEvent.js';
2
3
  import { telemetryStoreDebug } from './telemetryStoreDebug.js';
3
4
  /**
4
5
  * Creates a trace instance that can emit trace lifecycle events
@@ -93,14 +94,10 @@ import { telemetryStoreDebug } from './telemetryStoreDebug.js';
93
94
  telemetryStoreDebug('Trace %s already completed, ignoring error', traceId);
94
95
  return;
95
96
  }
96
- telemetryStoreDebug('Error in trace %s: %s', traceId, err.message);
97
+ telemetryStoreDebug('Error in trace %s', traceId);
97
98
  if (!isStarted) start();
98
- // Convert Error to serializable object
99
- const errorData = {
100
- message: err.message,
101
- name: err.name,
102
- stack: err.stack
103
- };
99
+ // Messages and stacks can repeat private input, request URLs and response bodies.
100
+ const errorData = telemetryError(err);
104
101
  emitTraceEvent('trace.error', errorData);
105
102
  isCompleted = true;
106
103
  };
@@ -1 +1 @@
1
- {"version":3,"sources":["../../../src/util/telemetry/trace.ts"],"sourcesContent":["import {\n type DefinedTelemetryTrace,\n type TelemetryEvent,\n type TelemetryLogger,\n type TelemetryTrace,\n} from '@sanity/telemetry'\n\nimport {createTraceId} from './createTraceId.js'\nimport {telemetryStoreDebug} from './telemetryStoreDebug.js'\n\n/**\n * Creates a trace instance that can emit trace lifecycle events\n * @internal\n */\nexport function createTrace<Data, Context = unknown, UserProperties = unknown>(\n definition: DefinedTelemetryTrace<Data, Context>,\n context: Context | undefined,\n sessionId: string,\n emit: (event: TelemetryEvent) => void,\n createLoggerFn: <UserProperties>(\n sessionId: string,\n emit: (event: TelemetryEvent) => void,\n ) => TelemetryLogger<UserProperties>,\n): TelemetryTrace<UserProperties, Data> {\n const traceId = createTraceId()\n telemetryStoreDebug('Creating trace %s with traceId: %s', definition.name, traceId)\n\n let isStarted = false\n let isCompleted = false\n\n const emitTraceEvent = (type: string, data?: Data | Error) => {\n if (isCompleted) return\n\n const baseEvent = {\n context: context as Context,\n createdAt: new Date().toISOString(),\n name: definition.name,\n sessionId,\n traceId,\n version: definition.version,\n }\n\n let traceEvent: TelemetryEvent\n\n switch (type) {\n case 'trace.complete': {\n traceEvent = {\n ...baseEvent,\n type: 'trace.complete',\n ...(data && {data}),\n } as TelemetryEvent\n\n break\n }\n case 'trace.error': {\n traceEvent = {\n ...baseEvent,\n data,\n type: 'trace.error',\n } as TelemetryEvent\n\n break\n }\n case 'trace.log': {\n traceEvent = {\n ...baseEvent,\n data,\n type: 'trace.log',\n } as TelemetryEvent\n\n break\n }\n case 'trace.start': {\n traceEvent = {\n ...baseEvent,\n type: 'trace.start',\n } as TelemetryEvent\n\n break\n }\n default: {\n return // Unknown type\n }\n }\n\n emit(traceEvent)\n }\n\n const start = () => {\n if (isStarted) {\n telemetryStoreDebug('Trace %s already started', traceId)\n return\n }\n telemetryStoreDebug('Starting trace %s', traceId)\n isStarted = true\n emitTraceEvent('trace.start')\n }\n\n const log = (data: Data) => {\n telemetryStoreDebug('Logging data for trace %s', traceId)\n if (!isStarted) start()\n emitTraceEvent('trace.log', data)\n }\n\n const complete = () => {\n if (isCompleted) {\n telemetryStoreDebug('Trace %s already completed', traceId)\n return\n }\n telemetryStoreDebug('Completing trace %s', traceId)\n if (!isStarted) start()\n emitTraceEvent('trace.complete')\n isCompleted = true\n }\n\n const error = (err: Error) => {\n if (isCompleted) {\n telemetryStoreDebug('Trace %s already completed, ignoring error', traceId)\n return\n }\n telemetryStoreDebug('Error in trace %s: %s', traceId, err.message)\n if (!isStarted) start()\n // Convert Error to serializable object\n const errorData = {\n message: err.message,\n name: err.name,\n stack: err.stack,\n }\n emitTraceEvent('trace.error', errorData)\n isCompleted = true\n }\n\n const awaitPromise = <P extends Promise<unknown>>(promise: P, finalData?: Data): P => {\n if (!isStarted) start()\n\n return promise\n .then((result) => {\n if (finalData !== undefined) {\n log(finalData)\n }\n complete()\n return result\n })\n .catch((err) => {\n error(err)\n throw err\n }) as P\n }\n\n const newContext = (name: string): TelemetryLogger<UserProperties> => {\n const contextEmit = (event: TelemetryEvent) => {\n // For trace events, we can add context, but for log events we need to be careful\n if (event.type.startsWith('trace.')) {\n const existingContext = (event as {context?: unknown}).context\n emit({\n ...event,\n context: {\n ...(typeof existingContext === 'object' && existingContext ? existingContext : {}),\n contextName: name,\n },\n } as TelemetryEvent)\n } else {\n // For log events, we can't add context as it's not part of TelemetryLogEvent\n // Just emit as-is\n emit(event)\n }\n }\n\n return createLoggerFn<UserProperties>(sessionId, contextEmit)\n }\n\n return {\n await: awaitPromise,\n complete,\n error,\n log,\n newContext,\n start,\n }\n}\n"],"names":["createTraceId","telemetryStoreDebug","createTrace","definition","context","sessionId","emit","createLoggerFn","traceId","name","isStarted","isCompleted","emitTraceEvent","type","data","baseEvent","createdAt","Date","toISOString","version","traceEvent","start","log","complete","error","err","message","errorData","stack","awaitPromise","promise","finalData","then","result","undefined","catch","newContext","contextEmit","event","startsWith","existingContext","contextName","await"],"mappings":"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"}
1
+ {"version":3,"sources":["../../../src/util/telemetry/trace.ts"],"sourcesContent":["import {\n type DefinedTelemetryTrace,\n type TelemetryEvent,\n type TelemetryLogger,\n type TelemetryTrace,\n} from '@sanity/telemetry'\n\nimport {createTraceId} from './createTraceId.js'\nimport {telemetryError} from './sanitizeTelemetryEvent.js'\nimport {telemetryStoreDebug} from './telemetryStoreDebug.js'\n\n/**\n * Creates a trace instance that can emit trace lifecycle events\n * @internal\n */\nexport function createTrace<Data, Context = unknown, UserProperties = unknown>(\n definition: DefinedTelemetryTrace<Data, Context>,\n context: Context | undefined,\n sessionId: string,\n emit: (event: TelemetryEvent) => void,\n createLoggerFn: <UserProperties>(\n sessionId: string,\n emit: (event: TelemetryEvent) => void,\n ) => TelemetryLogger<UserProperties>,\n): TelemetryTrace<UserProperties, Data> {\n const traceId = createTraceId()\n telemetryStoreDebug('Creating trace %s with traceId: %s', definition.name, traceId)\n\n let isStarted = false\n let isCompleted = false\n\n const emitTraceEvent = (type: string, data?: Data | Error) => {\n if (isCompleted) return\n\n const baseEvent = {\n context: context as Context,\n createdAt: new Date().toISOString(),\n name: definition.name,\n sessionId,\n traceId,\n version: definition.version,\n }\n\n let traceEvent: TelemetryEvent\n\n switch (type) {\n case 'trace.complete': {\n traceEvent = {\n ...baseEvent,\n type: 'trace.complete',\n ...(data && {data}),\n } as TelemetryEvent\n\n break\n }\n case 'trace.error': {\n traceEvent = {\n ...baseEvent,\n data,\n type: 'trace.error',\n } as TelemetryEvent\n\n break\n }\n case 'trace.log': {\n traceEvent = {\n ...baseEvent,\n data,\n type: 'trace.log',\n } as TelemetryEvent\n\n break\n }\n case 'trace.start': {\n traceEvent = {\n ...baseEvent,\n type: 'trace.start',\n } as TelemetryEvent\n\n break\n }\n default: {\n return // Unknown type\n }\n }\n\n emit(traceEvent)\n }\n\n const start = () => {\n if (isStarted) {\n telemetryStoreDebug('Trace %s already started', traceId)\n return\n }\n telemetryStoreDebug('Starting trace %s', traceId)\n isStarted = true\n emitTraceEvent('trace.start')\n }\n\n const log = (data: Data) => {\n telemetryStoreDebug('Logging data for trace %s', traceId)\n if (!isStarted) start()\n emitTraceEvent('trace.log', data)\n }\n\n const complete = () => {\n if (isCompleted) {\n telemetryStoreDebug('Trace %s already completed', traceId)\n return\n }\n telemetryStoreDebug('Completing trace %s', traceId)\n if (!isStarted) start()\n emitTraceEvent('trace.complete')\n isCompleted = true\n }\n\n const error = (err: Error) => {\n if (isCompleted) {\n telemetryStoreDebug('Trace %s already completed, ignoring error', traceId)\n return\n }\n telemetryStoreDebug('Error in trace %s', traceId)\n if (!isStarted) start()\n // Messages and stacks can repeat private input, request URLs and response bodies.\n const errorData = telemetryError(err)\n emitTraceEvent('trace.error', errorData)\n isCompleted = true\n }\n\n const awaitPromise = <P extends Promise<unknown>>(promise: P, finalData?: Data): P => {\n if (!isStarted) start()\n\n return promise\n .then((result) => {\n if (finalData !== undefined) {\n log(finalData)\n }\n complete()\n return result\n })\n .catch((err) => {\n error(err)\n throw err\n }) as P\n }\n\n const newContext = (name: string): TelemetryLogger<UserProperties> => {\n const contextEmit = (event: TelemetryEvent) => {\n // For trace events, we can add context, but for log events we need to be careful\n if (event.type.startsWith('trace.')) {\n const existingContext = (event as {context?: unknown}).context\n emit({\n ...event,\n context: {\n ...(typeof existingContext === 'object' && existingContext ? existingContext : {}),\n contextName: name,\n },\n } as TelemetryEvent)\n } else {\n // For log events, we can't add context as it's not part of TelemetryLogEvent\n // Just emit as-is\n emit(event)\n }\n }\n\n return createLoggerFn<UserProperties>(sessionId, contextEmit)\n }\n\n return {\n await: awaitPromise,\n complete,\n error,\n log,\n newContext,\n start,\n }\n}\n"],"names":["createTraceId","telemetryError","telemetryStoreDebug","createTrace","definition","context","sessionId","emit","createLoggerFn","traceId","name","isStarted","isCompleted","emitTraceEvent","type","data","baseEvent","createdAt","Date","toISOString","version","traceEvent","start","log","complete","error","err","errorData","awaitPromise","promise","finalData","then","result","undefined","catch","newContext","contextEmit","event","startsWith","existingContext","contextName","await"],"mappings":"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"}
package/oclif.config.js CHANGED
@@ -16,7 +16,7 @@ export default {
16
16
  plugins: ['@oclif/plugin-help', '@sanity/runtime-cli', '@sanity/workflow-cli'],
17
17
  topics: {
18
18
  backups: {description: 'Manage dataset backups'},
19
- context: {description: 'Manage Context knowledge bases'},
19
+ context: {description: 'Manage Context knowledge bases and MCP endpoints'},
20
20
  cors: {description: 'Manage CORS origins for your project'},
21
21
  datasets: {description: 'Manage datasets in your project'},
22
22
  docs: {description: 'Browse and search Sanity documentation'},