@sanity/cli-core 0.1.0-alpha.9 → 1.0.1

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 (108) hide show
  1. package/dist/SanityCommand.js +81 -5
  2. package/dist/SanityCommand.js.map +1 -1
  3. package/dist/_exports/package-manager.d.ts +33 -0
  4. package/dist/_exports/package-manager.js +3 -0
  5. package/dist/_exports/package-manager.js.map +1 -0
  6. package/dist/_exports/request.d.ts +79 -0
  7. package/dist/_exports/request.js +7 -0
  8. package/dist/_exports/request.js.map +1 -0
  9. package/dist/_exports/ux.d.ts +61 -26
  10. package/dist/_exports/ux.js +1 -0
  11. package/dist/_exports/ux.js.map +1 -1
  12. package/dist/config/cli/getCliConfig.js +33 -33
  13. package/dist/config/cli/getCliConfig.js.map +1 -1
  14. package/dist/config/cli/getCliConfigSync.js +1 -1
  15. package/dist/config/cli/getCliConfigSync.js.map +1 -1
  16. package/dist/config/cli/schemas.js +7 -0
  17. package/dist/config/cli/schemas.js.map +1 -1
  18. package/dist/config/cli/types/cliConfig.js.map +1 -1
  19. package/dist/config/findProjectRoot.js +6 -2
  20. package/dist/config/findProjectRoot.js.map +1 -1
  21. package/dist/config/findProjectRootSync.js +7 -3
  22. package/dist/config/findProjectRootSync.js.map +1 -1
  23. package/dist/config/studio/getStudioWorkspaces.js +14 -1
  24. package/dist/config/studio/getStudioWorkspaces.js.map +1 -1
  25. package/dist/config/studio/readStudioConfig.worker.js +6 -5
  26. package/dist/config/studio/readStudioConfig.worker.js.map +1 -1
  27. package/dist/errors/NonInteractiveError.js +18 -0
  28. package/dist/errors/NonInteractiveError.js.map +1 -0
  29. package/dist/{util → errors}/NotFoundError.js +1 -1
  30. package/dist/errors/NotFoundError.js.map +1 -0
  31. package/dist/errors/ProjectRootNotFoundError.js +35 -0
  32. package/dist/errors/ProjectRootNotFoundError.js.map +1 -0
  33. package/dist/index.d.ts +939 -5689
  34. package/dist/index.js +8 -14
  35. package/dist/index.js.map +1 -1
  36. package/dist/loaders/studio/studioWorkerLoader.worker.js +102 -23
  37. package/dist/loaders/studio/studioWorkerLoader.worker.js.map +1 -1
  38. package/dist/loaders/studio/studioWorkerTask.js +18 -27
  39. package/dist/loaders/studio/studioWorkerTask.js.map +1 -1
  40. package/dist/loaders/tsx/tsxWorkerLoader.worker.js +3 -4
  41. package/dist/loaders/tsx/tsxWorkerLoader.worker.js.map +1 -1
  42. package/dist/loaders/tsx/tsxWorkerTask.js +4 -34
  43. package/dist/loaders/tsx/tsxWorkerTask.js.map +1 -1
  44. package/dist/request/createRequester.js +83 -0
  45. package/dist/request/createRequester.js.map +1 -0
  46. package/dist/services/apiClient.js +7 -3
  47. package/dist/services/apiClient.js.map +1 -1
  48. package/dist/services/cliUserConfig.js +5 -5
  49. package/dist/services/cliUserConfig.js.map +1 -1
  50. package/dist/services/getCliToken.js +2 -2
  51. package/dist/services/getCliToken.js.map +1 -1
  52. package/dist/util/doImport.js +2 -1
  53. package/dist/util/doImport.js.map +1 -1
  54. package/dist/util/environment/mockBrowserEnvironment.js +1 -0
  55. package/dist/util/environment/mockBrowserEnvironment.js.map +1 -1
  56. package/dist/util/getCliTelemetry.js +6 -8
  57. package/dist/util/getCliTelemetry.js.map +1 -1
  58. package/dist/util/getSanityUrl.js +4 -3
  59. package/dist/util/getSanityUrl.js.map +1 -1
  60. package/dist/util/importModule.js +32 -0
  61. package/dist/util/importModule.js.map +1 -0
  62. package/dist/util/isInteractive.js +1 -1
  63. package/dist/util/isInteractive.js.map +1 -1
  64. package/dist/util/packageManager.js +55 -0
  65. package/dist/util/packageManager.js.map +1 -0
  66. package/dist/util/promisifyWorker.js +72 -0
  67. package/dist/util/promisifyWorker.js.map +1 -0
  68. package/dist/util/readPackageJson.js +74 -0
  69. package/dist/util/readPackageJson.js.map +1 -0
  70. package/dist/ux/prompts.js +49 -1
  71. package/dist/ux/prompts.js.map +1 -1
  72. package/package.json +44 -40
  73. package/dist/_exports/tree.d.ts +0 -47
  74. package/dist/_exports/tree.js +0 -3
  75. package/dist/_exports/tree.js.map +0 -1
  76. package/dist/config/cli/getCliConfig.worker.js +0 -15
  77. package/dist/config/cli/getCliConfig.worker.js.map +0 -1
  78. package/dist/telemetry/cleanupOldTelemetryFiles.js +0 -30
  79. package/dist/telemetry/cleanupOldTelemetryFiles.js.map +0 -1
  80. package/dist/telemetry/createTelemetryStore.js +0 -95
  81. package/dist/telemetry/createTelemetryStore.js.map +0 -1
  82. package/dist/telemetry/createTraceId.js +0 -10
  83. package/dist/telemetry/createTraceId.js.map +0 -1
  84. package/dist/telemetry/findTelemetryFiles.js +0 -36
  85. package/dist/telemetry/findTelemetryFiles.js.map +0 -1
  86. package/dist/telemetry/flushTelemetryFiles.js +0 -107
  87. package/dist/telemetry/flushTelemetryFiles.js.map +0 -1
  88. package/dist/telemetry/generateTelemetryFilePath.js +0 -30
  89. package/dist/telemetry/generateTelemetryFilePath.js.map +0 -1
  90. package/dist/telemetry/logger.js +0 -54
  91. package/dist/telemetry/logger.js.map +0 -1
  92. package/dist/telemetry/telemetryStoreDebug.js +0 -7
  93. package/dist/telemetry/telemetryStoreDebug.js.map +0 -1
  94. package/dist/telemetry/trace.js +0 -150
  95. package/dist/telemetry/trace.js.map +0 -1
  96. package/dist/util/NotFoundError.js.map +0 -1
  97. package/dist/util/createExpiringConfig.js +0 -60
  98. package/dist/util/createExpiringConfig.js.map +0 -1
  99. package/dist/util/parseStringFlag.js +0 -19
  100. package/dist/util/parseStringFlag.js.map +0 -1
  101. package/dist/util/tree.js +0 -108
  102. package/dist/util/tree.js.map +0 -1
  103. package/dist/util/waitForAsync.js +0 -5
  104. package/dist/util/waitForAsync.js.map +0 -1
  105. package/dist/ux/formatObject.js +0 -9
  106. package/dist/ux/formatObject.js.map +0 -1
  107. package/dist/ux/printKeyValue.js +0 -16
  108. package/dist/ux/printKeyValue.js.map +0 -1
