@sanity/cli-core 0.0.2-alpha.2 → 0.1.0-alpha.10

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 (160) hide show
  1. package/dist/SanityCommand.js +34 -3
  2. package/dist/SanityCommand.js.map +1 -1
  3. package/dist/_exports/tree.d.ts +47 -0
  4. package/dist/_exports/tree.js +3 -0
  5. package/dist/_exports/tree.js.map +1 -0
  6. package/dist/_exports/ux.d.ts +40 -0
  7. package/dist/_exports/ux.js +6 -0
  8. package/dist/_exports/ux.js.map +1 -0
  9. package/dist/config/cli/getCliConfig.worker.js +2 -1
  10. package/dist/config/cli/getCliConfig.worker.js.map +1 -1
  11. package/dist/config/cli/schemas.js +5 -2
  12. package/dist/config/cli/schemas.js.map +1 -1
  13. package/dist/config/cli/types/cliConfig.js.map +1 -1
  14. package/dist/config/findProjectRoot.js +2 -2
  15. package/dist/config/findProjectRoot.js.map +1 -1
  16. package/dist/config/studio/getStudioConfig.js +0 -3
  17. package/dist/config/studio/getStudioConfig.js.map +1 -1
  18. package/dist/config/studio/getStudioWorkspaces.js +50 -0
  19. package/dist/config/studio/getStudioWorkspaces.js.map +1 -0
  20. package/dist/config/studio/isStudioConfig.js +19 -0
  21. package/dist/config/studio/isStudioConfig.js.map +1 -0
  22. package/dist/config/studio/readStudioConfig.js +13 -9
  23. package/dist/config/studio/readStudioConfig.js.map +1 -1
  24. package/dist/config/studio/readStudioConfig.worker.js +4 -30
  25. package/dist/config/studio/readStudioConfig.worker.js.map +1 -1
  26. package/dist/config/util/findStudioConfigPath.js +24 -3
  27. package/dist/config/util/findStudioConfigPath.js.map +1 -1
  28. package/dist/config/util/recursivelyResolveProjectRoot.js.map +1 -1
  29. package/dist/index.d.ts +6027 -36
  30. package/dist/index.js +14 -3
  31. package/dist/index.js.map +1 -1
  32. package/dist/loaders/studio/studioWorkerLoader.worker.js +10 -24
  33. package/dist/loaders/studio/studioWorkerLoader.worker.js.map +1 -1
  34. package/dist/loaders/studio/studioWorkerTask.js +41 -11
  35. package/dist/loaders/studio/studioWorkerTask.js.map +1 -1
  36. package/dist/loaders/tsx/tsxWorkerTask.js +3 -0
  37. package/dist/loaders/tsx/tsxWorkerTask.js.map +1 -1
  38. package/dist/services/apiClient.js +18 -17
  39. package/dist/services/apiClient.js.map +1 -1
  40. package/dist/telemetry/cleanupOldTelemetryFiles.js +30 -0
  41. package/dist/telemetry/cleanupOldTelemetryFiles.js.map +1 -0
  42. package/dist/telemetry/createTelemetryStore.js +95 -0
  43. package/dist/telemetry/createTelemetryStore.js.map +1 -0
  44. package/dist/telemetry/createTraceId.js +10 -0
  45. package/dist/telemetry/createTraceId.js.map +1 -0
  46. package/dist/telemetry/findTelemetryFiles.js +36 -0
  47. package/dist/telemetry/findTelemetryFiles.js.map +1 -0
  48. package/dist/telemetry/flushTelemetryFiles.js +107 -0
  49. package/dist/telemetry/flushTelemetryFiles.js.map +1 -0
  50. package/dist/telemetry/generateTelemetryFilePath.js +30 -0
  51. package/dist/telemetry/generateTelemetryFilePath.js.map +1 -0
  52. package/dist/telemetry/getTelemetryBaseInfo.js +33 -0
  53. package/dist/telemetry/getTelemetryBaseInfo.js.map +1 -0
  54. package/dist/telemetry/logger.js +54 -0
  55. package/dist/telemetry/logger.js.map +1 -0
  56. package/dist/telemetry/telemetryStoreDebug.js +7 -0
  57. package/dist/telemetry/telemetryStoreDebug.js.map +1 -0
  58. package/dist/telemetry/trace.js +150 -0
  59. package/dist/telemetry/trace.js.map +1 -0
  60. package/dist/telemetry/types.js +5 -0
  61. package/dist/telemetry/types.js.map +1 -0
  62. package/dist/types.js +2 -0
  63. package/dist/types.js.map +1 -1
  64. package/dist/util/doImport.js +16 -0
  65. package/dist/util/doImport.js.map +1 -0
  66. package/dist/util/environment/getStudioEnvironmentVariables.js +2 -1
  67. package/dist/util/environment/getStudioEnvironmentVariables.js.map +1 -1
  68. package/dist/util/getCliTelemetry.js +36 -0
  69. package/dist/util/getCliTelemetry.js.map +1 -0
  70. package/dist/util/isStaging.js +10 -0
  71. package/dist/util/isStaging.js.map +1 -0
  72. package/dist/util/normalizePath.js +12 -0
  73. package/dist/util/normalizePath.js.map +1 -0
  74. package/dist/util/parseStringFlag.js +19 -0
  75. package/dist/util/parseStringFlag.js.map +1 -0
  76. package/dist/util/readNDJSON.js +18 -0
  77. package/dist/util/readNDJSON.js.map +1 -0
  78. package/dist/util/resolveLocalPackage.js +34 -0
  79. package/dist/util/resolveLocalPackage.js.map +1 -0
  80. package/dist/util/tree.js +108 -0
  81. package/dist/util/tree.js.map +1 -0
  82. package/dist/util/waitForAsync.js +5 -0
  83. package/dist/util/waitForAsync.js.map +1 -0
  84. package/dist/ux/boxen.js +3 -0
  85. package/dist/ux/boxen.js.map +1 -0
  86. package/dist/ux/colorizeJson.js +6 -6
  87. package/dist/ux/colorizeJson.js.map +1 -1
  88. package/dist/ux/prompts.js +3 -0
  89. package/dist/ux/prompts.js.map +1 -0
  90. package/dist/ux/spinner.js +1 -1
  91. package/dist/ux/spinner.js.map +1 -1
  92. package/package.json +53 -28
  93. package/dist/SanityCommand.d.ts +0 -56
  94. package/dist/config/__tests__/cliToken.test.js +0 -74
  95. package/dist/config/__tests__/cliToken.test.js.map +0 -1
  96. package/dist/config/__tests__/cliUserConfig.test.js +0 -131
  97. package/dist/config/__tests__/cliUserConfig.test.js.map +0 -1
  98. package/dist/config/__tests__/findProjectRoot.test.js +0 -159
  99. package/dist/config/__tests__/findProjectRoot.test.js.map +0 -1
  100. package/dist/config/__tests__/findProjectRootSync.test.js +0 -112
  101. package/dist/config/__tests__/findProjectRootSync.test.js.map +0 -1
  102. package/dist/config/__tests__/getCliConfigSync.test.js +0 -31
  103. package/dist/config/__tests__/getCliConfigSync.test.js.map +0 -1
  104. package/dist/config/cli/getCliConfig.d.ts +0 -16
  105. package/dist/config/cli/getCliConfig.worker.d.ts +0 -1
  106. package/dist/config/cli/getCliConfigSync.d.ts +0 -12
  107. package/dist/config/cli/schemas.d.ts +0 -255
  108. package/dist/config/cli/types/cliConfig.d.ts +0 -74
  109. package/dist/config/cli/types/userViteConfig.d.ts +0 -5
  110. package/dist/config/findProjectRoot.d.ts +0 -14
  111. package/dist/config/findProjectRootSync.d.ts +0 -27
  112. package/dist/config/studio/getStudioConfig.d.ts +0 -14
  113. package/dist/config/studio/readStudioConfig.d.ts +0 -190
  114. package/dist/config/studio/readStudioConfig.worker.d.ts +0 -1
  115. package/dist/config/util/configPathsSync.d.ts +0 -17
  116. package/dist/config/util/findAppConfigPath.d.ts +0 -8
  117. package/dist/config/util/findConfigsPaths.d.ts +0 -16
  118. package/dist/config/util/findStudioConfigPath.d.ts +0 -9
  119. package/dist/config/util/isSanityV2StudioRoot.d.ts +0 -8
  120. package/dist/config/util/recursivelyResolveProjectRoot.d.ts +0 -27
  121. package/dist/debug.d.ts +0 -15
  122. package/dist/loaders/studio/studioWorkerLoader.worker.d.ts +0 -1
  123. package/dist/loaders/studio/studioWorkerTask.d.ts +0 -40
  124. package/dist/loaders/tsx/tsxWorkerLoader.worker.d.ts +0 -1
  125. package/dist/loaders/tsx/tsxWorkerTask.d.ts +0 -28
  126. package/dist/services/apiClient.d.ts +0 -53
  127. package/dist/services/cliUserConfig.d.ts +0 -57
  128. package/dist/services/getCliToken.d.ts +0 -7
  129. package/dist/types.d.ts +0 -7
  130. package/dist/util/NotFoundError.d.ts +0 -20
  131. package/dist/util/__tests__/createExpiringConfig.test.js +0 -400
  132. package/dist/util/__tests__/createExpiringConfig.test.js.map +0 -1
  133. package/dist/util/createExpiringConfig.d.ts +0 -37
  134. package/dist/util/environment/getStudioEnvironmentVariables.d.ts +0 -12
  135. package/dist/util/environment/mockBrowserEnvironment.d.ts +0 -17
  136. package/dist/util/environment/setupBrowserStubs.d.ts +0 -10
  137. package/dist/util/environment/stubs.d.ts +0 -254
  138. package/dist/util/fileExists.d.ts +0 -9
  139. package/dist/util/generateHelpUrl.d.ts +0 -8
  140. package/dist/util/getEmptyAuth.d.ts +0 -5
  141. package/dist/util/getSanityEnvVar.d.ts +0 -19
  142. package/dist/util/getSanityUrl.d.ts +0 -5
  143. package/dist/util/getUserConfig.d.ts +0 -2
  144. package/dist/util/isCi.d.ts +0 -1
  145. package/dist/util/isHttpError.d.ts +0 -29
  146. package/dist/util/isHttpError.js +0 -18
  147. package/dist/util/isHttpError.js.map +0 -1
  148. package/dist/util/isInteractive.d.ts +0 -1
  149. package/dist/util/isRecord.d.ts +0 -8
  150. package/dist/util/isTrueish.d.ts +0 -1
  151. package/dist/util/readJsonFile.d.ts +0 -14
  152. package/dist/util/safeStructuredClone.d.ts +0 -8
  153. package/dist/util/tryGetDefaultExport.d.ts +0 -5
  154. package/dist/util/writeJsonFile.d.ts +0 -9
  155. package/dist/ux/colorizeJson.d.ts +0 -1
  156. package/dist/ux/formatObject.d.ts +0 -1
  157. package/dist/ux/logSymbols.d.ts +0 -1
  158. package/dist/ux/printKeyValue.d.ts +0 -1
  159. package/dist/ux/spinner.d.ts +0 -1
  160. package/dist/ux/timer.d.ts +0 -12
package/dist/index.js CHANGED
@@ -4,31 +4,42 @@ export { cliConfigSchema } from './config/cli/schemas.js';
4
4
  export * from './config/findProjectRoot.js';
5
5
  export * from './config/findProjectRootSync.js';
6
6
  export * from './config/studio/getStudioConfig.js';
7
+ export * from './config/studio/getStudioWorkspaces.js';
7
8
  export * from './config/util/findConfigsPaths.js';
8
9
  export * from './config/util/findStudioConfigPath.js';
9
10
  export * from './debug.js';
11
+ export * from './loaders/studio/studioWorkerTask.js';
10
12
  export * from './loaders/tsx/tsxWorkerTask.js';
11
13
  export * from './SanityCommand.js';
12
14
  export * from './services/apiClient.js';
13
15
  export * from './services/cliUserConfig.js';
14
16
  export * from './services/getCliToken.js';
17
+ export * from './telemetry/createTelemetryStore.js';
18
+ export * from './telemetry/flushTelemetryFiles.js';
19
+ export * from './telemetry/getTelemetryBaseInfo.js';
20
+ export * from './telemetry/telemetryStoreDebug.js';
15
21
  export * from './util/createExpiringConfig.js';
22
+ export { doImport } from './util/doImport.js';
16
23
  export * from './util/environment/mockBrowserEnvironment.js';
17
24
  export * from './util/fileExists.js';
25
+ export { clearCliTelemetry, CLI_TELEMETRY_SYMBOL, getCliTelemetry, setCliTelemetry } from './util/getCliTelemetry.js';
18
26
  export * from './util/getEmptyAuth.js';
19
27
  export * from './util/getSanityEnvVar.js';
20
28
  export * from './util/getSanityUrl.js';
21
29
  export * from './util/getUserConfig.js';
22
30
  export * from './util/isCi.js';
23
- export * from './util/isHttpError.js';
24
31
  export * from './util/isInteractive.js';
32
+ export * from './util/isRecord.js';
33
+ export * from './util/isStaging.js';
25
34
  export * from './util/isTrueish.js';
