@sanity/cli-core 0.0.0-20260410130107

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 (155) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +3 -0
  3. package/dist/SanityCommand.js +196 -0
  4. package/dist/SanityCommand.js.map +1 -0
  5. package/dist/_exports/index.d.ts +1391 -0
  6. package/dist/_exports/index.js +39 -0
  7. package/dist/_exports/index.js.map +1 -0
  8. package/dist/_exports/package-manager.d.ts +33 -0
  9. package/dist/_exports/package-manager.js +3 -0
  10. package/dist/_exports/package-manager.js.map +1 -0
  11. package/dist/_exports/request.d.ts +79 -0
  12. package/dist/_exports/request.js +7 -0
  13. package/dist/_exports/request.js.map +1 -0
  14. package/dist/_exports/ux.d.ts +75 -0
  15. package/dist/_exports/ux.js +7 -0
  16. package/dist/_exports/ux.js.map +1 -0
  17. package/dist/config/cli/getCliConfig.js +68 -0
  18. package/dist/config/cli/getCliConfig.js.map +1 -0
  19. package/dist/config/cli/getCliConfigSync.js +51 -0
  20. package/dist/config/cli/getCliConfigSync.js.map +1 -0
  21. package/dist/config/cli/schemas.js +62 -0
  22. package/dist/config/cli/schemas.js.map +1 -0
  23. package/dist/config/cli/types/cliConfig.js +5 -0
  24. package/dist/config/cli/types/cliConfig.js.map +1 -0
  25. package/dist/config/cli/types/userViteConfig.js +5 -0
  26. package/dist/config/cli/types/userViteConfig.js.map +1 -0
  27. package/dist/config/findProjectRoot.js +62 -0
  28. package/dist/config/findProjectRoot.js.map +1 -0
  29. package/dist/config/findProjectRootSync.js +88 -0
  30. package/dist/config/findProjectRootSync.js.map +1 -0
  31. package/dist/config/studio/getStudioConfig.js +13 -0
  32. package/dist/config/studio/getStudioConfig.js.map +1 -0
  33. package/dist/config/studio/getStudioWorkspaces.js +63 -0
  34. package/dist/config/studio/getStudioWorkspaces.js.map +1 -0
  35. package/dist/config/studio/isStudioConfig.js +19 -0
  36. package/dist/config/studio/isStudioConfig.js.map +1 -0
  37. package/dist/config/studio/readStudioConfig.js +82 -0
  38. package/dist/config/studio/readStudioConfig.js.map +1 -0
  39. package/dist/config/studio/readStudioConfig.worker.js +25 -0
  40. package/dist/config/studio/readStudioConfig.worker.js.map +1 -0
  41. package/dist/config/util/configPathsSync.js +85 -0
  42. package/dist/config/util/configPathsSync.js.map +1 -0
  43. package/dist/config/util/findAppConfigPath.js +22 -0
  44. package/dist/config/util/findAppConfigPath.js.map +1 -0
  45. package/dist/config/util/findConfigsPaths.js +21 -0
  46. package/dist/config/util/findConfigsPaths.js.map +1 -0
  47. package/dist/config/util/findStudioConfigPath.js +52 -0
  48. package/dist/config/util/findStudioConfigPath.js.map +1 -0
  49. package/dist/config/util/isSanityV2StudioRoot.js +22 -0
  50. package/dist/config/util/isSanityV2StudioRoot.js.map +1 -0
  51. package/dist/config/util/recursivelyResolveProjectRoot.js +28 -0
  52. package/dist/config/util/recursivelyResolveProjectRoot.js.map +1 -0
  53. package/dist/debug.js +15 -0
  54. package/dist/debug.js.map +1 -0
  55. package/dist/errors/NonInteractiveError.js +18 -0
  56. package/dist/errors/NonInteractiveError.js.map +1 -0
  57. package/dist/errors/NotFoundError.js +27 -0
  58. package/dist/errors/NotFoundError.js.map +1 -0
  59. package/dist/errors/ProjectRootNotFoundError.js +35 -0
  60. package/dist/errors/ProjectRootNotFoundError.js.map +1 -0
  61. package/dist/exitCodes.js +17 -0
  62. package/dist/exitCodes.js.map +1 -0
  63. package/dist/loaders/studio/studioWorkerLoader.worker.js +205 -0
  64. package/dist/loaders/studio/studioWorkerLoader.worker.js.map +1 -0
  65. package/dist/loaders/studio/studioWorkerTask.js +90 -0
  66. package/dist/loaders/studio/studioWorkerTask.js.map +1 -0
  67. package/dist/loaders/tsx/tsxWorkerLoader.worker.js +11 -0
  68. package/dist/loaders/tsx/tsxWorkerLoader.worker.js.map +1 -0
  69. package/dist/loaders/tsx/tsxWorkerTask.js +34 -0
  70. package/dist/loaders/tsx/tsxWorkerTask.js.map +1 -0
  71. package/dist/request/createRequester.js +83 -0
  72. package/dist/request/createRequester.js.map +1 -0
  73. package/dist/services/apiClient.js +97 -0
  74. package/dist/services/apiClient.js.map +1 -0
  75. package/dist/services/cliTokenCache.js +25 -0
  76. package/dist/services/cliTokenCache.js.map +1 -0
  77. package/dist/services/cliUserConfig.js +144 -0
  78. package/dist/services/cliUserConfig.js.map +1 -0
  79. package/dist/services/getCliToken.js +26 -0
  80. package/dist/services/getCliToken.js.map +1 -0
  81. package/dist/telemetry/getCliTelemetry.js +47 -0
  82. package/dist/telemetry/getCliTelemetry.js.map +1 -0
  83. package/dist/telemetry/getTelemetryBaseInfo.js +33 -0
  84. package/dist/telemetry/getTelemetryBaseInfo.js.map +1 -0
  85. package/dist/telemetry/readNDJSON.js +18 -0
  86. package/dist/telemetry/readNDJSON.js.map +1 -0
  87. package/dist/telemetry/types.js +5 -0
  88. package/dist/telemetry/types.js.map +1 -0
  89. package/dist/types.js +5 -0
  90. package/dist/types.js.map +1 -0
  91. package/dist/util/doImport.js +17 -0
  92. package/dist/util/doImport.js.map +1 -0
  93. package/dist/util/environment/getStudioEnvironmentVariables.js +36 -0
  94. package/dist/util/environment/getStudioEnvironmentVariables.js.map +1 -0
  95. package/dist/util/environment/mockBrowserEnvironment.js +47 -0
  96. package/dist/util/environment/mockBrowserEnvironment.js.map +1 -0
  97. package/dist/util/environment/setupBrowserStubs.js +42 -0
  98. package/dist/util/environment/setupBrowserStubs.js.map +1 -0
  99. package/dist/util/environment/stubs.js +142 -0
  100. package/dist/util/environment/stubs.js.map +1 -0
  101. package/dist/util/fileExists.js +13 -0
  102. package/dist/util/fileExists.js.map +1 -0
  103. package/dist/util/generateHelpUrl.js +11 -0
  104. package/dist/util/generateHelpUrl.js.map +1 -0
  105. package/dist/util/getEmptyAuth.js +16 -0
  106. package/dist/util/getEmptyAuth.js.map +1 -0
  107. package/dist/util/getSanityEnvVar.js +24 -0
  108. package/dist/util/getSanityEnvVar.js.map +1 -0
  109. package/dist/util/getSanityUrl.js +9 -0
  110. package/dist/util/getSanityUrl.js.map +1 -0
  111. package/dist/util/importModule.js +32 -0
  112. package/dist/util/importModule.js.map +1 -0
  113. package/dist/util/isCi.js +7 -0
  114. package/dist/util/isCi.js.map +1 -0
  115. package/dist/util/isInteractive.js +5 -0
  116. package/dist/util/isInteractive.js.map +1 -0
  117. package/dist/util/isRecord.js +11 -0
  118. package/dist/util/isRecord.js.map +1 -0
  119. package/dist/util/isStaging.js +10 -0
  120. package/dist/util/isStaging.js.map +1 -0
  121. package/dist/util/isTrueish.js +10 -0
  122. package/dist/util/isTrueish.js.map +1 -0
  123. package/dist/util/normalizePath.js +12 -0
  124. package/dist/util/normalizePath.js.map +1 -0
  125. package/dist/util/packageManager.js +55 -0
  126. package/dist/util/packageManager.js.map +1 -0
  127. package/dist/util/promisifyWorker.js +72 -0
  128. package/dist/util/promisifyWorker.js.map +1 -0
  129. package/dist/util/readJsonFile.js +26 -0
  130. package/dist/util/readJsonFile.js.map +1 -0
  131. package/dist/util/readJsonFileSync.js +26 -0
  132. package/dist/util/readJsonFileSync.js.map +1 -0
  133. package/dist/util/readPackageJson.js +74 -0
  134. package/dist/util/readPackageJson.js.map +1 -0
  135. package/dist/util/resolveLocalPackage.js +82 -0
  136. package/dist/util/resolveLocalPackage.js.map +1 -0
  137. package/dist/util/safeStructuredClone.js +43 -0
  138. package/dist/util/safeStructuredClone.js.map +1 -0
  139. package/dist/util/tryGetDefaultExport.js +18 -0
  140. package/dist/util/tryGetDefaultExport.js.map +1 -0
  141. package/dist/util/writeJsonFileSync.js +19 -0
  142. package/dist/util/writeJsonFileSync.js.map +1 -0
  143. package/dist/ux/boxen.js +3 -0
  144. package/dist/ux/boxen.js.map +1 -0
  145. package/dist/ux/colorizeJson.js +32 -0
  146. package/dist/ux/colorizeJson.js.map +1 -0
  147. package/dist/ux/logSymbols.js +3 -0
  148. package/dist/ux/logSymbols.js.map +1 -0
  149. package/dist/ux/prompts.js +51 -0
  150. package/dist/ux/prompts.js.map +1 -0
  151. package/dist/ux/spinner.js +3 -0
  152. package/dist/ux/spinner.js.map +1 -0
  153. package/dist/ux/timer.js +29 -0
  154. package/dist/ux/timer.js.map +1 -0
  155. package/package.json +111 -0