@@ -1,3 +1,51 @@
1
- export * from '@inquirer/prompts';
1
+ import * as inquirer from '@inquirer/prompts';
2
+ import { NonInteractiveError } from '../errors/NonInteractiveError.js';
3
+ import { isInteractive } from '../util/isInteractive.js';
4
+ export { Separator } from '@inquirer/prompts';
5
+ function assertInteractive(promptName) {
6
+ if (!isInteractive()) {
7
+ throw new NonInteractiveError(promptName);
8
+ }
9
+ }
10
+ export const checkbox = (...args)=>{
11
+ assertInteractive('checkbox');
12
+ return inquirer.checkbox(...args);
13
+ };
14
+ export const confirm = (...args)=>{
15
+ assertInteractive('confirm');
16
+ return inquirer.confirm(...args);
17
+ };
18
+ export const editor = (...args)=>{
19
+ assertInteractive('editor');
20
+ return inquirer.editor(...args);
21
+ };
22
+ export const expand = (...args)=>{
23
+ assertInteractive('expand');
24
+ return inquirer.expand(...args);
25
+ };
26
+ export const input = (...args)=>{
27
+ assertInteractive('input');
28
+ return inquirer.input(...args);
29
+ };
30
+ export const number = (...args)=>{
31
+ assertInteractive('number');
32
+ return inquirer.number(...args);
33
+ };
34
+ export const password = (...args)=>{
35
+ assertInteractive('password');
36
+ return inquirer.password(...args);
37
+ };
38
+ export const rawlist = (...args)=>{
39
+ assertInteractive('rawlist');
40
+ return inquirer.rawlist(...args);
41
+ };
42
+ export const search = (...args)=>{
43
+ assertInteractive('search');
44
+ return inquirer.search(...args);
45
+ };
46
+ export const select = (...args)=>{
47
+ assertInteractive('select');
48
+ return inquirer.select(...args);
49
+ };
2
50
 
3
51
  //# sourceMappingURL=prompts.js.map