35
+ export * from './util/normalizePath.js';
36
+ export * from './util/parseStringFlag.js';
37
+ export * from './util/readNDJSON.js';
38
+ export * from './util/resolveLocalPackage.js';
26
39
  export * from './util/tryGetDefaultExport.js';
27
40
  export * from './ux/colorizeJson.js';
28
41
  export * from './ux/formatObject.js';
29
- export * from './ux/logSymbols.js';
30
42
  export * from './ux/printKeyValue.js';
31
- export * from './ux/spinner.js';
32
43
  export * from './ux/timer.js';
33
44
 
34
45
  //# sourceMappingURL=index.js.map
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/index.ts"],"sourcesContent":["export * from './config/cli/getCliConfig.js'\nexport * from './config/cli/getCliConfigSync.js'\nexport {cliConfigSchema} from './config/cli/schemas.js'\nexport {type CliConfig} from './config/cli/types/cliConfig.js'\nexport {type UserViteConfig} from './config/cli/types/userViteConfig.js'\nexport * from './config/findProjectRoot.js'\nexport * from './config/findProjectRootSync.js'\nexport * from './config/studio/getStudioConfig.js'\nexport * from './config/util/findConfigsPaths.js'\nexport * from './config/util/findStudioConfigPath.js'\nexport {type ProjectRootResult} from './config/util/recursivelyResolveProjectRoot.js'\nexport * from './debug.js'\nexport * from './loaders/tsx/tsxWorkerTask.js'\nexport * from './SanityCommand.js'\nexport * from './services/apiClient.js'\nexport * from './services/cliUserConfig.js'\nexport * from './services/getCliToken.js'\nexport {type Output} from './types.js'\nexport * from './util/createExpiringConfig.js'\nexport * from './util/environment/mockBrowserEnvironment.js'\nexport * from './util/fileExists.js'\nexport * from './util/getEmptyAuth.js'\nexport * from './util/getSanityEnvVar.js'\nexport * from './util/getSanityUrl.js'\nexport * from './util/getUserConfig.js'\nexport * from './util/isCi.js'\nexport * from './util/isHttpError.js'\nexport * from './util/isInteractive.js'\nexport * from './util/isTrueish.js'\nexport * from './util/tryGetDefaultExport.js'\nexport * from './ux/colorizeJson.js'\nexport * from './ux/formatObject.js'\nexport * from './ux/logSymbols.js'\nexport * from './ux/printKeyValue.js'\nexport * from './ux/spinner.js'\nexport * from './ux/timer.js'\n"],"names":["cliConfigSchema"],"mappings":"AAAA,cAAc,+BAA8B;AAC5C,cAAc,mCAAkC;AAChD,SAAQA,eAAe,QAAO,0BAAyB;AAGvD,cAAc,8BAA6B;AAC3C,cAAc,kCAAiC;AAC/C,cAAc,qCAAoC;AAClD,cAAc,oCAAmC;AACjD,cAAc,wCAAuC;AAErD,cAAc,aAAY;AAC1B,cAAc,iCAAgC;AAC9C,cAAc,qBAAoB;AAClC,cAAc,0BAAyB;AACvC,cAAc,8BAA6B;AAC3C,cAAc,4BAA2B;AAEzC,cAAc,iCAAgC;AAC9C,cAAc,+CAA8C;AAC5D,cAAc,uBAAsB;AACpC,cAAc,yBAAwB;AACtC,cAAc,4BAA2B;AACzC,cAAc,yBAAwB;AACtC,cAAc,0BAAyB;AACvC,cAAc,iBAAgB;AAC9B,cAAc,wBAAuB;AACrC,cAAc,0BAAyB;AACvC,cAAc,sBAAqB;AACnC,cAAc,gCAA+B;AAC7C,cAAc,uBAAsB;AACpC,cAAc,uBAAsB;AACpC,cAAc,qBAAoB;AAClC,cAAc,wBAAuB;AACrC,cAAc,kBAAiB;AAC/B,cAAc,gBAAe"}
1
+ {"version":3,"sources":["../src/index.ts"],"sourcesContent":["export * from './config/cli/getCliConfig.js'\nexport * from './config/cli/getCliConfigSync.js'\nexport {cliConfigSchema} from './config/cli/schemas.js'\nexport {type CliConfig} from './config/cli/types/cliConfig.js'\nexport {type UserViteConfig} from './config/cli/types/userViteConfig.js'\nexport * from './config/findProjectRoot.js'\nexport * from './config/findProjectRootSync.js'\nexport * from './config/studio/getStudioConfig.js'\nexport * from './config/studio/getStudioWorkspaces.js'\nexport * from './config/util/findConfigsPaths.js'\nexport * from './config/util/findStudioConfigPath.js'\nexport {type ProjectRootResult} from './config/util/recursivelyResolveProjectRoot.js'\nexport * from './debug.js'\nexport * from './loaders/studio/studioWorkerTask.js'\nexport * from './loaders/tsx/tsxWorkerTask.js'\nexport * from './SanityCommand.js'\nexport * from './services/apiClient.js'\nexport * from './services/cliUserConfig.js'\nexport * from './services/getCliToken.js'\nexport * from './telemetry/createTelemetryStore.js'\nexport * from './telemetry/flushTelemetryFiles.js'\nexport * from './telemetry/getTelemetryBaseInfo.js'\nexport * from './telemetry/telemetryStoreDebug.js'\nexport {\n type CLITelemetryStore,\n type ConsentInformation,\n type TelemetryUserProperties,\n} from './telemetry/types.js'\nexport {type Output, type SanityOrgUser} from './types.js'\nexport * from './util/createExpiringConfig.js'\nexport {doImport} from './util/doImport.js'\nexport * from './util/environment/mockBrowserEnvironment.js'\nexport * from './util/fileExists.js'\nexport {\n clearCliTelemetry,\n CLI_TELEMETRY_SYMBOL,\n getCliTelemetry,\n setCliTelemetry,\n} from './util/getCliTelemetry.js'\nexport * from './util/getEmptyAuth.js'\nexport * from './util/getSanityEnvVar.js'\nexport * from './util/getSanityUrl.js'\nexport * from './util/getUserConfig.js'\nexport * from './util/isCi.js'\nexport * from './util/isInteractive.js'\nexport * from './util/isRecord.js'\nexport * from './util/isStaging.js'\nexport * from './util/isTrueish.js'\nexport * from './util/normalizePath.js'\nexport * from './util/parseStringFlag.js'\nexport * from './util/readNDJSON.js'\nexport * from './util/resolveLocalPackage.js'\nexport * from './util/tryGetDefaultExport.js'\nexport * from './ux/colorizeJson.js'\nexport * from './ux/formatObject.js'\nexport * from './ux/printKeyValue.js'\nexport * from './ux/timer.js'\n"],"names":["cliConfigSchema","doImport","clearCliTelemetry","CLI_TELEMETRY_SYMBOL","getCliTelemetry","setCliTelemetry"],"mappings":"AAAA,cAAc,+BAA8B;AAC5C,cAAc,mCAAkC;AAChD,SAAQA,eAAe,QAAO,0BAAyB;AAGvD,cAAc,8BAA6B;AAC3C,cAAc,kCAAiC;AAC/C,cAAc,qCAAoC;AAClD,cAAc,yCAAwC;AACtD,cAAc,oCAAmC;AACjD,cAAc,wCAAuC;AAErD,cAAc,aAAY;AAC1B,cAAc,uCAAsC;AACpD,cAAc,iCAAgC;AAC9C,cAAc,qBAAoB;AAClC,cAAc,0BAAyB;AACvC,cAAc,8BAA6B;AAC3C,cAAc,4BAA2B;AACzC,cAAc,sCAAqC;AACnD,cAAc,qCAAoC;AAClD,cAAc,sCAAqC;AACnD,cAAc,qCAAoC;AAOlD,cAAc,iCAAgC;AAC9C,SAAQC,QAAQ,QAAO,qBAAoB;AAC3C,cAAc,+CAA8C;AAC5D,cAAc,uBAAsB;AACpC,SACEC,iBAAiB,EACjBC,oBAAoB,EACpBC,eAAe,EACfC,eAAe,QACV,4BAA2B;AAClC,cAAc,yBAAwB;AACtC,cAAc,4BAA2B;AACzC,cAAc,yBAAwB;AACtC,cAAc,0BAAyB;AACvC,cAAc,iBAAgB;AAC9B,cAAc,0BAAyB;AACvC,cAAc,qBAAoB;AAClC,cAAc,sBAAqB;AACnC,cAAc,sBAAqB;AACnC,cAAc,0BAAyB;AACvC,cAAc,4BAA2B;AACzC,cAAc,uBAAsB;AACpC,cAAc,gCAA+B;AAC7C,cAAc,gCAA+B;AAC7C,cAAc,uBAAsB;AACpC,cAAc,uBAAsB;AACpC,cAAc,wBAAuB;AACrC,cAAc,gBAAe"}
@@ -1,8 +1,5 @@
1
1
  import { isMainThread } from 'node:worker_threads';
2
- import { createServer, loadEnv, mergeConfig } from 'vite';
3
- import { ViteNodeRunner } from 'vite-node/client';
4
- import { ViteNodeServer } from 'vite-node/server';
5
- import { installSourcemapsSupport } from 'vite-node/source-map';
2
+ import { createServer, createServerModuleRunner, loadEnv, mergeConfig } from 'vite';
6
3
  import { getCliConfig } from '../../config/cli/getCliConfig.js';
7
4
  import { getStudioEnvironmentVariables } from '../../util/environment/getStudioEnvironmentVariables.js';
8
5
  import { setupBrowserStubs } from '../../util/environment/setupBrowserStubs.js';
@@ -48,6 +45,7 @@ try {
48
45
  cliConfig = await getCliConfig(rootPath);
49
46
  } catch (err) {
50
47
  if (!isNotFoundError(err)) {
48
+ // eslint-disable-next-line no-console
51
49
  console.warn('[warn] Failed to load CLI config:', err);
52
50
  }
53
51
  }
@@ -74,26 +72,14 @@ const env = loadEnv(server.config.mode, server.config.envDir, '');
74
72
  for(const key in env){
75
73
  process.env[key] ??= env[key];
76
74
  }