@@ -0,0 +1,83 @@
1
+ import { getIt } from 'get-it';
2
+ import { debug, headers, httpErrors, promise } from 'get-it/middleware';
3
+ import { readPackageUpSync } from 'read-package-up';
4
+ let cachedPkg;
5
+ /**
6
+ * Creates a `get-it` requester with a standard set of middleware.
7
+ *
8
+ * Default middleware (in order):
9
+ * 1. `httpErrors()` — throw on HTTP error status codes
10
+ * 2. `headers({'User-Agent': '@sanity/cli-core@<version>'})` — identify CLI requests
11
+ * 3. `debug({verbose: true, namespace: 'sanity:cli'})` — debug logging
12
+ * 4. `promise({onlyBody: true})` — return body directly (must be last)
13
+ *
14
+ * @param options - Optional configuration to disable or customize middleware
15
+ * @returns A configured `get-it` requester
16
+ * @public
17
+ */ export function createRequester(options) {
18
+ const opts = options?.middleware;
19
+ const middleware = [];
20
+ // 1. httpErrors
21
+ if (opts?.httpErrors !== false) {
22
+ middleware.push(httpErrors());
23
+ }
24
+ // 2. headers
25
+ if (opts?.headers !== false) {
26
+ const customHeaders = typeof opts?.headers === 'object' ? opts.headers : {};
27
+ const allHeaders = customHeaders['User-Agent'] ? {
28
+ ...customHeaders
29
+ } : {
30
+ get ['User-Agent'] () {
31
+ const pkg = getPackageInfo();
32
+ return `${pkg.name}@${pkg.version}`;
33
+ },
34
+ ...customHeaders
35
+ };
36
+ middleware.push(headers(allHeaders));
37
+ }
38
+ // 3. debug
39
+ if (opts?.debug !== false) {
40
+ const debugDefaults = {
41
+ namespace: 'sanity:cli',
42
+ verbose: true
43
+ };
44
+ const customDebug = typeof opts?.debug === 'object' ? opts.debug : {};
45
+ middleware.push(debug({
46
+ ...debugDefaults,
47
+ ...customDebug
48
+ }));
49
+ }
50
+ // 4. promise (must be last)
51
+ if (opts?.promise !== false) {
52
+ const promiseDefaults = {
53
+ onlyBody: true
54
+ };
55
+ const customPromise = typeof opts?.promise === 'object' ? opts.promise : {};
56
+ middleware.push(promise({
57
+ ...promiseDefaults,
58
+ ...customPromise
59
+ }));
60
+ }
61
+ return getIt(middleware);
62
+ }
63
+ /**
64
+ * Reads the nearest `package.json` to determine the name and version of the `@sanity/cli-core` package.
65
+ *
66
+ * @returns The name and version of the package
67
+ * @internal
68
+ */ function getPackageInfo() {
69
+ if (cachedPkg) return cachedPkg;
70
+ const result = readPackageUpSync({
71
+ cwd: import.meta.dirname
72
+ });
73
+ if (!result) {
74
+ throw new Error('Unable to resolve @sanity/cli-core package root');
75
+ }
76
+ cachedPkg = {
77
+ name: result.packageJson.name ?? '@sanity/cli-core',
78
+ version: result.packageJson.version ?? '0.0.0'
79
+ };
80
+ return cachedPkg;
81
+ }
82
+
83
+ //# sourceMappingURL=createRequester.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../src/request/createRequester.ts"],"sourcesContent":["import {getIt, type Requester} from 'get-it'\nimport {debug, headers, httpErrors, promise} from 'get-it/middleware'\nimport {readPackageUpSync} from 'read-package-up'\n\nlet cachedPkg: {name: string; version: string} | undefined\n\n/**\n * Options for configuring individual middleware in {@link createRequester}.\n *\n * Each key corresponds to a middleware. Omitting a key uses the default,\n * `false` disables it, and an object merges with the defaults.\n *\n * @public\n */\nexport type MiddlewareOptions = {\n debug?: false | {namespace?: string; verbose?: boolean}\n headers?: false | Record<string, string>\n httpErrors?: false\n promise?: false | {onlyBody?: boolean}\n}\n\n/**\n * Creates a `get-it` requester with a standard set of middleware.\n *\n * Default middleware (in order):\n * 1. `httpErrors()` — throw on HTTP error status codes\n * 2. `headers({'User-Agent': '@sanity/cli-core@<version>'})` — identify CLI requests\n * 3. `debug({verbose: true, namespace: 'sanity:cli'})` — debug logging\n * 4. `promise({onlyBody: true})` — return body directly (must be last)\n *\n * @param options - Optional configuration to disable or customize middleware\n * @returns A configured `get-it` requester\n * @public\n */\nexport function createRequester(options?: {middleware?: MiddlewareOptions}): Requester {\n const opts = options?.middleware\n\n const middleware = []\n\n // 1. httpErrors\n if (opts?.httpErrors !== false) {\n middleware.push(httpErrors())\n }\n\n // 2. headers\n if (opts?.headers !== false) {\n const customHeaders = typeof opts?.headers === 'object' ? opts.headers : {}\n const allHeaders = customHeaders['User-Agent']\n ? {...customHeaders}\n : {\n get ['User-Agent']() {\n const pkg = getPackageInfo()\n return `${pkg.name}@${pkg.version}`\n },\n ...customHeaders,\n }\n middleware.push(headers(allHeaders))\n }\n\n // 3. debug\n if (opts?.debug !== false) {\n const debugDefaults = {namespace: 'sanity:cli', verbose: true}\n const customDebug = typeof opts?.debug === 'object' ? opts.debug : {}\n middleware.push(debug({...debugDefaults, ...customDebug}))\n }\n\n // 4. promise (must be last)\n if (opts?.promise !== false) {\n const promiseDefaults = {onlyBody: true}\n const customPromise = typeof opts?.promise === 'object' ? opts.promise : {}\n middleware.push(promise({...promiseDefaults, ...customPromise}))\n }\n\n return getIt(middleware)\n}\n\n/**\n * Reads the nearest `package.json` to determine the name and version of the `@sanity/cli-core` package.\n *\n * @returns The name and version of the package\n * @internal\n */\nfunction getPackageInfo(): {name: string; version: string} {\n if (cachedPkg) return cachedPkg\n\n const result = readPackageUpSync({cwd: import.meta.dirname})\n if (!result) {\n throw new Error('Unable to resolve @sanity/cli-core package root')\n }\n\n cachedPkg = {\n name: result.packageJson.name ?? '@sanity/cli-core',\n version: result.packageJson.version ?? '0.0.0',\n }\n return cachedPkg\n}\n"],"names":["getIt","debug","headers","httpErrors","promise","readPackageUpSync","cachedPkg","createRequester","options","opts","middleware","push","customHeaders","allHeaders","pkg","getPackageInfo","name","version","debugDefaults","namespace","verbose","customDebug","promiseDefaults","onlyBody","customPromise","result","cwd","dirname","Error","packageJson"],"mappings":"AAAA,SAAQA,KAAK,QAAuB,SAAQ;AAC5C,SAAQC,KAAK,EAAEC,OAAO,EAAEC,UAAU,EAAEC,OAAO,QAAO,oBAAmB;AACrE,SAAQC,iBAAiB,QAAO,kBAAiB;AAEjD,IAAIC;AAiBJ;;;;;;;;;;;;CAYC,GACD,OAAO,SAASC,gBAAgBC,OAA0C;IACxE,MAAMC,OAAOD,SAASE;IAEtB,MAAMA,aAAa,EAAE;IAErB,gBAAgB;IAChB,IAAID,MAAMN,eAAe,OAAO;QAC9BO,WAAWC,IAAI,CAACR;IAClB;IAEA,aAAa;IACb,IAAIM,MAAMP,YAAY,OAAO;QAC3B,MAAMU,gBAAgB,OAAOH,MAAMP,YAAY,WAAWO,KAAKP,OAAO,GAAG,CAAC;QAC1E,MAAMW,aAAaD,aAAa,CAAC,aAAa,GAC1C;YAAC,GAAGA,aAAa;QAAA,IACjB;YACE,IAAI,CAAC,aAAa,IAAG;gBACnB,MAAME,MAAMC;gBACZ,OAAO,GAAGD,IAAIE,IAAI,CAAC,CAAC,EAAEF,IAAIG,OAAO,EAAE;YACrC;YACA,GAAGL,aAAa;QAClB;QACJF,WAAWC,IAAI,CAACT,QAAQW;IAC1B;IAEA,WAAW;IACX,IAAIJ,MAAMR,UAAU,OAAO;QACzB,MAAMiB,gBAAgB;YAACC,WAAW;YAAcC,SAAS;QAAI;QAC7D,MAAMC,cAAc,OAAOZ,MAAMR,UAAU,WAAWQ,KAAKR,KAAK,GAAG,CAAC;QACpES,WAAWC,IAAI,CAACV,MAAM;YAAC,GAAGiB,aAAa;YAAE,GAAGG,WAAW;QAAA;IACzD;IAEA,4BAA4B;IAC5B,IAAIZ,MAAML,YAAY,OAAO;QAC3B,MAAMkB,kBAAkB;YAACC,UAAU;QAAI;QACvC,MAAMC,gBAAgB,OAAOf,MAAML,YAAY,WAAWK,KAAKL,OAAO,GAAG,CAAC;QAC1EM,WAAWC,IAAI,CAACP,QAAQ;YAAC,GAAGkB,eAAe;YAAE,GAAGE,aAAa;QAAA;IAC/D;IAEA,OAAOxB,MAAMU;AACf;AAEA;;;;;CAKC,GACD,SAASK;IACP,IAAIT,WAAW,OAAOA;IAEtB,MAAMmB,SAASpB,kBAAkB;QAACqB,KAAK,YAAYC,OAAO;IAAA;IAC1D,IAAI,CAACF,QAAQ;QACX,MAAM,IAAIG,MAAM;IAClB;IAEAtB,YAAY;QACVU,MAAMS,OAAOI,WAAW,CAACb,IAAI,IAAI;QACjCC,SAASQ,OAAOI,WAAW,CAACZ,OAAO,IAAI;IACzC;IACA,OAAOX;AACT"}
@@ -0,0 +1,97 @@
1
+ import { styleText } from 'node:util';
2
+ import { createClient, requester as defaultRequester, isHttpError } from '@sanity/client';
3
+ import { generateHelpUrl } from '../util/generateHelpUrl.js';
4
+ import { getCliToken } from './getCliToken.js';
5
+ const apiHosts = {
6
+ staging: 'https://api.sanity.work'
7
+ };
8
+ const CLI_REQUEST_TAG_PREFIX = 'sanity.cli';
9
+ /**
10
+ * Create a "global" (unscoped) Sanity API client.
11
+ *
12
+ * @public
13
+ *
14
+ * @param options - The options to use for the client.
15
+ * @returns Promise that resolves to a configured Sanity API client.
16
+ */ export async function getGlobalCliClient({ requireUser, token: providedToken, unauthenticated, ...config }) {
17
+ const requester = defaultRequester.clone();
18
+ requester.use(authErrors());
19
+ const sanityEnv = process.env.SANITY_INTERNAL_ENV || 'production';
20
+ const apiHost = apiHosts[sanityEnv];
21
+ // Use the provided token if set, otherwise fall back to the stored CLI token (unless unauthenticated)
22
+ const token = providedToken || (unauthenticated ? undefined : await getCliToken());
23
+ // If the token is not set and requireUser is true, throw an error
24
+ if (!token && requireUser) {
25
+ throw new Error('You must login first - run "sanity login"');
26
+ }
27
+ return createClient({
28
+ ...apiHost ? {
29
+ apiHost
30
+ } : {},
31
+ // Suppress browser token warning since we mock browser environment in workers
32
+ ignoreBrowserTokenWarning: true,
33
+ requester,
34
+ requestTagPrefix: CLI_REQUEST_TAG_PREFIX,
35
+ token,
36
+ useCdn: false,
37
+ useProjectHostname: false,
38
+ ...config
39
+ });
40
+ }
41
+ /**
42
+ * Create a "project" (scoped) Sanity API client.
43
+ *
44
+ * @public
45
+ *
46
+ * @param options - The options to use for the client.
47
+ * @returns Promise that resolves to a configured Sanity API client.
48
+ */ export async function getProjectCliClient({ requireUser, token: providedToken, ...config }) {
49
+ const requester = defaultRequester.clone();
50
+ requester.use(authErrors());
51
+ const sanityEnv = process.env.SANITY_INTERNAL_ENV || 'production';
52
+ const apiHost = apiHosts[sanityEnv];
53
+ // Use the provided token if it is set, otherwise get the token from the config file
54
+ const token = providedToken || await getCliToken();
55
+ // If the token is not set and requireUser is true, throw an error
56
+ if (!token && requireUser) {
57
+ throw new Error('You must login first - run "sanity login"');
58
+ }
59
+ return createClient({
60
+ ...apiHost ? {
61
+ apiHost
62
+ } : {},
63
+ // Suppress browser token warning since we mock browser environment in workers
64
+ ignoreBrowserTokenWarning: true,
65
+ requester,
66
+ requestTagPrefix: CLI_REQUEST_TAG_PREFIX,
67
+ token,
68
+ useCdn: false,
69
+ useProjectHostname: true,
70
+ ...config
71
+ });
72
+ }
73
+ /**
74
+ * `get-it` middleware that checks for 401 authentication errors and extends the error with more
75
+ * helpful guidance on what to do next.
76
+ *
77
+ * @returns get-it middleware with `onError` handler
78
+ * @internal
79
+ */ function authErrors() {
80
+ return {
81
+ onError: (err)=>{
82
+ if (!err || !isReqResError(err)) {
83
+ return err;
84
+ }
85
+ const statusCode = isHttpError(err) && err.response.body.statusCode;
86
+ if (statusCode === 401) {
87
+ err.message = `${err.message}. You may need to login again with ${styleText('cyan', 'sanity login')}.\nFor more information, see ${generateHelpUrl('cli-errors')}.`;
88
+ }
89
+ return err;
90
+ }
91
+ };
92
+ }
93
+ function isReqResError(err) {
94
+ return Object.prototype.hasOwnProperty.call(err, 'response');
95
+ }
96
+
97
+ //# sourceMappingURL=apiClient.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../src/services/apiClient.ts"],"sourcesContent":["import {styleText} from 'node:util'\n\nimport {\n type ClientConfig,\n type ClientError,\n createClient,\n requester as defaultRequester,\n isHttpError,\n type SanityClient,\n type ServerError,\n} from '@sanity/client'\n\nimport {generateHelpUrl} from '../util/generateHelpUrl.js'\nimport {getCliToken} from './getCliToken.js'\n\nconst apiHosts: Record<string, string | undefined> = {\n staging: 'https://api.sanity.work',\n}\n\nconst CLI_REQUEST_TAG_PREFIX = 'sanity.cli'\n\n/**\n * @public\n */\nexport interface GlobalCliClientOptions extends ClientConfig {\n /**\n * The API version to use for this client.\n */\n apiVersion: string\n\n /**\n * Whether to require a user to be authenticated to use this client.\n * Default: `false`.\n * Throws an error if `true` and user is not authenticated.\n */\n requireUser?: boolean\n\n /**\n * Whether to skip reading the stored CLI token. When `true`, the client will\n * have no token unless one is explicitly provided.\n * Default: `false`.\n */\n unauthenticated?: boolean\n}\n\n/**\n * Create a \"global\" (unscoped) Sanity API client.\n *\n * @public\n *\n * @param options - The options to use for the client.\n * @returns Promise that resolves to a configured Sanity API client.\n */\nexport async function getGlobalCliClient({\n requireUser,\n token: providedToken,\n unauthenticated,\n ...config\n}: GlobalCliClientOptions): Promise<SanityClient> {\n const requester = defaultRequester.clone()\n requester.use(authErrors())\n\n const sanityEnv = process.env.SANITY_INTERNAL_ENV || 'production'\n\n const apiHost = apiHosts[sanityEnv]\n\n // Use the provided token if set, otherwise fall back to the stored CLI token (unless unauthenticated)\n const token = providedToken || (unauthenticated ? undefined : await getCliToken())\n\n // If the token is not set and requireUser is true, throw an error\n if (!token && requireUser) {\n throw new Error('You must login first - run \"sanity login\"')\n }\n\n return createClient({\n ...(apiHost ? {apiHost} : {}),\n // Suppress browser token warning since we mock browser environment in workers\n ignoreBrowserTokenWarning: true,\n requester,\n requestTagPrefix: CLI_REQUEST_TAG_PREFIX,\n token,\n useCdn: false,\n useProjectHostname: false,\n ...config,\n })\n}\n\n/**\n * @public\n */\nexport interface ProjectCliClientOptions extends ClientConfig {\n /**\n * The API version to use for this client.\n */\n apiVersion: string\n\n /**\n * The project ID to use for this client.\n */\n projectId: string\n\n /**\n * The dataset to use for this client.\n */\n dataset?: string\n\n /**\n * Whether to require a user to be authenticated to use this client.\n * Default: `false`.\n * Throws an error if `true` and user is not authenticated.\n */\n requireUser?: boolean\n}\n\n/**\n * Create a \"project\" (scoped) Sanity API client.\n *\n * @public\n *\n * @param options - The options to use for the client.\n * @returns Promise that resolves to a configured Sanity API client.\n */\nexport async function getProjectCliClient({\n requireUser,\n token: providedToken,\n ...config\n}: ProjectCliClientOptions): Promise<SanityClient> {\n const requester = defaultRequester.clone()\n requester.use(authErrors())\n\n const sanityEnv = process.env.SANITY_INTERNAL_ENV || 'production'\n\n const apiHost = apiHosts[sanityEnv]\n\n // Use the provided token if it is set, otherwise get the token from the config file\n const token = providedToken || (await getCliToken())\n\n // If the token is not set and requireUser is true, throw an error\n if (!token && requireUser) {\n throw new Error('You must login first - run \"sanity login\"')\n }\n\n return createClient({\n ...(apiHost ? {apiHost} : {}),\n // Suppress browser token warning since we mock browser environment in workers\n ignoreBrowserTokenWarning: true,\n requester,\n requestTagPrefix: CLI_REQUEST_TAG_PREFIX,\n token,\n useCdn: false,\n useProjectHostname: true,\n ...config,\n })\n}\n\n/**\n * `get-it` middleware that checks for 401 authentication errors and extends the error with more\n * helpful guidance on what to do next.\n *\n * @returns get-it middleware with `onError` handler\n * @internal\n */\nfunction authErrors() {\n return {\n onError: (err: Error | null) => {\n if (!err || !isReqResError(err)) {\n return err\n }\n\n const statusCode = isHttpError(err) && err.response.body.statusCode\n if (statusCode === 401) {\n err.message = `${err.message}. You may need to login again with ${styleText('cyan', 'sanity login')}.\\nFor more information, see ${generateHelpUrl('cli-errors')}.`\n }\n\n return err\n },\n }\n}\n\nfunction isReqResError(err: Error): err is ClientError | ServerError {\n return Object.prototype.hasOwnProperty.call(err, 'response')\n}\n"],"names":["styleText","createClient","requester","defaultRequester","isHttpError","generateHelpUrl","getCliToken","apiHosts","staging","CLI_REQUEST_TAG_PREFIX","getGlobalCliClient","requireUser","token","providedToken","unauthenticated","config","clone","use","authErrors","sanityEnv","process","env","SANITY_INTERNAL_ENV","apiHost","undefined","Error","ignoreBrowserTokenWarning","requestTagPrefix","useCdn","useProjectHostname","getProjectCliClient","onError","err","isReqResError","statusCode","response","body","message","Object","prototype","hasOwnProperty","call"],"mappings":"AAAA,SAAQA,SAAS,QAAO,YAAW;AAEnC,SAGEC,YAAY,EACZC,aAAaC,gBAAgB,EAC7BC,WAAW,QAGN,iBAAgB;AAEvB,SAAQC,eAAe,QAAO,6BAA4B;AAC1D,SAAQC,WAAW,QAAO,mBAAkB;AAE5C,MAAMC,WAA+C;IACnDC,SAAS;AACX;AAEA,MAAMC,yBAAyB;AA0B/B;;;;;;;CAOC,GACD,OAAO,eAAeC,mBAAmB,EACvCC,WAAW,EACXC,OAAOC,aAAa,EACpBC,eAAe,EACf,GAAGC,QACoB;IACvB,MAAMb,YAAYC,iBAAiBa,KAAK;IACxCd,UAAUe,GAAG,CAACC;IAEd,MAAMC,YAAYC,QAAQC,GAAG,CAACC,mBAAmB,IAAI;IAErD,MAAMC,UAAUhB,QAAQ,CAACY,UAAU;IAEnC,sGAAsG;IACtG,MAAMP,QAAQC,iBAAkBC,CAAAA,kBAAkBU,YAAY,MAAMlB,aAAY;IAEhF,kEAAkE;IAClE,IAAI,CAACM,SAASD,aAAa;QACzB,MAAM,IAAIc,MAAM;IAClB;IAEA,OAAOxB,aAAa;QAClB,GAAIsB,UAAU;YAACA;QAAO,IAAI,CAAC,CAAC;QAC5B,8EAA8E;QAC9EG,2BAA2B;QAC3BxB;QACAyB,kBAAkBlB;QAClBG;QACAgB,QAAQ;QACRC,oBAAoB;QACpB,GAAGd,MAAM;IACX;AACF;AA6BA;;;;;;;CAOC,GACD,OAAO,eAAee,oBAAoB,EACxCnB,WAAW,EACXC,OAAOC,aAAa,EACpB,GAAGE,QACqB;IACxB,MAAMb,YAAYC,iBAAiBa,KAAK;IACxCd,UAAUe,GAAG,CAACC;IAEd,MAAMC,YAAYC,QAAQC,GAAG,CAACC,mBAAmB,IAAI;IAErD,MAAMC,UAAUhB,QAAQ,CAACY,UAAU;IAEnC,oFAAoF;IACpF,MAAMP,QAAQC,iBAAkB,MAAMP;IAEtC,kEAAkE;IAClE,IAAI,CAACM,SAASD,aAAa;QACzB,MAAM,IAAIc,MAAM;IAClB;IAEA,OAAOxB,aAAa;QAClB,GAAIsB,UAAU;YAACA;QAAO,IAAI,CAAC,CAAC;QAC5B,8EAA8E;QAC9EG,2BAA2B;QAC3BxB;QACAyB,kBAAkBlB;QAClBG;QACAgB,QAAQ;QACRC,oBAAoB;QACpB,GAAGd,MAAM;IACX;AACF;AAEA;;;;;;CAMC,GACD,SAASG;IACP,OAAO;QACLa,SAAS,CAACC;YACR,IAAI,CAACA,OAAO,CAACC,cAAcD,MAAM;gBAC/B,OAAOA;YACT;YAEA,MAAME,aAAa9B,YAAY4B,QAAQA,IAAIG,QAAQ,CAACC,IAAI,CAACF,UAAU;YACnE,IAAIA,eAAe,KAAK;gBACtBF,IAAIK,OAAO,GAAG,GAAGL,IAAIK,OAAO,CAAC,mCAAmC,EAAErC,UAAU,QAAQ,gBAAgB,6BAA6B,EAAEK,gBAAgB,cAAc,CAAC,CAAC;YACrK;YAEA,OAAO2B;QACT;IACF;AACF;AAEA,SAASC,cAAcD,GAAU;IAC/B,OAAOM,OAAOC,SAAS,CAACC,cAAc,CAACC,IAAI,CAACT,KAAK;AACnD"}
@@ -0,0 +1,25 @@
1
+ /**
2
+ * Module-level cache for the CLI auth token, shared between
3
+ * `getCliToken` (reads/writes) and `setCliUserConfig` (invalidates).
4
+ *
5
+ * Extracted into its own module to avoid a circular dependency
6
+ * between `cliUserConfig.ts` and `getCliToken.ts`.
7
+ */ let cachedToken;
8
+ export function getCachedToken() {
9
+ return cachedToken;
10
+ }
11
+ export function setCachedToken(token) {
12
+ cachedToken = token;
13
+ }
14
+ /**
15
+ * Clear the in-process token cache so the next `getCliToken()` call
16
+ * re-reads from disk or the environment.
17
+ *
18
+ * Called automatically by `setCliUserConfig('authToken', ...)`.
19
+ *
20
+ * @internal
21
+ */ export function clearCliTokenCache() {
22
+ cachedToken = undefined;
23
+ }
24
+
25
+ //# sourceMappingURL=cliTokenCache.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../src/services/cliTokenCache.ts"],"sourcesContent":["/**\n * Module-level cache for the CLI auth token, shared between\n * `getCliToken` (reads/writes) and `setCliUserConfig` (invalidates).\n *\n * Extracted into its own module to avoid a circular dependency\n * between `cliUserConfig.ts` and `getCliToken.ts`.\n */\nlet cachedToken: string | undefined\n\nexport function getCachedToken(): string | undefined {\n return cachedToken\n}\n\nexport function setCachedToken(token: string | undefined): void {\n cachedToken = token\n}\n\n/**\n * Clear the in-process token cache so the next `getCliToken()` call\n * re-reads from disk or the environment.\n *\n * Called automatically by `setCliUserConfig('authToken', ...)`.\n *\n * @internal\n */\nexport function clearCliTokenCache(): void {\n cachedToken = undefined\n}\n"],"names":["cachedToken","getCachedToken","setCachedToken","token","clearCliTokenCache","undefined"],"mappings":"AAAA;;;;;;CAMC,GACD,IAAIA;AAEJ,OAAO,SAASC;IACd,OAAOD;AACT;AAEA,OAAO,SAASE,eAAeC,KAAyB;IACtDH,cAAcG;AAChB;AAEA;;;;;;;CAOC,GACD,OAAO,SAASC;IACdJ,cAAcK;AAChB"}
@@ -0,0 +1,144 @@
1
+ import { mkdirSync } from 'node:fs';
2
+ import { homedir } from 'node:os';
3
+ import { dirname, join as joinPath } from 'node:path';
4
+ import { z } from 'zod/mini';
5
+ import { debug } from '../debug.js';
6
+ import { readJsonFileSync } from '../util/readJsonFileSync.js';
7
+ import { writeJsonFileSync } from '../util/writeJsonFileSync.js';
8
+ import { clearCliTokenCache } from './cliTokenCache.js';
9
+ const cliUserConfigSchema = {
10
+ authToken: z.optional(z.string())
11
+ };
12
+ /**
13
+ * Set the config value for the given property.
14
+ * Validates that the passed value adheres to the defined CLI config schema.
15
+ *
16
+ * @param prop - The property to set the value for
17
+ * @param value - The value to set
18
+ * @internal
19
+ */ export function setCliUserConfig(prop, value) {
20
+ const config = readConfig();
21
+ const result = cliUserConfigSchema[prop].safeParse(value);
22
+ if (!result.success) {
23
+ const message = result.error.issues.map(({ message, path })=>`[${path.join('.')}] ${message}`).join('\n');
24
+ throw new Error(`Invalid value for config property "${prop}": ${message}`);
25
+ }
26
+ const configPath = getCliUserConfigPath();
27
+ mkdirSync(dirname(configPath), {
28
+ recursive: true
29
+ });
30
+ // When value is undefined, explicitly delete the key rather than relying
31
+ // on JSON.stringify silently dropping undefined values.
32
+ if (value === undefined) {
33
+ const { [prop]: _, ...rest } = config;
34
+ writeJsonFileSync(configPath, rest, {
35
+ pretty: true
36
+ });
37
+ } else {
38
+ writeJsonFileSync(configPath, {
39
+ ...config,
40
+ [prop]: value
41
+ }, {
42
+ pretty: true
43
+ });
44
+ }
45
+ // Invalidate the in-process token cache so subsequent getCliToken() calls
46
+ // pick up the new value from disk.
47
+ clearCliTokenCache();
48
+ }
49
+ /**
50
+ * Get the config value for the given property
51
+ *
52
+ * @param prop - The property to get the value for
53
+ * @returns The value of the given property
54
+ * @internal
55
+ */ export function getCliUserConfig(prop) {
56
+ const config = readConfig();
57
+ const result = cliUserConfigSchema[prop].safeParse(config[prop]);
58
+ if (!result.success) {
59
+ debug('Ignoring invalid stored value for "%s", returning undefined', prop);
60
+ return undefined;
61
+ }
62
+ return result.data;
63
+ }
64
+ /**
65
+ * Get a key-value store backed by the CLI user configuration file
66
+ * (`~/.config/sanity/config.json`).
67
+ *
68
+ * Each call to `get`, `set`, or `delete` performs a full synchronous
69
+ * read-modify-write cycle. This is intentional: sync I/O prevents
70
+ * intra-process race conditions that occurred with the previous async
71
+ * (configstore-backed) implementation.
72
+ *
73
+ * Note: there is no file-level locking, so concurrent writes from
74
+ * separate CLI processes could still conflict. In practice this is
75
+ * unlikely since CLI config writes are rare and user-initiated.
76
+ *
77
+ * @returns A {@link ConfigStore} for the CLI config file
78
+ * @public
79
+ */ export function getUserConfig() {
80
+ return {
81
+ get (key) {
82
+ const config = readConfig();
83
+ return config[key];
84
+ },
85
+ set (key, value) {
86
+ const config = readConfig();
87
+ const configPath = getCliUserConfigPath();
88
+ mkdirSync(dirname(configPath), {
89
+ recursive: true
90
+ });
91
+ writeJsonFileSync(configPath, {
92
+ ...config,
93
+ [key]: value
94
+ }, {
95
+ pretty: true
96
+ });
97
+ if (key === 'authToken') clearCliTokenCache();
98
+ },
99
+ // No mkdirSync needed: if readConfig() succeeded the directory already exists.
100
+ delete (key) {
101
+ const config = readConfig();
102
+ if (!(key in config)) return;
103
+ const { [key]: _, ...rest } = config;
104
+ writeJsonFileSync(getCliUserConfigPath(), rest, {
105
+ pretty: true
106
+ });
107
+ if (key === 'authToken') clearCliTokenCache();
108
+ }
109
+ };
110
+ }
111
+ /**
112
+ * Read the whole configuration from file system. If the file does not exist,
113
+ * is corrupt, or otherwise unreadable, an empty object is returned. This is
114
+ * intentional: the config only holds recoverable data (auth tokens, telemetry
115
+ * consent) so silently resetting is preferable to blocking the user.
116
+ *
117
+ * @returns The whole CLI configuration.
118
+ * @internal
119
+ */ function readConfig() {
120
+ try {
121
+ const config = readJsonFileSync(getCliUserConfigPath());
122
+ if (!config || typeof config !== 'object' || Array.isArray(config)) {
123
+ throw new Error('Invalid config file - expected an object');
124
+ }
125
+ return config;
126
+ } catch (err) {
127
+ debug('Failed to read CLI config file: %s', err instanceof Error ? err.message : `${err}`);
128
+ return {};
129
+ }
130
+ }
131
+ /**
132
+ * Get the file system location for the CLI user configuration file.
133
+ * Takes into account the active environment (staging vs production).
134
+ * The file is located in the user's home directory under the `.config` directory.
135
+ *
136
+ * @returns The path to the CLI configuration file.
137
+ * @internal
138
+ */ function getCliUserConfigPath() {
139
+ const sanityEnvSuffix = process.env.SANITY_INTERNAL_ENV === 'staging' ? '-staging' : '';
140
+ const cliConfigPath = process.env.SANITY_CLI_CONFIG_PATH || joinPath(homedir(), '.config', `sanity${sanityEnvSuffix}`, 'config.json');
141
+ return cliConfigPath;
142
+ }
143
+
144
+ //# sourceMappingURL=cliUserConfig.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../src/services/cliUserConfig.ts"],"sourcesContent":["import {mkdirSync} from 'node:fs'\nimport {homedir} from 'node:os'\nimport {dirname, join as joinPath} from 'node:path'\n\nimport {z} from 'zod/mini'\n\nimport {debug} from '../debug.js'\nimport {readJsonFileSync} from '../util/readJsonFileSync.js'\nimport {writeJsonFileSync} from '../util/writeJsonFileSync.js'\nimport {clearCliTokenCache} from './cliTokenCache.js'\n\nconst cliUserConfigSchema = {\n authToken: z.optional(z.string()),\n}\n\n/**\n * Set the config value for the given property.\n * Validates that the passed value adheres to the defined CLI config schema.\n *\n * @param prop - The property to set the value for\n * @param value - The value to set\n * @internal\n */\nexport function setCliUserConfig(prop: 'authToken', value: string | undefined): void {\n const config = readConfig()\n const result = cliUserConfigSchema[prop].safeParse(value)\n if (!result.success) {\n const message = result.error.issues\n .map(({message, path}) => `[${path.join('.')}] ${message}`)\n .join('\\n')\n\n throw new Error(`Invalid value for config property \"${prop}\": ${message}`)\n }\n\n const configPath = getCliUserConfigPath()\n mkdirSync(dirname(configPath), {recursive: true})\n\n // When value is undefined, explicitly delete the key rather than relying\n // on JSON.stringify silently dropping undefined values.\n if (value === undefined) {\n const {[prop]: _, ...rest} = config\n writeJsonFileSync(configPath, rest, {pretty: true})\n } else {\n writeJsonFileSync(configPath, {...config, [prop]: value}, {pretty: true})\n }\n\n // Invalidate the in-process token cache so subsequent getCliToken() calls\n // pick up the new value from disk.\n clearCliTokenCache()\n}\n\n/**\n * Get the config value for the given property\n *\n * @param prop - The property to get the value for\n * @returns The value of the given property\n * @internal\n */\nexport function getCliUserConfig(prop: 'authToken'): string | undefined {\n const config = readConfig()\n const result = cliUserConfigSchema[prop].safeParse(config[prop])\n if (!result.success) {\n debug('Ignoring invalid stored value for \"%s\", returning undefined', prop)\n return undefined\n }\n\n return result.data\n}\n\n/**\n * A raw key-value store for CLI user configuration.\n * Unlike the typed `getCliUserConfig`/`setCliUserConfig`, this operates on\n * arbitrary keys without schema validation.\n *\n * @public\n */\nexport interface ConfigStore {\n /** Remove a key from the config file. */\n delete: (key: string) => void\n /** Read a value by key. Returns `undefined` if the key does not exist. */\n get: (key: string) => unknown\n /** Write a value by key, merging it into the existing config. */\n set: (key: string, value: unknown) => void\n}\n\n/**\n * Get a key-value store backed by the CLI user configuration file\n * (`~/.config/sanity/config.json`).\n *\n * Each call to `get`, `set`, or `delete` performs a full synchronous\n * read-modify-write cycle. This is intentional: sync I/O prevents\n * intra-process race conditions that occurred with the previous async\n * (configstore-backed) implementation.\n *\n * Note: there is no file-level locking, so concurrent writes from\n * separate CLI processes could still conflict. In practice this is\n * unlikely since CLI config writes are rare and user-initiated.\n *\n * @returns A {@link ConfigStore} for the CLI config file\n * @public\n */\nexport function getUserConfig(): ConfigStore {\n return {\n get(key: string): unknown {\n const config = readConfig()\n return config[key]\n },\n\n set(key: string, value: unknown): void {\n const config = readConfig()\n const configPath = getCliUserConfigPath()\n mkdirSync(dirname(configPath), {recursive: true})\n writeJsonFileSync(configPath, {...config, [key]: value}, {pretty: true})\n if (key === 'authToken') clearCliTokenCache()\n },\n\n // No mkdirSync needed: if readConfig() succeeded the directory already exists.\n delete(key: string): void {\n const config = readConfig()\n if (!(key in config)) return\n const {[key]: _, ...rest} = config\n writeJsonFileSync(getCliUserConfigPath(), rest, {pretty: true})\n if (key === 'authToken') clearCliTokenCache()\n },\n }\n}\n\n/**\n * Read the whole configuration from file system. If the file does not exist,\n * is corrupt, or otherwise unreadable, an empty object is returned. This is\n * intentional: the config only holds recoverable data (auth tokens, telemetry\n * consent) so silently resetting is preferable to blocking the user.\n *\n * @returns The whole CLI configuration.\n * @internal\n */\nfunction readConfig(): Record<string, unknown> {\n try {\n const config = readJsonFileSync(getCliUserConfigPath())\n if (!config || typeof config !== 'object' || Array.isArray(config)) {\n throw new Error('Invalid config file - expected an object')\n }\n return config\n } catch (err: unknown) {\n debug('Failed to read CLI config file: %s', err instanceof Error ? err.message : `${err}`)\n return {}\n }\n}\n\n/**\n * Get the file system location for the CLI user configuration file.\n * Takes into account the active environment (staging vs production).\n * The file is located in the user's home directory under the `.config` directory.\n *\n * @returns The path to the CLI configuration file.\n * @internal\n */\nfunction getCliUserConfigPath() {\n const sanityEnvSuffix = process.env.SANITY_INTERNAL_ENV === 'staging' ? '-staging' : ''\n const cliConfigPath =\n process.env.SANITY_CLI_CONFIG_PATH ||\n joinPath(homedir(), '.config', `sanity${sanityEnvSuffix}`, 'config.json')\n\n return cliConfigPath\n}\n"],"names":["mkdirSync","homedir","dirname","join","joinPath","z","debug","readJsonFileSync","writeJsonFileSync","clearCliTokenCache","cliUserConfigSchema","authToken","optional","string","setCliUserConfig","prop","value","config","readConfig","result","safeParse","success","message","error","issues","map","path","Error","configPath","getCliUserConfigPath","recursive","undefined","_","rest","pretty","getCliUserConfig","data","getUserConfig","get","key","set","delete","Array","isArray","err","sanityEnvSuffix","process","env","SANITY_INTERNAL_ENV","cliConfigPath","SANITY_CLI_CONFIG_PATH"],"mappings":"AAAA,SAAQA,SAAS,QAAO,UAAS;AACjC,SAAQC,OAAO,QAAO,UAAS;AAC/B,SAAQC,OAAO,EAAEC,QAAQC,QAAQ,QAAO,YAAW;AAEnD,SAAQC,CAAC,QAAO,WAAU;AAE1B,SAAQC,KAAK,QAAO,cAAa;AACjC,SAAQC,gBAAgB,QAAO,8BAA6B;AAC5D,SAAQC,iBAAiB,QAAO,+BAA8B;AAC9D,SAAQC,kBAAkB,QAAO,qBAAoB;AAErD,MAAMC,sBAAsB;IAC1BC,WAAWN,EAAEO,QAAQ,CAACP,EAAEQ,MAAM;AAChC;AAEA;;;;;;;CAOC,GACD,OAAO,SAASC,iBAAiBC,IAAiB,EAAEC,KAAyB;IAC3E,MAAMC,SAASC;IACf,MAAMC,SAAST,mBAAmB,CAACK,KAAK,CAACK,SAAS,CAACJ;IACnD,IAAI,CAACG,OAAOE,OAAO,EAAE;QACnB,MAAMC,UAAUH,OAAOI,KAAK,CAACC,MAAM,CAChCC,GAAG,CAAC,CAAC,EAACH,OAAO,EAAEI,IAAI,EAAC,GAAK,CAAC,CAAC,EAAEA,KAAKvB,IAAI,CAAC,KAAK,EAAE,EAAEmB,SAAS,EACzDnB,IAAI,CAAC;QAER,MAAM,IAAIwB,MAAM,CAAC,mCAAmC,EAAEZ,KAAK,GAAG,EAAEO,SAAS;IAC3E;IAEA,MAAMM,aAAaC;IACnB7B,UAAUE,QAAQ0B,aAAa;QAACE,WAAW;IAAI;IAE/C,yEAAyE;IACzE,wDAAwD;IACxD,IAAId,UAAUe,WAAW;QACvB,MAAM,EAAC,CAAChB,KAAK,EAAEiB,CAAC,EAAE,GAAGC,MAAK,GAAGhB;QAC7BT,kBAAkBoB,YAAYK,MAAM;YAACC,QAAQ;QAAI;IACnD,OAAO;QACL1B,kBAAkBoB,YAAY;YAAC,GAAGX,MAAM;YAAE,CAACF,KAAK,EAAEC;QAAK,GAAG;YAACkB,QAAQ;QAAI;IACzE;IAEA,0EAA0E;IAC1E,mCAAmC;IACnCzB;AACF;AAEA;;;;;;CAMC,GACD,OAAO,SAAS0B,iBAAiBpB,IAAiB;IAChD,MAAME,SAASC;IACf,MAAMC,SAAST,mBAAmB,CAACK,KAAK,CAACK,SAAS,CAACH,MAAM,CAACF,KAAK;IAC/D,IAAI,CAACI,OAAOE,OAAO,EAAE;QACnBf,MAAM,+DAA+DS;QACrE,OAAOgB;IACT;IAEA,OAAOZ,OAAOiB,IAAI;AACpB;AAkBA;;;;;;;;;;;;;;;CAeC,GACD,OAAO,SAASC;IACd,OAAO;QACLC,KAAIC,GAAW;YACb,MAAMtB,SAASC;YACf,OAAOD,MAAM,CAACsB,IAAI;QACpB;QAEAC,KAAID,GAAW,EAAEvB,KAAc;YAC7B,MAAMC,SAASC;YACf,MAAMU,aAAaC;YACnB7B,UAAUE,QAAQ0B,aAAa;gBAACE,WAAW;YAAI;YAC/CtB,kBAAkBoB,YAAY;gBAAC,GAAGX,MAAM;gBAAE,CAACsB,IAAI,EAAEvB;YAAK,GAAG;gBAACkB,QAAQ;YAAI;YACtE,IAAIK,QAAQ,aAAa9B;QAC3B;QAEA,+EAA+E;QAC/EgC,QAAOF,GAAW;YAChB,MAAMtB,SAASC;YACf,IAAI,CAAEqB,CAAAA,OAAOtB,MAAK,GAAI;YACtB,MAAM,EAAC,CAACsB,IAAI,EAAEP,CAAC,EAAE,GAAGC,MAAK,GAAGhB;YAC5BT,kBAAkBqB,wBAAwBI,MAAM;gBAACC,QAAQ;YAAI;YAC7D,IAAIK,QAAQ,aAAa9B;QAC3B;IACF;AACF;AAEA;;;;;;;;CAQC,GACD,SAASS;IACP,IAAI;QACF,MAAMD,SAASV,iBAAiBsB;QAChC,IAAI,CAACZ,UAAU,OAAOA,WAAW,YAAYyB,MAAMC,OAAO,CAAC1B,SAAS;YAClE,MAAM,IAAIU,MAAM;QAClB;QACA,OAAOV;IACT,EAAE,OAAO2B,KAAc;QACrBtC,MAAM,sCAAsCsC,eAAejB,QAAQiB,IAAItB,OAAO,GAAG,GAAGsB,KAAK;QACzF,OAAO,CAAC;IACV;AACF;AAEA;;;;;;;CAOC,GACD,SAASf;IACP,MAAMgB,kBAAkBC,QAAQC,GAAG,CAACC,mBAAmB,KAAK,YAAY,aAAa;IACrF,MAAMC,gBACJH,QAAQC,GAAG,CAACG,sBAAsB,IAClC9C,SAASH,WAAW,WAAW,CAAC,MAAM,EAAE4C,iBAAiB,EAAE;IAE7D,OAAOI;AACT"}
@@ -0,0 +1,26 @@
1
+ import { getCachedToken, setCachedToken } from './cliTokenCache.js';
2
+ import { getCliUserConfig } from './cliUserConfig.js';
3
+ // Re-export so existing consumers don't break
4
+ export { clearCliTokenCache } from './cliTokenCache.js';
5
+ /**
6
+ * Get the CLI authentication token from the environment or the config file
7
+ *
8
+ * @returns A promise that resolves to a CLI token, or undefined if no token is found
9
+ * @internal
10
+ */ export async function getCliToken() {
11
+ const cached = getCachedToken();
12
+ if (cached !== undefined) {
13
+ return cached;
14
+ }
15
+ const token = process.env.SANITY_AUTH_TOKEN;
16
+ if (token) {
17
+ const trimmed = token.trim();
18
+ setCachedToken(trimmed);
19
+ return trimmed;
20
+ }
21
+ const configToken = getCliUserConfig('authToken');
22
+ setCachedToken(configToken);
23
+ return configToken;
24
+ }
25
+
26
+ //# sourceMappingURL=getCliToken.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../src/services/getCliToken.ts"],"sourcesContent":["import {getCachedToken, setCachedToken} from './cliTokenCache.js'\nimport {getCliUserConfig} from './cliUserConfig.js'\n\n// Re-export so existing consumers don't break\nexport {clearCliTokenCache} from './cliTokenCache.js'\n\n/**\n * Get the CLI authentication token from the environment or the config file\n *\n * @returns A promise that resolves to a CLI token, or undefined if no token is found\n * @internal\n */\nexport async function getCliToken(): Promise<string | undefined> {\n const cached = getCachedToken()\n if (cached !== undefined) {\n return cached\n }\n\n const token = process.env.SANITY_AUTH_TOKEN\n if (token) {\n const trimmed = token.trim()\n setCachedToken(trimmed)\n return trimmed\n }\n\n const configToken = getCliUserConfig('authToken')\n setCachedToken(configToken)\n return configToken\n}\n"],"names":["getCachedToken","setCachedToken","getCliUserConfig","clearCliTokenCache","getCliToken","cached","undefined","token","process","env","SANITY_AUTH_TOKEN","trimmed","trim","configToken"],"mappings":"AAAA,SAAQA,cAAc,EAAEC,cAAc,QAAO,qBAAoB;AACjE,SAAQC,gBAAgB,QAAO,qBAAoB;AAEnD,8CAA8C;AAC9C,SAAQC,kBAAkB,QAAO,qBAAoB;AAErD;;;;;CAKC,GACD,OAAO,eAAeC;IACpB,MAAMC,SAASL;IACf,IAAIK,WAAWC,WAAW;QACxB,OAAOD;IACT;IAEA,MAAME,QAAQC,QAAQC,GAAG,CAACC,iBAAiB;IAC3C,IAAIH,OAAO;QACT,MAAMI,UAAUJ,MAAMK,IAAI;QAC1BX,eAAeU;QACf,OAAOA;IACT;IAEA,MAAME,cAAcX,iBAAiB;IACrCD,eAAeY;IACf,OAAOA;AACT"}
@@ -0,0 +1,47 @@
1
+ import { ux } from '@oclif/core';
2
+ import { noopLogger } from '@sanity/telemetry';
3
+ /**
4
+ * @public
5
+ * Symbol used to store CLI telemetry state on globalThis.
6
+ * Use the accessor functions instead of accessing this directly.
7
+ */ export const CLI_TELEMETRY_SYMBOL = Symbol.for('sanity.cli.telemetry');
8
+ function getState() {
9
+ return globalThis[CLI_TELEMETRY_SYMBOL];
10
+ }
11
+ /**
12
+ * @public
13
+ */ export function getCliTelemetry() {
14
+ const state = getState();
15
+ // This should never happen, but if it does, we return a noop logger to avoid errors.
16
+ if (!state) {
17
+ ux.warn('CLI telemetry not initialized, returning noop logger');
18
+ return noopLogger;
19
+ }
20
+ return state.logger;
21
+ }
22
+ /**
23
+ * Sets the global CLI telemetry state.
24
+ * @internal
25
+ */ export function setCliTelemetry(telemetry, options) {
26
+ ;
27
+ globalThis[CLI_TELEMETRY_SYMBOL] = {
28
+ logger: telemetry,
29
+ reportTraceError: options?.reportTraceError
30
+ };
31
+ }
32
+ /**
33
+ * Reports an error to the CLI command trace. Called from SanityCommand.catch()
34
+ * for real command errors (not user aborts).
35
+ * @internal
36
+ */ export function reportCliTraceError(error) {
37
+ getState()?.reportTraceError?.(error);
38
+ }
39
+ /**
40
+ * Clears the global CLI telemetry store.
41
+ * @internal
42
+ */ export function clearCliTelemetry() {
43
+ const global = globalThis;
44
+ delete global[CLI_TELEMETRY_SYMBOL];
45
+ }
46
+
47
+ //# sourceMappingURL=getCliTelemetry.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../src/telemetry/getCliTelemetry.ts"],"sourcesContent":["import {ux} from '@oclif/core'\nimport {noopLogger} from '@sanity/telemetry'\n\nimport {type CLITelemetryStore} from './types.js'\n\n/**\n * @public\n * Symbol used to store CLI telemetry state on globalThis.\n * Use the accessor functions instead of accessing this directly.\n */\nexport const CLI_TELEMETRY_SYMBOL = Symbol.for('sanity.cli.telemetry')\n\ntype TraceErrorReporter = (error: Error) => void\n\ninterface CliTelemetryState {\n logger: CLITelemetryStore\n\n reportTraceError?: TraceErrorReporter\n}\n\ntype GlobalWithTelemetry = typeof globalThis & {\n [CLI_TELEMETRY_SYMBOL]?: CliTelemetryState\n}\n\nfunction getState(): CliTelemetryState | undefined {\n return (globalThis as GlobalWithTelemetry)[CLI_TELEMETRY_SYMBOL]\n}\n\n/**\n * @public\n */\nexport function getCliTelemetry(): CLITelemetryStore {\n const state = getState()\n // This should never happen, but if it does, we return a noop logger to avoid errors.\n if (!state) {\n ux.warn('CLI telemetry not initialized, returning noop logger')\n return noopLogger\n }\n\n return state.logger\n}\n\n/**\n * Sets the global CLI telemetry state.\n * @internal\n */\nexport function setCliTelemetry(\n telemetry: CLITelemetryStore,\n options?: {reportTraceError?: TraceErrorReporter},\n): void {\n ;(globalThis as GlobalWithTelemetry)[CLI_TELEMETRY_SYMBOL] = {\n logger: telemetry,\n reportTraceError: options?.reportTraceError,\n }\n}\n\n/**\n * Reports an error to the CLI command trace. Called from SanityCommand.catch()\n * for real command errors (not user aborts).\n * @internal\n */\nexport function reportCliTraceError(error: Error): void {\n getState()?.reportTraceError?.(error)\n}\n\n/**\n * Clears the global CLI telemetry store.\n * @internal\n */\nexport function clearCliTelemetry(): void {\n const global = globalThis as GlobalWithTelemetry\n delete global[CLI_TELEMETRY_SYMBOL]\n}\n"],"names":["ux","noopLogger","CLI_TELEMETRY_SYMBOL","Symbol","for","getState","globalThis","getCliTelemetry","state","warn","logger","setCliTelemetry","telemetry","options","reportTraceError","reportCliTraceError","error","clearCliTelemetry","global"],"mappings":"AAAA,SAAQA,EAAE,QAAO,cAAa;AAC9B,SAAQC,UAAU,QAAO,oBAAmB;AAI5C;;;;CAIC,GACD,OAAO,MAAMC,uBAAuBC,OAAOC,GAAG,CAAC,wBAAuB;AActE,SAASC;IACP,OAAO,AAACC,UAAkC,CAACJ,qBAAqB;AAClE;AAEA;;CAEC,GACD,OAAO,SAASK;IACd,MAAMC,QAAQH;IACd,qFAAqF;IACrF,IAAI,CAACG,OAAO;QACVR,GAAGS,IAAI,CAAC;QACR,OAAOR;IACT;IAEA,OAAOO,MAAME,MAAM;AACrB;AAEA;;;CAGC,GACD,OAAO,SAASC,gBACdC,SAA4B,EAC5BC,OAAiD;;IAE/CP,UAAkC,CAACJ,qBAAqB,GAAG;QAC3DQ,QAAQE;QACRE,kBAAkBD,SAASC;IAC7B;AACF;AAEA;;;;CAIC,GACD,OAAO,SAASC,oBAAoBC,KAAY;IAC9CX,YAAYS,mBAAmBE;AACjC;AAEA;;;CAGC,GACD,OAAO,SAASC;IACd,MAAMC,SAASZ;IACf,OAAOY,MAAM,CAAChB,qBAAqB;AACrC"}
@@ -0,0 +1,33 @@
1
+ import { createHash } from 'node:crypto';
2
+ import { tmpdir } from 'node:os';
3
+ import { join } from 'node:path';
4
+ import { getCliToken } from '../services/getCliToken.js';
5
+ import { isStaging } from '../util/isStaging.js';
6
+ /**
7
+ * Gets the base telemetry information needed for file operations.
8
+ *
9
+ * This shared utility extracts common logic used by:
10
+ * - `generateTelemetryFilePath` - for generating session-specific file paths
11
+ * - `findTelemetryFiles` - for discovering all telemetry files via glob patterns
12
+ *
13
+ * @returns Promise resolving to base telemetry information
14
+ * @throws Error if no auth token is found
15
+ * @internal
16
+ */ export async function getTelemetryBaseInfo() {
17
+ const token = await getCliToken();
18
+ if (!token) {
19
+ throw new Error('No auth token found - user must be logged in for telemetry');
20
+ }
21
+ const hashedToken = createHash('sha256').update(token).digest('hex').slice(0, 8);
22
+ const environment = isStaging() ? 'staging' : 'production';
23
+ const directory = join(tmpdir(), '.config', 'sanity');
24
+ const basePattern = `telemetry-${hashedToken}-${environment}`;
25
+ return {
26
+ basePattern,
27
+ directory,
28
+ environment,
29
+ hashedToken
30
+ };
31
+ }
32
+
33
+ //# sourceMappingURL=getTelemetryBaseInfo.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../src/telemetry/getTelemetryBaseInfo.ts"],"sourcesContent":["import {createHash} from 'node:crypto'\nimport {tmpdir} from 'node:os'\nimport {join} from 'node:path'\n\nimport {getCliToken} from '../services/getCliToken.js'\nimport {isStaging} from '../util/isStaging.js'\n\n/**\n * Base information needed for telemetry file operations.\n * Contains common data used by both file path generation and pattern matching.\n */\ninterface TelemetryBaseInfo {\n /** Base filename pattern without sessionId suffix */\n basePattern: string\n /** Base directory where telemetry files are stored */\n directory: string\n /** Environment: 'staging' or 'production' */\n environment: string\n /** Hashed token (first 8 chars of SHA256) for privacy */\n hashedToken: string\n}\n\n/**\n * Gets the base telemetry information needed for file operations.\n *\n * This shared utility extracts common logic used by:\n * - `generateTelemetryFilePath` - for generating session-specific file paths\n * - `findTelemetryFiles` - for discovering all telemetry files via glob patterns\n *\n * @returns Promise resolving to base telemetry information\n * @throws Error if no auth token is found\n * @internal\n */\nexport async function getTelemetryBaseInfo(): Promise<TelemetryBaseInfo> {\n const token = await getCliToken()\n if (!token) {\n throw new Error('No auth token found - user must be logged in for telemetry')\n }\n\n const hashedToken = createHash('sha256').update(token).digest('hex').slice(0, 8)\n const environment = isStaging() ? 'staging' : 'production'\n const directory = join(tmpdir(), '.config', 'sanity')\n const basePattern = `telemetry-${hashedToken}-${environment}`\n\n return {\n basePattern,\n directory,\n environment,\n hashedToken,\n }\n}\n"],"names":["createHash","tmpdir","join","getCliToken","isStaging","getTelemetryBaseInfo","token","Error","hashedToken","update","digest","slice","environment","directory","basePattern"],"mappings":"AAAA,SAAQA,UAAU,QAAO,cAAa;AACtC,SAAQC,MAAM,QAAO,UAAS;AAC9B,SAAQC,IAAI,QAAO,YAAW;AAE9B,SAAQC,WAAW,QAAO,6BAA4B;AACtD,SAAQC,SAAS,QAAO,uBAAsB;AAiB9C;;;;;;;;;;CAUC,GACD,OAAO,eAAeC;IACpB,MAAMC,QAAQ,MAAMH;IACpB,IAAI,CAACG,OAAO;QACV,MAAM,IAAIC,MAAM;IAClB;IAEA,MAAMC,cAAcR,WAAW,UAAUS,MAAM,CAACH,OAAOI,MAAM,CAAC,OAAOC,KAAK,CAAC,GAAG;IAC9E,MAAMC,cAAcR,cAAc,YAAY;IAC9C,MAAMS,YAAYX,KAAKD,UAAU,WAAW;IAC5C,MAAMa,cAAc,CAAC,UAAU,EAAEN,YAAY,CAAC,EAAEI,aAAa;IAE7D,OAAO;QACLE;QACAD;QACAD;QACAJ;IACF;AACF"}
@@ -0,0 +1,18 @@
1
+ import { readFile } from 'node:fs/promises';
2
+ /**
3
+ * Reads and parses an NDJSON (newline-delimited JSON) file containing telemetry events.
4
+ *
5
+ * @param filePath - Path to the NDJSON file
6
+ * @returns Promise resolving to array of parsed telemetry events
7
+ * @throws Error if file cannot be read or contains invalid JSON
8
+ *
9
+ * @internal
10
+ */ export async function readNDJSON(filePath) {
11
+ const content = await readFile(filePath, 'utf8');
12
+ if (!content.trim()) {
13
+ return [];
14
+ }
15
+ return content.trim().split('\n').filter(Boolean).map((line)=>JSON.parse(line));
16
+ }
17
+
18
+ //# sourceMappingURL=readNDJSON.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../src/telemetry/readNDJSON.ts"],"sourcesContent":["import {readFile} from 'node:fs/promises'\n\n/**\n * Reads and parses an NDJSON (newline-delimited JSON) file containing telemetry events.\n *\n * @param filePath - Path to the NDJSON file\n * @returns Promise resolving to array of parsed telemetry events\n * @throws Error if file cannot be read or contains invalid JSON\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 return content\n .trim()\n .split('\\n')\n .filter(Boolean)\n .map((line) => JSON.parse(line) as T)\n}\n"],"names":["readFile","readNDJSON","filePath","content","trim","split","filter","Boolean","map","line","JSON","parse"],"mappings":"AAAA,SAAQA,QAAQ,QAAO,mBAAkB;AAEzC;;;;;;;;CAQC,GACD,OAAO,eAAeC,WAAcC,QAAgB;IAClD,MAAMC,UAAU,MAAMH,SAASE,UAAU;IAEzC,IAAI,CAACC,QAAQC,IAAI,IAAI;QACnB,OAAO,EAAE;IACX;IAEA,OAAOD,QACJC,IAAI,GACJC,KAAK,CAAC,MACNC,MAAM,CAACC,SACPC,GAAG,CAAC,CAACC,OAASC,KAAKC,KAAK,CAACF;AAC9B"}
@@ -0,0 +1,5 @@
1
+ /**
2
+ * @public
3
+ */ export { };
4
+
5
+ //# sourceMappingURL=types.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../src/telemetry/types.ts"],"sourcesContent":["import {type ConsentStatus, type TelemetryLogger} from '@sanity/telemetry'\n\nexport type ConsentInformation =\n | {\n reason: 'fetchError' | 'unauthenticated'\n status: Extract<ConsentStatus, 'undetermined'>\n }\n | {\n reason?: 'localOverride'\n status: Extract<ConsentStatus, 'denied'>\n }\n | {\n reason?: never\n status: Extract<ConsentStatus, 'granted'>\n }\n | {\n reason?: never\n status: Extract<ConsentStatus, 'unset'>\n }\n\n/**\n * @public\n */\nexport interface TelemetryUserProperties {\n cliVersion: string\n cpuArchitecture: string\n machinePlatform: string\n runtime: string\n runtimeVersion: string\n\n dataset?: string\n projectId?: string\n}\n\n/**\n * @public\n */\nexport type CLITelemetryStore = TelemetryLogger<TelemetryUserProperties>\n"],"names":[],"mappings":"AAkCA;;CAEC,GACD,WAAwE"}
package/dist/types.js ADDED
@@ -0,0 +1,5 @@
1
+ // @todo
2
+ // Replace with SanityUser type from client once implemented
3
+ export { };
4
+
5
+ //# sourceMappingURL=types.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/types.ts"],"sourcesContent":["import {type Command} from '@oclif/core'\n\nexport interface Output {\n error: Command['error']\n log: Command['log']\n warn: Command['warn']\n}\n\nexport type RequireProps<T, K extends keyof T> = Omit<T, K> & Required<Pick<T, K>>\n\n// @todo\n// Replace with SanityUser type from client once implemented\nexport type SanityOrgUser = {\n email: string\n id: string\n name: string\n profileImage?: string\n provider: 'github' | 'google' | 'sanity' | `saml-${string}`\n tosAcceptedAt?: string\n}\n"],"names":[],"mappings":"AAUA,QAAQ;AACR,4DAA4D;AAC5D,WAOC"}
@@ -0,0 +1,17 @@
1
+ // Only file that should be using dynamic import
2
+ import { pathToFileURL } from 'node:url';
3
+ /**
4
+ * This function is a replacement for built in dynamic import
5
+ * This handles the case for windows file paths especially for absolute paths.
6
+ *
7
+ * @param source - File path
8
+ */ export function doImport(source) {
9
+ // Absolute paths in windows are not valid URLs and are not supported by import().
10
+ // We need to convert the path to a file URL.
11
+ // See: https://github.com/nodejs/node/issues/31710
12
+ const url = /^file:\/\//.test(source) ? source : pathToFileURL(source).href;
13
+ // eslint-disable-next-line no-restricted-syntax
14
+ return import(/* @vite-ignore */ url);
15
+ }
16
+
17
+ //# sourceMappingURL=doImport.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../src/util/doImport.ts"],"sourcesContent":["// Only file that should be using dynamic import\nimport {pathToFileURL} from 'node:url'\n\n/**\n * This function is a replacement for built in dynamic import\n * This handles the case for windows file paths especially for absolute paths.\n *\n * @param source - File path\n */\nexport function doImport(source: string) {\n // Absolute paths in windows are not valid URLs and are not supported by import().\n // We need to convert the path to a file URL.\n // See: https://github.com/nodejs/node/issues/31710\n const url = /^file:\\/\\//.test(source) ? source : pathToFileURL(source).href\n // eslint-disable-next-line no-restricted-syntax\n return import(\n /* @vite-ignore */\n url\n )\n}\n"],"names":["pathToFileURL","doImport","source","url","test","href"],"mappings":"AAAA,gDAAgD;AAChD,SAAQA,aAAa,QAAO,WAAU;AAEtC;;;;;CAKC,GACD,OAAO,SAASC,SAASC,MAAc;IACrC,kFAAkF;IAClF,6CAA6C;IAC7C,mDAAmD;IACnD,MAAMC,MAAM,aAAaC,IAAI,CAACF,UAAUA,SAASF,cAAcE,QAAQG,IAAI;IAC3E,gDAAgD;IAChD,OAAO,MAAM,CACX,gBAAgB,GAChBF;AAEJ"}