@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.
- package/README.md +3 -2
- package/dist/actions/build/shouldAutoUpdate.js +1 -26
- package/dist/actions/build/shouldAutoUpdate.js.map +1 -1
- package/dist/actions/context/mcpEndpoint.js +165 -0
- package/dist/actions/context/mcpEndpoint.js.map +1 -0
- package/dist/actions/context/resolveOrganizationId.js +8 -1
- package/dist/actions/context/resolveOrganizationId.js.map +1 -1
- package/dist/actions/deploy/deployApp.js +10 -3
- package/dist/actions/deploy/deployApp.js.map +1 -1
- package/dist/actions/deploy/deployStudio.js +11 -4
- package/dist/actions/deploy/deployStudio.js.map +1 -1
- package/dist/actions/init/initAction.js +3 -3
- package/dist/actions/init/initAction.js.map +1 -1
- package/dist/actions/init/plan/verifyCoupon.js +0 -1
- package/dist/actions/init/plan/verifyCoupon.js.map +1 -1
- package/dist/actions/init/scaffoldTemplate.js +1 -1
- package/dist/actions/init/scaffoldTemplate.js.map +1 -1
- package/dist/commands/api.js +10 -0
- package/dist/commands/api.js.map +1 -1
- package/dist/commands/backups/download.js +6 -0
- package/dist/commands/backups/download.js.map +1 -1
- package/dist/commands/context/create.js +8 -22
- package/dist/commands/context/create.js.map +1 -1
- package/dist/commands/context/delete.js +8 -1
- package/dist/commands/context/delete.js.map +1 -1
- package/dist/commands/context/issues/list.js +0 -5
- package/dist/commands/context/issues/list.js.map +1 -1
- package/dist/commands/context/list.js +6 -14
- package/dist/commands/context/list.js.map +1 -1
- package/dist/commands/context/mcp/create.js +152 -0
- package/dist/commands/context/mcp/create.js.map +1 -0
- package/dist/commands/context/mcp/delete.js +82 -0
- package/dist/commands/context/mcp/delete.js.map +1 -0
- package/dist/commands/context/mcp/get.js +42 -0
- package/dist/commands/context/mcp/get.js.map +1 -0
- package/dist/commands/context/mcp/list.js +87 -0
- package/dist/commands/context/mcp/list.js.map +1 -0
- package/dist/commands/context/mcp/status.js +98 -0
- package/dist/commands/context/mcp/status.js.map +1 -0
- package/dist/commands/context/mcp/update.js +225 -0
- package/dist/commands/context/mcp/update.js.map +1 -0
- package/dist/commands/context/query.js +6 -14
- package/dist/commands/context/query.js.map +1 -1
- package/dist/commands/context/update.js +12 -1
- package/dist/commands/context/update.js.map +1 -1
- package/dist/commands/datasets/create.js +6 -0
- package/dist/commands/datasets/create.js.map +1 -1
- package/dist/commands/datasets/embeddings/enable.js +6 -0
- package/dist/commands/datasets/embeddings/enable.js.map +1 -1
- package/dist/commands/datasets/import.js +6 -0
- package/dist/commands/datasets/import.js.map +1 -1
- package/dist/commands/deploy.js +7 -0
- package/dist/commands/deploy.js.map +1 -1
- package/dist/commands/documents/validate.js +6 -0
- package/dist/commands/documents/validate.js.map +1 -1
- package/dist/commands/exec.js +4 -2
- package/dist/commands/exec.js.map +1 -1
- package/dist/commands/init.js +13 -0
- package/dist/commands/init.js.map +1 -1
- package/dist/commands/manifest/extract.js +6 -0
- package/dist/commands/manifest/extract.js.map +1 -1
- package/dist/commands/media/create-aspect.js +7 -0
- package/dist/commands/media/create-aspect.js.map +1 -1
- package/dist/commands/migrations/run.js +6 -0
- package/dist/commands/migrations/run.js.map +1 -1
- package/dist/commands/organizations/create.js +6 -0
- package/dist/commands/organizations/create.js.map +1 -1
- package/dist/commands/organizations/update.js +6 -0
- package/dist/commands/organizations/update.js.map +1 -1
- package/dist/commands/schemas/delete.js +6 -0
- package/dist/commands/schemas/delete.js.map +1 -1
- package/dist/commands/schemas/deploy.js +6 -0
- package/dist/commands/schemas/deploy.js.map +1 -1
- package/dist/commands/schemas/extract.js +28 -3
- package/dist/commands/schemas/extract.js.map +1 -1
- package/dist/commands/schemas/list.js +6 -0
- package/dist/commands/schemas/list.js.map +1 -1
- package/dist/commands/schemas/validate.js +6 -0
- package/dist/commands/schemas/validate.js.map +1 -1
- package/dist/commands/tokens/rotate.js +6 -0
- package/dist/commands/tokens/rotate.js.map +1 -1
- package/dist/commands/typegen/generate.js +25 -1
- package/dist/commands/typegen/generate.js.map +1 -1
- package/dist/exports/invokeSanityCli/commandPolicies/mcpPolicy.js +6 -0
- package/dist/exports/invokeSanityCli/commandPolicies/mcpPolicy.js.map +1 -1
- package/dist/generated/apiRoutes.js +22 -0
- package/dist/generated/apiRoutes.js.map +1 -1
- package/dist/hooks/prerun/setupTelemetry.js +2 -3
- package/dist/hooks/prerun/setupTelemetry.js.map +1 -1
- package/dist/prompts/promptForText.js +12 -0
- package/dist/prompts/promptForText.js.map +1 -0
- package/dist/services/context.js +174 -1
- package/dist/services/context.js.map +1 -1
- package/dist/services/telemetry.js +4 -2
- package/dist/services/telemetry.js.map +1 -1
- package/dist/telemetry/cli.telemetry.js +1 -1
- package/dist/telemetry/cli.telemetry.js.map +1 -1
- package/dist/telemetry/init.telemetry.js +1 -1
- package/dist/telemetry/init.telemetry.js.map +1 -1
- package/dist/telemetry/typegen.telemetry.js +12 -0
- package/dist/telemetry/typegen.telemetry.js.map +1 -0
- package/dist/util/parseArguments.js +12 -40
- package/dist/util/parseArguments.js.map +1 -1
- package/dist/util/sharedFlags.js.map +1 -1
- package/dist/util/telemetry/cliCommandIdentity.js +222 -0
- package/dist/util/telemetry/cliCommandIdentity.js.map +1 -0
- package/dist/util/telemetry/cliTelemetryFlags.js +186 -0
- package/dist/util/telemetry/cliTelemetryFlags.js.map +1 -0
- package/dist/util/telemetry/commandTelemetry.js +56 -11
- package/dist/util/telemetry/commandTelemetry.js.map +1 -1
- package/dist/util/telemetry/createTelemetryStore.js +13 -5
- package/dist/util/telemetry/createTelemetryStore.js.map +1 -1
- package/dist/util/telemetry/createTraceId.js +3 -3
- package/dist/util/telemetry/createTraceId.js.map +1 -1
- package/dist/util/telemetry/flushTelemetryFiles.js +14 -4
- package/dist/util/telemetry/flushTelemetryFiles.js.map +1 -1
- package/dist/util/telemetry/logger.js +1 -1
- package/dist/util/telemetry/logger.js.map +1 -1
- package/dist/util/telemetry/readNDJSON.js +1 -1
- package/dist/util/telemetry/readNDJSON.js.map +1 -1
- package/dist/util/telemetry/sanitizeTelemetryEvent.js +304 -0
- package/dist/util/telemetry/sanitizeTelemetryEvent.js.map +1 -0
- package/dist/util/telemetry/trace.js +4 -7
- package/dist/util/telemetry/trace.js.map +1 -1
- package/oclif.config.js +1 -1
- package/oclif.manifest.json +2380 -1526
- package/package.json +8 -11
|
@@ -1,10 +1,10 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { randomUUID } from 'node:crypto';
|
|
2
2
|
/**
|
|
3
|
-
* Creates a unique
|
|
3
|
+
* Creates a unique ID for correlating telemetry events within a trace.
|
|
4
4
|
*
|
|
5
5
|
* @internal
|
|
6
6
|
*/ export function createTraceId() {
|
|
7
|
-
return
|
|
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 {
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
97
|
+
telemetryStoreDebug('Error in trace %s', traceId);
|
|
97
98
|
if (!isStarted) start();
|
|
98
|
-
//
|
|
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
|
|
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'},
|