77
- // Now we're providing the glue that ensures node-specific loading and execution works.
78
- const node = new ViteNodeServer(server);
79
- // Should make it easier to debug any crashes in the imported code…
80
- installSourcemapsSupport({
81
- getSourceMap: (source)=>node.getSourceMap(source)
75
+ // Now we're using Vite's Module Runner (replaces vite-node in Vite 4+)
76
+ const runner = await createServerModuleRunner(server.environments.ssr, {
77
+ hmr: false,
78
+ sourcemapInterceptor: 'prepareStackTrace'
82
79
  });
83
- const runner = new ViteNodeRunner({
84
- base: server.config.base,
85
- async fetchModule (id) {
86
- return node.fetchModule(id);
87
- },
88
- resolveId (id, importer) {
89
- return node.resolveId(id, importer);
90
- },
91
- root: server.config.root
92
- });
93
- // Copied from `vite-node` - it appears that this applies the `define` config from
94
- // vite, but it also takes a surprisingly long time to execute. Not clear at this
95
- // point why this is, so we should investigate whether it's necessary or not.
96
- await runner.executeId('/@vite/env');
97
- await runner.executeId(workerScriptPath);
80
+ // Apply the `define` config from vite - imports environment variables
81
+ await runner.import('/@vite/env');
82
+ // Execute the worker script
83
+ await runner.import(workerScriptPath);
98
84
 
99
85
  //# sourceMappingURL=studioWorkerLoader.worker.js.map
@@ -1 +1 @@
1
- {"version":3,"sources":["../../../src/loaders/studio/studioWorkerLoader.worker.ts"],"sourcesContent":["import {isMainThread} from 'node:worker_threads'\n\nimport {createServer, type InlineConfig, loadEnv, mergeConfig} from 'vite'\nimport {ViteNodeRunner} from 'vite-node/client'\nimport {ViteNodeServer} from 'vite-node/server'\nimport {installSourcemapsSupport} from 'vite-node/source-map'\n\nimport {getCliConfig} from '../../config/cli/getCliConfig.js'\nimport {type CliConfig} from '../../config/cli/types/cliConfig.js'\nimport {getStudioEnvironmentVariables} from '../../util/environment/getStudioEnvironmentVariables.js'\nimport {setupBrowserStubs} from '../../util/environment/setupBrowserStubs.js'\nimport {isRecord} from '../../util/isRecord.js'\nimport {isNotFoundError} from '../../util/NotFoundError.js'\n\nif (isMainThread) {\n throw new Error('Should be child of thread, not the main thread')\n}\n\nconst rootPath = process.env.STUDIO_WORKER_STUDIO_ROOT_PATH\nif (!rootPath) {\n throw new Error('Missing `STUDIO_WORKER_STUDIO_ROOT_PATH` environment variable')\n}\n\nconst workerScriptPath = process.env.STUDIO_WORKER_TASK_FILE\nif (!workerScriptPath) {\n throw new Error('Missing `STUDIO_WORKER_TASK_FILE` environment variable')\n}\n\nawait setupBrowserStubs()\n\nconst studioEnvVars = await getStudioEnvironmentVariables(rootPath)\n\nconst defaultViteConfig: InlineConfig = {\n build: {target: 'node'},\n configFile: false, // @todo Should use `vite` prop from `sanity.cli.ts` (if any)\n // Inject environment variables as compile-time constants for Vite\n define: Object.fromEntries(\n Object.entries(studioEnvVars).map(([key, value]) => [\n `process.env.${key}`,\n JSON.stringify(value),\n ]),\n ),\n logLevel: 'error',\n optimizeDeps: {disabled: true}, // @todo is this necessary? cant remember why was added\n root: rootPath,\n server: {\n hmr: false,\n watch: null,\n },\n}\n\n// Allow the CLI config (`sanity.cli.(js|ts)`) to define a `vite` property which can\n// extend/modify the default vite configuration for the studio.\nlet cliConfig: CliConfig | undefined\ntry {\n cliConfig = await getCliConfig(rootPath)\n} catch (err) {\n if (!isNotFoundError(err)) {\n console.warn('[warn] Failed to load CLI config:', err)\n }\n}\n\nlet viteConfig = defaultViteConfig\nif (typeof cliConfig?.vite === 'function') {\n viteConfig = (await cliConfig.vite(viteConfig, {\n command: 'build',\n isSsrBuild: true,\n mode: 'production',\n })) as InlineConfig\n} else if (isRecord(cliConfig?.vite)) {\n viteConfig = mergeConfig(viteConfig, cliConfig.vite)\n}\n\n// Vite will build the files we give it - targetting Node.js instead of the browser.\n// We include the inject plugin in order to provide the stubs for the undefined global APIs.\nconst server = await createServer(viteConfig)\n\n// Bit of a hack, but seems necessary based on the `node-vite` binary implementation\nawait server.pluginContainer.buildStart({})\n\n// Load environment variables from `.env` files in the same way as Vite does.\n// Note that Sanity also provides environment variables through `process.env.*` for compat reasons,\n// and so we need to do the same here.\n// @todo is this in line with sanity?\nconst env = loadEnv(server.config.mode, server.config.envDir, '')\nfor (const key in env) {\n process.env[key] ??= env[key]\n}\n\n// Now we're providing the glue that ensures node-specific loading and execution works.\nconst node = new ViteNodeServer(server)\n\n// Should make it easier to debug any crashes in the imported code…\ninstallSourcemapsSupport({\n getSourceMap: (source) => node.getSourceMap(source),\n})\n\nconst runner = new ViteNodeRunner({\n base: server.config.base,\n async fetchModule(id) {\n return node.fetchModule(id)\n },\n resolveId(id, importer) {\n return node.resolveId(id, importer)\n },\n root: server.config.root,\n})\n\n// Copied from `vite-node` - it appears that this applies the `define` config from\n// vite, but it also takes a surprisingly long time to execute. Not clear at this\n// point why this is, so we should investigate whether it's necessary or not.\nawait runner.executeId('/@vite/env')\n\nawait runner.executeId(workerScriptPath)\n"],"names":["isMainThread","createServer","loadEnv","mergeConfig","ViteNodeRunner","ViteNodeServer","installSourcemapsSupport","getCliConfig","getStudioEnvironmentVariables","setupBrowserStubs","isRecord","isNotFoundError","Error","rootPath","process","env","STUDIO_WORKER_STUDIO_ROOT_PATH","workerScriptPath","STUDIO_WORKER_TASK_FILE","studioEnvVars","defaultViteConfig","build","target","configFile","define","Object","fromEntries","entries","map","key","value","JSON","stringify","logLevel","optimizeDeps","disabled","root","server","hmr","watch","cliConfig","err","console","warn","viteConfig","vite","command","isSsrBuild","mode","pluginContainer","buildStart","config","envDir","node","getSourceMap","source","runner","base","fetchModule","id","resolveId","importer","executeId"],"mappings":"AAAA,SAAQA,YAAY,QAAO,sBAAqB;AAEhD,SAAQC,YAAY,EAAqBC,OAAO,EAAEC,WAAW,QAAO,OAAM;AAC1E,SAAQC,cAAc,QAAO,mBAAkB;AAC/C,SAAQC,cAAc,QAAO,mBAAkB;AAC/C,SAAQC,wBAAwB,QAAO,uBAAsB;AAE7D,SAAQC,YAAY,QAAO,mCAAkC;AAE7D,SAAQC,6BAA6B,QAAO,0DAAyD;AACrG,SAAQC,iBAAiB,QAAO,8CAA6C;AAC7E,SAAQC,QAAQ,QAAO,yBAAwB;AAC/C,SAAQC,eAAe,QAAO,8BAA6B;AAE3D,IAAIX,cAAc;IAChB,MAAM,IAAIY,MAAM;AAClB;AAEA,MAAMC,WAAWC,QAAQC,GAAG,CAACC,8BAA8B;AAC3D,IAAI,CAACH,UAAU;IACb,MAAM,IAAID,MAAM;AAClB;AAEA,MAAMK,mBAAmBH,QAAQC,GAAG,CAACG,uBAAuB;AAC5D,IAAI,CAACD,kBAAkB;IACrB,MAAM,IAAIL,MAAM;AAClB;AAEA,MAAMH;AAEN,MAAMU,gBAAgB,MAAMX,8BAA8BK;AAE1D,MAAMO,oBAAkC;IACtCC,OAAO;QAACC,QAAQ;IAAM;IACtBC,YAAY;IACZ,kEAAkE;IAClEC,QAAQC,OAAOC,WAAW,CACxBD,OAAOE,OAAO,CAACR,eAAeS,GAAG,CAAC,CAAC,CAACC,KAAKC,MAAM,GAAK;YAClD,CAAC,YAAY,EAAED,KAAK;YACpBE,KAAKC,SAAS,CAACF;SAChB;IAEHG,UAAU;IACVC,cAAc;QAACC,UAAU;IAAI;IAC7BC,MAAMvB;IACNwB,QAAQ;QACNC,KAAK;QACLC,OAAO;IACT;AACF;AAEA,oFAAoF;AACpF,+DAA+D;AAC/D,IAAIC;AACJ,IAAI;IACFA,YAAY,MAAMjC,aAAaM;AACjC,EAAE,OAAO4B,KAAK;IACZ,IAAI,CAAC9B,gBAAgB8B,MAAM;QACzBC,QAAQC,IAAI,CAAC,qCAAqCF;IACpD;AACF;AAEA,IAAIG,aAAaxB;AACjB,IAAI,OAAOoB,WAAWK,SAAS,YAAY;IACzCD,aAAc,MAAMJ,UAAUK,IAAI,CAACD,YAAY;QAC7CE,SAAS;QACTC,YAAY;QACZC,MAAM;IACR;AACF,OAAO,IAAItC,SAAS8B,WAAWK,OAAO;IACpCD,aAAazC,YAAYyC,YAAYJ,UAAUK,IAAI;AACrD;AAEA,oFAAoF;AACpF,4FAA4F;AAC5F,MAAMR,SAAS,MAAMpC,aAAa2C;AAElC,oFAAoF;AACpF,MAAMP,OAAOY,eAAe,CAACC,UAAU,CAAC,CAAC;AAEzC,6EAA6E;AAC7E,mGAAmG;AACnG,sCAAsC;AACtC,qCAAqC;AACrC,MAAMnC,MAAMb,QAAQmC,OAAOc,MAAM,CAACH,IAAI,EAAEX,OAAOc,MAAM,CAACC,MAAM,EAAE;AAC9D,IAAK,MAAMvB,OAAOd,IAAK;IACrBD,QAAQC,GAAG,CAACc,IAAI,KAAKd,GAAG,CAACc,IAAI;AAC/B;AAEA,uFAAuF;AACvF,MAAMwB,OAAO,IAAIhD,eAAegC;AAEhC,mEAAmE;AACnE/B,yBAAyB;IACvBgD,cAAc,CAACC,SAAWF,KAAKC,YAAY,CAACC;AAC9C;AAEA,MAAMC,SAAS,IAAIpD,eAAe;IAChCqD,MAAMpB,OAAOc,MAAM,CAACM,IAAI;IACxB,MAAMC,aAAYC,EAAE;QAClB,OAAON,KAAKK,WAAW,CAACC;IAC1B;IACAC,WAAUD,EAAE,EAAEE,QAAQ;QACpB,OAAOR,KAAKO,SAAS,CAACD,IAAIE;IAC5B;IACAzB,MAAMC,OAAOc,MAAM,CAACf,IAAI;AAC1B;AAEA,kFAAkF;AAClF,iFAAiF;AACjF,6EAA6E;AAC7E,MAAMoB,OAAOM,SAAS,CAAC;AAEvB,MAAMN,OAAOM,SAAS,CAAC7C"}
1
+ {"version":3,"sources":["../../../src/loaders/studio/studioWorkerLoader.worker.ts"],"sourcesContent":["import {isMainThread} from 'node:worker_threads'\n\nimport {createServer, createServerModuleRunner, type InlineConfig, loadEnv, mergeConfig} from 'vite'\n\nimport {getCliConfig} from '../../config/cli/getCliConfig.js'\nimport {type CliConfig} from '../../config/cli/types/cliConfig.js'\nimport {getStudioEnvironmentVariables} from '../../util/environment/getStudioEnvironmentVariables.js'\nimport {setupBrowserStubs} from '../../util/environment/setupBrowserStubs.js'\nimport {isRecord} from '../../util/isRecord.js'\nimport {isNotFoundError} from '../../util/NotFoundError.js'\n\nif (isMainThread) {\n throw new Error('Should be child of thread, not the main thread')\n}\n\nconst rootPath = process.env.STUDIO_WORKER_STUDIO_ROOT_PATH\nif (!rootPath) {\n throw new Error('Missing `STUDIO_WORKER_STUDIO_ROOT_PATH` environment variable')\n}\n\nconst workerScriptPath = process.env.STUDIO_WORKER_TASK_FILE\nif (!workerScriptPath) {\n throw new Error('Missing `STUDIO_WORKER_TASK_FILE` environment variable')\n}\n\nawait setupBrowserStubs()\n\nconst studioEnvVars = await getStudioEnvironmentVariables(rootPath)\n\nconst defaultViteConfig: InlineConfig = {\n build: {target: 'node'},\n configFile: false, // @todo Should use `vite` prop from `sanity.cli.ts` (if any)\n // Inject environment variables as compile-time constants for Vite\n define: Object.fromEntries(\n Object.entries(studioEnvVars).map(([key, value]) => [\n `process.env.${key}`,\n JSON.stringify(value),\n ]),\n ),\n logLevel: 'error',\n optimizeDeps: {disabled: true}, // @todo is this necessary? cant remember why was added\n root: rootPath,\n server: {\n hmr: false,\n watch: null,\n },\n}\n\n// Allow the CLI config (`sanity.cli.(js|ts)`) to define a `vite` property which can\n// extend/modify the default vite configuration for the studio.\nlet cliConfig: CliConfig | undefined\ntry {\n cliConfig = await getCliConfig(rootPath)\n} catch (err) {\n if (!isNotFoundError(err)) {\n // eslint-disable-next-line no-console\n console.warn('[warn] Failed to load CLI config:', err)\n }\n}\n\nlet viteConfig = defaultViteConfig\nif (typeof cliConfig?.vite === 'function') {\n viteConfig = (await cliConfig.vite(viteConfig, {\n command: 'build',\n isSsrBuild: true,\n mode: 'production',\n })) as InlineConfig\n} else if (isRecord(cliConfig?.vite)) {\n viteConfig = mergeConfig(viteConfig, cliConfig.vite)\n}\n\n// Vite will build the files we give it - targetting Node.js instead of the browser.\n// We include the inject plugin in order to provide the stubs for the undefined global APIs.\nconst server = await createServer(viteConfig)\n\n// Bit of a hack, but seems necessary based on the `node-vite` binary implementation\nawait server.pluginContainer.buildStart({})\n\n// Load environment variables from `.env` files in the same way as Vite does.\n// Note that Sanity also provides environment variables through `process.env.*` for compat reasons,\n// and so we need to do the same here.\n// @todo is this in line with sanity?\nconst env = loadEnv(server.config.mode, server.config.envDir, '')\nfor (const key in env) {\n process.env[key] ??= env[key]\n}\n\n// Now we're using Vite's Module Runner (replaces vite-node in Vite 4+)\nconst runner = await createServerModuleRunner(server.environments.ssr, {\n hmr: false,\n sourcemapInterceptor: 'prepareStackTrace',\n})\n\n// Apply the `define` config from vite - imports environment variables\nawait runner.import('/@vite/env')\n\n// Execute the worker script\nawait runner.import(workerScriptPath)\n"],"names":["isMainThread","createServer","createServerModuleRunner","loadEnv","mergeConfig","getCliConfig","getStudioEnvironmentVariables","setupBrowserStubs","isRecord","isNotFoundError","Error","rootPath","process","env","STUDIO_WORKER_STUDIO_ROOT_PATH","workerScriptPath","STUDIO_WORKER_TASK_FILE","studioEnvVars","defaultViteConfig","build","target","configFile","define","Object","fromEntries","entries","map","key","value","JSON","stringify","logLevel","optimizeDeps","disabled","root","server","hmr","watch","cliConfig","err","console","warn","viteConfig","vite","command","isSsrBuild","mode","pluginContainer","buildStart","config","envDir","runner","environments","ssr","sourcemapInterceptor","import"],"mappings":"AAAA,SAAQA,YAAY,QAAO,sBAAqB;AAEhD,SAAQC,YAAY,EAAEC,wBAAwB,EAAqBC,OAAO,EAAEC,WAAW,QAAO,OAAM;AAEpG,SAAQC,YAAY,QAAO,mCAAkC;AAE7D,SAAQC,6BAA6B,QAAO,0DAAyD;AACrG,SAAQC,iBAAiB,QAAO,8CAA6C;AAC7E,SAAQC,QAAQ,QAAO,yBAAwB;AAC/C,SAAQC,eAAe,QAAO,8BAA6B;AAE3D,IAAIT,cAAc;IAChB,MAAM,IAAIU,MAAM;AAClB;AAEA,MAAMC,WAAWC,QAAQC,GAAG,CAACC,8BAA8B;AAC3D,IAAI,CAACH,UAAU;IACb,MAAM,IAAID,MAAM;AAClB;AAEA,MAAMK,mBAAmBH,QAAQC,GAAG,CAACG,uBAAuB;AAC5D,IAAI,CAACD,kBAAkB;IACrB,MAAM,IAAIL,MAAM;AAClB;AAEA,MAAMH;AAEN,MAAMU,gBAAgB,MAAMX,8BAA8BK;AAE1D,MAAMO,oBAAkC;IACtCC,OAAO;QAACC,QAAQ;IAAM;IACtBC,YAAY;IACZ,kEAAkE;IAClEC,QAAQC,OAAOC,WAAW,CACxBD,OAAOE,OAAO,CAACR,eAAeS,GAAG,CAAC,CAAC,CAACC,KAAKC,MAAM,GAAK;YAClD,CAAC,YAAY,EAAED,KAAK;YACpBE,KAAKC,SAAS,CAACF;SAChB;IAEHG,UAAU;IACVC,cAAc;QAACC,UAAU;IAAI;IAC7BC,MAAMvB;IACNwB,QAAQ;QACNC,KAAK;QACLC,OAAO;IACT;AACF;AAEA,oFAAoF;AACpF,+DAA+D;AAC/D,IAAIC;AACJ,IAAI;IACFA,YAAY,MAAMjC,aAAaM;AACjC,EAAE,OAAO4B,KAAK;IACZ,IAAI,CAAC9B,gBAAgB8B,MAAM;QACzB,sCAAsC;QACtCC,QAAQC,IAAI,CAAC,qCAAqCF;IACpD;AACF;AAEA,IAAIG,aAAaxB;AACjB,IAAI,OAAOoB,WAAWK,SAAS,YAAY;IACzCD,aAAc,MAAMJ,UAAUK,IAAI,CAACD,YAAY;QAC7CE,SAAS;QACTC,YAAY;QACZC,MAAM;IACR;AACF,OAAO,IAAItC,SAAS8B,WAAWK,OAAO;IACpCD,aAAatC,YAAYsC,YAAYJ,UAAUK,IAAI;AACrD;AAEA,oFAAoF;AACpF,4FAA4F;AAC5F,MAAMR,SAAS,MAAMlC,aAAayC;AAElC,oFAAoF;AACpF,MAAMP,OAAOY,eAAe,CAACC,UAAU,CAAC,CAAC;AAEzC,6EAA6E;AAC7E,mGAAmG;AACnG,sCAAsC;AACtC,qCAAqC;AACrC,MAAMnC,MAAMV,QAAQgC,OAAOc,MAAM,CAACH,IAAI,EAAEX,OAAOc,MAAM,CAACC,MAAM,EAAE;AAC9D,IAAK,MAAMvB,OAAOd,IAAK;IACrBD,QAAQC,GAAG,CAACc,IAAI,KAAKd,GAAG,CAACc,IAAI;AAC/B;AAEA,uEAAuE;AACvE,MAAMwB,SAAS,MAAMjD,yBAAyBiC,OAAOiB,YAAY,CAACC,GAAG,EAAE;IACrEjB,KAAK;IACLkB,sBAAsB;AACxB;AAEA,sEAAsE;AACtE,MAAMH,OAAOI,MAAM,CAAC;AAEpB,4BAA4B;AAC5B,MAAMJ,OAAOI,MAAM,CAACxC"}
@@ -29,17 +29,7 @@ import { isRecord } from '../../util/isRecord.js';
29
29
  * @internal
30
30
  */ export function studioWorkerTask(filePath, options) {
31
31
  return new Promise((resolve, reject)=>{
32
- if (!/\.worker\.(js|ts)$/.test(filePath.pathname)) {
33
- throw new Error('Studio worker tasks must include `.worker.(js|ts)` in path');
34
- }
35
- const worker = new Worker(new URL('studioWorkerLoader.worker.js', import.meta.url), {
36
- ...options,
37
- env: {
38
- ...isRecord(options.env) ? options.env : process.env,
39
- STUDIO_WORKER_STUDIO_ROOT_PATH: options.studioRootPath,
40
- STUDIO_WORKER_TASK_FILE: filePath.pathname
41
- }
42
- });
32
+ const worker = createStudioWorker(filePath, options);
43
33
  worker.addListener('error', function onWorkerError(err) {
44
34
  reject(new Error(`Fail to load file through worker: ${err.message}`));
45
35
  cleanup();
@@ -65,5 +55,45 @@ import { isRecord } from '../../util/isRecord.js';
65
55
  }
66
56
  });
67
57
  }
58
+ /**
59
+ * Creates a new worker for a studio worker task.
60
+ *
61
+ * This uses a combination of vite for "bundling" + jsdom for emulating a browser
62
+ * environment under the hood, which means that the same thing that will work in vite
63
+ * _should_ work in the worker - to a degree. If the user has defined any typescript
64
+ * path aliases, these will have to be added as aliases to the vite config - the same
65
+ * behavior as you would see with regular vite. Other things that are accounted for:
66
+ *
67
+ * - TypeScript support (+JSX, enums and other "compilation needed" features)
68
+ * - CSS, font and other file imports will resolve to a file path
69
+ * - CSS module imports will resolve to a javascript object of class names
70
+ * - Environment variables are available both as `import.meta.env` and `process.env`,
71
+ * and `.env` files are loaded in the same way that they would in a Sanity studio.
72
+ * - Browser globals not available in a Node.js environment but _are_ provided by JSDOM
73
+ * are defined directly to the Node environment as globals. While this polutes the
74
+ * global namespace, it is done only in the worker thread.
75
+ * - Certain browser globals that are _not_ available in JSDOM are also provided to the
76
+ * global namespace - things like `requestIdleCallback`, `IntersectionObserver` etc.
77
+ * These are provided with a minimal stub implementation to make them not crash.
78
+ *
79
+ * @param filePath - Path to the worker file (`.ts` works and is encouraged)
80
+ * @param options - Options to pass to the worker
81
+ * @returns A promise that resolves with the message from the worker
82
+ * @throws If the file does not exist
83
+ * @throws If the worker exits with a non-zero code
84
+ * @internal
85
+ */ export function createStudioWorker(filePath, options) {
86
+ if (!/\.worker\.(js|ts)$/.test(filePath.pathname)) {
87
+ throw new Error('Studio worker tasks must include `.worker.(js|ts)` in path');
88
+ }
89
+ return new Worker(new URL('studioWorkerLoader.worker.js', import.meta.url), {
90
+ ...options,
91
+ env: {
92
+ ...isRecord(options.env) ? options.env : process.env,
93
+ STUDIO_WORKER_STUDIO_ROOT_PATH: options.studioRootPath,
94
+ STUDIO_WORKER_TASK_FILE: filePath.pathname
95
+ }
96
+ });
97
+ }
68
98
 
69
99
  //# sourceMappingURL=studioWorkerTask.js.map
@@ -1 +1 @@
1
- {"version":3,"sources":["../../../src/loaders/studio/studioWorkerTask.ts"],"sourcesContent":["import {Worker, type WorkerOptions} from 'node:worker_threads'\n\nimport {type RequireProps} from '../../types.js'\nimport {isRecord} from '../../util/isRecord.js'\n\n/**\n * Options for the studio worker task\n *\n * @internal\n */\ninterface StudioWorkerTaskOptions extends RequireProps<WorkerOptions, 'name'> {\n studioRootPath: string\n}\n\n/**\n * Executes a worker file in a Sanity Studio browser context.\n *\n * This uses a combination of vite for \"bundling\" + jsdom for emulating a browser\n * environment under the hood, which means that the same thing that will work in vite\n * _should_ work in the worker - to a degree. If the user has defined any typescript\n * path aliases, these will have to be added as aliases to the vite config - the same\n * behavior as you would see with regular vite. Other things that are accounted for:\n *\n * - TypeScript support (+JSX, enums and other \"compilation needed\" features)\n * - CSS, font and other file imports will resolve to a file path\n * - CSS module imports will resolve to a javascript object of class names\n * - Environment variables are available both as `import.meta.env` and `process.env`,\n * and `.env` files are loaded in the same way that they would in a Sanity studio.\n * - Browser globals not available in a Node.js environment but _are_ provided by JSDOM\n * are defined directly to the Node environment as globals. While this polutes the\n * global namespace, it is done only in the worker thread.\n * - Certain browser globals that are _not_ available in JSDOM are also provided to the\n * global namespace - things like `requestIdleCallback`, `IntersectionObserver` etc.\n * These are provided with a minimal stub implementation to make them not crash.\n *\n * @param filePath - Path to the worker file (`.ts` works and is encouraged)\n * @param options - Options to pass to the worker\n * @returns A promise that resolves with the message from the worker\n * @throws If the file does not exist\n * @throws If the worker exits with a non-zero code\n * @internal\n */\nexport function studioWorkerTask(\n filePath: URL,\n options: StudioWorkerTaskOptions,\n): Promise<unknown> {\n return new Promise((resolve, reject) => {\n if (!/\\.worker\\.(js|ts)$/.test(filePath.pathname)) {\n throw new Error('Studio worker tasks must include `.worker.(js|ts)` in path')\n }\n\n const worker = new Worker(new URL('studioWorkerLoader.worker.js', import.meta.url), {\n ...options,\n env: {\n ...(isRecord(options.env) ? options.env : process.env),\n STUDIO_WORKER_STUDIO_ROOT_PATH: options.studioRootPath,\n STUDIO_WORKER_TASK_FILE: filePath.pathname,\n },\n })\n\n worker.addListener('error', function onWorkerError(err) {\n reject(new Error(`Fail to load file through worker: ${err.message}`))\n cleanup()\n })\n worker.addListener('exit', function onWorkerExit(code) {\n if (code > 0) {\n reject(new Error(`Worker exited with code ${code}`))\n }\n })\n worker.addListener('messageerror', function onWorkerMessageError(err) {\n reject(new Error(`Fail to parse message from worker: ${err}`))\n cleanup()\n })\n worker.addListener('message', function onWorkerMessage(message) {\n resolve(message)\n cleanup()\n })\n\n function cleanup() {\n // Allow the worker a _bit_ of time to clean up, but ensure that we don't have\n // lingering processes hanging around forever if the worker doesn't exit on its\n // own.\n setImmediate(() => worker.terminate())\n }\n })\n}\n"],"names":["Worker","isRecord","studioWorkerTask","filePath","options","Promise","resolve","reject","test","pathname","Error","worker","URL","url","env","process","STUDIO_WORKER_STUDIO_ROOT_PATH","studioRootPath","STUDIO_WORKER_TASK_FILE","addListener","onWorkerError","err","message","cleanup","onWorkerExit","code","onWorkerMessageError","onWorkerMessage","setImmediate","terminate"],"mappings":"AAAA,SAAQA,MAAM,QAA2B,sBAAqB;AAG9D,SAAQC,QAAQ,QAAO,yBAAwB;AAW/C;;;;;;;;;;;;;;;;;;;;;;;;;;;CA2BC,GACD,OAAO,SAASC,iBACdC,QAAa,EACbC,OAAgC;IAEhC,OAAO,IAAIC,QAAQ,CAACC,SAASC;QAC3B,IAAI,CAAC,qBAAqBC,IAAI,CAACL,SAASM,QAAQ,GAAG;YACjD,MAAM,IAAIC,MAAM;QAClB;QAEA,MAAMC,SAAS,IAAIX,OAAO,IAAIY,IAAI,gCAAgC,YAAYC,GAAG,GAAG;YAClF,GAAGT,OAAO;YACVU,KAAK;gBACH,GAAIb,SAASG,QAAQU,GAAG,IAAIV,QAAQU,GAAG,GAAGC,QAAQD,GAAG;gBACrDE,gCAAgCZ,QAAQa,cAAc;gBACtDC,yBAAyBf,SAASM,QAAQ;YAC5C;QACF;QAEAE,OAAOQ,WAAW,CAAC,SAAS,SAASC,cAAcC,GAAG;YACpDd,OAAO,IAAIG,MAAM,CAAC,kCAAkC,EAAEW,IAAIC,OAAO,EAAE;YACnEC;QACF;QACAZ,OAAOQ,WAAW,CAAC,QAAQ,SAASK,aAAaC,IAAI;YACnD,IAAIA,OAAO,GAAG;gBACZlB,OAAO,IAAIG,MAAM,CAAC,wBAAwB,EAAEe,MAAM;YACpD;QACF;QACAd,OAAOQ,WAAW,CAAC,gBAAgB,SAASO,qBAAqBL,GAAG;YAClEd,OAAO,IAAIG,MAAM,CAAC,mCAAmC,EAAEW,KAAK;YAC5DE;QACF;QACAZ,OAAOQ,WAAW,CAAC,WAAW,SAASQ,gBAAgBL,OAAO;YAC5DhB,QAAQgB;YACRC;QACF;QAEA,SAASA;YACP,8EAA8E;YAC9E,+EAA+E;YAC/E,OAAO;YACPK,aAAa,IAAMjB,OAAOkB,SAAS;QACrC;IACF;AACF"}
1
+ {"version":3,"sources":["../../../src/loaders/studio/studioWorkerTask.ts"],"sourcesContent":["import {Worker, type WorkerOptions} from 'node:worker_threads'\n\nimport {type RequireProps} from '../../types.js'\nimport {isRecord} from '../../util/isRecord.js'\n\n/**\n * Options for the studio worker task\n *\n * @internal\n */\ninterface StudioWorkerTaskOptions extends RequireProps<WorkerOptions, 'name'> {\n studioRootPath: string\n}\n\n/**\n * Executes a worker file in a Sanity Studio browser context.\n *\n * This uses a combination of vite for \"bundling\" + jsdom for emulating a browser\n * environment under the hood, which means that the same thing that will work in vite\n * _should_ work in the worker - to a degree. If the user has defined any typescript\n * path aliases, these will have to be added as aliases to the vite config - the same\n * behavior as you would see with regular vite. Other things that are accounted for:\n *\n * - TypeScript support (+JSX, enums and other \"compilation needed\" features)\n * - CSS, font and other file imports will resolve to a file path\n * - CSS module imports will resolve to a javascript object of class names\n * - Environment variables are available both as `import.meta.env` and `process.env`,\n * and `.env` files are loaded in the same way that they would in a Sanity studio.\n * - Browser globals not available in a Node.js environment but _are_ provided by JSDOM\n * are defined directly to the Node environment as globals. While this polutes the\n * global namespace, it is done only in the worker thread.\n * - Certain browser globals that are _not_ available in JSDOM are also provided to the\n * global namespace - things like `requestIdleCallback`, `IntersectionObserver` etc.\n * These are provided with a minimal stub implementation to make them not crash.\n *\n * @param filePath - Path to the worker file (`.ts` works and is encouraged)\n * @param options - Options to pass to the worker\n * @returns A promise that resolves with the message from the worker\n * @throws If the file does not exist\n * @throws If the worker exits with a non-zero code\n * @internal\n */\nexport function studioWorkerTask<T = unknown>(\n filePath: URL,\n options: StudioWorkerTaskOptions,\n): Promise<T> {\n return new Promise<T>((resolve, reject) => {\n const worker = createStudioWorker(filePath, options)\n\n worker.addListener('error', function onWorkerError(err) {\n reject(new Error(`Fail to load file through worker: ${err.message}`))\n cleanup()\n })\n worker.addListener('exit', function onWorkerExit(code) {\n if (code > 0) {\n reject(new Error(`Worker exited with code ${code}`))\n }\n })\n worker.addListener('messageerror', function onWorkerMessageError(err) {\n reject(new Error(`Fail to parse message from worker: ${err}`))\n cleanup()\n })\n worker.addListener('message', function onWorkerMessage(message) {\n resolve(message)\n cleanup()\n })\n\n function cleanup() {\n // Allow the worker a _bit_ of time to clean up, but ensure that we don't have\n // lingering processes hanging around forever if the worker doesn't exit on its\n // own.\n setImmediate(() => worker.terminate())\n }\n })\n}\n\n/**\n * Creates a new worker for a studio worker task.\n *\n * This uses a combination of vite for \"bundling\" + jsdom for emulating a browser\n * environment under the hood, which means that the same thing that will work in vite\n * _should_ work in the worker - to a degree. If the user has defined any typescript\n * path aliases, these will have to be added as aliases to the vite config - the same\n * behavior as you would see with regular vite. Other things that are accounted for:\n *\n * - TypeScript support (+JSX, enums and other \"compilation needed\" features)\n * - CSS, font and other file imports will resolve to a file path\n * - CSS module imports will resolve to a javascript object of class names\n * - Environment variables are available both as `import.meta.env` and `process.env`,\n * and `.env` files are loaded in the same way that they would in a Sanity studio.\n * - Browser globals not available in a Node.js environment but _are_ provided by JSDOM\n * are defined directly to the Node environment as globals. While this polutes the\n * global namespace, it is done only in the worker thread.\n * - Certain browser globals that are _not_ available in JSDOM are also provided to the\n * global namespace - things like `requestIdleCallback`, `IntersectionObserver` etc.\n * These are provided with a minimal stub implementation to make them not crash.\n *\n * @param filePath - Path to the worker file (`.ts` works and is encouraged)\n * @param options - Options to pass to the worker\n * @returns A promise that resolves with the message from the worker\n * @throws If the file does not exist\n * @throws If the worker exits with a non-zero code\n * @internal\n */\nexport function createStudioWorker(filePath: URL, options: StudioWorkerTaskOptions) {\n if (!/\\.worker\\.(js|ts)$/.test(filePath.pathname)) {\n throw new Error('Studio worker tasks must include `.worker.(js|ts)` in path')\n }\n\n return new Worker(new URL('studioWorkerLoader.worker.js', import.meta.url), {\n ...options,\n env: {\n ...(isRecord(options.env) ? options.env : process.env),\n STUDIO_WORKER_STUDIO_ROOT_PATH: options.studioRootPath,\n STUDIO_WORKER_TASK_FILE: filePath.pathname,\n },\n })\n}\n"],"names":["Worker","isRecord","studioWorkerTask","filePath","options","Promise","resolve","reject","worker","createStudioWorker","addListener","onWorkerError","err","Error","message","cleanup","onWorkerExit","code","onWorkerMessageError","onWorkerMessage","setImmediate","terminate","test","pathname","URL","url","env","process","STUDIO_WORKER_STUDIO_ROOT_PATH","studioRootPath","STUDIO_WORKER_TASK_FILE"],"mappings":"AAAA,SAAQA,MAAM,QAA2B,sBAAqB;AAG9D,SAAQC,QAAQ,QAAO,yBAAwB;AAW/C;;;;;;;;;;;;;;;;;;;;;;;;;;;CA2BC,GACD,OAAO,SAASC,iBACdC,QAAa,EACbC,OAAgC;IAEhC,OAAO,IAAIC,QAAW,CAACC,SAASC;QAC9B,MAAMC,SAASC,mBAAmBN,UAAUC;QAE5CI,OAAOE,WAAW,CAAC,SAAS,SAASC,cAAcC,GAAG;YACpDL,OAAO,IAAIM,MAAM,CAAC,kCAAkC,EAAED,IAAIE,OAAO,EAAE;YACnEC;QACF;QACAP,OAAOE,WAAW,CAAC,QAAQ,SAASM,aAAaC,IAAI;YACnD,IAAIA,OAAO,GAAG;gBACZV,OAAO,IAAIM,MAAM,CAAC,wBAAwB,EAAEI,MAAM;YACpD;QACF;QACAT,OAAOE,WAAW,CAAC,gBAAgB,SAASQ,qBAAqBN,GAAG;YAClEL,OAAO,IAAIM,MAAM,CAAC,mCAAmC,EAAED,KAAK;YAC5DG;QACF;QACAP,OAAOE,WAAW,CAAC,WAAW,SAASS,gBAAgBL,OAAO;YAC5DR,QAAQQ;YACRC;QACF;QAEA,SAASA;YACP,8EAA8E;YAC9E,+EAA+E;YAC/E,OAAO;YACPK,aAAa,IAAMZ,OAAOa,SAAS;QACrC;IACF;AACF;AAEA;;;;;;;;;;;;;;;;;;;;;;;;;;;CA2BC,GACD,OAAO,SAASZ,mBAAmBN,QAAa,EAAEC,OAAgC;IAChF,IAAI,CAAC,qBAAqBkB,IAAI,CAACnB,SAASoB,QAAQ,GAAG;QACjD,MAAM,IAAIV,MAAM;IAClB;IAEA,OAAO,IAAIb,OAAO,IAAIwB,IAAI,gCAAgC,YAAYC,GAAG,GAAG;QAC1E,GAAGrB,OAAO;QACVsB,KAAK;YACH,GAAIzB,SAASG,QAAQsB,GAAG,IAAItB,QAAQsB,GAAG,GAAGC,QAAQD,GAAG;YACrDE,gCAAgCxB,QAAQyB,cAAc;YACtDC,yBAAyB3B,SAASoB,QAAQ;QAC5C;IACF;AACF"}
@@ -1,6 +1,7 @@
1
1
  import { URL } from 'node:url';
2
2
  import { Worker } from 'node:worker_threads';
3
3
  import { getTsconfig } from 'get-tsconfig';
4
+ import { debug } from '../../debug.js';
4
5
  import { isRecord } from '../../util/isRecord.js';
5
6
  /**
6
7
  * Executes a worker file with tsx registered. This means you can import other
@@ -31,6 +32,7 @@ import { isRecord } from '../../util/isRecord.js';
31
32
  });
32
33
  return new Promise((resolve, reject)=>{
33
34
  worker.addListener('error', function onWorkerError(err) {
35
+ debug(`Failed to load file through worker for: ${filePath.pathname}`, err);
34
36
  reject(new Error(`Failed to load file through worker: ${err.message}`, {
35
37
  cause: err
36
38
  }));
@@ -42,6 +44,7 @@ import { isRecord } from '../../util/isRecord.js';
42
44
  }
43
45
  });
44
46
  worker.addListener('messageerror', function onWorkerMessageError(err) {
47
+ debug(`Failed to parse message from worker for: ${filePath.pathname}`, err);
45
48
  reject(new Error(`Fail to parse message from worker: ${err}`));
46
49
  cleanup();
47
50
  });
@@ -1 +1 @@
1
- {"version":3,"sources":["../../../src/loaders/tsx/tsxWorkerTask.ts"],"sourcesContent":["import {URL} from 'node:url'\nimport {Worker, type WorkerOptions} from 'node:worker_threads'\n\nimport {getTsconfig} from 'get-tsconfig'\n\nimport {type RequireProps} from '../../types.js'\nimport {isRecord} from '../../util/isRecord.js'\n\n/**\n * Options for the tsx worker task\n *\n * @internal\n */\ninterface TsxWorkerTaskOptions extends RequireProps<WorkerOptions, 'name'> {\n rootPath: string\n}\n\n/**\n * Executes a worker file with tsx registered. This means you can import other\n * typescript with fairly rich syntax, and still have that only apply to the worker\n * thread instead of the full parent process. The worker should emit a message when\n * complete using `parentPort`. Once it has received a single message will resolve the\n * returned promise with that message. If you are expecting multiple messages, you will\n * have to implement another method ;)\n *\n * @param filePath - Path to the worker file\n * @param options - Options to pass to the worker\n * @returns A promise that resolves with the message from the worker\n * @throws If the file does not exist\n * @throws If the worker exits with a non-zero code\n * @internal\n */\nexport function tsxWorkerTask<T = unknown>(\n filePath: URL,\n options: TsxWorkerTaskOptions,\n): Promise<T> {\n const tsconfig = getTsconfig(options.rootPath)\n\n const env = {\n ...(isRecord(options.env) ? options.env : process.env),\n ...(tsconfig?.path ? {TSX_TSCONFIG_PATH: tsconfig.path} : {}),\n TSX_WORKER_TASK_SCRIPT: filePath.pathname,\n }\n\n const worker = new Worker(new URL('tsxWorkerLoader.worker.js', import.meta.url), {\n ...options,\n env,\n })\n\n return new Promise((resolve, reject) => {\n worker.addListener('error', function onWorkerError(err) {\n reject(new Error(`Failed to load file through worker: ${err.message}`, {cause: err}))\n cleanup()\n })\n worker.addListener('exit', function onWorkerExit(code) {\n if (code > 0) {\n reject(new Error(`Worker exited with code ${code}`))\n }\n })\n worker.addListener('messageerror', function onWorkerMessageError(err) {\n reject(new Error(`Fail to parse message from worker: ${err}`))\n cleanup()\n })\n worker.addListener('message', function onWorkerMessage(message) {\n resolve(message)\n cleanup()\n })\n\n function cleanup() {\n // Allow the worker a _bit_ of time to clean up, but ensure that we don't have\n // lingering processes hanging around forever if the worker doesn't exit on its\n // own.\n setImmediate(() => worker.terminate())\n }\n })\n}\n"],"names":["URL","Worker","getTsconfig","isRecord","tsxWorkerTask","filePath","options","tsconfig","rootPath","env","process","path","TSX_TSCONFIG_PATH","TSX_WORKER_TASK_SCRIPT","pathname","worker","url","Promise","resolve","reject","addListener","onWorkerError","err","Error","message","cause","cleanup","onWorkerExit","code","onWorkerMessageError","onWorkerMessage","setImmediate","terminate"],"mappings":"AAAA,SAAQA,GAAG,QAAO,WAAU;AAC5B,SAAQC,MAAM,QAA2B,sBAAqB;AAE9D,SAAQC,WAAW,QAAO,eAAc;AAGxC,SAAQC,QAAQ,QAAO,yBAAwB;AAW/C;;;;;;;;;;;;;;CAcC,GACD,OAAO,SAASC,cACdC,QAAa,EACbC,OAA6B;IAE7B,MAAMC,WAAWL,YAAYI,QAAQE,QAAQ;IAE7C,MAAMC,MAAM;QACV,GAAIN,SAASG,QAAQG,GAAG,IAAIH,QAAQG,GAAG,GAAGC,QAAQD,GAAG;QACrD,GAAIF,UAAUI,OAAO;YAACC,mBAAmBL,SAASI,IAAI;QAAA,IAAI,CAAC,CAAC;QAC5DE,wBAAwBR,SAASS,QAAQ;IAC3C;IAEA,MAAMC,SAAS,IAAId,OAAO,IAAID,IAAI,6BAA6B,YAAYgB,GAAG,GAAG;QAC/E,GAAGV,OAAO;QACVG;IACF;IAEA,OAAO,IAAIQ,QAAQ,CAACC,SAASC;QAC3BJ,OAAOK,WAAW,CAAC,SAAS,SAASC,cAAcC,GAAG;YACpDH,OAAO,IAAII,MAAM,CAAC,oCAAoC,EAAED,IAAIE,OAAO,EAAE,EAAE;gBAACC,OAAOH;YAAG;YAClFI;QACF;QACAX,OAAOK,WAAW,CAAC,QAAQ,SAASO,aAAaC,IAAI;YACnD,IAAIA,OAAO,GAAG;gBACZT,OAAO,IAAII,MAAM,CAAC,wBAAwB,EAAEK,MAAM;YACpD;QACF;QACAb,OAAOK,WAAW,CAAC,gBAAgB,SAASS,qBAAqBP,GAAG;YAClEH,OAAO,IAAII,MAAM,CAAC,mCAAmC,EAAED,KAAK;YAC5DI;QACF;QACAX,OAAOK,WAAW,CAAC,WAAW,SAASU,gBAAgBN,OAAO;YAC5DN,QAAQM;YACRE;QACF;QAEA,SAASA;YACP,8EAA8E;YAC9E,+EAA+E;YAC/E,OAAO;YACPK,aAAa,IAAMhB,OAAOiB,SAAS;QACrC;IACF;AACF"}
1
+ {"version":3,"sources":["../../../src/loaders/tsx/tsxWorkerTask.ts"],"sourcesContent":["import {URL} from 'node:url'\nimport {Worker, type WorkerOptions} from 'node:worker_threads'\n\nimport {getTsconfig} from 'get-tsconfig'\n\nimport {debug} from '../../debug.js'\nimport {type RequireProps} from '../../types.js'\nimport {isRecord} from '../../util/isRecord.js'\n\n/**\n * Options for the tsx worker task\n *\n * @internal\n */\ninterface TsxWorkerTaskOptions extends RequireProps<WorkerOptions, 'name'> {\n rootPath: string\n}\n\n/**\n * Executes a worker file with tsx registered. This means you can import other\n * typescript with fairly rich syntax, and still have that only apply to the worker\n * thread instead of the full parent process. The worker should emit a message when\n * complete using `parentPort`. Once it has received a single message will resolve the\n * returned promise with that message. If you are expecting multiple messages, you will\n * have to implement another method ;)\n *\n * @param filePath - Path to the worker file\n * @param options - Options to pass to the worker\n * @returns A promise that resolves with the message from the worker\n * @throws If the file does not exist\n * @throws If the worker exits with a non-zero code\n * @internal\n */\nexport function tsxWorkerTask<T = unknown>(\n filePath: URL,\n options: TsxWorkerTaskOptions,\n): Promise<T> {\n const tsconfig = getTsconfig(options.rootPath)\n\n const env = {\n ...(isRecord(options.env) ? options.env : process.env),\n ...(tsconfig?.path ? {TSX_TSCONFIG_PATH: tsconfig.path} : {}),\n TSX_WORKER_TASK_SCRIPT: filePath.pathname,\n }\n\n const worker = new Worker(new URL('tsxWorkerLoader.worker.js', import.meta.url), {\n ...options,\n env,\n })\n\n return new Promise((resolve, reject) => {\n worker.addListener('error', function onWorkerError(err) {\n debug(`Failed to load file through worker for: ${filePath.pathname}`, err)\n reject(new Error(`Failed to load file through worker: ${err.message}`, {cause: err}))\n cleanup()\n })\n worker.addListener('exit', function onWorkerExit(code) {\n if (code > 0) {\n reject(new Error(`Worker exited with code ${code}`))\n }\n })\n worker.addListener('messageerror', function onWorkerMessageError(err) {\n debug(`Failed to parse message from worker for: ${filePath.pathname}`, err)\n reject(new Error(`Fail to parse message from worker: ${err}`))\n cleanup()\n })\n worker.addListener('message', function onWorkerMessage(message) {\n resolve(message)\n cleanup()\n })\n\n function cleanup() {\n // Allow the worker a _bit_ of time to clean up, but ensure that we don't have\n // lingering processes hanging around forever if the worker doesn't exit on its\n // own.\n setImmediate(() => worker.terminate())\n }\n })\n}\n"],"names":["URL","Worker","getTsconfig","debug","isRecord","tsxWorkerTask","filePath","options","tsconfig","rootPath","env","process","path","TSX_TSCONFIG_PATH","TSX_WORKER_TASK_SCRIPT","pathname","worker","url","Promise","resolve","reject","addListener","onWorkerError","err","Error","message","cause","cleanup","onWorkerExit","code","onWorkerMessageError","onWorkerMessage","setImmediate","terminate"],"mappings":"AAAA,SAAQA,GAAG,QAAO,WAAU;AAC5B,SAAQC,MAAM,QAA2B,sBAAqB;AAE9D,SAAQC,WAAW,QAAO,eAAc;AAExC,SAAQC,KAAK,QAAO,iBAAgB;AAEpC,SAAQC,QAAQ,QAAO,yBAAwB;AAW/C;;;;;;;;;;;;;;CAcC,GACD,OAAO,SAASC,cACdC,QAAa,EACbC,OAA6B;IAE7B,MAAMC,WAAWN,YAAYK,QAAQE,QAAQ;IAE7C,MAAMC,MAAM;QACV,GAAIN,SAASG,QAAQG,GAAG,IAAIH,QAAQG,GAAG,GAAGC,QAAQD,GAAG;QACrD,GAAIF,UAAUI,OAAO;YAACC,mBAAmBL,SAASI,IAAI;QAAA,IAAI,CAAC,CAAC;QAC5DE,wBAAwBR,SAASS,QAAQ;IAC3C;IAEA,MAAMC,SAAS,IAAIf,OAAO,IAAID,IAAI,6BAA6B,YAAYiB,GAAG,GAAG;QAC/E,GAAGV,OAAO;QACVG;IACF;IAEA,OAAO,IAAIQ,QAAQ,CAACC,SAASC;QAC3BJ,OAAOK,WAAW,CAAC,SAAS,SAASC,cAAcC,GAAG;YACpDpB,MAAM,CAAC,wCAAwC,EAAEG,SAASS,QAAQ,EAAE,EAAEQ;YACtEH,OAAO,IAAII,MAAM,CAAC,oCAAoC,EAAED,IAAIE,OAAO,EAAE,EAAE;gBAACC,OAAOH;YAAG;YAClFI;QACF;QACAX,OAAOK,WAAW,CAAC,QAAQ,SAASO,aAAaC,IAAI;YACnD,IAAIA,OAAO,GAAG;gBACZT,OAAO,IAAII,MAAM,CAAC,wBAAwB,EAAEK,MAAM;YACpD;QACF;QACAb,OAAOK,WAAW,CAAC,gBAAgB,SAASS,qBAAqBP,GAAG;YAClEpB,MAAM,CAAC,yCAAyC,EAAEG,SAASS,QAAQ,EAAE,EAAEQ;YACvEH,OAAO,IAAII,MAAM,CAAC,mCAAmC,EAAED,KAAK;YAC5DI;QACF;QACAX,OAAOK,WAAW,CAAC,WAAW,SAASU,gBAAgBN,OAAO;YAC5DN,QAAQM;YACRE;QACF;QAEA,SAASA;YACP,8EAA8E;YAC9E,+EAA+E;YAC/E,OAAO;YACPK,aAAa,IAAMhB,OAAOiB,SAAS;QACrC;IACF;AACF"}
@@ -1,7 +1,6 @@
1
1
  import { ux } from '@oclif/core';
2
- import { createClient, requester as defaultRequester } from '@sanity/client';
2
+ import { createClient, requester as defaultRequester, isHttpError } from '@sanity/client';
3
3
  import { generateHelpUrl } from '../util/generateHelpUrl.js';
4
- import { isHttpError } from '../util/isHttpError.js';
5
4
  import { getCliToken } from './getCliToken.js';
6
5
  const apiHosts = {
7
6
  staging: 'https://api.sanity.work'
@@ -10,19 +9,20 @@ const CLI_REQUEST_TAG_PREFIX = 'sanity.cli';
10
9
  /**
11
10
  * Create a "global" (unscoped) Sanity API client.
12
11
  *
12
+ * @public
13
+ *
13
14
  * @param options - The options to use for the client.
14
15
  * @returns Promise that resolves to a configured Sanity API client.
15
- */ export async function getGlobalCliClient({ requireUser, ...config }) {
16
+ */ export async function getGlobalCliClient({ requireUser, token: providedToken, ...config }) {
16
17
  const requester = defaultRequester.clone();
17
18
  requester.use(authErrors());
18
19
  const sanityEnv = process.env.SANITY_INTERNAL_ENV || 'production';
19
20
  const apiHost = apiHosts[sanityEnv];
20
- let token;
21
- if (requireUser) {
22
- token = await getCliToken();
23
- if (!token) {
24
- throw new Error('You must login first - run "sanity login"');
25
- }
21
+ // Use the provided token if it is set, otherwise get the token from the config file
22
+ const token = providedToken || 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
26
  }
27
27
  return createClient({
28
28
  ...apiHost ? {
@@ -37,21 +37,22 @@ const CLI_REQUEST_TAG_PREFIX = 'sanity.cli';
37
37
  });
38
38
  }
39
39
  /**
40
- * Create a "global" (unscoped) Sanity API client.
40
+ * Create a "project" (scoped) Sanity API client.
41
+ *
42
+ * @public
41
43
  *
42
44
  * @param options - The options to use for the client.
43
45
  * @returns Promise that resolves to a configured Sanity API client.
44
- */ export async function getProjectCliClient({ requireUser, ...config }) {
46
+ */ export async function getProjectCliClient({ requireUser, token: providedToken, ...config }) {
45
47
  const requester = defaultRequester.clone();
46
48
  requester.use(authErrors());
47
49
  const sanityEnv = process.env.SANITY_INTERNAL_ENV || 'production';
48
50
  const apiHost = apiHosts[sanityEnv];
49
- let token;
50
- if (requireUser) {
51
- token = await getCliToken();
52
- if (!token) {
53
- throw new Error('You must login first - run "sanity login"');
54
- }
51
+ // Use the provided token if it is set, otherwise get the token from the config file
52
+ const token = providedToken || await getCliToken();
53
+ // If the token is not set and requireUser is true, throw an error
54
+ if (!token && requireUser) {
55
+ throw new Error('You must login first - run "sanity login"');
55
56
  }
56
57
  return createClient({
57
58
  ...apiHost ? {
@@ -1 +1 @@
1
- {"version":3,"sources":["../../src/services/apiClient.ts"],"sourcesContent":["import {ux} from '@oclif/core'\nimport {\n type ClientConfig,\n type ClientError,\n createClient,\n requester as defaultRequester,\n type SanityClient,\n type ServerError,\n} from '@sanity/client'\n\nimport {generateHelpUrl} from '../util/generateHelpUrl.js'\nimport {isHttpError} from '../util/isHttpError.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 * @internal\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/**\n * Create a \"global\" (unscoped) Sanity API client.\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 ...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 let token: string | undefined\n if (requireUser) {\n token = await getCliToken()\n if (!token) {\n throw new Error('You must login first - run \"sanity login\"')\n }\n }\n\n return createClient({\n ...(apiHost ? {apiHost} : {}),\n requester,\n requestTagPrefix: CLI_REQUEST_TAG_PREFIX,\n token,\n useCdn: false,\n useProjectHostname: false,\n ...config,\n })\n}\n\n/**\n * @internal\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 \"global\" (unscoped) Sanity API client.\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 ...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 let token: string | undefined\n if (requireUser) {\n token = await getCliToken()\n if (!token) {\n throw new Error('You must login first - run \"sanity login\"')\n }\n }\n\n return createClient({\n ...(apiHost ? {apiHost} : {}),\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 ${ux.colorize('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":["ux","createClient","requester","defaultRequester","generateHelpUrl","isHttpError","getCliToken","apiHosts","staging","CLI_REQUEST_TAG_PREFIX","getGlobalCliClient","requireUser","config","clone","use","authErrors","sanityEnv","process","env","SANITY_INTERNAL_ENV","apiHost","token","Error","requestTagPrefix","useCdn","useProjectHostname","getProjectCliClient","onError","err","isReqResError","statusCode","response","body","message","colorize","Object","prototype","hasOwnProperty","call"],"mappings":"AAAA,SAAQA,EAAE,QAAO,cAAa;AAC9B,SAGEC,YAAY,EACZC,aAAaC,gBAAgB,QAGxB,iBAAgB;AAEvB,SAAQC,eAAe,QAAO,6BAA4B;AAC1D,SAAQC,WAAW,QAAO,yBAAwB;AAClD,SAAQC,WAAW,QAAO,mBAAkB;AAE5C,MAAMC,WAA+C;IACnDC,SAAS;AACX;AAEA,MAAMC,yBAAyB;AAmB/B;;;;;CAKC,GACD,OAAO,eAAeC,mBAAmB,EACvCC,WAAW,EACX,GAAGC,QACoB;IACvB,MAAMV,YAAYC,iBAAiBU,KAAK;IACxCX,UAAUY,GAAG,CAACC;IAEd,MAAMC,YAAYC,QAAQC,GAAG,CAACC,mBAAmB,IAAI;IAErD,MAAMC,UAAUb,QAAQ,CAACS,UAAU;IAEnC,IAAIK;IACJ,IAAIV,aAAa;QACfU,QAAQ,MAAMf;QACd,IAAI,CAACe,OAAO;YACV,MAAM,IAAIC,MAAM;QAClB;IACF;IAEA,OAAOrB,aAAa;QAClB,GAAImB,UAAU;YAACA;QAAO,IAAI,CAAC,CAAC;QAC5BlB;QACAqB,kBAAkBd;QAClBY;QACAG,QAAQ;QACRC,oBAAoB;QACpB,GAAGb,MAAM;IACX;AACF;AA6BA;;;;;CAKC,GACD,OAAO,eAAec,oBAAoB,EACxCf,WAAW,EACX,GAAGC,QACqB;IACxB,MAAMV,YAAYC,iBAAiBU,KAAK;IACxCX,UAAUY,GAAG,CAACC;IAEd,MAAMC,YAAYC,QAAQC,GAAG,CAACC,mBAAmB,IAAI;IAErD,MAAMC,UAAUb,QAAQ,CAACS,UAAU;IAEnC,IAAIK;IACJ,IAAIV,aAAa;QACfU,QAAQ,MAAMf;QACd,IAAI,CAACe,OAAO;YACV,MAAM,IAAIC,MAAM;QAClB;IACF;IAEA,OAAOrB,aAAa;QAClB,GAAImB,UAAU;YAACA;QAAO,IAAI,CAAC,CAAC;QAC5BlB;QACAqB,kBAAkBd;QAClBY;QACAG,QAAQ;QACRC,oBAAoB;QACpB,GAAGb,MAAM;IACX;AACF;AAEA;;;;;;CAMC,GACD,SAASG;IACP,OAAO;QACLY,SAAS,CAACC;YACR,IAAI,CAACA,OAAO,CAACC,cAAcD,MAAM;gBAC/B,OAAOA;YACT;YAEA,MAAME,aAAazB,YAAYuB,QAAQA,IAAIG,QAAQ,CAACC,IAAI,CAACF,UAAU;YACnE,IAAIA,eAAe,KAAK;gBACtBF,IAAIK,OAAO,GAAG,GAAGL,IAAIK,OAAO,CAAC,mCAAmC,EAAEjC,GAAGkC,QAAQ,CAAC,QAAQ,gBAAgB,6BAA6B,EAAE9B,gBAAgB,cAAc,CAAC,CAAC;YACvK;YAEA,OAAOwB;QACT;IACF;AACF;AAEA,SAASC,cAAcD,GAAU;IAC/B,OAAOO,OAAOC,SAAS,CAACC,cAAc,CAACC,IAAI,CAACV,KAAK;AACnD"}
1
+ {"version":3,"sources":["../../src/services/apiClient.ts"],"sourcesContent":["import {ux} from '@oclif/core'\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/**\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 ...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 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 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 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 ${ux.colorize('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":["ux","createClient","requester","defaultRequester","isHttpError","generateHelpUrl","getCliToken","apiHosts","staging","CLI_REQUEST_TAG_PREFIX","getGlobalCliClient","requireUser","token","providedToken","config","clone","use","authErrors","sanityEnv","process","env","SANITY_INTERNAL_ENV","apiHost","Error","requestTagPrefix","useCdn","useProjectHostname","getProjectCliClient","onError","err","isReqResError","statusCode","response","body","message","colorize","Object","prototype","hasOwnProperty","call"],"mappings":"AAAA,SAAQA,EAAE,QAAO,cAAa;AAC9B,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;AAmB/B;;;;;;;CAOC,GACD,OAAO,eAAeC,mBAAmB,EACvCC,WAAW,EACXC,OAAOC,aAAa,EACpB,GAAGC,QACoB;IACvB,MAAMZ,YAAYC,iBAAiBY,KAAK;IACxCb,UAAUc,GAAG,CAACC;IAEd,MAAMC,YAAYC,QAAQC,GAAG,CAACC,mBAAmB,IAAI;IAErD,MAAMC,UAAUf,QAAQ,CAACW,UAAU;IAEnC,oFAAoF;IACpF,MAAMN,QAAQC,iBAAkB,MAAMP;IAEtC,kEAAkE;IAClE,IAAI,CAACM,SAASD,aAAa;QACzB,MAAM,IAAIY,MAAM;IAClB;IAEA,OAAOtB,aAAa;QAClB,GAAIqB,UAAU;YAACA;QAAO,IAAI,CAAC,CAAC;QAC5BpB;QACAsB,kBAAkBf;QAClBG;QACAa,QAAQ;QACRC,oBAAoB;QACpB,GAAGZ,MAAM;IACX;AACF;AA6BA;;;;;;;CAOC,GACD,OAAO,eAAea,oBAAoB,EACxChB,WAAW,EACXC,OAAOC,aAAa,EACpB,GAAGC,QACqB;IACxB,MAAMZ,YAAYC,iBAAiBY,KAAK;IACxCb,UAAUc,GAAG,CAACC;IAEd,MAAMC,YAAYC,QAAQC,GAAG,CAACC,mBAAmB,IAAI;IAErD,MAAMC,UAAUf,QAAQ,CAACW,UAAU;IAEnC,oFAAoF;IACpF,MAAMN,QAAQC,iBAAkB,MAAMP;IAEtC,kEAAkE;IAClE,IAAI,CAACM,SAASD,aAAa;QACzB,MAAM,IAAIY,MAAM;IAClB;IAEA,OAAOtB,aAAa;QAClB,GAAIqB,UAAU;YAACA;QAAO,IAAI,CAAC,CAAC;QAC5BpB;QACAsB,kBAAkBf;QAClBG;QACAa,QAAQ;QACRC,oBAAoB;QACpB,GAAGZ,MAAM;IACX;AACF;AAEA;;;;;;CAMC,GACD,SAASG;IACP,OAAO;QACLW,SAAS,CAACC;YACR,IAAI,CAACA,OAAO,CAACC,cAAcD,MAAM;gBAC/B,OAAOA;YACT;YAEA,MAAME,aAAa3B,YAAYyB,QAAQA,IAAIG,QAAQ,CAACC,IAAI,CAACF,UAAU;YACnE,IAAIA,eAAe,KAAK;gBACtBF,IAAIK,OAAO,GAAG,GAAGL,IAAIK,OAAO,CAAC,mCAAmC,EAAElC,GAAGmC,QAAQ,CAAC,QAAQ,gBAAgB,6BAA6B,EAAE9B,gBAAgB,cAAc,CAAC,CAAC;YACvK;YAEA,OAAOwB;QACT;IACF;AACF;AAEA,SAASC,cAAcD,GAAU;IAC/B,OAAOO,OAAOC,SAAS,CAACC,cAAc,CAACC,IAAI,CAACV,KAAK;AACnD"}
@@ -0,0 +1,30 @@
1
+ import { rm, stat } from 'node:fs/promises';
2
+ import { findTelemetryFiles } from './findTelemetryFiles.js';
3
+ import { telemetryStoreDebug } from './telemetryStoreDebug.js';
4
+ /**
5
+ * Cleans up telemetry files older than the specified number of days
6
+ * @internal
7
+ */ export async function cleanupOldTelemetryFiles(maxAgeDays = 7) {
8
+ try {
9
+ const files = await findTelemetryFiles();
10
+ const cutoffTime = Date.now() - maxAgeDays * 24 * 60 * 60 * 1000;
11
+ for (const filePath of files){
12
+ try {
13
+ const stats = await stat(filePath);
14
+ if (stats.mtime.getTime() < cutoffTime) {
15
+ telemetryStoreDebug('Cleaning up old telemetry file: %s', filePath);
16
+ await rm(filePath, {
17
+ force: true
18
+ });
19
+ }
20
+ } catch (error) {
21
+ telemetryStoreDebug('Error checking/removing old file %s: %o', filePath, error);
22
+ }
23
+ }
24
+ } catch (error) {
25
+ telemetryStoreDebug('Error during cleanup: %o', error);
26
+ // Don't throw - cleanup is best effort
27
+ }
28
+ }
29
+
30
+ //# sourceMappingURL=cleanupOldTelemetryFiles.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../src/telemetry/cleanupOldTelemetryFiles.ts"],"sourcesContent":["import {rm, stat} from 'node:fs/promises'\n\nimport {findTelemetryFiles} from './findTelemetryFiles.js'\nimport {telemetryStoreDebug} from './telemetryStoreDebug.js'\n\n/**\n * Cleans up telemetry files older than the specified number of days\n * @internal\n */\nexport async function cleanupOldTelemetryFiles(maxAgeDays: number = 7): Promise<void> {\n try {\n const files = await findTelemetryFiles()\n const cutoffTime = Date.now() - maxAgeDays * 24 * 60 * 60 * 1000\n\n for (const filePath of files) {\n try {\n const stats = await stat(filePath)\n if (stats.mtime.getTime() < cutoffTime) {\n telemetryStoreDebug('Cleaning up old telemetry file: %s', filePath)\n await rm(filePath, {force: true})\n }\n } catch (error) {\n telemetryStoreDebug('Error checking/removing old file %s: %o', filePath, error)\n }\n }\n } catch (error) {\n telemetryStoreDebug('Error during cleanup: %o', error)\n // Don't throw - cleanup is best effort\n }\n}\n"],"names":["rm","stat","findTelemetryFiles","telemetryStoreDebug","cleanupOldTelemetryFiles","maxAgeDays","files","cutoffTime","Date","now","filePath","stats","mtime","getTime","force","error"],"mappings":"AAAA,SAAQA,EAAE,EAAEC,IAAI,QAAO,mBAAkB;AAEzC,SAAQC,kBAAkB,QAAO,0BAAyB;AAC1D,SAAQC,mBAAmB,QAAO,2BAA0B;AAE5D;;;CAGC,GACD,OAAO,eAAeC,yBAAyBC,aAAqB,CAAC;IACnE,IAAI;QACF,MAAMC,QAAQ,MAAMJ;QACpB,MAAMK,aAAaC,KAAKC,GAAG,KAAKJ,aAAa,KAAK,KAAK,KAAK;QAE5D,KAAK,MAAMK,YAAYJ,MAAO;YAC5B,IAAI;gBACF,MAAMK,QAAQ,MAAMV,KAAKS;gBACzB,IAAIC,MAAMC,KAAK,CAACC,OAAO,KAAKN,YAAY;oBACtCJ,oBAAoB,sCAAsCO;oBAC1D,MAAMV,GAAGU,UAAU;wBAACI,OAAO;oBAAI;gBACjC;YACF,EAAE,OAAOC,OAAO;gBACdZ,oBAAoB,2CAA2CO,UAAUK;YAC3E;QACF;IACF,EAAE,OAAOA,OAAO;QACdZ,oBAAoB,4BAA4BY;IAChD,uCAAuC;IACzC;AACF"}
@@ -0,0 +1,95 @@
1
+ import { appendFileSync } from 'node:fs';
2
+ import { mkdir } from 'node:fs/promises';
3
+ import { dirname } from 'node:path';
4
+ import { generateTelemetryFilePath } from './generateTelemetryFilePath.js';
5
+ import { createLogger } from './logger.js';
6
+ import { telemetryStoreDebug } from './telemetryStoreDebug.js';
7
+ /**
8
+ * Creates a file-based telemetry store with cached consent and reliable synchronous I/O.
9
+ *
10
+ * Key optimizations:
11
+ * - Consent resolved once at creation and cached (vs checking on every emit)
12
+ * - File path generated and directory created once during initialization
13
+ * - Synchronous file writes to ensure events are captured even during process exit
14
+ *
15
+ * @param sessionId - Unique session identifier for file isolation
16
+ * @param options - Configuration options
17
+ * @returns TelemetryStore instance compatible with the telemetry interface
18
+ *
19
+ * @internal
20
+ */ export function createTelemetryStore(sessionId, options) {
21
+ telemetryStoreDebug('Creating telemetry store with sessionId: %s', sessionId);
22
+ let cachedConsent = null;
23
+ let filePath = null;
24
+ const initializeConsent = async ()=>{
25
+ if (cachedConsent) return;
26
+ try {
27
+ cachedConsent = await options.resolveConsent();
28
+ telemetryStoreDebug('Cached consent status: %s', cachedConsent.status);
29
+ } catch (error) {
30
+ telemetryStoreDebug('Failed to initialize consent, treating as undetermined: %o', error);
31
+ cachedConsent = {
32
+ reason: 'fetchError',
33
+ status: 'undetermined'
34
+ };
35
+ }
36
+ };
37
+ const initializeFilePath = async ()=>{
38
+ if (filePath) return;
39
+ try {
40
+ filePath = await generateTelemetryFilePath(sessionId);
41
+ telemetryStoreDebug('Generated file path: %s', filePath);
42
+ await mkdir(dirname(filePath), {
43
+ recursive: true
44
+ });
45
+ telemetryStoreDebug('Created directory structure for: %s', filePath);
46
+ } catch (error) {
47
+ telemetryStoreDebug('Failed to initialize file path: %o', error);
48
+ filePath = null;
49
+ }
50
+ };
51
+ const emit = (event)=>{
52
+ if (!cachedConsent || cachedConsent.status !== 'granted') {
53
+ if (cachedConsent) {
54
+ telemetryStoreDebug('Cached consent not granted (%s), skipping event: %s', cachedConsent.status, event.type);
55
+ } else {
56
+ telemetryStoreDebug('Consent not resolved, skipping event: %s', event.type);
57
+ }
58
+ return;
59
+ }
60
+ if (!filePath) {
61
+ telemetryStoreDebug('File path not initialized, skipping event: %s', event.type);
62
+ return;
63
+ }
64
+ telemetryStoreDebug('Emitting event: %s', event.type);
65
+ try {
66
+ const eventLine = JSON.stringify(event) + '\n';
67
+ // We use synchronous file writes to ensure telemetry events are captured even when
68
+ // the process exits abruptly (process.exit, uncaught exceptions, SIGTERM, etc.).
69
+ // The performance impact is probably negligible and is worth the trade-off
70
+ // for 100% reliability. Async writes would be lost when the event loop
71
+ // shuts down during process exit.
72
+ appendFileSync(filePath, eventLine, 'utf8');
73
+ telemetryStoreDebug('Successfully wrote event to file: %s', filePath);
74
+ } catch (error) {
75
+ telemetryStoreDebug('Failed to write telemetry event: %o', error);
76
+ // Silent failure - don't break CLI functionality
77
+ }
78
+ };
79
+ const logger = createLogger(sessionId, emit);
80
+ // Initialize both consent and file path concurrently
81
+ Promise.allSettled([
82
+ initializeConsent(),
83
+ initializeFilePath()
84
+ ]).then((results)=>{
85
+ for (const [index, result] of results.entries()){
86
+ if (result.status === 'rejected') {
87
+ const type = index === 0 ? 'consent' : 'file path';
88
+ telemetryStoreDebug('Error initializing %s: %o', type, result.reason);
89
+ }
90
+ }
91
+ });
92
+ return logger;
93
+ }
94
+
95
+ //# sourceMappingURL=createTelemetryStore.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../src/telemetry/createTelemetryStore.ts"],"sourcesContent":["import {appendFileSync} from 'node:fs'\nimport {mkdir} from 'node:fs/promises'\nimport {dirname} from 'node:path'\n\nimport {type TelemetryEvent} from '@sanity/telemetry'\n\nimport {generateTelemetryFilePath} from './generateTelemetryFilePath.js'\nimport {createLogger} from './logger.js'\nimport {telemetryStoreDebug} from './telemetryStoreDebug.js'\nimport {type CLITelemetryStore, type ConsentInformation, TelemetryUserProperties} from './types.js'\n\n/**\n * FILE MANAGEMENT STRATEGY:\n *\n * The telemetry system uses a multi-file approach to handle concurrent CLI processes:\n *\n * 1. WRITING (per session):\n * - Each CLI session gets a unique file: telemetry-\\{hash\\}-\\{env\\}-\\{sessionId\\}.ndjson\n * - Prevents write conflicts when multiple CLI commands run simultaneously\n * - Events are written using an RxJS queue for ordered processing with retry logic\n *\n * 2. FLUSHING (aggregate all sessions):\n * - findTelemetryFiles() discovers ALL telemetry files for user/environment\n * - Events are collected from all session files and sent as a batch\n * - Files are deleted after successful transmission\n *\n * 3. CLEANUP (background maintenance):\n * - cleanupOldTelemetryFiles() removes stale files older than 7 days\n * - Prevents disk space accumulation from abandoned sessions\n */\n\ninterface CreateTelemetryStoreOptions {\n resolveConsent: () => Promise<ConsentInformation>\n}\n\n/**\n * Creates a file-based telemetry store with cached consent and reliable synchronous I/O.\n *\n * Key optimizations:\n * - Consent resolved once at creation and cached (vs checking on every emit)\n * - File path generated and directory created once during initialization\n * - Synchronous file writes to ensure events are captured even during process exit\n *\n * @param sessionId - Unique session identifier for file isolation\n * @param options - Configuration options\n * @returns TelemetryStore instance compatible with the telemetry interface\n *\n * @internal\n */\nexport function createTelemetryStore(\n sessionId: string,\n options: CreateTelemetryStoreOptions,\n): CLITelemetryStore {\n telemetryStoreDebug('Creating telemetry store with sessionId: %s', sessionId)\n\n let cachedConsent: ConsentInformation | null = null\n let filePath: string | null = null\n\n const initializeConsent = async () => {\n if (cachedConsent) return\n\n try {\n cachedConsent = await options.resolveConsent()\n telemetryStoreDebug('Cached consent status: %s', cachedConsent.status)\n } catch (error) {\n telemetryStoreDebug('Failed to initialize consent, treating as undetermined: %o', error)\n cachedConsent = {reason: 'fetchError', status: 'undetermined'}\n }\n }\n\n const initializeFilePath = async () => {\n if (filePath) return\n\n try {\n filePath = await generateTelemetryFilePath(sessionId)\n telemetryStoreDebug('Generated file path: %s', filePath)\n\n await mkdir(dirname(filePath), {recursive: true})\n telemetryStoreDebug('Created directory structure for: %s', filePath)\n } catch (error) {\n telemetryStoreDebug('Failed to initialize file path: %o', error)\n filePath = null\n }\n }\n\n const emit = (event: TelemetryEvent) => {\n if (!cachedConsent || cachedConsent.status !== 'granted') {\n if (cachedConsent) {\n telemetryStoreDebug(\n 'Cached consent not granted (%s), skipping event: %s',\n cachedConsent.status,\n event.type,\n )\n } else {\n telemetryStoreDebug('Consent not resolved, skipping event: %s', event.type)\n }\n return\n }\n\n if (!filePath) {\n telemetryStoreDebug('File path not initialized, skipping event: %s', event.type)\n return\n }\n\n telemetryStoreDebug('Emitting event: %s', event.type)\n\n try {\n const eventLine = JSON.stringify(event) + '\\n'\n\n // We use synchronous file writes to ensure telemetry events are captured even when\n // the process exits abruptly (process.exit, uncaught exceptions, SIGTERM, etc.).\n // The performance impact is probably negligible and is worth the trade-off\n // for 100% reliability. Async writes would be lost when the event loop\n // shuts down during process exit.\n appendFileSync(filePath, eventLine, 'utf8')\n telemetryStoreDebug('Successfully wrote event to file: %s', filePath)\n } catch (error) {\n telemetryStoreDebug('Failed to write telemetry event: %o', error)\n // Silent failure - don't break CLI functionality\n }\n }\n\n const logger = createLogger<TelemetryUserProperties>(sessionId, emit)\n\n // Initialize both consent and file path concurrently\n Promise.allSettled([initializeConsent(), initializeFilePath()]).then((results) => {\n for (const [index, result] of results.entries()) {\n if (result.status === 'rejected') {\n const type = index === 0 ? 'consent' : 'file path'\n telemetryStoreDebug('Error initializing %s: %o', type, result.reason)\n }\n }\n })\n\n return logger\n}\n"],"names":["appendFileSync","mkdir","dirname","generateTelemetryFilePath","createLogger","telemetryStoreDebug","createTelemetryStore","sessionId","options","cachedConsent","filePath","initializeConsent","resolveConsent","status","error","reason","initializeFilePath","recursive","emit","event","type","eventLine","JSON","stringify","logger","Promise","allSettled","then","results","index","result","entries"],"mappings":"AAAA,SAAQA,cAAc,QAAO,UAAS;AACtC,SAAQC,KAAK,QAAO,mBAAkB;AACtC,SAAQC,OAAO,QAAO,YAAW;AAIjC,SAAQC,yBAAyB,QAAO,iCAAgC;AACxE,SAAQC,YAAY,QAAO,cAAa;AACxC,SAAQC,mBAAmB,QAAO,2BAA0B;AA2B5D;;;;;;;;;;;;;CAaC,GACD,OAAO,SAASC,qBACdC,SAAiB,EACjBC,OAAoC;IAEpCH,oBAAoB,+CAA+CE;IAEnE,IAAIE,gBAA2C;IAC/C,IAAIC,WAA0B;IAE9B,MAAMC,oBAAoB;QACxB,IAAIF,eAAe;QAEnB,IAAI;YACFA,gBAAgB,MAAMD,QAAQI,cAAc;YAC5CP,oBAAoB,6BAA6BI,cAAcI,MAAM;QACvE,EAAE,OAAOC,OAAO;YACdT,oBAAoB,8DAA8DS;YAClFL,gBAAgB;gBAACM,QAAQ;gBAAcF,QAAQ;YAAc;QAC/D;IACF;IAEA,MAAMG,qBAAqB;QACzB,IAAIN,UAAU;QAEd,IAAI;YACFA,WAAW,MAAMP,0BAA0BI;YAC3CF,oBAAoB,2BAA2BK;YAE/C,MAAMT,MAAMC,QAAQQ,WAAW;gBAACO,WAAW;YAAI;YAC/CZ,oBAAoB,uCAAuCK;QAC7D,EAAE,OAAOI,OAAO;YACdT,oBAAoB,sCAAsCS;YAC1DJ,WAAW;QACb;IACF;IAEA,MAAMQ,OAAO,CAACC;QACZ,IAAI,CAACV,iBAAiBA,cAAcI,MAAM,KAAK,WAAW;YACxD,IAAIJ,eAAe;gBACjBJ,oBACE,uDACAI,cAAcI,MAAM,EACpBM,MAAMC,IAAI;YAEd,OAAO;gBACLf,oBAAoB,4CAA4Cc,MAAMC,IAAI;YAC5E;YACA;QACF;QAEA,IAAI,CAACV,UAAU;YACbL,oBAAoB,iDAAiDc,MAAMC,IAAI;YAC/E;QACF;QAEAf,oBAAoB,sBAAsBc,MAAMC,IAAI;QAEpD,IAAI;YACF,MAAMC,YAAYC,KAAKC,SAAS,CAACJ,SAAS;YAE1C,mFAAmF;YACnF,iFAAiF;YACjF,2EAA2E;YAC3E,uEAAuE;YACvE,kCAAkC;YAClCnB,eAAeU,UAAUW,WAAW;YACpChB,oBAAoB,wCAAwCK;QAC9D,EAAE,OAAOI,OAAO;YACdT,oBAAoB,uCAAuCS;QAC3D,iDAAiD;QACnD;IACF;IAEA,MAAMU,SAASpB,aAAsCG,WAAWW;IAEhE,qDAAqD;IACrDO,QAAQC,UAAU,CAAC;QAACf;QAAqBK;KAAqB,EAAEW,IAAI,CAAC,CAACC;QACpE,KAAK,MAAM,CAACC,OAAOC,OAAO,IAAIF,QAAQG,OAAO,GAAI;YAC/C,IAAID,OAAOjB,MAAM,KAAK,YAAY;gBAChC,MAAMO,OAAOS,UAAU,IAAI,YAAY;gBACvCxB,oBAAoB,6BAA6Be,MAAMU,OAAOf,MAAM;YACtE;QACF;IACF;IAEA,OAAOS;AACT"}
@@ -0,0 +1,10 @@
1
+ import { typeid } from 'typeid-js';
2
+ /**
3
+ * Creates a unique trace ID using typeid
4
+ *
5
+ * @internal
6
+ */ export function createTraceId() {
7
+ return typeid('trace').toString();
8
+ }
9
+
10
+ //# sourceMappingURL=createTraceId.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../src/telemetry/createTraceId.ts"],"sourcesContent":["import {typeid} from 'typeid-js'\n\ntype TraceId = string & {__type: 'TraceId'}\n\n/**\n * Creates a unique trace ID using typeid\n *\n * @internal\n */\nexport function createTraceId(): TraceId {\n return typeid('trace').toString() as TraceId\n}\n"],"names":["typeid","createTraceId","toString"],"mappings":"AAAA,SAAQA,MAAM,QAAO,YAAW;AAIhC;;;;CAIC,GACD,OAAO,SAASC;IACd,OAAOD,OAAO,SAASE,QAAQ;AACjC"}