@@ -1 +1 @@
1
- {"version":3,"sources":["../../src/ux/prompts.ts"],"sourcesContent":["export * from '@inquirer/prompts'\n"],"names":[],"mappings":"AAAA,cAAc,oBAAmB"}
1
+ {"version":3,"sources":["../../src/ux/prompts.ts"],"sourcesContent":["import * as inquirer from '@inquirer/prompts'\n\nimport {NonInteractiveError} from '../errors/NonInteractiveError.js'\nimport {isInteractive} from '../util/isInteractive.js'\n\nexport {Separator} from '@inquirer/prompts'\n\nfunction assertInteractive(promptName: string): void {\n if (!isInteractive()) {\n throw new NonInteractiveError(promptName)\n }\n}\n\nexport const checkbox: typeof inquirer.checkbox = (...args) => {\n assertInteractive('checkbox')\n return inquirer.checkbox(...args)\n}\n\nexport const confirm: typeof inquirer.confirm = (...args) => {\n assertInteractive('confirm')\n return inquirer.confirm(...args)\n}\n\nexport const editor: typeof inquirer.editor = (...args) => {\n assertInteractive('editor')\n return inquirer.editor(...args)\n}\n\nexport const expand: typeof inquirer.expand = (...args) => {\n assertInteractive('expand')\n return inquirer.expand(...args)\n}\n\nexport const input: typeof inquirer.input = (...args) => {\n assertInteractive('input')\n return inquirer.input(...args)\n}\n\nexport const number: typeof inquirer.number = (...args) => {\n assertInteractive('number')\n return inquirer.number(...args)\n}\n\nexport const password: typeof inquirer.password = (...args) => {\n assertInteractive('password')\n return inquirer.password(...args)\n}\n\nexport const rawlist: typeof inquirer.rawlist = (...args) => {\n assertInteractive('rawlist')\n return inquirer.rawlist(...args)\n}\n\nexport const search: typeof inquirer.search = (...args) => {\n assertInteractive('search')\n return inquirer.search(...args)\n}\n\nexport const select: typeof inquirer.select = (...args) => {\n assertInteractive('select')\n return inquirer.select(...args)\n}\n"],"names":["inquirer","NonInteractiveError","isInteractive","Separator","assertInteractive","promptName","checkbox","args","confirm","editor","expand","input","number","password","rawlist","search","select"],"mappings":"AAAA,YAAYA,cAAc,oBAAmB;AAE7C,SAAQC,mBAAmB,QAAO,mCAAkC;AACpE,SAAQC,aAAa,QAAO,2BAA0B;AAEtD,SAAQC,SAAS,QAAO,oBAAmB;AAE3C,SAASC,kBAAkBC,UAAkB;IAC3C,IAAI,CAACH,iBAAiB;QACpB,MAAM,IAAID,oBAAoBI;IAChC;AACF;AAEA,OAAO,MAAMC,WAAqC,CAAC,GAAGC;IACpDH,kBAAkB;IAClB,OAAOJ,SAASM,QAAQ,IAAIC;AAC9B,EAAC;AAED,OAAO,MAAMC,UAAmC,CAAC,GAAGD;IAClDH,kBAAkB;IAClB,OAAOJ,SAASQ,OAAO,IAAID;AAC7B,EAAC;AAED,OAAO,MAAME,SAAiC,CAAC,GAAGF;IAChDH,kBAAkB;IAClB,OAAOJ,SAASS,MAAM,IAAIF;AAC5B,EAAC;AAED,OAAO,MAAMG,SAAiC,CAAC,GAAGH;IAChDH,kBAAkB;IAClB,OAAOJ,SAASU,MAAM,IAAIH;AAC5B,EAAC;AAED,OAAO,MAAMI,QAA+B,CAAC,GAAGJ;IAC9CH,kBAAkB;IAClB,OAAOJ,SAASW,KAAK,IAAIJ;AAC3B,EAAC;AAED,OAAO,MAAMK,SAAiC,CAAC,GAAGL;IAChDH,kBAAkB;IAClB,OAAOJ,SAASY,MAAM,IAAIL;AAC5B,EAAC;AAED,OAAO,MAAMM,WAAqC,CAAC,GAAGN;IACpDH,kBAAkB;IAClB,OAAOJ,SAASa,QAAQ,IAAIN;AAC9B,EAAC;AAED,OAAO,MAAMO,UAAmC,CAAC,GAAGP;IAClDH,kBAAkB;IAClB,OAAOJ,SAASc,OAAO,IAAIP;AAC7B,EAAC;AAED,OAAO,MAAMQ,SAAiC,CAAC,GAAGR;IAChDH,kBAAkB;IAClB,OAAOJ,SAASe,MAAM,IAAIR;AAC5B,EAAC;AAED,OAAO,MAAMS,SAAiC,CAAC,GAAGT;IAChDH,kBAAkB;IAClB,OAAOJ,SAASgB,MAAM,IAAIT;AAC5B,EAAC"}
package/package.json CHANGED
@@ -1,87 +1,94 @@
1
1
  {
2
2
  "name": "@sanity/cli-core",
3
- "version": "0.1.0-alpha.9",
3
+ "version": "1.0.1",
4
4
  "description": "Sanity CLI core package",
5
5
  "keywords": [
6
- "sanity",
6
+ "cli",
7
7
  "cms",
8
+ "content",
8
9
  "headless",
9
10
  "realtime",
10
- "content",
11
- "cli",
11
+ "sanity",
12
12
  "tool"
13
13
  ],
14
14
  "homepage": "https://github.com/sanity-io/cli",
15
15
  "bugs": "https://github.com/sanity-io/cli/issues",
16
+ "license": "MIT",
17
+ "author": "Sanity.io <hello@sanity.io>",
16
18
  "repository": {
17
19
  "type": "git",
18
20
  "url": "git+https://github.com/sanity-io/cli.git",
19
21
  "directory": "packages/@sanity/cli-core"
20
22
  },
21
- "license": "MIT",
22
- "author": "Sanity.io <hello@sanity.io>",
23
- "sideEffects": false,
23
+ "files": [
24
+ "./dist"
25
+ ],
24
26
  "type": "module",
27
+ "sideEffects": false,
28
+ "main": "./dist/index.js",
29
+ "types": "dist/index.d.ts",
25
30
  "exports": {
26
31
  ".": {
27
32
  "source": "./src/index.ts",
28
33
  "default": "./dist/index.js"
29
34
  },
30
- "./tree": {
31
- "source": "./src/_exports/tree.ts",
32
- "default": "./dist/_exports/tree.js"
33
- },
34
35
  "./ux": {
35
36
  "source": "./src/_exports/ux.ts",
36
37
  "default": "./dist/_exports/ux.js"
37
38
  },
39
+ "./request": {
40
+ "source": "./src/_exports/request.ts",
41
+ "default": "./dist/_exports/request.js"
42
+ },
43
+ "./package-manager": {
44
+ "source": "./src/_exports/package-manager.ts",
45
+ "default": "./dist/_exports/package-manager.js"
46
+ },
38
47
  "./package.json": "./package.json"
39
48
  },
40
- "main": "./dist/index.js",
41
- "types": "dist/index.d.ts",
42
- "files": [
43
- "./dist"
44
- ],
49
+ "publishConfig": {
50
+ "access": "public"
51
+ },
45
52
  "dependencies": {
46
- "@inquirer/prompts": "^8.2.0",
47
- "@oclif/core": "^4.8.0",
48
- "@sanity/client": "^7.14.1",
49
- "@sanity/types": "^5.8.1",
53
+ "@inquirer/prompts": "^8.3.0",
54
+ "@oclif/core": "^4.8.3",
55
+ "@rexxars/jiti": "^2.6.2",
56
+ "@sanity/client": "^7.17.0",
50
57
  "babel-plugin-react-compiler": "^1.0.0",
51
58
  "boxen": "^8.0.1",
52
59
  "configstore": "^7.0.0",
53
60
  "debug": "^4.4.3",
54
- "get-tsconfig": "^4.13.1",
55
- "import-meta-resolve": "^4.1.0",
56
- "jsdom": "^27.4.0",
61
+ "get-it": "^8.7.0",
62
+ "get-tsconfig": "^4.13.6",
63
+ "import-meta-resolve": "^4.2.0",
64
+ "jsdom": "^28.1.0",
57
65
  "json-lexer": "^1.2.0",
58
66
  "log-symbols": "^7.0.1",
59
67
  "ora": "^9.0.0",
68
+ "read-package-up": "^12.0.0",
60
69
  "rxjs": "^7.8.2",
61
- "tinyglobby": "^0.2.15",
62
70
  "tsx": "^4.21.0",
63
- "typeid-js": "^1.2.0",
64
71
  "vite": "^7.3.1",
72
+ "vite-node": "^5.3.0",
65
73
  "zod": "^4.3.6"
66
74
  },
67
75
  "devDependencies": {
68
- "@eslint/compat": "^2.0.2",
69
- "@sanity/codegen": "^5.9.2",
70
- "@sanity/pkg-utils": "^10.4.4",
76
+ "@eslint/compat": "^2.0.3",
77
+ "@sanity/pkg-utils": "^10.4.8",
71
78
  "@sanity/telemetry": "^0.8.1",
72
- "@swc/cli": "^0.7.10",
73
- "@swc/core": "^1.15.11",
79
+ "@swc/cli": "^0.8.0",
80
+ "@swc/core": "^1.15.18",
74
81
  "@types/debug": "^4.1.12",
75
- "@types/jsdom": "^27.0.0",
76
- "@types/node": "^20.19.30",
77
- "eslint": "^9.39.2",
78
- "publint": "^0.3.17",
79
- "sanity": "^5.8.1",
82
+ "@types/jsdom": "^28.0.0",
83
+ "@types/node": "^20.19.37",
84
+ "eslint": "^9.39.4",
85
+ "publint": "^0.3.18",
86
+ "sanity": "^5.14.1",
80
87
  "typescript": "^5.9.3",
81
88
  "vitest": "^4.0.18",
82
- "@sanity/eslint-config-cli": "0.0.0-alpha.1",
83
89
  "@repo/tsconfig": "3.70.0",
84
- "@repo/package.config": "0.0.1"
90
+ "@repo/package.config": "0.0.1",
91
+ "@sanity/eslint-config-cli": "1.0.0"
85
92
  },
86
93
  "peerDependencies": {
87
94
  "@sanity/telemetry": ">=0.8.1 <0.9.0"
@@ -89,9 +96,6 @@
89
96
  "engines": {
90
97
  "node": ">=20.19.1 <22 || >=22.12"
91
98
  },
92
- "publishConfig": {
93
- "access": "public"
94
- },
95
99
  "scripts": {
96
100
  "build": "swc --delete-dir-on-start --strip-leading-paths --out-dir dist/ src --ignore '**/*.test.ts'",
97
101
  "build:types": "pkg-utils build --emitDeclarationOnly",
@@ -1,47 +0,0 @@
1
- import {Path} from '@sanity/types'
2
-
3
- declare interface BaseNode {
4
- path: Path
5
- }
6
-
7
- /**
8
- * Converts a set of markers with paths into a tree of markers where the paths
9
- * are embedded in the tree
10
- */
11
- export declare function convertToTree<const Node extends BaseNode>(nodes: Node[]): Tree<Node>
12
-
13
- /**
14
- * Recursively formats a given tree into a printed user-friendly tree structure
15
- */
16
- export declare const formatTree: <Node extends BaseNode>({
17
- getMessage,
18
- getNodes: getLeaves,
19
- indent,
20
- node,
21
- paddingLength,
22
- }: Options<Node>) => string
23
-
24
- /**
25
- * Recursively calculates the max length of all the keys in the given validation
26
- * tree respecting extra length due to indentation depth. Used to calculate the
27
- * padding for the rest of the tree.
28
- */
29
- export declare const maxKeyLength: (
30
- children?: Record<string, Tree<BaseNode>>,
31
- depth?: number,
32
- ) => number
33
-
34
- declare interface Options<Node extends BaseNode> {
35
- getMessage: (node: Node) => string
36
- paddingLength: number
37
- getNodes?: (node: Tree<Node>) => Node[] | undefined
38
- indent?: string
39
- node?: Record<string, Tree<Node>>
40
- }
41
-
42
- export declare interface Tree<Node extends BaseNode> {
43
- children?: Record<string, Tree<Node>>
44
- nodes?: Node[]
45
- }
46
-
47
- export {}
@@ -1,3 +0,0 @@
1
- export * from '../util/tree.js';
2
-
3
- //# sourceMappingURL=tree.js.map
@@ -1 +0,0 @@
1
- {"version":3,"sources":["../../src/_exports/tree.ts"],"sourcesContent":["export * from '../util/tree.js'\n"],"names":[],"mappings":"AAAA,cAAc,kBAAiB"}
@@ -1,15 +0,0 @@
1
- import { isMainThread, parentPort, workerData } from 'node:worker_threads';
2
- import { z } from 'zod';
3
- import { doImport } from '../../util/doImport.js';
4
- if (isMainThread || !parentPort) {
5
- throw new Error('Should only be run in a worker!');
6
- }
7
- const { configPath } = z.object({
8
- configPath: z.string()
9
- }).parse(workerData);
10
- const { default: loadedConfig } = await doImport(configPath);
11
- // This might throw on unserializable properties, but that's actually wanted in this
12
- // case, since we _need_ any unserializable props (such as vite config functions)
13
- parentPort.postMessage(loadedConfig);
14
-
15
- //# sourceMappingURL=getCliConfig.worker.js.map
@@ -1 +0,0 @@
1
- {"version":3,"sources":["../../../src/config/cli/getCliConfig.worker.ts"],"sourcesContent":["import {isMainThread, parentPort, workerData} from 'node:worker_threads'\n\nimport {z} from 'zod'\n\nimport {doImport} from '../../util/doImport.js'\n\nif (isMainThread || !parentPort) {\n throw new Error('Should only be run in a worker!')\n}\n\nconst {configPath} = z.object({configPath: z.string()}).parse(workerData)\n\nconst {default: loadedConfig} = await doImport(configPath)\n\n// This might throw on unserializable properties, but that's actually wanted in this\n// case, since we _need_ any unserializable props (such as vite config functions)\nparentPort.postMessage(loadedConfig)\n"],"names":["isMainThread","parentPort","workerData","z","doImport","Error","configPath","object","string","parse","default","loadedConfig","postMessage"],"mappings":"AAAA,SAAQA,YAAY,EAAEC,UAAU,EAAEC,UAAU,QAAO,sBAAqB;AAExE,SAAQC,CAAC,QAAO,MAAK;AAErB,SAAQC,QAAQ,QAAO,yBAAwB;AAE/C,IAAIJ,gBAAgB,CAACC,YAAY;IAC/B,MAAM,IAAII,MAAM;AAClB;AAEA,MAAM,EAACC,UAAU,EAAC,GAAGH,EAAEI,MAAM,CAAC;IAACD,YAAYH,EAAEK,MAAM;AAAE,GAAGC,KAAK,CAACP;AAE9D,MAAM,EAACQ,SAASC,YAAY,EAAC,GAAG,MAAMP,SAASE;AAE/C,oFAAoF;AACpF,iFAAiF;AACjFL,WAAWW,WAAW,CAACD"}
@@ -1,30 +0,0 @@
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
@@ -1 +0,0 @@
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"}
@@ -1,95 +0,0 @@
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
@@ -1 +0,0 @@
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"}
@@ -1,10 +0,0 @@
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
@@ -1 +0,0 @@
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"}
@@ -1,36 +0,0 @@
1
- import { join } from 'node:path';
2
- import { glob } from 'tinyglobby';
3
- import { normalizePath } from '../util/normalizePath.js';
4
- import { getTelemetryBaseInfo } from './getTelemetryBaseInfo.js';
5
- import { telemetryStoreDebug } from './telemetryStoreDebug.js';
6
- /**
7
- * Discovers and returns paths to all telemetry files for the current user/environment.
8
- *
9
- * This function is used during:
10
- * - Flush operations: to collect and send events from all CLI sessions
11
- * - Cleanup operations: to find old files that should be removed
12
- *
13
- * Uses glob patterns to match files across all sessions (not just the current one).
14
- *
15
- * @returns Promise resolving to array of file paths, empty if no files exist
16
- * @internal
17
- */ export async function findTelemetryFiles() {
18
- try {
19
- const { basePattern, directory } = await getTelemetryBaseInfo();
20
- const pattern = `${basePattern}-*.ndjson`;
21
- const fullPattern = join(directory, pattern);
22
- telemetryStoreDebug('Looking for files matching pattern: %s', fullPattern);
23
- // Converts windows backslashes to forward slashes for glob pattern
24
- const matchingFiles = await glob(normalizePath(fullPattern));
25
- telemetryStoreDebug('Found %d matching telemetry files', matchingFiles.length);
26
- return matchingFiles;
27
- } catch (error) {
28
- if (error.code === 'ENOENT') {
29
- telemetryStoreDebug('Telemetry directory does not exist yet');
30
- return [];
31
- }
32
- throw error;
33
- }
34
- }
35
-
36
- //# sourceMappingURL=findTelemetryFiles.js.map
@@ -1 +0,0 @@
1
- {"version":3,"sources":["../../src/telemetry/findTelemetryFiles.ts"],"sourcesContent":["import {join} from 'node:path'\n\nimport {glob} from 'tinyglobby'\n\nimport {normalizePath} from '../util/normalizePath.js'\nimport {getTelemetryBaseInfo} from './getTelemetryBaseInfo.js'\nimport {telemetryStoreDebug} from './telemetryStoreDebug.js'\n\n/**\n * Discovers and returns paths to all telemetry files for the current user/environment.\n *\n * This function is used during:\n * - Flush operations: to collect and send events from all CLI sessions\n * - Cleanup operations: to find old files that should be removed\n *\n * Uses glob patterns to match files across all sessions (not just the current one).\n *\n * @returns Promise resolving to array of file paths, empty if no files exist\n * @internal\n */\nexport async function findTelemetryFiles(): Promise<string[]> {\n try {\n const {basePattern, directory} = await getTelemetryBaseInfo()\n const pattern = `${basePattern}-*.ndjson`\n const fullPattern = join(directory, pattern)\n telemetryStoreDebug('Looking for files matching pattern: %s', fullPattern)\n\n // Converts windows backslashes to forward slashes for glob pattern\n const matchingFiles = await glob(normalizePath(fullPattern))\n telemetryStoreDebug('Found %d matching telemetry files', matchingFiles.length)\n return matchingFiles\n } catch (error) {\n if ((error as {code?: string}).code === 'ENOENT') {\n telemetryStoreDebug('Telemetry directory does not exist yet')\n return []\n }\n throw error\n }\n}\n"],"names":["join","glob","normalizePath","getTelemetryBaseInfo","telemetryStoreDebug","findTelemetryFiles","basePattern","directory","pattern","fullPattern","matchingFiles","length","error","code"],"mappings":"AAAA,SAAQA,IAAI,QAAO,YAAW;AAE9B,SAAQC,IAAI,QAAO,aAAY;AAE/B,SAAQC,aAAa,QAAO,2BAA0B;AACtD,SAAQC,oBAAoB,QAAO,4BAA2B;AAC9D,SAAQC,mBAAmB,QAAO,2BAA0B;AAE5D;;;;;;;;;;;CAWC,GACD,OAAO,eAAeC;IACpB,IAAI;QACF,MAAM,EAACC,WAAW,EAAEC,SAAS,EAAC,GAAG,MAAMJ;QACvC,MAAMK,UAAU,GAAGF,YAAY,SAAS,CAAC;QACzC,MAAMG,cAAcT,KAAKO,WAAWC;QACpCJ,oBAAoB,0CAA0CK;QAE9D,mEAAmE;QACnE,MAAMC,gBAAgB,MAAMT,KAAKC,cAAcO;QAC/CL,oBAAoB,qCAAqCM,cAAcC,MAAM;QAC7E,OAAOD;IACT,EAAE,OAAOE,OAAO;QACd,IAAI,AAACA,MAA0BC,IAAI,KAAK,UAAU;YAChDT,oBAAoB;YACpB,OAAO,EAAE;QACX;QACA,MAAMQ;IACR;AACF"}
@@ -1,107 +0,0 @@
1
- import { rm } from 'node:fs/promises';
2
- import { catchError, defer, from, lastValueFrom, mergeMap, of, reduce, switchMap, tap } from 'rxjs';
3
- import { readNDJSON } from '../util/readNDJSON.js';
4
- import { cleanupOldTelemetryFiles } from './cleanupOldTelemetryFiles.js';
5
- import { findTelemetryFiles } from './findTelemetryFiles.js';
6
- import { telemetryStoreDebug } from './telemetryStoreDebug.js';
7
- /**
8
- * Standalone, stateless function to flush telemetry files.
9
- *
10
- * This function can be used independently of the telemetry store, making it
11
- * suitable for use in child processes or other contexts where store state
12
- * is not available.
13
- *
14
- * @param options - Configuration for consent resolution and event sending
15
- * @returns Promise that resolves when flush operation is complete
16
- *
17
- * @internal
18
- */ export async function flushTelemetryFiles(options) {
19
- telemetryStoreDebug('Starting standalone flush operation');
20
- // Helper function for deleting files with consistent error handling
21
- const deleteFiles = (files, reason)=>{
22
- if (files.length === 0) {
23
- // of() is not same as of(undefined) in rxjs
24
- // eslint-disable-next-line unicorn/no-useless-undefined
25
- return of(undefined);
26
- }
27
- return from(files).pipe(mergeMap((filePath)=>from(rm(filePath, {
28
- force: true
29
- })).pipe(tap(()=>{
30
- telemetryStoreDebug(`Deleted file ${reason}: %s`, filePath);
31
- }), catchError((error)=>{
32
- telemetryStoreDebug('Error deleting file %s: %o', filePath, error);
33
- // of() is not same as of(undefined) in rxjs
34
- // eslint-disable-next-line unicorn/no-useless-undefined
35
- return of(undefined);
36
- }))), // of() is not same as of(undefined) in rxjs
37
- // eslint-disable-next-line unicorn/no-useless-undefined
38
- switchMap(()=>of(undefined)));
39
- };
40
- const flush$ = defer(()=>from(options.resolveConsent())).pipe(tap((currentConsent)=>{
41
- telemetryStoreDebug('Current consent status for flush: %s', currentConsent.status);
42
- }), switchMap((currentConsent)=>{
43
- // First cleanup old files, then process current files
44
- return defer(()=>from(cleanupOldTelemetryFiles())).pipe(switchMap(()=>defer(()=>from(findTelemetryFiles()))), switchMap((filePaths)=>{
45
- if (filePaths.length === 0) {
46
- telemetryStoreDebug('No telemetry files found, nothing to flush');
47
- return of({
48
- allEvents: [],
49
- consent: currentConsent,
50
- filesToDelete: []
51
- });
52
- }
53
- telemetryStoreDebug('Found %d telemetry files to process', filePaths.length);
54
- return from(filePaths).pipe(mergeMap((filePath)=>{
55
- return defer(()=>from(readNDJSON(filePath))).pipe(tap((events)=>{
56
- telemetryStoreDebug('Read %d events from %s', events.length, filePath);
57
- }), catchError((error)=>{
58
- if (error.code === 'ENOENT') {
59
- telemetryStoreDebug('File %s no longer exists, skipping', filePath);
60
- return of([]);
61
- }
62
- telemetryStoreDebug('Error reading file %s: %o', filePath, error);
63
- return of([]);
64
- }), switchMap((events)=>of({
65
- events,
66
- filePath: events.length > 0 ? filePath : ''
67
- })));
68
- }), reduce((acc, current)=>{
69
- if (current.filePath) {
70
- acc.allEvents.push(...current.events);
71
- acc.filesToDelete.push(current.filePath);
72
- }
73
- return acc;
74
- }, {
75
- allEvents: [],
76
- filesToDelete: []
77
- }), switchMap((result)=>of({
78
- ...result,
79
- consent: currentConsent
80
- })));
81
- }));
82
- }), switchMap(({ allEvents, consent, filesToDelete })=>{
83
- telemetryStoreDebug('Found %d total events to flush from %d files', allEvents.length, filesToDelete.length);
84
- if (consent.status !== 'granted' || allEvents.length === 0) {
85
- if (consent.status === 'granted') {
86
- telemetryStoreDebug('No events to send, cleaning up empty files');
87
- return deleteFiles(filesToDelete, 'empty files');
88
- } else {
89
- telemetryStoreDebug('Consent not granted (%s), cleaning up %d files without sending events', consent.status, filesToDelete.length);
90
- return deleteFiles(filesToDelete, `without sending (consent: ${consent.status})`);
91
- }
92
- }
93
- // Send events and then delete files
94
- telemetryStoreDebug('Sending %d events to backend', allEvents.length);
95
- return defer(()=>from(options.sendEvents(allEvents))).pipe(tap(()=>{
96
- telemetryStoreDebug('Successfully sent events, deleting %d files', filesToDelete.length);
97
- }), switchMap(()=>deleteFiles(filesToDelete, 'after successful send')));
98
- }), tap(()=>{
99
- telemetryStoreDebug('Standalone flush operation completed successfully');
100
- }), switchMap(()=>of(undefined)), catchError((error)=>{
101
- telemetryStoreDebug('Error during standalone flush operation: %o', error);
102
- throw error;
103
- }));
104
- return lastValueFrom(flush$);
105
- }
106
-
107
- //# sourceMappingURL=flushTelemetryFiles.js.map
@@ -1 +0,0 @@
1
- {"version":3,"sources":["../../src/telemetry/flushTelemetryFiles.ts"],"sourcesContent":["import {rm} from 'node:fs/promises'\n\nimport {type TelemetryEvent} from '@sanity/telemetry'\nimport {catchError, defer, from, lastValueFrom, mergeMap, of, reduce, switchMap, tap} from 'rxjs'\n\nimport {readNDJSON} from '../util/readNDJSON.js'\nimport {cleanupOldTelemetryFiles} from './cleanupOldTelemetryFiles.js'\nimport {findTelemetryFiles} from './findTelemetryFiles.js'\nimport {telemetryStoreDebug} from './telemetryStoreDebug.js'\nimport {type ConsentInformation} from './types.js'\n\ninterface FlushTelemetryFilesOptions {\n resolveConsent: () => Promise<ConsentInformation>\n sendEvents: (events: TelemetryEvent[]) => Promise<void>\n}\n\n/**\n * Standalone, stateless function to flush telemetry files.\n *\n * This function can be used independently of the telemetry store, making it\n * suitable for use in child processes or other contexts where store state\n * is not available.\n *\n * @param options - Configuration for consent resolution and event sending\n * @returns Promise that resolves when flush operation is complete\n *\n * @internal\n */\nexport async function flushTelemetryFiles(options: FlushTelemetryFilesOptions): Promise<void> {\n telemetryStoreDebug('Starting standalone flush operation')\n\n // Helper function for deleting files with consistent error handling\n const deleteFiles = (files: string[], reason: string) => {\n if (files.length === 0) {\n // of() is not same as of(undefined) in rxjs\n // eslint-disable-next-line unicorn/no-useless-undefined\n return of(undefined)\n }\n\n return from(files).pipe(\n mergeMap((filePath) =>\n from(rm(filePath, {force: true})).pipe(\n tap(() => {\n telemetryStoreDebug(`Deleted file ${reason}: %s`, filePath)\n }),\n catchError((error) => {\n telemetryStoreDebug('Error deleting file %s: %o', filePath, error)\n // of() is not same as of(undefined) in rxjs\n // eslint-disable-next-line unicorn/no-useless-undefined\n return of(undefined)\n }),\n ),\n ),\n // of() is not same as of(undefined) in rxjs\n // eslint-disable-next-line unicorn/no-useless-undefined\n switchMap(() => of(undefined)),\n )\n }\n\n const flush$ = defer(() => from(options.resolveConsent())).pipe(\n tap((currentConsent) => {\n telemetryStoreDebug('Current consent status for flush: %s', currentConsent.status)\n }),\n switchMap((currentConsent) => {\n // First cleanup old files, then process current files\n return defer(() => from(cleanupOldTelemetryFiles())).pipe(\n switchMap(() => defer(() => from(findTelemetryFiles()))),\n switchMap((filePaths) => {\n if (filePaths.length === 0) {\n telemetryStoreDebug('No telemetry files found, nothing to flush')\n return of({allEvents: [], consent: currentConsent, filesToDelete: []})\n }\n\n telemetryStoreDebug('Found %d telemetry files to process', filePaths.length)\n\n return from(filePaths).pipe(\n mergeMap((filePath) => {\n return defer(() => from(readNDJSON<TelemetryEvent>(filePath))).pipe(\n tap((events) => {\n telemetryStoreDebug('Read %d events from %s', events.length, filePath)\n }),\n catchError((error) => {\n if ((error as {code?: string}).code === 'ENOENT') {\n telemetryStoreDebug('File %s no longer exists, skipping', filePath)\n return of([])\n }\n telemetryStoreDebug('Error reading file %s: %o', filePath, error)\n return of([])\n }),\n switchMap((events) => of({events, filePath: events.length > 0 ? filePath : ''})),\n )\n }),\n reduce(\n (acc: {allEvents: TelemetryEvent[]; filesToDelete: string[]}, current) => {\n if (current.filePath) {\n acc.allEvents.push(...current.events)\n acc.filesToDelete.push(current.filePath)\n }\n return acc\n },\n {allEvents: [], filesToDelete: []},\n ),\n switchMap((result) => of({...result, consent: currentConsent})),\n )\n }),\n )\n }),\n switchMap(({allEvents, consent, filesToDelete}) => {\n telemetryStoreDebug(\n 'Found %d total events to flush from %d files',\n allEvents.length,\n filesToDelete.length,\n )\n\n if (consent.status !== 'granted' || allEvents.length === 0) {\n if (consent.status === 'granted') {\n telemetryStoreDebug('No events to send, cleaning up empty files')\n return deleteFiles(filesToDelete, 'empty files')\n } else {\n telemetryStoreDebug(\n 'Consent not granted (%s), cleaning up %d files without sending events',\n consent.status,\n filesToDelete.length,\n )\n return deleteFiles(filesToDelete, `without sending (consent: ${consent.status})`)\n }\n }\n\n // Send events and then delete files\n telemetryStoreDebug('Sending %d events to backend', allEvents.length)\n\n return defer(() => from(options.sendEvents(allEvents))).pipe(\n tap(() => {\n telemetryStoreDebug('Successfully sent events, deleting %d files', filesToDelete.length)\n }),\n switchMap(() => deleteFiles(filesToDelete, 'after successful send')),\n )\n }),\n tap(() => {\n telemetryStoreDebug('Standalone flush operation completed successfully')\n }),\n switchMap(() => of(undefined as void)),\n catchError((error) => {\n telemetryStoreDebug('Error during standalone flush operation: %o', error)\n throw error\n }),\n )\n\n return lastValueFrom(flush$)\n}\n"],"names":["rm","catchError","defer","from","lastValueFrom","mergeMap","of","reduce","switchMap","tap","readNDJSON","cleanupOldTelemetryFiles","findTelemetryFiles","telemetryStoreDebug","flushTelemetryFiles","options","deleteFiles","files","reason","length","undefined","pipe","filePath","force","error","flush$","resolveConsent","currentConsent","status","filePaths","allEvents","consent","filesToDelete","events","code","acc","current","push","result","sendEvents"],"mappings":"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"}