@stripe/extensibility-tool-utils 0.6.2

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 (105) hide show
  1. package/LICENSE.md +19 -0
  2. package/dist/cli/cli-ux.d.ts +30 -0
  3. package/dist/cli/cli-ux.d.ts.map +1 -0
  4. package/dist/cli/context.d.ts +24 -0
  5. package/dist/cli/context.d.ts.map +1 -0
  6. package/dist/cli/guards.d.ts +24 -0
  7. package/dist/cli/guards.d.ts.map +1 -0
  8. package/dist/cli/index.d.ts +6 -0
  9. package/dist/cli/index.d.ts.map +1 -0
  10. package/dist/cli/output-path.d.ts +11 -0
  11. package/dist/cli/output-path.d.ts.map +1 -0
  12. package/dist/extensibility-tool-utils-alpha.d.ts +383 -0
  13. package/dist/extensibility-tool-utils-beta.d.ts +383 -0
  14. package/dist/extensibility-tool-utils-internal.d.ts +1866 -0
  15. package/dist/extensibility-tool-utils-public.d.ts +383 -0
  16. package/dist/file-editor/assertions/index.d.ts +68 -0
  17. package/dist/file-editor/assertions/index.d.ts.map +1 -0
  18. package/dist/file-editor/document.d.ts +107 -0
  19. package/dist/file-editor/document.d.ts.map +1 -0
  20. package/dist/file-editor/errors.d.ts +66 -0
  21. package/dist/file-editor/errors.d.ts.map +1 -0
  22. package/dist/file-editor/facades/api-extractor.d.ts +34 -0
  23. package/dist/file-editor/facades/api-extractor.d.ts.map +1 -0
  24. package/dist/file-editor/facades/brands.d.ts +45 -0
  25. package/dist/file-editor/facades/brands.d.ts.map +1 -0
  26. package/dist/file-editor/facades/package-json.d.ts +55 -0
  27. package/dist/file-editor/facades/package-json.d.ts.map +1 -0
  28. package/dist/file-editor/facades/stripe-app-manifest.d.ts +62 -0
  29. package/dist/file-editor/facades/stripe-app-manifest.d.ts.map +1 -0
  30. package/dist/file-editor/facades/tsconfig-options.d.ts +76 -0
  31. package/dist/file-editor/facades/tsconfig-options.d.ts.map +1 -0
  32. package/dist/file-editor/facades/tsconfig.d.ts +43 -0
  33. package/dist/file-editor/facades/tsconfig.d.ts.map +1 -0
  34. package/dist/file-editor/fingerprint.d.ts +39 -0
  35. package/dist/file-editor/fingerprint.d.ts.map +1 -0
  36. package/dist/file-editor/formats/adapter.d.ts +29 -0
  37. package/dist/file-editor/formats/adapter.d.ts.map +1 -0
  38. package/dist/file-editor/formats/detect.d.ts +9 -0
  39. package/dist/file-editor/formats/detect.d.ts.map +1 -0
  40. package/dist/file-editor/formats/index.d.ts +13 -0
  41. package/dist/file-editor/formats/index.d.ts.map +1 -0
  42. package/dist/file-editor/formats/jsonc.d.ts +14 -0
  43. package/dist/file-editor/formats/jsonc.d.ts.map +1 -0
  44. package/dist/file-editor/formats/yaml.d.ts +11 -0
  45. package/dist/file-editor/formats/yaml.d.ts.map +1 -0
  46. package/dist/file-editor/index.d.ts +42 -0
  47. package/dist/file-editor/index.d.ts.map +1 -0
  48. package/dist/file-editor/pointer.d.ts +74 -0
  49. package/dist/file-editor/pointer.d.ts.map +1 -0
  50. package/dist/file-editor/schema.d.ts +72 -0
  51. package/dist/file-editor/schema.d.ts.map +1 -0
  52. package/dist/file-editor/state/fs-manifest.d.ts +30 -0
  53. package/dist/file-editor/state/fs-manifest.d.ts.map +1 -0
  54. package/dist/file-editor/state/in-memory.d.ts +5 -0
  55. package/dist/file-editor/state/in-memory.d.ts.map +1 -0
  56. package/dist/file-editor/state/store.d.ts +19 -0
  57. package/dist/file-editor/state/store.d.ts.map +1 -0
  58. package/dist/file-editor/transaction.d.ts +60 -0
  59. package/dist/file-editor/transaction.d.ts.map +1 -0
  60. package/dist/file-editor/types.d.ts +131 -0
  61. package/dist/file-editor/types.d.ts.map +1 -0
  62. package/dist/file-editor/util/atomic-write.d.ts +7 -0
  63. package/dist/file-editor/util/atomic-write.d.ts.map +1 -0
  64. package/dist/file-editor/util/diff.d.ts +20 -0
  65. package/dist/file-editor/util/diff.d.ts.map +1 -0
  66. package/dist/file-editor/value-at-pointer.d.ts +46 -0
  67. package/dist/file-editor/value-at-pointer.d.ts.map +1 -0
  68. package/dist/index.cjs +2967 -0
  69. package/dist/index.d.ts +26 -0
  70. package/dist/index.d.ts.map +1 -0
  71. package/dist/index.js +2847 -0
  72. package/dist/logging/create-logger.d.ts +29 -0
  73. package/dist/logging/create-logger.d.ts.map +1 -0
  74. package/dist/logging/index.d.ts +10 -0
  75. package/dist/logging/index.d.ts.map +1 -0
  76. package/dist/logging/levels.d.ts +21 -0
  77. package/dist/logging/levels.d.ts.map +1 -0
  78. package/dist/naming/inflection.d.ts +15 -0
  79. package/dist/naming/inflection.d.ts.map +1 -0
  80. package/dist/naming/metadata-policy.d.ts +35 -0
  81. package/dist/naming/metadata-policy.d.ts.map +1 -0
  82. package/dist/naming/stripe-api-case.d.ts +9 -0
  83. package/dist/naming/stripe-api-case.d.ts.map +1 -0
  84. package/dist/naming/types.d.ts +11 -0
  85. package/dist/naming/types.d.ts.map +1 -0
  86. package/dist/naming/validate.d.ts +6 -0
  87. package/dist/naming/validate.d.ts.map +1 -0
  88. package/dist/templates/filesystem-fs.d.ts +20 -0
  89. package/dist/templates/filesystem-fs.d.ts.map +1 -0
  90. package/dist/templates/generator.d.ts +305 -0
  91. package/dist/templates/generator.d.ts.map +1 -0
  92. package/dist/templates/in-memory-fs.d.ts +44 -0
  93. package/dist/templates/in-memory-fs.d.ts.map +1 -0
  94. package/dist/templates/index.d.ts +28 -0
  95. package/dist/templates/index.d.ts.map +1 -0
  96. package/dist/templates/simple-templates.d.ts +75 -0
  97. package/dist/templates/simple-templates.d.ts.map +1 -0
  98. package/dist/templates/template-manager.d.ts +54 -0
  99. package/dist/templates/template-manager.d.ts.map +1 -0
  100. package/dist/templates/types.d.ts +87 -0
  101. package/dist/templates/types.d.ts.map +1 -0
  102. package/dist/tsconfig.build.tsbuildinfo +1 -0
  103. package/dist/workspace-versions.d.ts +30 -0
  104. package/dist/workspace-versions.d.ts.map +1 -0
  105. package/package.json +60 -0
package/dist/index.cjs ADDED
@@ -0,0 +1,2967 @@
1
+ "use strict";
2
+ var __create = Object.create;
3
+ var __defProp = Object.defineProperty;
4
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
5
+ var __getOwnPropNames = Object.getOwnPropertyNames;
6
+ var __getProtoOf = Object.getPrototypeOf;
7
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
8
+ var __export = (target, all) => {
9
+ for (var name in all)
10
+ __defProp(target, name, { get: all[name], enumerable: true });
11
+ };
12
+ var __copyProps = (to, from, except, desc) => {
13
+ if (from && typeof from === "object" || typeof from === "function") {
14
+ for (let key of __getOwnPropNames(from))
15
+ if (!__hasOwnProp.call(to, key) && key !== except)
16
+ __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
17
+ }
18
+ return to;
19
+ };
20
+ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
21
+ // If the importer is in node compatibility mode or this is not an ESM
22
+ // file that has been converted to a CommonJS file using a Babel-
23
+ // compatible transform (i.e. "__esModule" has not been set), then set
24
+ // "default" to the CommonJS "module.exports" for node compatibility.
25
+ isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
26
+ mod
27
+ ));
28
+ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
29
+
30
+ // src/index.ts
31
+ var index_exports = {};
32
+ __export(index_exports, {
33
+ _ApiExtractorDoc: () => _ApiExtractorDoc,
34
+ _AssertionError: () => _AssertionError,
35
+ _CliUx: () => _CliUx,
36
+ _ConflictAbortedError: () => _ConflictAbortedError,
37
+ _ConflictError: () => _ConflictError,
38
+ _Document: () => _Document,
39
+ _FileEditorError: () => _FileEditorError,
40
+ _FormatNotSupportedError: () => _FormatNotSupportedError,
41
+ _FsCentralManifestStore: () => _FsCentralManifestStore,
42
+ _GeneratorDefectError: () => _GeneratorDefectError,
43
+ _GeneratorInputError: () => _GeneratorInputError,
44
+ _GeneratorRunner: () => _GeneratorRunner,
45
+ _InMemoryStateStore: () => _InMemoryStateStore,
46
+ _POINTER_STRING: () => _POINTER_STRING,
47
+ _PackageJsonDoc: () => _PackageJsonDoc,
48
+ _ParseError: () => _ParseError,
49
+ _PathError: () => _PathError,
50
+ _SchemaValidationError: () => _SchemaValidationError,
51
+ _SingleTemplateManager: () => _SingleTemplateManager,
52
+ _StripeAppManifestDoc: () => _StripeAppManifestDoc,
53
+ _TemplateManager: () => _TemplateManager,
54
+ _Transaction: () => _Transaction,
55
+ _TsConfigDoc: () => _TsConfigDoc,
56
+ _TypeMismatchError: () => _TypeMismatchError,
57
+ _WriteAbortedError: () => _WriteAbortedError,
58
+ _beginTransaction: () => _beginTransaction,
59
+ _canonicalNormalize: () => _canonicalNormalize,
60
+ _canonicalStringify: () => _canonicalStringify,
61
+ _check: () => _check,
62
+ _compilePointer: () => _compilePointer,
63
+ _compositeGenerator: () => _compositeGenerator,
64
+ _createCliContext: () => _createCliContext,
65
+ _createFilesystemTemplateFS: () => _createFilesystemTemplateFS,
66
+ _createInMemoryTemplateFS: () => _createInMemoryTemplateFS,
67
+ _createLogger: () => _createLogger,
68
+ _createSimpleSingleFileTemplate: () => _createSimpleSingleFileTemplate,
69
+ _createSimpleTemplate: () => _createSimpleTemplate,
70
+ _defaultMeta: () => _defaultMeta,
71
+ _detectFormat: () => _detectFormat,
72
+ _detectMeta: () => _detectMeta,
73
+ _diffHunks: () => _hunks,
74
+ _diffUnified: () => _unified,
75
+ _escapeToken: () => _escapeToken,
76
+ _fingerprint: () => _fingerprint,
77
+ _getAdapter: () => _getAdapter,
78
+ _hasPointer: () => _hasPointer,
79
+ _inferStripeApiName: () => _inferStripeApiName,
80
+ _isRecord: () => _isRecord,
81
+ _isRecordWithValueType: () => _isRecordWithValueType,
82
+ _isSemverRange: () => _isSemverRange,
83
+ _isSemverVersion: () => _isSemverVersion,
84
+ _jsonAdapter: () => jsonAdapter,
85
+ _jsoncAdapter: () => jsoncAdapter,
86
+ _looksPlural: () => _looksPlural,
87
+ _looksSingular: () => _looksSingular,
88
+ _openApiExtractor: () => _openApiExtractor,
89
+ _openDocument: () => _openDocument,
90
+ _openDocumentFromDisk: () => _openDocumentFromDisk,
91
+ _openPackageJson: () => _openPackageJson,
92
+ _openStripeAppManifest: () => _openStripeAppManifest,
93
+ _openTsConfig: () => _openTsConfig,
94
+ _parseArrayIndex: () => _parseArrayIndex,
95
+ _parseLogLevel: () => _parseLogLevel,
96
+ _parsePointer: () => _parsePointer,
97
+ _pointer: () => _pointer,
98
+ _registerFormatAdapter: () => _registerFormatAdapter,
99
+ _resolvePointer: () => _resolvePointer,
100
+ _runAssertions: () => _runAssertions,
101
+ _runAsyncAssertions: () => _runAsyncAssertions,
102
+ _semverRange: () => _semverRange,
103
+ _semverVersion: () => _semverVersion,
104
+ _sha256Hex: () => _sha256Hex,
105
+ _stripeMetadataPolicy: () => _stripeMetadataPolicy,
106
+ _toCapitalized: () => _toCapitalized,
107
+ _toPascalCase: () => _toPascalCase,
108
+ _toPlural: () => _toPlural,
109
+ _toSentenceDisplayName: () => _toSentenceDisplayName,
110
+ _toSingular: () => _toSingular,
111
+ _toSnakeCase: () => _toSnakeCase,
112
+ _toStripeApiCase: () => _toStripeApiCase,
113
+ _tokenizeIdentifier: () => _tokenizeIdentifier,
114
+ _truncateName: () => _truncateName,
115
+ _tryParseSemverRange: () => _tryParseSemverRange,
116
+ _tryParseSemverVersion: () => _tryParseSemverVersion,
117
+ _tryValidateWithSchema: () => _tryValidateWithSchema,
118
+ _unescapeToken: () => _unescapeToken,
119
+ _validateApiName: () => _validateApiName,
120
+ _validateDisplayName: () => _validateDisplayName,
121
+ _validateWithSchema: () => _validateWithSchema,
122
+ _workspaceVersion: () => _workspaceVersion,
123
+ _writeJsonOutput: () => _writeJsonOutput,
124
+ _yamlAdapter: () => yamlAdapter
125
+ });
126
+ module.exports = __toCommonJS(index_exports);
127
+
128
+ // src/naming/stripe-api-case.ts
129
+ var UPPERCASE_ACRONYM_TOKENS = [
130
+ "HTTPS",
131
+ "HTTP",
132
+ "UUID",
133
+ "JSON",
134
+ "XML",
135
+ "OAuth",
136
+ "URL",
137
+ "URI",
138
+ "API",
139
+ "SQL",
140
+ "MFA",
141
+ "2FA",
142
+ "3DS",
143
+ "CSV",
144
+ "HTML",
145
+ "PDF",
146
+ "JWT",
147
+ "SHA",
148
+ "TLS",
149
+ "SSL",
150
+ "ACH",
151
+ "ID"
152
+ ];
153
+ var COMPOSITE_CASE_TOKENS = ["OAuth"];
154
+ function isUppercaseRun(token) {
155
+ return /^[A-Z0-9]+$/.test(token) && /[A-Z]/.test(token);
156
+ }
157
+ function splitUppercaseRun(token) {
158
+ const pieces = [];
159
+ let rest = token;
160
+ while (rest.length > 0) {
161
+ const matched = UPPERCASE_ACRONYM_TOKENS.find(
162
+ (candidate) => rest.startsWith(candidate.toUpperCase())
163
+ );
164
+ if (matched) {
165
+ pieces.push(matched);
166
+ rest = rest.slice(matched.length);
167
+ continue;
168
+ }
169
+ pieces.push(rest);
170
+ break;
171
+ }
172
+ return pieces;
173
+ }
174
+ function mergeCompositeTokens(tokens) {
175
+ const merged = [];
176
+ for (let index = 0; index < tokens.length; index += 1) {
177
+ const current = tokens[index];
178
+ const next = tokens[index + 1];
179
+ if (!current) {
180
+ continue;
181
+ }
182
+ if (next) {
183
+ const combined = `${current}${next}`;
184
+ const matched = COMPOSITE_CASE_TOKENS.find(
185
+ (candidate) => candidate.toLowerCase() === combined.toLowerCase()
186
+ );
187
+ if (matched) {
188
+ merged.push(matched);
189
+ index += 1;
190
+ continue;
191
+ }
192
+ }
193
+ merged.push(current);
194
+ }
195
+ return merged;
196
+ }
197
+ function tokenizeSegment(segment) {
198
+ const matches = segment.match(
199
+ /(?:[0-9]+[A-Z]+(?=$|[A-Z][a-z]))|(?:[A-Z]+(?=[A-Z][a-z]))|(?:[A-Z]?[a-z]+)|(?:[A-Z]+)|(?:[0-9]+)/g
200
+ ) ?? [segment];
201
+ return mergeCompositeTokens(matches).flatMap((token) => {
202
+ if (/^[0-9]+[A-Z]+$/.test(token)) {
203
+ const digitPrefix = /^[0-9]+/.exec(token)?.[0] ?? "";
204
+ const upperSuffix = token.slice(digitPrefix.length);
205
+ return [digitPrefix, ...splitUppercaseRun(upperSuffix)];
206
+ }
207
+ return isUppercaseRun(token) && token.length > 1 ? splitUppercaseRun(token) : [token];
208
+ });
209
+ }
210
+ function isAcronymToken(token) {
211
+ const upper = token.toUpperCase();
212
+ return token === upper && UPPERCASE_ACRONYM_TOKENS.some((candidate) => candidate.toUpperCase() === upper) || COMPOSITE_CASE_TOKENS.some(
213
+ (candidate) => candidate.toLowerCase() === token.toLowerCase()
214
+ );
215
+ }
216
+ function _tokenizeIdentifier(input) {
217
+ return input.replace(/[\s-]+/g, "_").split("_").filter(Boolean).flatMap((segment) => tokenizeSegment(segment)).filter(Boolean);
218
+ }
219
+ function _toStripeApiCase(input) {
220
+ return _tokenizeIdentifier(input).map((token) => token.replace(/[^A-Za-z0-9]/g, "").toLowerCase()).filter(Boolean).join("_");
221
+ }
222
+ function _toSentenceDisplayName(input) {
223
+ const tokens = _tokenizeIdentifier(input);
224
+ if (tokens.length === 0) {
225
+ return input;
226
+ }
227
+ return tokens.map((token, index) => {
228
+ if (/^[0-9]+$/.test(token)) {
229
+ return token;
230
+ }
231
+ if (isAcronymToken(token)) {
232
+ return token.toUpperCase();
233
+ }
234
+ const lower = token.toLowerCase();
235
+ if (index === 0) {
236
+ return lower.charAt(0).toUpperCase() + lower.slice(1);
237
+ }
238
+ return lower;
239
+ }).join(" ");
240
+ }
241
+ function _truncateName(value, maxLength) {
242
+ return value.slice(0, maxLength);
243
+ }
244
+
245
+ // src/naming/inflection.ts
246
+ var import_inflected = require("inflected");
247
+ function _toPascalCase(value) {
248
+ return (0, import_inflected.camelize)(value);
249
+ }
250
+ function _toSnakeCase(value) {
251
+ return (0, import_inflected.underscore)(value);
252
+ }
253
+ function _toPlural(value) {
254
+ return (0, import_inflected.pluralize)(value);
255
+ }
256
+ function _toSingular(value) {
257
+ return (0, import_inflected.singularize)(value);
258
+ }
259
+ function _looksPlural(value) {
260
+ const singular = (0, import_inflected.singularize)(value);
261
+ return singular !== value;
262
+ }
263
+ function _toCapitalized(value) {
264
+ return (0, import_inflected.capitalize)(value);
265
+ }
266
+ function _looksSingular(value) {
267
+ return (0, import_inflected.pluralize)(value) !== value;
268
+ }
269
+
270
+ // src/naming/metadata-policy.ts
271
+ function _inferStripeApiName(context) {
272
+ return _toStripeApiCase(context.logicalName);
273
+ }
274
+ function inferStripeDisplayName(context) {
275
+ return _toSentenceDisplayName(context.logicalName);
276
+ }
277
+ function pluralizeStripeMetadata(context) {
278
+ return _toPlural(context.singular);
279
+ }
280
+ var _stripeMetadataPolicy = {
281
+ type: {
282
+ apiName: {
283
+ mode: "infer-if-missing",
284
+ infer: _inferStripeApiName,
285
+ pluralization: {
286
+ mode: "infer-if-missing",
287
+ inflect: pluralizeStripeMetadata
288
+ }
289
+ },
290
+ displayName: {
291
+ mode: "infer-if-missing",
292
+ infer: inferStripeDisplayName,
293
+ pluralization: {
294
+ mode: "infer-if-missing",
295
+ inflect: pluralizeStripeMetadata
296
+ }
297
+ }
298
+ },
299
+ field: {
300
+ apiName: {
301
+ mode: "infer-if-missing",
302
+ infer: _inferStripeApiName
303
+ },
304
+ displayName: {
305
+ mode: "infer-if-missing",
306
+ infer: inferStripeDisplayName
307
+ }
308
+ },
309
+ method: {
310
+ apiName: {
311
+ mode: "infer-if-missing",
312
+ infer: _inferStripeApiName
313
+ },
314
+ displayName: {
315
+ mode: "infer-if-missing",
316
+ infer: inferStripeDisplayName
317
+ }
318
+ }
319
+ };
320
+
321
+ // src/naming/validate.ts
322
+ function _validateApiName(value) {
323
+ const reasons = [];
324
+ if (value.length === 0) {
325
+ reasons.push("API name must not be empty.");
326
+ }
327
+ if (value.length > 40) {
328
+ reasons.push("API name must not exceed 40 characters.");
329
+ }
330
+ const firstCharacter = value[0];
331
+ if (firstCharacter && !/[a-z]/.test(firstCharacter)) {
332
+ reasons.push("API name must start with a lowercase letter.");
333
+ }
334
+ if (value.endsWith("_")) {
335
+ reasons.push("API name must not end with an underscore.");
336
+ }
337
+ if (/[^a-z0-9_]/.test(value)) {
338
+ reasons.push(
339
+ "API name must contain only lowercase letters, digits, and underscores."
340
+ );
341
+ }
342
+ if (value.includes("__")) {
343
+ reasons.push("API name must not contain consecutive underscores.");
344
+ }
345
+ return {
346
+ valid: reasons.length === 0,
347
+ reasons
348
+ };
349
+ }
350
+ function _validateDisplayName(value) {
351
+ const reasons = [];
352
+ if (value.length === 0) {
353
+ reasons.push("Display name must not be empty.");
354
+ }
355
+ if (value.length > 40) {
356
+ reasons.push("Display name must not exceed 40 characters.");
357
+ }
358
+ return {
359
+ valid: reasons.length === 0,
360
+ reasons
361
+ };
362
+ }
363
+
364
+ // src/logging/levels.ts
365
+ var VALID_LOG_LEVELS = /* @__PURE__ */ new Set([
366
+ "trace",
367
+ "debug",
368
+ "info",
369
+ "warn",
370
+ "error",
371
+ "fatal",
372
+ "silent"
373
+ ]);
374
+ function isValidLogLevel(value) {
375
+ return VALID_LOG_LEVELS.has(value);
376
+ }
377
+ function _parseLogLevel(input) {
378
+ if (input === void 0 || input === "") return "info";
379
+ const normalized = input.trim().toLowerCase();
380
+ if (isValidLogLevel(normalized)) return normalized;
381
+ return "info";
382
+ }
383
+
384
+ // src/logging/create-logger.ts
385
+ var import_pino = __toESM(require("pino"), 1);
386
+ function _createLogger(options) {
387
+ const level = options?.level ?? _parseLogLevel(process.env.LOG_LEVEL);
388
+ const colorize = process.env.NO_COLOR === void 0 && process.stderr.isTTY;
389
+ return (0, import_pino.default)({
390
+ ...options?.name !== void 0 && { name: options.name },
391
+ level,
392
+ transport: {
393
+ target: "pino-pretty",
394
+ options: { destination: 2, colorize }
395
+ }
396
+ });
397
+ }
398
+
399
+ // src/cli/cli-ux.ts
400
+ var _CliUx = class {
401
+ #stdout;
402
+ #stderr;
403
+ constructor(options) {
404
+ this.#stdout = options?.stdout ?? process.stdout;
405
+ this.#stderr = options?.stderr ?? process.stderr;
406
+ }
407
+ /** Write a product output line to stdout (or the configured stream). */
408
+ print(message) {
409
+ this.#stdout.write(message + "\n");
410
+ }
411
+ /** Write a status/progress line to stderr (or the configured stream). */
412
+ log(message) {
413
+ this.#stderr.write(message + "\n");
414
+ }
415
+ /**
416
+ * Error message to **stderr** (or the configured stderr stream).
417
+ *
418
+ * Behaviorally identical to {@link _CliUx.log} today. Use `error()` for failures
419
+ * and `log()` for progress/status — the distinction supports future
420
+ * formatting differentiation (color, prefixes, severity filtering).
421
+ */
422
+ error(message) {
423
+ this.#stderr.write(message + "\n");
424
+ }
425
+ };
426
+
427
+ // src/cli/context.ts
428
+ function _createCliContext(options) {
429
+ return { ux: options?.ux ?? new _CliUx() };
430
+ }
431
+
432
+ // src/cli/output-path.ts
433
+ var import_node_fs = require("fs");
434
+ function _writeJsonOutput(outputPath, data) {
435
+ if (outputPath === void 0) return;
436
+ (0, import_node_fs.writeFileSync)(outputPath, JSON.stringify(data, null, 2) + "\n", "utf-8");
437
+ }
438
+
439
+ // src/cli/guards.ts
440
+ function _isRecord(value) {
441
+ return typeof value === "object" && value !== null && !Array.isArray(value);
442
+ }
443
+ function _isRecordWithValueType(guard) {
444
+ return (value) => {
445
+ if (!_isRecord(value)) return false;
446
+ return Object.values(value).every(guard);
447
+ };
448
+ }
449
+
450
+ // src/templates/template-manager.ts
451
+ var _TemplateManager = class {
452
+ templates;
453
+ fs;
454
+ /**
455
+ * Create a new TemplateManager
456
+ * @param templates - Object mapping string keys to templates
457
+ * @param fs - Filesystem abstraction for template file reading
458
+ *
459
+ * @example
460
+ * ```typescript
461
+ * import { fs } from './fs/index.js';
462
+ *
463
+ * const manager = new TemplateManager({
464
+ * foo: fooTemplate,
465
+ * bar: barTemplate,
466
+ * }, fs)
467
+ *
468
+ * await manager.generate('foo', params)
469
+ * ```
470
+ */
471
+ constructor(templates, fs) {
472
+ this.templates = new Map(Object.entries(templates));
473
+ this.fs = fs;
474
+ }
475
+ /**
476
+ * Generate output from a template
477
+ * @param key - Template key to generate from
478
+ * @param params - Template-specific parameters
479
+ * @returns Template output with files and any additional fields
480
+ * @throws Error if template key is not found
481
+ */
482
+ async generate(key, params) {
483
+ const template = this.templates.get(key);
484
+ if (!template) {
485
+ let msg = `Template not found for key: ${key}.`;
486
+ const supported = [...this.templates.keys()];
487
+ if (supported.length > 0) {
488
+ msg += ` Supported: ${supported.join(", ")}`;
489
+ }
490
+ throw new Error(msg);
491
+ }
492
+ const context = {
493
+ fs: this.fs
494
+ };
495
+ return await template.generate(params, context);
496
+ }
497
+ /**
498
+ * Get all template entries as [key, template] pairs
499
+ */
500
+ getTemplateEntries() {
501
+ return [...this.templates.entries()];
502
+ }
503
+ /**
504
+ * Get list of all supported template keys
505
+ */
506
+ getSupportedKeys() {
507
+ return [...this.templates.keys()];
508
+ }
509
+ };
510
+
511
+ // src/templates/in-memory-fs.ts
512
+ var import_mustache = __toESM(require("mustache"), 1);
513
+ function _createInMemoryTemplateFS(image, pathPrefix = "") {
514
+ const index = /* @__PURE__ */ new Map();
515
+ const allPaths = [];
516
+ for (const entry of image) {
517
+ index.set(entry.path, entry.content);
518
+ allPaths.push(entry.path);
519
+ }
520
+ return {
521
+ textFile(...segments) {
522
+ const fullSegments = pathPrefix ? [pathPrefix, ...segments] : segments;
523
+ const requestedPath = fullSegments.join("/");
524
+ const content = index.get(requestedPath);
525
+ if (content === void 0) {
526
+ const available = Array.from(index.keys()).slice(0, 5).join(", ");
527
+ throw new Error(
528
+ `Template not found: ${requestedPath}
529
+ Available paths (first 5): ${available}...`
530
+ );
531
+ }
532
+ return content;
533
+ },
534
+ mustache(params, ...segments) {
535
+ const template = this.textFile(...segments);
536
+ return import_mustache.default.render(template, params, void 0, {
537
+ escape: (text) => text
538
+ });
539
+ },
540
+ scope(prefix) {
541
+ const newPrefix = pathPrefix ? `${pathPrefix}/${prefix}` : prefix;
542
+ return _createInMemoryTemplateFS(image, newPrefix);
543
+ },
544
+ _getPathsUnder(prefix) {
545
+ const normalizedPrefix = prefix.endsWith("/") ? prefix : prefix + "/";
546
+ return allPaths.filter((p) => p.startsWith(normalizedPrefix));
547
+ }
548
+ };
549
+ }
550
+
551
+ // src/templates/simple-templates.ts
552
+ function _createSimpleTemplate(generateContent, pathTemplate) {
553
+ return {
554
+ generate: (params, _context) => ({
555
+ files: [
556
+ {
557
+ path: pathTemplate(params),
558
+ content: generateContent(params)
559
+ }
560
+ ]
561
+ })
562
+ };
563
+ }
564
+ var _SingleTemplateManager = class {
565
+ template;
566
+ fs;
567
+ /**
568
+ * Create a new SingleTemplateManager
569
+ * @param template - The single template to use
570
+ * @param fs - Filesystem for template file reading
571
+ */
572
+ constructor(template, fs) {
573
+ this.template = template;
574
+ this.fs = fs;
575
+ }
576
+ /**
577
+ * Generate output from the template
578
+ * @param params - Template parameters
579
+ * @returns Template output
580
+ */
581
+ async generate(params) {
582
+ const context = {
583
+ fs: this.fs
584
+ };
585
+ return await this.template.generate(params, context);
586
+ }
587
+ };
588
+ function _createSimpleSingleFileTemplate(generateContent, pathTemplate) {
589
+ const template = _createSimpleTemplate(generateContent, pathTemplate);
590
+ return new _SingleTemplateManager(template, _createInMemoryTemplateFS([]));
591
+ }
592
+
593
+ // src/templates/filesystem-fs.ts
594
+ var import_node_fs2 = require("fs");
595
+ var import_node_path = require("path");
596
+ var import_mustache2 = __toESM(require("mustache"), 1);
597
+ function _createFilesystemTemplateFS(templateDir = process.cwd()) {
598
+ return {
599
+ textFile(...segments) {
600
+ return (0, import_node_fs2.readFileSync)((0, import_node_path.join)(templateDir, ...segments), "utf-8");
601
+ },
602
+ mustache(params, ...segments) {
603
+ const template = (0, import_node_fs2.readFileSync)((0, import_node_path.join)(templateDir, ...segments), "utf-8");
604
+ return import_mustache2.default.render(template, params, void 0, {
605
+ escape: (text) => text
606
+ });
607
+ },
608
+ scope(prefix) {
609
+ return _createFilesystemTemplateFS((0, import_node_path.join)(templateDir, prefix));
610
+ }
611
+ };
612
+ }
613
+
614
+ // src/templates/generator.ts
615
+ var import_node_fs3 = require("fs");
616
+ var import_node_path2 = require("path");
617
+ var import_mustache3 = __toESM(require("mustache"), 1);
618
+ var _GeneratorInputError = class extends Error {
619
+ remediation;
620
+ constructor(message, remediation, options) {
621
+ super(message, options);
622
+ this.name = "_GeneratorInputError";
623
+ this.remediation = remediation;
624
+ }
625
+ };
626
+ var _GeneratorDefectError = class extends Error {
627
+ generatorId;
628
+ constructor(message, generatorId, options) {
629
+ super(message, options);
630
+ this.name = "_GeneratorDefectError";
631
+ this.generatorId = generatorId;
632
+ }
633
+ };
634
+ var PATH_TOKEN_PATTERN = /___([a-zA-Z][a-zA-Z0-9]*)___/g;
635
+ function resolvePathTokens(segment, locals) {
636
+ PATH_TOKEN_PATTERN.lastIndex = 0;
637
+ return segment.replace(PATH_TOKEN_PATTERN, (_match, token) => {
638
+ const value = locals[token];
639
+ if (value === void 0) {
640
+ throw new _GeneratorDefectError(
641
+ `Path token "___${token}___" has no corresponding value in locals(). Available keys: ${Object.keys(locals).join(", ")}`
642
+ );
643
+ }
644
+ if (typeof value !== "string") {
645
+ throw new _GeneratorDefectError(
646
+ `Path token "___${token}___" must resolve to a string, got ${typeof value}`
647
+ );
648
+ }
649
+ return value;
650
+ });
651
+ }
652
+ function walkDir(root, dir = root) {
653
+ const entries = (0, import_node_fs3.readdirSync)(dir).sort();
654
+ const paths = [];
655
+ for (const entry of entries) {
656
+ const fullPath = (0, import_node_path2.join)(dir, entry);
657
+ const stat2 = (0, import_node_fs3.statSync)(fullPath);
658
+ if (stat2.isDirectory()) {
659
+ paths.push(...walkDir(root, fullPath));
660
+ } else {
661
+ paths.push((0, import_node_path2.relative)(root, fullPath).split(import_node_path2.sep).join("/"));
662
+ }
663
+ }
664
+ return paths;
665
+ }
666
+ function assertSafePath(outputPath) {
667
+ if ((0, import_node_path2.isAbsolute)(outputPath)) {
668
+ throw new _GeneratorDefectError(
669
+ `Generator produced an unsafe path: ${outputPath}. Paths must be relative and cannot contain '..' segments.`
670
+ );
671
+ }
672
+ const segments = outputPath.split(/[\\/]/);
673
+ for (const segment of segments) {
674
+ if (segment === "..") {
675
+ throw new _GeneratorDefectError(
676
+ `Generator produced an unsafe path: ${outputPath}. Paths must be relative and cannot contain '..' segments.`
677
+ );
678
+ }
679
+ }
680
+ }
681
+ function assertNoMalformedTokens(templatePath) {
682
+ const segments = templatePath.split("/");
683
+ const anyTokenLike = /___[^_/].*?___/g;
684
+ for (const segment of segments) {
685
+ anyTokenLike.lastIndex = 0;
686
+ let match;
687
+ while ((match = anyTokenLike.exec(segment)) !== null) {
688
+ const occurrence = match[0];
689
+ PATH_TOKEN_PATTERN.lastIndex = 0;
690
+ const isValid = PATH_TOKEN_PATTERN.test(occurrence);
691
+ PATH_TOKEN_PATTERN.lastIndex = 0;
692
+ if (!isValid) {
693
+ throw new _GeneratorDefectError(
694
+ `Path "${templatePath}" contains what looks like an unresolved ___token___. Token names must be camelCase alphanumeric (e.g., ___packageName___, not ___package_name___).`
695
+ );
696
+ }
697
+ }
698
+ }
699
+ }
700
+ var MUSTACHE_EXT = ".mustache";
701
+ var FILES_SUBDIR = "files";
702
+ var _GeneratorRunner = class {
703
+ #generators;
704
+ constructor(generator) {
705
+ this.#generators = Array.isArray(generator) ? generator : [generator];
706
+ }
707
+ /**
708
+ * Plan phase — compute all files that would be generated, without writing
709
+ * anything to disk.
710
+ *
711
+ * Also runs `beforeExecute` hooks to collect preflight results, and
712
+ * `describeSideEffects` to surface planned side effects. Neither of these
713
+ * write anything or produce observable external effects during plan.
714
+ *
715
+ * Use this for dry runs, previews, and testing.
716
+ *
717
+ * Validates that all output paths are safe (no `..`, no leading `/`) and
718
+ * that no two generators produce the same output path.
719
+ *
720
+ * @param params - User-provided parameters for the generator
721
+ * @param context - Generator context (standalone or project). Passed to
722
+ * `beforeExecute` hooks. Callers must supply a real context — there is no
723
+ * synthetic plan-time sentinel. For dry-run calls outside of a project,
724
+ * use a standalone context with an appropriate `targetDir`.
725
+ */
726
+ async plan(params, context) {
727
+ const files = [];
728
+ const preflightResults = [];
729
+ const plannedSideEffects = [];
730
+ for (const generator of this.#generators) {
731
+ if (generator.beforeExecute) {
732
+ const result = await generator.beforeExecute(params, context);
733
+ preflightResults.push(result);
734
+ }
735
+ if (generator.describeSideEffects) {
736
+ const effects = await generator.describeSideEffects(params);
737
+ plannedSideEffects.push(...effects);
738
+ }
739
+ }
740
+ for (const generator of this.#generators) {
741
+ const locals = generator.locals(params);
742
+ const filesDir = (0, import_node_path2.join)(generator.filesDir, FILES_SUBDIR);
743
+ const fs = _createFilesystemTemplateFS(filesDir);
744
+ if (!(0, import_node_fs3.existsSync)(filesDir)) {
745
+ throw new _GeneratorDefectError(
746
+ `Generator at "${generator.filesDir}" is missing a files/ subdirectory. Expected to find templates at "${filesDir}".`
747
+ );
748
+ }
749
+ const templatePaths = walkDir(filesDir);
750
+ for (const templatePath of templatePaths) {
751
+ assertNoMalformedTokens(templatePath);
752
+ const resolvedPath = templatePath.split("/").map((segment) => {
753
+ const resolved = resolvePathTokens(segment, locals);
754
+ if (resolved === "") {
755
+ throw new _GeneratorDefectError(
756
+ `Template token in path segment "${segment}" resolved to empty string`
757
+ );
758
+ }
759
+ return resolved;
760
+ }).join("/");
761
+ const stripped = resolvedPath.endsWith(MUSTACHE_EXT) ? resolvedPath.slice(0, -MUSTACHE_EXT.length) : resolvedPath;
762
+ if (stripped === "") {
763
+ throw new _GeneratorDefectError(
764
+ `Generator produced an empty output path after processing template "${templatePath}".`
765
+ );
766
+ }
767
+ assertSafePath(stripped);
768
+ const outputPath = import_node_path2.posix.normalize(stripped);
769
+ const pathSegments = templatePath.split("/");
770
+ let content;
771
+ if (templatePath.endsWith(MUSTACHE_EXT)) {
772
+ content = fs.mustache(locals, ...pathSegments);
773
+ } else {
774
+ content = fs.textFile(...pathSegments);
775
+ }
776
+ const description = resolveDescription(
777
+ templatePath,
778
+ generator.descriptions,
779
+ locals,
780
+ fs
781
+ );
782
+ files.push({
783
+ path: outputPath,
784
+ content,
785
+ ...description !== void 0 && { description }
786
+ });
787
+ }
788
+ }
789
+ const seen = /* @__PURE__ */ new Set();
790
+ for (const file of files) {
791
+ if (seen.has(file.path)) {
792
+ throw new _GeneratorDefectError(
793
+ `Generator produced duplicate output path: ${file.path}`
794
+ );
795
+ }
796
+ seen.add(file.path);
797
+ }
798
+ return { files, preflightResults, plannedSideEffects };
799
+ }
800
+ /**
801
+ * Execute phase — runs the full lifecycle:
802
+ * 1. `plan()` to collect preflight results and planned files
803
+ * 2. If any preflight returned `proceed: false`, return early without writing
804
+ * 3. `context.writeFiles()` to write files to disk (delegated to caller)
805
+ * 4. If the write was aborted, return early without calling `afterExecute`
806
+ * 5. `afterExecute` hooks for all generators with the write result
807
+ */
808
+ async execute(params, context) {
809
+ const plan = await this.plan(params, context);
810
+ const rejected = plan.preflightResults.some((r) => !r.proceed);
811
+ if (rejected) {
812
+ return {
813
+ files: plan.files,
814
+ preflightResults: plan.preflightResults,
815
+ plannedSideEffects: plan.plannedSideEffects,
816
+ writeResult: void 0,
817
+ afterExecuteResults: []
818
+ };
819
+ }
820
+ const writeResult = await context.writeFiles(plan.files);
821
+ if (writeResult.aborted) {
822
+ return {
823
+ files: plan.files,
824
+ preflightResults: plan.preflightResults,
825
+ plannedSideEffects: plan.plannedSideEffects,
826
+ writeResult,
827
+ afterExecuteResults: []
828
+ };
829
+ }
830
+ const afterExecuteResults = [];
831
+ for (const generator of this.#generators) {
832
+ if (generator.afterExecute) {
833
+ const result = await generator.afterExecute(params, context, writeResult);
834
+ afterExecuteResults.push(result);
835
+ }
836
+ }
837
+ return {
838
+ files: plan.files,
839
+ preflightResults: plan.preflightResults,
840
+ plannedSideEffects: plan.plannedSideEffects,
841
+ writeResult,
842
+ afterExecuteResults
843
+ };
844
+ }
845
+ };
846
+ function resolveDescription(templatePath, descriptions, locals, _fs) {
847
+ if (!descriptions) return void 0;
848
+ const template = descriptions[templatePath];
849
+ if (template === void 0) return void 0;
850
+ return import_mustache3.default.render(template, locals, void 0, {
851
+ escape: (t) => t
852
+ });
853
+ }
854
+ function _compositeGenerator(...generators) {
855
+ return new _GeneratorRunner(generators);
856
+ }
857
+
858
+ // src/workspace-versions.json
859
+ var workspace_versions_default = {
860
+ "@stripe/extensibility-api-objects": "0.3.1",
861
+ "@stripe/extensibility-custom-objects": "0.7.2",
862
+ "@stripe/extensibility-custom-objects-tools": "0.39.2",
863
+ "@stripe/extensibility-dev-tools": "0.23.1",
864
+ "@stripe/extensibility-doc-helpers": "0.3.0",
865
+ "@stripe/extensibility-eslint-plugin": "0.15.2",
866
+ "@stripe/extensibility-jsonschema-tools": "0.6.3",
867
+ "@stripe/extensibility-language-server": "0.2.8",
868
+ "@stripe/extensibility-script-build-tools": "1.5.4",
869
+ "@stripe/extensibility-sdk": "0.22.2",
870
+ "@stripe/extensibility-test-helpers": "0.2.5",
871
+ "@stripe/extensibility-tool-utils": "0.6.2"
872
+ };
873
+
874
+ // src/workspace-versions.ts
875
+ var _workspaceVersions = workspace_versions_default;
876
+ function _workspaceVersion(packageName) {
877
+ const v = _workspaceVersions[packageName];
878
+ if (v === void 0) {
879
+ throw new Error(
880
+ `Unknown workspace package "${packageName}". Check workspace-versions.json or run: tsx scripts/src/sync-workspace-versions.ts`
881
+ );
882
+ }
883
+ return v;
884
+ }
885
+
886
+ // src/file-editor/document.ts
887
+ var import_promises = require("fs/promises");
888
+
889
+ // src/file-editor/assertions/index.ts
890
+ var import_semver = __toESM(require("semver"), 1);
891
+
892
+ // src/file-editor/errors.ts
893
+ var _FileEditorError = class extends Error {
894
+ code;
895
+ constructor(code, message) {
896
+ super(message);
897
+ this.name = "_FileEditorError";
898
+ this.code = code;
899
+ }
900
+ };
901
+ var _ParseError = class extends _FileEditorError {
902
+ format;
903
+ offset;
904
+ snippet;
905
+ constructor(format, message, details = {}) {
906
+ super("PARSE_ERROR", message);
907
+ this.name = "_ParseError";
908
+ this.format = format;
909
+ this.offset = details.offset ?? null;
910
+ this.snippet = details.snippet ?? null;
911
+ }
912
+ };
913
+ var _PathError = class extends _FileEditorError {
914
+ pointer;
915
+ constructor(pointer, message) {
916
+ super("PATH_ERROR", message);
917
+ this.name = "_PathError";
918
+ this.pointer = pointer;
919
+ }
920
+ };
921
+ var _TypeMismatchError = class extends _FileEditorError {
922
+ pointer;
923
+ constructor(pointer, message) {
924
+ super("TYPE_MISMATCH", message);
925
+ this.name = "_TypeMismatchError";
926
+ this.pointer = pointer;
927
+ }
928
+ };
929
+ var _AssertionError = class extends _FileEditorError {
930
+ pointer;
931
+ expected;
932
+ actual;
933
+ constructor(pointer, message, details = {}) {
934
+ super("ASSERTION_ERROR", message);
935
+ this.name = "_AssertionError";
936
+ this.pointer = pointer;
937
+ this.expected = details.expected;
938
+ this.actual = details.actual;
939
+ }
940
+ };
941
+ var _ConflictError = class extends _FileEditorError {
942
+ conflict;
943
+ constructor(conflict) {
944
+ super(
945
+ "CONFLICT",
946
+ `Drift detected for ${conflict.path}: file was modified outside the tool.`
947
+ );
948
+ this.name = "_ConflictError";
949
+ this.conflict = conflict;
950
+ }
951
+ };
952
+ var _ConflictAbortedError = class extends _FileEditorError {
953
+ path;
954
+ constructor(path) {
955
+ super("CONFLICT_ABORTED", `Conflict resolution aborted for ${path}.`);
956
+ this.name = "_ConflictAbortedError";
957
+ this.path = path;
958
+ }
959
+ };
960
+ var _WriteAbortedError = class extends _FileEditorError {
961
+ path;
962
+ constructor(path) {
963
+ super("WRITE_ABORTED", `Write aborted for ${path} via confirmWrite hook.`);
964
+ this.name = "_WriteAbortedError";
965
+ this.path = path;
966
+ }
967
+ };
968
+ var _FormatNotSupportedError = class extends _FileEditorError {
969
+ path;
970
+ constructor(path, message) {
971
+ super(
972
+ "FORMAT_NOT_SUPPORTED",
973
+ message ?? `Cannot infer format for ${path}; pass options.format explicitly.`
974
+ );
975
+ this.name = "_FormatNotSupportedError";
976
+ this.path = path;
977
+ }
978
+ };
979
+
980
+ // src/file-editor/pointer.ts
981
+ function _parsePointer(pointer) {
982
+ if (pointer === "") return [];
983
+ if (!pointer.startsWith("/")) {
984
+ throw new _PathError(
985
+ pointer,
986
+ `Invalid JSON Pointer: must start with "/" or be empty.`
987
+ );
988
+ }
989
+ const parts = pointer.slice(1).split("/");
990
+ return parts.map(_unescapeToken);
991
+ }
992
+ function _compilePointer(segments) {
993
+ if (segments.length === 0) return "";
994
+ return "/" + segments.map((s) => _escapeToken(String(s))).join("/");
995
+ }
996
+ function _unescapeToken(token) {
997
+ return token.replace(/~1/g, "/").replace(/~0/g, "~");
998
+ }
999
+ function _escapeToken(raw) {
1000
+ return raw.replace(/~/g, "~0").replace(/\//g, "~1");
1001
+ }
1002
+ function _resolvePointer(root, pointer) {
1003
+ const segments = _parsePointer(pointer);
1004
+ let current = root;
1005
+ for (const seg of segments) {
1006
+ if (current === null || current === void 0) return void 0;
1007
+ if (Array.isArray(current)) {
1008
+ if (seg === "-") return void 0;
1009
+ const idx = _parseArrayIndex(seg);
1010
+ if (idx === null) return void 0;
1011
+ current = current[idx];
1012
+ } else if (typeof current === "object") {
1013
+ const obj = current;
1014
+ if (!Object.prototype.hasOwnProperty.call(obj, seg)) return void 0;
1015
+ current = obj[seg];
1016
+ } else {
1017
+ return void 0;
1018
+ }
1019
+ }
1020
+ return current;
1021
+ }
1022
+ function _hasPointer(root, pointer) {
1023
+ const segments = _parsePointer(pointer);
1024
+ let current = root;
1025
+ for (const seg of segments) {
1026
+ if (current === null || current === void 0) return false;
1027
+ if (Array.isArray(current)) {
1028
+ if (seg === "-") return false;
1029
+ const idx = _parseArrayIndex(seg);
1030
+ if (idx === null || idx >= current.length) return false;
1031
+ current = current[idx];
1032
+ } else if (typeof current === "object") {
1033
+ const obj = current;
1034
+ if (!Object.prototype.hasOwnProperty.call(obj, seg)) return false;
1035
+ current = obj[seg];
1036
+ } else {
1037
+ return false;
1038
+ }
1039
+ }
1040
+ return true;
1041
+ }
1042
+ function _parseArrayIndex(token) {
1043
+ if (token === "0") return 0;
1044
+ if (!/^[1-9][0-9]*$/.test(token)) return null;
1045
+ return Number.parseInt(token, 10);
1046
+ }
1047
+ function _pointer() {
1048
+ return makePointerProxy([]);
1049
+ }
1050
+ var _POINTER_STRING = "$";
1051
+ function makePointerProxy(segments) {
1052
+ return new Proxy(() => void 0, {
1053
+ get(_t, prop) {
1054
+ if (prop === _POINTER_STRING) return _compilePointer(segments);
1055
+ if (typeof prop === "symbol") return void 0;
1056
+ const next = /^\d+$/.test(prop) ? Number.parseInt(prop, 10) : prop;
1057
+ return makePointerProxy([...segments, next]);
1058
+ }
1059
+ });
1060
+ }
1061
+
1062
+ // src/file-editor/assertions/index.ts
1063
+ var DEFAULT_SEVERITY = "error";
1064
+ var _check = {
1065
+ /** The pointer must resolve to something (including `null`). @internal */
1066
+ exists(pointer, opts = {}) {
1067
+ return ({ path, value }) => {
1068
+ if (_hasPointer(value, pointer)) return [];
1069
+ return [
1070
+ {
1071
+ path,
1072
+ pointer,
1073
+ code: "MISSING_KEY",
1074
+ severity: opts.severity ?? DEFAULT_SEVERITY,
1075
+ message: opts.message ?? `required value is missing at ${pointer}`,
1076
+ expected: "a value to be present",
1077
+ actual: void 0
1078
+ }
1079
+ ];
1080
+ };
1081
+ },
1082
+ /** The value at the pointer must deep-equal `expected`. @internal */
1083
+ equals(pointer, expected, opts = {}) {
1084
+ return ({ path, value }) => {
1085
+ const actual = _resolvePointer(value, pointer);
1086
+ if (deepEqual(actual, expected)) return [];
1087
+ return [
1088
+ {
1089
+ path,
1090
+ pointer,
1091
+ code: "VALUE_MISMATCH",
1092
+ severity: opts.severity ?? DEFAULT_SEVERITY,
1093
+ message: opts.message ?? `expected ${pointer} to equal ${JSON.stringify(expected)}`,
1094
+ expected,
1095
+ actual
1096
+ }
1097
+ ];
1098
+ };
1099
+ },
1100
+ /** The value at the pointer must equal one of `allowed`. @internal */
1101
+ oneOf(pointer, allowed, opts = {}) {
1102
+ return ({ path, value }) => {
1103
+ const actual = _resolvePointer(value, pointer);
1104
+ if (allowed.some((candidate) => deepEqual(actual, candidate))) return [];
1105
+ return [
1106
+ {
1107
+ path,
1108
+ pointer,
1109
+ code: "VALUE_NOT_ALLOWED",
1110
+ severity: opts.severity ?? DEFAULT_SEVERITY,
1111
+ message: opts.message ?? `expected ${pointer} to be one of ${allowed.map((a) => JSON.stringify(a)).join(", ")}`,
1112
+ expected: allowed,
1113
+ actual
1114
+ }
1115
+ ];
1116
+ };
1117
+ },
1118
+ /** The string value at the pointer must match the given pattern. @internal */
1119
+ matches(pointer, pattern, opts = {}) {
1120
+ return ({ path, value }) => {
1121
+ const actual = _resolvePointer(value, pointer);
1122
+ if (typeof actual === "string" && pattern.test(actual)) return [];
1123
+ return [
1124
+ {
1125
+ path,
1126
+ pointer,
1127
+ code: typeof actual === "string" ? "PATTERN_MISMATCH" : "TYPE_MISMATCH",
1128
+ severity: opts.severity ?? DEFAULT_SEVERITY,
1129
+ message: opts.message ?? `expected ${pointer} to match ${pattern.toString()}`,
1130
+ expected: pattern.toString(),
1131
+ actual
1132
+ }
1133
+ ];
1134
+ };
1135
+ },
1136
+ /**
1137
+ * The value at the pointer must satisfy a caller predicate.
1138
+ * @internal
1139
+ */
1140
+ satisfies(pointer, predicate, opts = {}) {
1141
+ return ({ path, value }) => {
1142
+ const actual = _resolvePointer(value, pointer);
1143
+ if (predicate(actual)) return [];
1144
+ return [
1145
+ {
1146
+ path,
1147
+ pointer,
1148
+ code: "PREDICATE_FAILED",
1149
+ severity: opts.severity ?? DEFAULT_SEVERITY,
1150
+ message: opts.message ?? `predicate failed at ${pointer}`,
1151
+ actual
1152
+ }
1153
+ ];
1154
+ };
1155
+ },
1156
+ /**
1157
+ * The value at the pointer must validate against the supplied Standard
1158
+ * Schema (Zod v4, Valibot, ArkType, etc.). Each schema issue becomes a
1159
+ * `SCHEMA_VIOLATION` diagnostic with the pointer extended by the issue's
1160
+ * path.
1161
+ *
1162
+ * NOTE: the validator is invoked synchronously and its result awaited at
1163
+ * call time, so this returns a `Promise<_Diagnostic[]>` rather than a
1164
+ * synchronous `_Assertion`. Wrap the result with `_runAsyncAssertions` if
1165
+ * you need to compose it with other async checks.
1166
+ * @internal
1167
+ */
1168
+ matchesSchema(pointer, schema, opts = {}) {
1169
+ return async ({ path, value }) => {
1170
+ const actual = _resolvePointer(value, pointer);
1171
+ const result = await schema["~standard"].validate(actual);
1172
+ if (!result.issues) return [];
1173
+ return result.issues.map(
1174
+ (issue) => ({
1175
+ path,
1176
+ pointer: appendIssuePath(pointer, issue.path),
1177
+ code: "SCHEMA_VIOLATION",
1178
+ severity: opts.severity ?? DEFAULT_SEVERITY,
1179
+ message: opts.message ?? issue.message,
1180
+ actual
1181
+ })
1182
+ );
1183
+ };
1184
+ },
1185
+ /**
1186
+ * The string value at the pointer must be a valid semver range satisfying
1187
+ * the supplied constraint.
1188
+ * @internal
1189
+ */
1190
+ semverSatisfies(pointer, range, opts = {}) {
1191
+ return ({ path, value }) => {
1192
+ const actual = _resolvePointer(value, pointer);
1193
+ if (typeof actual !== "string") {
1194
+ return [
1195
+ {
1196
+ path,
1197
+ pointer,
1198
+ code: "TYPE_MISMATCH",
1199
+ severity: opts.severity ?? DEFAULT_SEVERITY,
1200
+ message: opts.message ?? `expected a string version at ${pointer}`,
1201
+ expected: "semver string",
1202
+ actual
1203
+ }
1204
+ ];
1205
+ }
1206
+ const valid = import_semver.default.validRange(actual);
1207
+ if (!valid) {
1208
+ return [
1209
+ {
1210
+ path,
1211
+ pointer,
1212
+ code: "INVALID_SEMVER",
1213
+ severity: opts.severity ?? DEFAULT_SEVERITY,
1214
+ message: opts.message ?? `value at ${pointer} is not a valid semver range`,
1215
+ expected: "valid semver range",
1216
+ actual
1217
+ }
1218
+ ];
1219
+ }
1220
+ if (!import_semver.default.subset(valid, range)) {
1221
+ return [
1222
+ {
1223
+ path,
1224
+ pointer,
1225
+ code: "SEMVER_OUT_OF_RANGE",
1226
+ severity: opts.severity ?? DEFAULT_SEVERITY,
1227
+ message: opts.message ?? `value at ${pointer} (${actual}) is not a subset of ${range}`,
1228
+ expected: range,
1229
+ actual
1230
+ }
1231
+ ];
1232
+ }
1233
+ return [];
1234
+ };
1235
+ }
1236
+ };
1237
+ function _runAssertions(path, value, assertions) {
1238
+ const results = [];
1239
+ for (const a of assertions) {
1240
+ results.push(...a({ path, value }));
1241
+ }
1242
+ return results;
1243
+ }
1244
+ async function _runAsyncAssertions(path, value, assertions) {
1245
+ const results = [];
1246
+ for (const a of assertions) {
1247
+ results.push(...await a({ path, value }));
1248
+ }
1249
+ return results;
1250
+ }
1251
+ function appendIssuePath(base, issuePath) {
1252
+ if (!issuePath || issuePath.length === 0) return base;
1253
+ const suffix = issuePath.map((seg) => {
1254
+ const key = typeof seg === "object" ? seg.key : seg;
1255
+ const s = typeof key === "symbol" ? key.description ?? "" : String(key);
1256
+ return s.replace(/~/g, "~0").replace(/\//g, "~1");
1257
+ }).join("/");
1258
+ return base + "/" + suffix;
1259
+ }
1260
+ function deepEqual(a, b) {
1261
+ if (Object.is(a, b)) return true;
1262
+ if (a === null || b === null) return false;
1263
+ if (typeof a !== typeof b) return false;
1264
+ if (Array.isArray(a)) {
1265
+ if (!Array.isArray(b) || a.length !== b.length) return false;
1266
+ for (let i = 0; i < a.length; i++) {
1267
+ if (!deepEqual(a[i], b[i])) return false;
1268
+ }
1269
+ return true;
1270
+ }
1271
+ if (typeof a === "object" && typeof b === "object") {
1272
+ const ao = a;
1273
+ const bo = b;
1274
+ const keys = Object.keys(ao);
1275
+ if (keys.length !== Object.keys(bo).length) return false;
1276
+ for (const k of keys) {
1277
+ if (!deepEqual(ao[k], bo[k])) return false;
1278
+ }
1279
+ return true;
1280
+ }
1281
+ return false;
1282
+ }
1283
+
1284
+ // src/file-editor/fingerprint.ts
1285
+ var import_node_crypto = require("crypto");
1286
+ var import_jsonc_parser = require("jsonc-parser");
1287
+ var import_yaml = require("yaml");
1288
+ function _canonicalNormalize(text, format) {
1289
+ const value = parseForCanonical(text, format);
1290
+ return _canonicalStringify(value);
1291
+ }
1292
+ function _fingerprint(text, format) {
1293
+ const canonical = _canonicalNormalize(text, format);
1294
+ return _sha256Hex(canonical);
1295
+ }
1296
+ function _sha256Hex(input) {
1297
+ const hash = (0, import_node_crypto.createHash)("sha256");
1298
+ hash.update(input);
1299
+ return hash.digest("hex");
1300
+ }
1301
+ function parseForCanonical(text, format) {
1302
+ const stripped = stripBom(text);
1303
+ switch (format) {
1304
+ case "json":
1305
+ return parseStrictJson(stripped);
1306
+ case "jsonc":
1307
+ return parseJsoncForCanonical(stripped);
1308
+ case "yaml":
1309
+ return parseYamlForCanonical(stripped);
1310
+ }
1311
+ }
1312
+ function stripBom(text) {
1313
+ return text.charCodeAt(0) === 65279 ? text.slice(1) : text;
1314
+ }
1315
+ function parseStrictJson(text) {
1316
+ try {
1317
+ return JSON.parse(text);
1318
+ } catch (err) {
1319
+ const message = err instanceof Error ? err.message : String(err);
1320
+ throw new _ParseError("json", `Invalid JSON: ${message}`);
1321
+ }
1322
+ }
1323
+ function parseJsoncForCanonical(text) {
1324
+ const errors = [];
1325
+ const value = (0, import_jsonc_parser.parse)(text, errors, {
1326
+ allowTrailingComma: true,
1327
+ disallowComments: false
1328
+ });
1329
+ if (errors.length > 0) {
1330
+ const first = errors[0];
1331
+ if (first === void 0) return value;
1332
+ throw new _ParseError(
1333
+ "jsonc",
1334
+ `Invalid JSONC at offset ${String(first.offset)}: ${(0, import_jsonc_parser.printParseErrorCode)(first.error)}`,
1335
+ { offset: first.offset }
1336
+ );
1337
+ }
1338
+ return value;
1339
+ }
1340
+ function parseYamlForCanonical(text) {
1341
+ try {
1342
+ return (0, import_yaml.parse)(text, { merge: true, prettyErrors: false });
1343
+ } catch (err) {
1344
+ const message = err instanceof Error ? err.message : String(err);
1345
+ throw new _ParseError("yaml", `Invalid YAML: ${message}`);
1346
+ }
1347
+ }
1348
+ function _canonicalStringify(value) {
1349
+ return stringify(value);
1350
+ }
1351
+ function stringify(value) {
1352
+ if (value === null) return "null";
1353
+ if (value === void 0) return "null";
1354
+ if (typeof value === "string") return JSON.stringify(value.normalize("NFC"));
1355
+ if (typeof value === "number") {
1356
+ if (!Number.isFinite(value)) {
1357
+ throw new _ParseError(
1358
+ "json",
1359
+ `Cannot canonicalize non-finite number: ${value.toString()}`
1360
+ );
1361
+ }
1362
+ return JSON.stringify(value);
1363
+ }
1364
+ if (typeof value === "boolean") return value ? "true" : "false";
1365
+ if (typeof value === "bigint") return value.toString();
1366
+ if (Array.isArray(value)) {
1367
+ return "[" + value.map(stringify).join(",") + "]";
1368
+ }
1369
+ if (typeof value === "object") {
1370
+ const obj = value;
1371
+ const keys = Object.keys(obj).sort();
1372
+ const parts = [];
1373
+ for (const key of keys) {
1374
+ const v = obj[key];
1375
+ if (v === void 0) continue;
1376
+ parts.push(JSON.stringify(key.normalize("NFC")) + ":" + stringify(v));
1377
+ }
1378
+ return "{" + parts.join(",") + "}";
1379
+ }
1380
+ throw new _ParseError("json", `Cannot canonicalize value of type ${typeof value}`);
1381
+ }
1382
+
1383
+ // src/file-editor/formats/jsonc.ts
1384
+ var import_jsonc_parser2 = require("jsonc-parser");
1385
+
1386
+ // src/file-editor/formats/adapter.ts
1387
+ function _defaultMeta() {
1388
+ return { indent: " ", eol: "\n", hasFinalNewline: true, hasBom: false };
1389
+ }
1390
+ function _detectMeta(text) {
1391
+ const hasBom = text.charCodeAt(0) === 65279;
1392
+ const body = hasBom ? text.slice(1) : text;
1393
+ const eol = body.includes("\r\n") ? "\r\n" : "\n";
1394
+ const hasFinalNewline = body.endsWith("\n");
1395
+ const indent = detectIndent(body);
1396
+ return { indent, eol, hasFinalNewline, hasBom };
1397
+ }
1398
+ function detectIndent(text) {
1399
+ const lines = text.split(/\r?\n/);
1400
+ for (const line of lines) {
1401
+ const m = /^([ \t]+)\S/.exec(line);
1402
+ if (m?.[1]) return m[1];
1403
+ }
1404
+ return " ";
1405
+ }
1406
+
1407
+ // src/file-editor/formats/jsonc.ts
1408
+ function createJsoncFlavor(format, allowComments) {
1409
+ return {
1410
+ format,
1411
+ permitsComments: allowComments,
1412
+ parse(text) {
1413
+ const errors = [];
1414
+ const body = stripBom2(text);
1415
+ const value = (0, import_jsonc_parser2.parse)(body, errors, {
1416
+ allowTrailingComma: allowComments,
1417
+ disallowComments: !allowComments
1418
+ });
1419
+ if (errors.length > 0) {
1420
+ const first = errors[0];
1421
+ if (first === void 0) return { value, meta: _detectMeta(text) };
1422
+ throw new _ParseError(
1423
+ format,
1424
+ `Invalid ${format.toUpperCase()} at offset ${String(first.offset)}: ${(0, import_jsonc_parser2.printParseErrorCode)(first.error)}`,
1425
+ { offset: first.offset, snippet: text.slice(first.offset, first.offset + 40) }
1426
+ );
1427
+ }
1428
+ return { value, meta: _detectMeta(text) };
1429
+ },
1430
+ applyEdits(text, edits, meta) {
1431
+ let current = text === "" ? "{}" : text;
1432
+ for (const edit of edits) {
1433
+ current = applyOne(current, edit, meta, format);
1434
+ }
1435
+ if (meta.hasFinalNewline && !current.endsWith(meta.eol)) {
1436
+ current = current + meta.eol;
1437
+ }
1438
+ if (meta.hasBom && !current.startsWith("\uFEFF")) {
1439
+ current = "\uFEFF" + current;
1440
+ }
1441
+ return current;
1442
+ }
1443
+ };
1444
+ }
1445
+ function applyOne(text, edit, meta, format) {
1446
+ const path = pointerToPath(edit.pointer);
1447
+ const formattingOptions = {
1448
+ tabSize: meta.indent.startsWith(" ") ? 1 : meta.indent.length || 2,
1449
+ insertSpaces: !meta.indent.startsWith(" "),
1450
+ eol: meta.eol
1451
+ };
1452
+ const currentValue = parseCurrent(text, format);
1453
+ switch (edit.kind) {
1454
+ case "set": {
1455
+ const jsonEdits = (0, import_jsonc_parser2.modify)(text, path, edit.value, { formattingOptions });
1456
+ return (0, import_jsonc_parser2.applyEdits)(text, jsonEdits);
1457
+ }
1458
+ case "addIfMissing": {
1459
+ if (_hasPointer(currentValue, edit.pointer)) return text;
1460
+ const jsonEdits = (0, import_jsonc_parser2.modify)(text, path, edit.value, { formattingOptions });
1461
+ return (0, import_jsonc_parser2.applyEdits)(text, jsonEdits);
1462
+ }
1463
+ case "updateIfPresent": {
1464
+ if (!_hasPointer(currentValue, edit.pointer)) return text;
1465
+ if (!edit.updater) {
1466
+ throw new _PathError(
1467
+ edit.pointer,
1468
+ "updateIfPresent requires an updater function."
1469
+ );
1470
+ }
1471
+ const prior = _resolvePointer(currentValue, edit.pointer);
1472
+ const next = edit.updater(prior);
1473
+ const jsonEdits = (0, import_jsonc_parser2.modify)(text, path, next, { formattingOptions });
1474
+ return (0, import_jsonc_parser2.applyEdits)(text, jsonEdits);
1475
+ }
1476
+ case "remove": {
1477
+ if (!_hasPointer(currentValue, edit.pointer)) return text;
1478
+ const jsonEdits = (0, import_jsonc_parser2.modify)(text, path, void 0, { formattingOptions });
1479
+ return (0, import_jsonc_parser2.applyEdits)(text, jsonEdits);
1480
+ }
1481
+ case "merge": {
1482
+ const prior = _resolvePointer(currentValue, edit.pointer);
1483
+ const merged = mergeValues(prior, edit.value, edit.mergeOptions, edit.pointer);
1484
+ const jsonEdits = (0, import_jsonc_parser2.modify)(text, path, merged, { formattingOptions });
1485
+ return (0, import_jsonc_parser2.applyEdits)(text, jsonEdits);
1486
+ }
1487
+ case "arrayAppend": {
1488
+ const prior = _resolvePointer(currentValue, edit.pointer);
1489
+ if (prior === void 0) {
1490
+ const jsonEdits2 = (0, import_jsonc_parser2.modify)(text, path, [edit.value], { formattingOptions });
1491
+ return (0, import_jsonc_parser2.applyEdits)(text, jsonEdits2);
1492
+ }
1493
+ if (!Array.isArray(prior)) {
1494
+ throw new _TypeMismatchError(
1495
+ edit.pointer,
1496
+ `arrayAppend requires an array at ${edit.pointer}`
1497
+ );
1498
+ }
1499
+ const appendPath = [...path, prior.length];
1500
+ const jsonEdits = (0, import_jsonc_parser2.modify)(text, appendPath, edit.value, {
1501
+ formattingOptions,
1502
+ isArrayInsertion: true
1503
+ });
1504
+ return (0, import_jsonc_parser2.applyEdits)(text, jsonEdits);
1505
+ }
1506
+ case "arrayUpsert": {
1507
+ const prior = _resolvePointer(currentValue, edit.pointer);
1508
+ const keyFn = edit.arrayKey;
1509
+ if (!keyFn) throw new _PathError(edit.pointer, "arrayUpsert requires a keyFn.");
1510
+ const newElement = edit.value;
1511
+ if (prior === void 0) {
1512
+ const jsonEdits2 = (0, import_jsonc_parser2.modify)(text, path, [newElement], { formattingOptions });
1513
+ return (0, import_jsonc_parser2.applyEdits)(text, jsonEdits2);
1514
+ }
1515
+ if (!Array.isArray(prior)) {
1516
+ throw new _TypeMismatchError(
1517
+ edit.pointer,
1518
+ `arrayUpsert requires an array at ${edit.pointer}`
1519
+ );
1520
+ }
1521
+ const targetKey = keyFn(newElement);
1522
+ const idx = prior.findIndex((el) => keyFn(el) === targetKey);
1523
+ if (idx >= 0) {
1524
+ const jsonEdits2 = (0, import_jsonc_parser2.modify)(text, [...path, idx], newElement, { formattingOptions });
1525
+ return (0, import_jsonc_parser2.applyEdits)(text, jsonEdits2);
1526
+ }
1527
+ const appendPath = [...path, prior.length];
1528
+ const jsonEdits = (0, import_jsonc_parser2.modify)(text, appendPath, newElement, {
1529
+ formattingOptions,
1530
+ isArrayInsertion: true
1531
+ });
1532
+ return (0, import_jsonc_parser2.applyEdits)(text, jsonEdits);
1533
+ }
1534
+ }
1535
+ }
1536
+ function pointerToPath(pointer) {
1537
+ const segments = _parsePointer(pointer);
1538
+ return segments.map((seg) => {
1539
+ const idx = _parseArrayIndex(seg);
1540
+ return idx ?? seg;
1541
+ });
1542
+ }
1543
+ function stripBom2(text) {
1544
+ return text.charCodeAt(0) === 65279 ? text.slice(1) : text;
1545
+ }
1546
+ function parseCurrent(text, format) {
1547
+ const errors = [];
1548
+ const value = (0, import_jsonc_parser2.parse)(stripBom2(text), errors, {
1549
+ allowTrailingComma: format === "jsonc",
1550
+ disallowComments: format === "json"
1551
+ });
1552
+ if (errors.length > 0) {
1553
+ const first = errors[0];
1554
+ if (first === void 0) return value;
1555
+ throw new _ParseError(
1556
+ format,
1557
+ `Invalid ${format}: ${(0, import_jsonc_parser2.printParseErrorCode)(first.error)}`,
1558
+ {
1559
+ offset: first.offset
1560
+ }
1561
+ );
1562
+ }
1563
+ return value;
1564
+ }
1565
+ function mergeValues(prior, incoming, options, pointer) {
1566
+ const deep = options?.deep ?? true;
1567
+ const arrayStrategy = options?.arrays ?? "replace";
1568
+ if (prior === void 0 || prior === null) return incoming;
1569
+ if (!isPlainObject(prior) || !isPlainObject(incoming)) {
1570
+ if (Array.isArray(prior) && Array.isArray(incoming)) {
1571
+ return mergeArrays(prior, incoming, arrayStrategy);
1572
+ }
1573
+ throw new _TypeMismatchError(
1574
+ pointer,
1575
+ "merge requires an object at the target pointer."
1576
+ );
1577
+ }
1578
+ const out = { ...prior };
1579
+ for (const [key, val] of Object.entries(incoming)) {
1580
+ const existing = out[key];
1581
+ if (deep && isPlainObject(existing) && isPlainObject(val)) {
1582
+ out[key] = mergeValues(existing, val, options, pointer + "/" + key);
1583
+ } else if (Array.isArray(existing) && Array.isArray(val)) {
1584
+ out[key] = mergeArrays(existing, val, arrayStrategy);
1585
+ } else {
1586
+ out[key] = val;
1587
+ }
1588
+ }
1589
+ return out;
1590
+ }
1591
+ function mergeArrays(a, b, strategy) {
1592
+ switch (strategy) {
1593
+ case "replace":
1594
+ return [...b];
1595
+ case "concat":
1596
+ return [...a, ...b];
1597
+ case "concatUnique": {
1598
+ const seen = /* @__PURE__ */ new Set();
1599
+ const out = [];
1600
+ for (const v of [...a, ...b]) {
1601
+ const key = stableStringify(v);
1602
+ if (!seen.has(key)) {
1603
+ seen.add(key);
1604
+ out.push(v);
1605
+ }
1606
+ }
1607
+ return out;
1608
+ }
1609
+ }
1610
+ }
1611
+ function stableStringify(v) {
1612
+ return _canonicalNormalize(JSON.stringify(v), "json");
1613
+ }
1614
+ function isPlainObject(v) {
1615
+ if (v === null || typeof v !== "object") return false;
1616
+ if (Array.isArray(v)) return false;
1617
+ const proto = Object.getPrototypeOf(v);
1618
+ return proto === Object.prototype || proto === null;
1619
+ }
1620
+ var jsonAdapter = createJsoncFlavor("json", false);
1621
+ var jsoncAdapter = createJsoncFlavor("jsonc", true);
1622
+
1623
+ // src/file-editor/formats/yaml.ts
1624
+ var import_yaml2 = __toESM(require("yaml"), 1);
1625
+ var yamlAdapter = {
1626
+ format: "yaml",
1627
+ permitsComments: true,
1628
+ parse(text) {
1629
+ const doc = import_yaml2.default.parseDocument(text, { merge: true, prettyErrors: false });
1630
+ const value = doc.toJS();
1631
+ if (doc.errors.length > 0) {
1632
+ const first = doc.errors[0];
1633
+ if (first === void 0) return { value, meta: _detectMeta(text) };
1634
+ throw new _ParseError("yaml", `Invalid YAML: ${first.message}`, {
1635
+ offset: first.pos[0],
1636
+ snippet: text.slice(first.pos[0], first.pos[0] + 40)
1637
+ });
1638
+ }
1639
+ return { value, meta: _detectMeta(text) };
1640
+ },
1641
+ applyEdits(text, edits, meta) {
1642
+ const source = text === "" ? "{}\n" : text;
1643
+ const doc = import_yaml2.default.parseDocument(source, { merge: true });
1644
+ if (doc.errors.length > 0) {
1645
+ const first = doc.errors[0];
1646
+ if (first !== void 0) {
1647
+ throw new _ParseError("yaml", `Invalid YAML: ${first.message}`);
1648
+ }
1649
+ }
1650
+ for (const edit of edits) {
1651
+ applyYamlEdit(doc, edit);
1652
+ }
1653
+ let result = doc.toString();
1654
+ if (meta.eol === "\r\n") {
1655
+ result = result.replace(/\r?\n/g, "\r\n");
1656
+ }
1657
+ const endsWithEol = result.endsWith(meta.eol);
1658
+ if (meta.hasFinalNewline && !endsWithEol) {
1659
+ result = result + meta.eol;
1660
+ } else if (!meta.hasFinalNewline && endsWithEol) {
1661
+ result = result.slice(0, -meta.eol.length);
1662
+ }
1663
+ if (meta.hasBom && !result.startsWith("\uFEFF")) {
1664
+ result = "\uFEFF" + result;
1665
+ }
1666
+ return result;
1667
+ }
1668
+ };
1669
+ function applyYamlEdit(doc, edit) {
1670
+ const segments = toYamlPath(edit.pointer);
1671
+ switch (edit.kind) {
1672
+ case "set": {
1673
+ setInWithParents(doc, segments, edit.value);
1674
+ return;
1675
+ }
1676
+ case "addIfMissing": {
1677
+ if (doc.hasIn(segments)) return;
1678
+ setInWithParents(doc, segments, edit.value);
1679
+ return;
1680
+ }
1681
+ case "updateIfPresent": {
1682
+ if (!doc.hasIn(segments)) return;
1683
+ if (!edit.updater)
1684
+ throw new _PathError(edit.pointer, "updateIfPresent requires an updater.");
1685
+ const prior = jsValueAt(doc, segments);
1686
+ const next = edit.updater(prior);
1687
+ doc.setIn(segments, next);
1688
+ return;
1689
+ }
1690
+ case "remove": {
1691
+ if (!doc.hasIn(segments)) return;
1692
+ doc.deleteIn(segments);
1693
+ return;
1694
+ }
1695
+ case "merge": {
1696
+ const prior = jsValueAt(doc, segments);
1697
+ const merged = mergeValues2(prior, edit.value, edit.mergeOptions, edit.pointer);
1698
+ setInWithParents(doc, segments, merged);
1699
+ return;
1700
+ }
1701
+ case "arrayAppend": {
1702
+ const node = segments.length === 0 ? doc.contents : doc.getIn(segments, true);
1703
+ if (node === void 0 || node === null) {
1704
+ setInWithParents(doc, segments, [edit.value]);
1705
+ return;
1706
+ }
1707
+ if (!(0, import_yaml2.isSeq)(node)) {
1708
+ throw new _TypeMismatchError(
1709
+ edit.pointer,
1710
+ `arrayAppend requires a sequence at ${edit.pointer}`
1711
+ );
1712
+ }
1713
+ node.add(edit.value);
1714
+ return;
1715
+ }
1716
+ case "arrayUpsert": {
1717
+ const keyFn = edit.arrayKey;
1718
+ if (!keyFn) throw new _PathError(edit.pointer, "arrayUpsert requires a keyFn.");
1719
+ const priorJs = jsValueAt(doc, segments);
1720
+ const newElement = edit.value;
1721
+ if (priorJs === void 0 || priorJs === null) {
1722
+ setInWithParents(doc, segments, [newElement]);
1723
+ return;
1724
+ }
1725
+ if (!Array.isArray(priorJs)) {
1726
+ throw new _TypeMismatchError(
1727
+ edit.pointer,
1728
+ `arrayUpsert requires a sequence at ${edit.pointer}`
1729
+ );
1730
+ }
1731
+ const targetKey = keyFn(newElement);
1732
+ const idx = priorJs.findIndex((el) => keyFn(el) === targetKey);
1733
+ if (idx >= 0) {
1734
+ doc.setIn([...segments, idx], newElement);
1735
+ } else {
1736
+ const seqNode = segments.length === 0 ? doc.contents : doc.getIn(segments, true);
1737
+ if ((0, import_yaml2.isSeq)(seqNode)) {
1738
+ seqNode.add(newElement);
1739
+ }
1740
+ }
1741
+ return;
1742
+ }
1743
+ }
1744
+ }
1745
+ function toYamlPath(pointer) {
1746
+ const segments = _parsePointer(pointer);
1747
+ return segments.map((seg) => {
1748
+ const idx = _parseArrayIndex(seg);
1749
+ return idx ?? seg;
1750
+ });
1751
+ }
1752
+ function jsValueAt(doc, segments) {
1753
+ if (segments.length === 0) {
1754
+ return doc.toJS();
1755
+ }
1756
+ return _resolvePointer(
1757
+ // eslint-disable-next-line @typescript-eslint/consistent-type-assertions -- yaml's toJS() returns any; widening to unknown is safe
1758
+ doc.toJS(),
1759
+ "/" + segments.map((s) => String(s)).join("/")
1760
+ );
1761
+ }
1762
+ function setInWithParents(doc, segments, value) {
1763
+ if (segments.length === 0) {
1764
+ doc.contents = doc.createNode(value);
1765
+ return;
1766
+ }
1767
+ const partial = [];
1768
+ for (let i = 0; i < segments.length - 1; i++) {
1769
+ const seg = segments[i];
1770
+ if (seg === void 0) break;
1771
+ partial.push(seg);
1772
+ if (!doc.hasIn(partial)) break;
1773
+ const cur = doc.getIn(partial, true);
1774
+ if (!(0, import_yaml2.isCollection)(cur)) {
1775
+ throw new _TypeMismatchError(
1776
+ "/" + partial.map(String).join("/"),
1777
+ `cannot descend through non-collection value`
1778
+ );
1779
+ }
1780
+ const needsIndex = typeof segments[i + 1] === "number";
1781
+ if (needsIndex && !(0, import_yaml2.isSeq)(cur)) {
1782
+ throw new _TypeMismatchError(
1783
+ "/" + partial.map(String).join("/"),
1784
+ `expected sequence, found mapping`
1785
+ );
1786
+ }
1787
+ if (!needsIndex && !(0, import_yaml2.isMap)(cur)) {
1788
+ throw new _TypeMismatchError(
1789
+ "/" + partial.map(String).join("/"),
1790
+ `expected mapping, found sequence`
1791
+ );
1792
+ }
1793
+ }
1794
+ doc.setIn(segments, doc.createNode(value));
1795
+ }
1796
+ function mergeValues2(prior, incoming, options, pointer) {
1797
+ const deep = options?.deep ?? true;
1798
+ const arrayStrategy = options?.arrays ?? "replace";
1799
+ if (prior === void 0 || prior === null) return incoming;
1800
+ if (!isPlainObject2(prior) || !isPlainObject2(incoming)) {
1801
+ if (Array.isArray(prior) && Array.isArray(incoming)) {
1802
+ return mergeArrays2(prior, incoming, arrayStrategy);
1803
+ }
1804
+ throw new _TypeMismatchError(
1805
+ pointer,
1806
+ "merge requires a mapping at the target pointer."
1807
+ );
1808
+ }
1809
+ const out = { ...prior };
1810
+ for (const [key, val] of Object.entries(incoming)) {
1811
+ const existing = out[key];
1812
+ if (deep && isPlainObject2(existing) && isPlainObject2(val)) {
1813
+ out[key] = mergeValues2(existing, val, options, pointer + "/" + key);
1814
+ } else if (Array.isArray(existing) && Array.isArray(val)) {
1815
+ out[key] = mergeArrays2(existing, val, arrayStrategy);
1816
+ } else {
1817
+ out[key] = val;
1818
+ }
1819
+ }
1820
+ return out;
1821
+ }
1822
+ function mergeArrays2(a, b, strategy) {
1823
+ switch (strategy) {
1824
+ case "replace":
1825
+ return [...b];
1826
+ case "concat":
1827
+ return [...a, ...b];
1828
+ case "concatUnique": {
1829
+ const seen = /* @__PURE__ */ new Set();
1830
+ const out = [];
1831
+ for (const v of [...a, ...b]) {
1832
+ const key = _canonicalNormalize(JSON.stringify(v), "json");
1833
+ if (!seen.has(key)) {
1834
+ seen.add(key);
1835
+ out.push(v);
1836
+ }
1837
+ }
1838
+ return out;
1839
+ }
1840
+ }
1841
+ }
1842
+ function isPlainObject2(v) {
1843
+ if (v === null || typeof v !== "object") return false;
1844
+ if (Array.isArray(v)) return false;
1845
+ const proto = Object.getPrototypeOf(v);
1846
+ return proto === Object.prototype || proto === null;
1847
+ }
1848
+
1849
+ // src/file-editor/formats/detect.ts
1850
+ var import_node_path3 = require("path");
1851
+ function _detectFormat(path, text) {
1852
+ const ext = (0, import_node_path3.extname)(path).toLowerCase();
1853
+ switch (ext) {
1854
+ case ".json":
1855
+ if (text !== void 0 && containsJsoncMarkers(text)) return "jsonc";
1856
+ return inferJsonFlavor(path);
1857
+ case ".jsonc":
1858
+ return "jsonc";
1859
+ case ".yaml":
1860
+ case ".yml":
1861
+ return "yaml";
1862
+ default:
1863
+ throw new _FormatNotSupportedError(path);
1864
+ }
1865
+ }
1866
+ var JSONC_PATH_HINTS = [
1867
+ "tsconfig.json",
1868
+ "tsconfig.base.json",
1869
+ "jsconfig.json",
1870
+ "api-extractor.json",
1871
+ ".vscode/settings.json",
1872
+ ".vscode/launch.json"
1873
+ ];
1874
+ function inferJsonFlavor(path) {
1875
+ const normalized = path.replace(/\\/g, "/").toLowerCase();
1876
+ for (const hint of JSONC_PATH_HINTS) {
1877
+ if (normalized.endsWith("/" + hint) || normalized === hint) return "jsonc";
1878
+ if (/\/tsconfig\.[^/]*\.json$/.test(normalized)) return "jsonc";
1879
+ }
1880
+ return "json";
1881
+ }
1882
+ function containsJsoncMarkers(text) {
1883
+ return scanForCommentOrTrailingComma(text);
1884
+ }
1885
+ function scanForCommentOrTrailingComma(text) {
1886
+ let inString = false;
1887
+ let escape = false;
1888
+ for (let i = 0; i < text.length; i++) {
1889
+ const ch = text.charCodeAt(i);
1890
+ if (escape) {
1891
+ escape = false;
1892
+ continue;
1893
+ }
1894
+ if (inString) {
1895
+ if (ch === 92) escape = true;
1896
+ else if (ch === 34) inString = false;
1897
+ continue;
1898
+ }
1899
+ if (ch === 34) {
1900
+ inString = true;
1901
+ continue;
1902
+ }
1903
+ if (ch === 47) {
1904
+ const next = text.charCodeAt(i + 1);
1905
+ if (next === 47 || next === 42) return true;
1906
+ }
1907
+ if (ch === 44) {
1908
+ for (let j = i + 1; j < text.length; j++) {
1909
+ const nc = text.charCodeAt(j);
1910
+ if (nc === 32 || nc === 9 || nc === 10 || nc === 13) continue;
1911
+ if (nc === 93 || nc === 125) return true;
1912
+ break;
1913
+ }
1914
+ }
1915
+ }
1916
+ return false;
1917
+ }
1918
+
1919
+ // src/file-editor/formats/index.ts
1920
+ var REGISTRY = /* @__PURE__ */ new Map([
1921
+ ["json", jsonAdapter],
1922
+ ["jsonc", jsoncAdapter],
1923
+ ["yaml", yamlAdapter]
1924
+ ]);
1925
+ function _getAdapter(format) {
1926
+ const adapter = REGISTRY.get(format);
1927
+ if (!adapter) {
1928
+ throw new Error(`No adapter registered for format "${format}"`);
1929
+ }
1930
+ return adapter;
1931
+ }
1932
+ function _registerFormatAdapter(format, adapter) {
1933
+ REGISTRY.set(format, adapter);
1934
+ }
1935
+
1936
+ // src/file-editor/document.ts
1937
+ var _Document = class {
1938
+ absolutePath;
1939
+ format;
1940
+ /** Optional Standard Schema; when set, {@link _Document.validate} runs it. */
1941
+ schema;
1942
+ /** Original on-disk text (may be `""` for new files). */
1943
+ originalText;
1944
+ meta;
1945
+ edits = [];
1946
+ staticDiagnostics = [];
1947
+ constructor(init) {
1948
+ this.absolutePath = init.absolutePath;
1949
+ this.format = init.format;
1950
+ this.originalText = init.originalText;
1951
+ this.meta = init.meta;
1952
+ this.schema = init.schema ?? null;
1953
+ }
1954
+ // ── Reads ──────────────────────────────────────────────────────────
1955
+ /** Get the value at a pointer. Reflects pending edits via `preview()`. */
1956
+ get(pointer) {
1957
+ const current = this.parseCurrentValue();
1958
+ return _resolvePointer(current, pointer);
1959
+ }
1960
+ /** Like `get` but throws {@link _AssertionError} on miss. */
1961
+ getRequired(pointer) {
1962
+ const current = this.parseCurrentValue();
1963
+ if (!_hasPointer(current, pointer)) {
1964
+ throw new _AssertionError(pointer, `required value is missing at ${pointer}`);
1965
+ }
1966
+ return _resolvePointer(current, pointer);
1967
+ }
1968
+ has(pointer) {
1969
+ return _hasPointer(this.parseCurrentValue(), pointer);
1970
+ }
1971
+ // ── Mutations (staged) ─────────────────────────────────────────────
1972
+ set(pointer, value, opts) {
1973
+ return this.stage({ kind: "set", pointer, value, ...this.opts(opts) });
1974
+ }
1975
+ addIfMissing(pointer, value, opts) {
1976
+ return this.stage({ kind: "addIfMissing", pointer, value, ...this.opts(opts) });
1977
+ }
1978
+ updateIfPresent(pointer, updater, opts) {
1979
+ return this.stage({
1980
+ kind: "updateIfPresent",
1981
+ pointer,
1982
+ // eslint-disable-next-line @typescript-eslint/consistent-type-assertions, @typescript-eslint/no-unsafe-type-assertion -- _PendingEdit.updater is typed as (unknown) => unknown; widening the typed updater is safe since it will receive the same runtime value
1983
+ updater,
1984
+ ...this.opts(opts)
1985
+ });
1986
+ }
1987
+ remove(pointer, opts) {
1988
+ return this.stage({ kind: "remove", pointer, ...this.opts(opts) });
1989
+ }
1990
+ merge(pointer, partial, opts) {
1991
+ const base = {
1992
+ kind: "merge",
1993
+ pointer,
1994
+ value: partial,
1995
+ ...this.opts(opts)
1996
+ };
1997
+ return this.stage(
1998
+ opts?.mergeOptions ? { ...base, mergeOptions: opts.mergeOptions } : base
1999
+ );
2000
+ }
2001
+ arrayAppend(pointer, value, opts) {
2002
+ return this.stage({ kind: "arrayAppend", pointer, value, ...this.opts(opts) });
2003
+ }
2004
+ arrayUpsert(pointer, value, keyFn, opts) {
2005
+ return this.stage({
2006
+ kind: "arrayUpsert",
2007
+ pointer,
2008
+ value,
2009
+ // eslint-disable-next-line @typescript-eslint/consistent-type-assertions, @typescript-eslint/no-unsafe-type-assertion -- _PendingEdit.arrayKey is typed as (unknown) => unknown; widening the typed keyFn is safe since it will receive the same runtime value
2010
+ arrayKey: keyFn,
2011
+ ...this.opts(opts)
2012
+ });
2013
+ }
2014
+ // ── Assertions ─────────────────────────────────────────────────────
2015
+ assert(...checks) {
2016
+ const results = _runAssertions(this.absolutePath, this.parseCurrentValue(), checks);
2017
+ this.staticDiagnostics.push(...results);
2018
+ return results;
2019
+ }
2020
+ /**
2021
+ * If a schema is configured, validate the current preview against it and
2022
+ * return a diagnostic per issue. Returns `[]` when no schema is set or
2023
+ * validation passes.
2024
+ *
2025
+ * Each call replaces any previously accumulated `SCHEMA_VIOLATION` diagnostics
2026
+ * so that calling `validate()` multiple times does not duplicate findings.
2027
+ */
2028
+ async validate() {
2029
+ if (!this.schema) return [];
2030
+ const result = await this.schema["~standard"].validate(this.parseCurrentValue());
2031
+ const withoutPriorSchema = this.staticDiagnostics.filter(
2032
+ (d) => d.code !== "SCHEMA_VIOLATION"
2033
+ );
2034
+ this.staticDiagnostics.length = 0;
2035
+ this.staticDiagnostics.push(...withoutPriorSchema);
2036
+ if (!result.issues) return [];
2037
+ const diagnostics = result.issues.map((issue) => ({
2038
+ path: this.absolutePath,
2039
+ pointer: issuePathToPointer(issue.path),
2040
+ code: "SCHEMA_VIOLATION",
2041
+ severity: "error",
2042
+ message: issue.message
2043
+ }));
2044
+ this.staticDiagnostics.push(...diagnostics);
2045
+ return diagnostics;
2046
+ }
2047
+ diagnostics() {
2048
+ return this.staticDiagnostics;
2049
+ }
2050
+ // ── Introspection ──────────────────────────────────────────────────
2051
+ pendingEdits() {
2052
+ return this.edits;
2053
+ }
2054
+ /** Serialize with all pending edits applied. Pure — no disk I/O. */
2055
+ preview() {
2056
+ if (this.edits.length === 0) return this.originalText;
2057
+ const adapter = _getAdapter(this.format);
2058
+ return adapter.applyEdits(this.originalText, this.edits, this.meta);
2059
+ }
2060
+ /** Reset all pending edits. Used by transactions on rollback. */
2061
+ rollback() {
2062
+ this.edits.length = 0;
2063
+ }
2064
+ /** @internal Metadata for transaction serialization. */
2065
+ getMeta() {
2066
+ return this.meta;
2067
+ }
2068
+ /** @internal Original text, for transaction fingerprinting. */
2069
+ getOriginalText() {
2070
+ return this.originalText;
2071
+ }
2072
+ // ── Internals ──────────────────────────────────────────────────────
2073
+ parseCurrentValue() {
2074
+ const text = this.preview();
2075
+ if (text === "") return void 0;
2076
+ const adapter = _getAdapter(this.format);
2077
+ return adapter.parse(text).value;
2078
+ }
2079
+ stage(edit) {
2080
+ if (edit.guard && !edit.guard(this.parseCurrentValue())) return this;
2081
+ this.edits.push(edit);
2082
+ return this;
2083
+ }
2084
+ opts(opts) {
2085
+ const out = {};
2086
+ if (opts?.conflictPolicy !== void 0) out.conflictPolicy = opts.conflictPolicy;
2087
+ if (opts?.guard !== void 0) {
2088
+ const g = opts.guard;
2089
+ out.guard = () => g(this);
2090
+ }
2091
+ return out;
2092
+ }
2093
+ };
2094
+ function issuePathToPointer(path) {
2095
+ if (!path || path.length === 0) return "";
2096
+ const parts = path.map((seg) => {
2097
+ const key = typeof seg === "object" ? seg.key : seg;
2098
+ const s = typeof key === "symbol" ? key.description ?? "" : String(key);
2099
+ return s.replace(/~/g, "~0").replace(/\//g, "~1");
2100
+ });
2101
+ return "/" + parts.join("/");
2102
+ }
2103
+ async function _openDocumentFromDisk(absolutePath, options = {}) {
2104
+ const schema = options.schema ?? null;
2105
+ let text;
2106
+ try {
2107
+ text = await (0, import_promises.readFile)(absolutePath, "utf8");
2108
+ } catch (err) {
2109
+ if (isNodeNotFound(err)) {
2110
+ const format2 = options.format ?? _detectFormat(absolutePath);
2111
+ return new _Document({
2112
+ absolutePath,
2113
+ format: format2,
2114
+ originalText: "",
2115
+ meta: _defaultMeta(),
2116
+ schema
2117
+ });
2118
+ }
2119
+ throw err;
2120
+ }
2121
+ const format = options.format ?? _detectFormat(absolutePath, text);
2122
+ const adapter = _getAdapter(format);
2123
+ const { meta } = adapter.parse(text);
2124
+ return new _Document({ absolutePath, format, originalText: text, meta, schema });
2125
+ }
2126
+ function isNodeNotFound(err) {
2127
+ return err !== null && typeof err === "object" && "code" in err && err.code === "ENOENT";
2128
+ }
2129
+ function _previewFingerprint(doc) {
2130
+ return _fingerprint(doc.preview(), doc.format);
2131
+ }
2132
+
2133
+ // src/file-editor/transaction.ts
2134
+ var import_promises3 = require("fs/promises");
2135
+
2136
+ // src/file-editor/state/in-memory.ts
2137
+ function _InMemoryStateStore() {
2138
+ const records = /* @__PURE__ */ new Map();
2139
+ return {
2140
+ // eslint-disable-next-line @typescript-eslint/require-await -- synchronous Map operation wrapped in async interface
2141
+ async read(path) {
2142
+ return records.get(path) ?? null;
2143
+ },
2144
+ // eslint-disable-next-line @typescript-eslint/require-await -- synchronous Map operation wrapped in async interface
2145
+ async write(path, record) {
2146
+ records.set(path, record);
2147
+ },
2148
+ // eslint-disable-next-line @typescript-eslint/require-await -- synchronous Map operation wrapped in async interface
2149
+ async delete(path) {
2150
+ records.delete(path);
2151
+ },
2152
+ // eslint-disable-next-line @typescript-eslint/require-await -- synchronous Map operation wrapped in async interface
2153
+ async list() {
2154
+ return [...records.entries()].map(([path, record]) => ({ path, record }));
2155
+ }
2156
+ };
2157
+ }
2158
+
2159
+ // src/file-editor/util/atomic-write.ts
2160
+ var import_node_crypto2 = require("crypto");
2161
+ var import_promises2 = require("fs/promises");
2162
+ var import_node_path4 = require("path");
2163
+ async function _atomicWriteText(path, text) {
2164
+ await (0, import_promises2.mkdir)((0, import_node_path4.dirname)(path), { recursive: true });
2165
+ const suffix = (0, import_node_crypto2.randomBytes)(6).toString("hex");
2166
+ const tmp = `${path}.${suffix}.tmp`;
2167
+ await (0, import_promises2.writeFile)(tmp, text, "utf8");
2168
+ try {
2169
+ await (0, import_promises2.rename)(tmp, path);
2170
+ } catch (err) {
2171
+ await (0, import_promises2.unlink)(tmp).catch((_unlinkErr) => void 0);
2172
+ throw err;
2173
+ }
2174
+ }
2175
+
2176
+ // src/file-editor/util/diff.ts
2177
+ function _unified(a, b, opts = {}) {
2178
+ const context = opts.context ?? 3;
2179
+ const nameA = opts.fileNameA ?? "a";
2180
+ const nameB = opts.fileNameB ?? "b";
2181
+ const hunksList = _hunks(a, b, { context });
2182
+ if (hunksList.length === 0) return "";
2183
+ const lines = [`--- ${nameA}`, `+++ ${nameB}`];
2184
+ for (const h of hunksList) {
2185
+ lines.push(
2186
+ `@@ -${String(h.oldStart)},${String(h.oldLines)} +${String(h.newStart)},${String(h.newLines)} @@`
2187
+ );
2188
+ for (const l of h.lines) {
2189
+ const prefix = l.kind === "context" ? " " : l.kind === "add" ? "+" : "-";
2190
+ lines.push(prefix + l.text);
2191
+ }
2192
+ }
2193
+ return lines.join("\n") + "\n";
2194
+ }
2195
+ function _hunks(a, b, opts = {}) {
2196
+ const context = opts.context ?? 3;
2197
+ const aLines = splitLines(a);
2198
+ const bLines = splitLines(b);
2199
+ const ops = diffLines(aLines, bLines);
2200
+ return groupIntoHunks(ops, context);
2201
+ }
2202
+ function splitLines(text) {
2203
+ if (text === "") return [];
2204
+ const parts = text.split(/\r?\n/);
2205
+ if (parts.length > 0 && parts.at(-1) === "" && /\r?\n$/.test(text)) {
2206
+ parts.pop();
2207
+ }
2208
+ return parts;
2209
+ }
2210
+ var Dp2D = class {
2211
+ data;
2212
+ cols;
2213
+ constructor(rows, cols) {
2214
+ this.cols = cols;
2215
+ this.data = new Array(rows * cols).fill(0);
2216
+ }
2217
+ get(row, col) {
2218
+ return this.data[row * this.cols + col] ?? 0;
2219
+ }
2220
+ set(row, col, val) {
2221
+ this.data[row * this.cols + col] = val;
2222
+ }
2223
+ };
2224
+ function diffLines(a, b) {
2225
+ const n = a.length;
2226
+ const m = b.length;
2227
+ const dp = new Dp2D(n + 1, m + 1);
2228
+ for (let i2 = n - 1; i2 >= 0; i2--) {
2229
+ for (let j2 = m - 1; j2 >= 0; j2--) {
2230
+ if (a[i2] === b[j2]) dp.set(i2, j2, dp.get(i2 + 1, j2 + 1) + 1);
2231
+ else dp.set(i2, j2, Math.max(dp.get(i2 + 1, j2), dp.get(i2, j2 + 1)));
2232
+ }
2233
+ }
2234
+ const ops = [];
2235
+ let i = 0;
2236
+ let j = 0;
2237
+ while (i < n && j < m) {
2238
+ const aLine = a[i] ?? "";
2239
+ const bLine = b[j] ?? "";
2240
+ if (aLine === bLine) {
2241
+ ops.push({ kind: "context", text: aLine, oldIdx: i, newIdx: j });
2242
+ i++;
2243
+ j++;
2244
+ } else if (dp.get(i + 1, j) >= dp.get(i, j + 1)) {
2245
+ ops.push({ kind: "del", text: aLine, oldIdx: i });
2246
+ i++;
2247
+ } else {
2248
+ ops.push({ kind: "add", text: bLine, newIdx: j });
2249
+ j++;
2250
+ }
2251
+ }
2252
+ while (i < n) {
2253
+ ops.push({ kind: "del", text: a[i] ?? "", oldIdx: i });
2254
+ i++;
2255
+ }
2256
+ while (j < m) {
2257
+ ops.push({ kind: "add", text: b[j] ?? "", newIdx: j });
2258
+ j++;
2259
+ }
2260
+ return ops;
2261
+ }
2262
+ function groupIntoHunks(ops, context) {
2263
+ const changeIndices = [];
2264
+ for (let k = 0; k < ops.length; k++) {
2265
+ const op = ops[k];
2266
+ if (op !== void 0 && op.kind !== "context") changeIndices.push(k);
2267
+ }
2268
+ if (changeIndices.length === 0) return [];
2269
+ const groups = [];
2270
+ let groupStart = Math.max(0, (changeIndices[0] ?? 0) - context);
2271
+ let groupEnd = Math.min(ops.length - 1, (changeIndices[0] ?? 0) + context);
2272
+ for (let k = 1; k < changeIndices.length; k++) {
2273
+ const idx = changeIndices[k] ?? 0;
2274
+ if (idx - context <= groupEnd + 1) {
2275
+ groupEnd = Math.min(ops.length - 1, idx + context);
2276
+ } else {
2277
+ groups.push({ start: groupStart, end: groupEnd });
2278
+ groupStart = Math.max(0, idx - context);
2279
+ groupEnd = Math.min(ops.length - 1, idx + context);
2280
+ }
2281
+ }
2282
+ groups.push({ start: groupStart, end: groupEnd });
2283
+ const result = [];
2284
+ for (const g of groups) {
2285
+ const slice = ops.slice(g.start, g.end + 1);
2286
+ const first = slice[0];
2287
+ if (first === void 0) continue;
2288
+ const lines = slice.map((op) => toDiffLine(op));
2289
+ const oldLines = slice.filter((op) => op.kind !== "add").length;
2290
+ const newLines = slice.filter((op) => op.kind !== "del").length;
2291
+ let oldStart;
2292
+ let newStart;
2293
+ if (first.kind === "context") {
2294
+ oldStart = first.oldIdx + 1;
2295
+ newStart = first.newIdx + 1;
2296
+ } else if (first.kind === "del") {
2297
+ oldStart = first.oldIdx + 1;
2298
+ const prevNew = findNewIndex(ops, g.start, "before");
2299
+ newStart = prevNew === 0 && newLines === 0 ? 0 : prevNew + 1;
2300
+ } else {
2301
+ const prevOld = findOldIndex(ops, g.start, "before");
2302
+ oldStart = prevOld === 0 && oldLines === 0 ? 0 : prevOld + 1;
2303
+ newStart = first.newIdx + 1;
2304
+ }
2305
+ result.push({ oldStart, oldLines, newStart, newLines, lines });
2306
+ }
2307
+ return result;
2308
+ }
2309
+ function toDiffLine(op) {
2310
+ if (op.kind === "context") {
2311
+ return {
2312
+ kind: "context",
2313
+ text: op.text,
2314
+ oldLineNo: op.oldIdx + 1,
2315
+ newLineNo: op.newIdx + 1
2316
+ };
2317
+ }
2318
+ if (op.kind === "del") {
2319
+ return { kind: "del", text: op.text, oldLineNo: op.oldIdx + 1, newLineNo: null };
2320
+ }
2321
+ return { kind: "add", text: op.text, oldLineNo: null, newLineNo: op.newIdx + 1 };
2322
+ }
2323
+ function findOldIndex(ops, from, _direction) {
2324
+ for (let k = from - 1; k >= 0; k--) {
2325
+ const op = ops[k];
2326
+ if (op === void 0) continue;
2327
+ if (op.kind === "context") return op.oldIdx + 1;
2328
+ if (op.kind === "del") return op.oldIdx + 1;
2329
+ }
2330
+ return 0;
2331
+ }
2332
+ function findNewIndex(ops, from, _direction) {
2333
+ for (let k = from - 1; k >= 0; k--) {
2334
+ const op = ops[k];
2335
+ if (op === void 0) continue;
2336
+ if (op.kind === "context") return op.newIdx + 1;
2337
+ if (op.kind === "add") return op.newIdx + 1;
2338
+ }
2339
+ return 0;
2340
+ }
2341
+
2342
+ // src/file-editor/transaction.ts
2343
+ var _Transaction = class {
2344
+ documents = /* @__PURE__ */ new Map();
2345
+ options;
2346
+ constructor(options = {}) {
2347
+ this.options = options;
2348
+ }
2349
+ /** Open or return the cached document for a path. Memoized per transaction. */
2350
+ async open(absolutePath) {
2351
+ const cached = this.documents.get(absolutePath);
2352
+ if (cached) return cached;
2353
+ const opts = this.options.format === void 0 ? {} : { format: this.options.format };
2354
+ const doc = await _openDocumentFromDisk(absolutePath, opts);
2355
+ this.documents.set(absolutePath, doc);
2356
+ return doc;
2357
+ }
2358
+ /** @internal — register a document that was opened externally (e.g. via a façade). */
2359
+ adopt(doc) {
2360
+ this.documents.set(doc.absolutePath, doc);
2361
+ }
2362
+ /** Drop all staged edits across every document in the transaction. */
2363
+ rollback() {
2364
+ for (const doc of this.documents.values()) doc.rollback();
2365
+ }
2366
+ /** Plan without writing. Invokes `onConflict`/`confirmWrite` as a real commit would. */
2367
+ async dryRun(options = {}) {
2368
+ return this.run(options, { write: false });
2369
+ }
2370
+ /** Apply staged edits to disk. */
2371
+ async commit(options = {}) {
2372
+ return this.run(options, { write: true });
2373
+ }
2374
+ async run(options, mode) {
2375
+ const store = this.options.stateStore ?? _InMemoryStateStore();
2376
+ const defaultPolicy = options.defaultPolicy ?? "error";
2377
+ const confirmationPolicy = this.options.confirmationPolicy ?? "on-conflict";
2378
+ const written = [];
2379
+ const skipped = [];
2380
+ const conflicts = [];
2381
+ const diagnostics = [];
2382
+ for (const doc of this.documents.values()) {
2383
+ const schemaDiagnostics = await doc.validate();
2384
+ diagnostics.push(...schemaDiagnostics);
2385
+ diagnostics.push(
2386
+ ...doc.diagnostics().filter((d) => !schemaDiagnostics.includes(d))
2387
+ );
2388
+ if (schemaDiagnostics.some((d) => d.severity === "error")) {
2389
+ skipped.push({ path: doc.absolutePath, reason: "schema-invalid" });
2390
+ continue;
2391
+ }
2392
+ const nextText = doc.preview();
2393
+ const hasEdits = doc.pendingEdits().length > 0;
2394
+ const fileExists = await pathExists(doc.absolutePath);
2395
+ if (!hasEdits && fileExists) {
2396
+ skipped.push({ path: doc.absolutePath, reason: "no-change" });
2397
+ continue;
2398
+ }
2399
+ if (!hasEdits && !fileExists) {
2400
+ skipped.push({ path: doc.absolutePath, reason: "no-change" });
2401
+ continue;
2402
+ }
2403
+ const stored = await store.read(doc.absolutePath);
2404
+ const diskText = fileExists ? await readExistingText(doc.absolutePath) : null;
2405
+ const conflict = await resolveDrift({
2406
+ doc,
2407
+ nextText,
2408
+ diskText,
2409
+ stored,
2410
+ defaultPolicy,
2411
+ onConflict: this.options.onConflict
2412
+ });
2413
+ if (conflict.decision === "skip") {
2414
+ skipped.push({ path: doc.absolutePath, reason: "conflict-skipped" });
2415
+ if (conflict.conflict) conflicts.push(conflict.conflict);
2416
+ continue;
2417
+ }
2418
+ if (conflict.conflict) conflicts.push(conflict.conflict);
2419
+ const reason = decideReason(conflict.conflict !== null, diskText !== null);
2420
+ const shouldConfirm = confirmationPolicy === "always" || confirmationPolicy === "on-conflict" && reason === "conflict";
2421
+ if (shouldConfirm && this.options.confirmWrite) {
2422
+ const req = buildWriteRequest({
2423
+ path: doc.absolutePath,
2424
+ format: doc.format,
2425
+ reason,
2426
+ currentText: diskText,
2427
+ nextText,
2428
+ conflict: conflict.conflict
2429
+ });
2430
+ const decision = await this.options.confirmWrite(req);
2431
+ if (decision === "abort") throw new _WriteAbortedError(doc.absolutePath);
2432
+ if (decision === "skip") {
2433
+ skipped.push({ path: doc.absolutePath, reason: "write-skipped" });
2434
+ continue;
2435
+ }
2436
+ }
2437
+ if (diskText === nextText) {
2438
+ skipped.push({ path: doc.absolutePath, reason: "no-change" });
2439
+ continue;
2440
+ }
2441
+ if (mode.write) {
2442
+ await _atomicWriteText(doc.absolutePath, nextText);
2443
+ const record = {
2444
+ fingerprint: _previewFingerprint(doc),
2445
+ rawSha256: _sha256Hex(nextText),
2446
+ writtenAt: (/* @__PURE__ */ new Date()).toISOString(),
2447
+ toolId: this.options.toolId ?? null,
2448
+ toolVersion: this.options.toolVersion ?? null,
2449
+ format: doc.format,
2450
+ baseText: nextText
2451
+ };
2452
+ await store.write(doc.absolutePath, record);
2453
+ }
2454
+ written.push({
2455
+ path: doc.absolutePath,
2456
+ bytesBefore: Buffer.byteLength(diskText ?? "", "utf8"),
2457
+ bytesAfter: Buffer.byteLength(nextText, "utf8"),
2458
+ unifiedDiff: _unified(diskText ?? "", nextText, {
2459
+ fileNameA: "a/" + doc.absolutePath,
2460
+ fileNameB: "b/" + doc.absolutePath
2461
+ })
2462
+ });
2463
+ }
2464
+ return { written, skipped, conflicts, diagnostics };
2465
+ }
2466
+ };
2467
+ async function resolveDrift(args) {
2468
+ const { doc, nextText, diskText, stored, defaultPolicy, onConflict } = args;
2469
+ if (diskText === nextText) return { decision: "proceed", conflict: null };
2470
+ if (stored === null) return { decision: "proceed", conflict: null };
2471
+ if (diskText !== null) {
2472
+ const diskFp = _fingerprint(diskText, doc.format);
2473
+ if (diskFp === stored.fingerprint) return { decision: "proceed", conflict: null };
2474
+ }
2475
+ const conflict = {
2476
+ path: doc.absolutePath,
2477
+ ours: nextText,
2478
+ theirs: diskText ?? "",
2479
+ base: stored.baseText ?? null,
2480
+ lastWriteAt: stored.writtenAt,
2481
+ lastWriteTool: stored.toolId
2482
+ };
2483
+ const policy = pickPolicy(doc, defaultPolicy);
2484
+ switch (policy) {
2485
+ case "error":
2486
+ throw new _ConflictError(conflict);
2487
+ case "skip":
2488
+ return { decision: "skip", conflict };
2489
+ case "overwrite":
2490
+ return { decision: "proceed", conflict };
2491
+ case "ask": {
2492
+ if (!onConflict) {
2493
+ throw new _FileEditorError(
2494
+ "CONFLICT_RESOLVER_MISSING",
2495
+ `conflictPolicy "ask" requires an onConflict resolver for ${doc.absolutePath}.`
2496
+ );
2497
+ }
2498
+ const resolution = await onConflict(conflict);
2499
+ if (resolution === "abort") throw new _ConflictAbortedError(doc.absolutePath);
2500
+ if (resolution === "skip") return { decision: "skip", conflict };
2501
+ return { decision: "proceed", conflict };
2502
+ }
2503
+ }
2504
+ }
2505
+ function pickPolicy(doc, fallback) {
2506
+ for (const edit of doc.pendingEdits()) {
2507
+ if (edit.conflictPolicy !== void 0) return edit.conflictPolicy;
2508
+ }
2509
+ return fallback;
2510
+ }
2511
+ function decideReason(isConflict, fileExisted) {
2512
+ if (isConflict) return "conflict";
2513
+ return fileExisted ? "update" : "new-file";
2514
+ }
2515
+ function buildWriteRequest(args) {
2516
+ const base = args.currentText ?? "";
2517
+ return {
2518
+ path: args.path,
2519
+ format: args.format,
2520
+ reason: args.reason,
2521
+ currentText: args.currentText,
2522
+ nextText: args.nextText,
2523
+ unifiedDiff: _unified(base, args.nextText, {
2524
+ fileNameA: "a/" + args.path,
2525
+ fileNameB: "b/" + args.path
2526
+ }),
2527
+ hunks: _hunks(base, args.nextText),
2528
+ conflict: args.conflict
2529
+ };
2530
+ }
2531
+ async function pathExists(path) {
2532
+ try {
2533
+ const s = await (0, import_promises3.stat)(path);
2534
+ return s.isFile();
2535
+ } catch {
2536
+ return false;
2537
+ }
2538
+ }
2539
+ async function readExistingText(path) {
2540
+ return (0, import_promises3.readFile)(path, "utf8");
2541
+ }
2542
+ function _beginTransaction(options = {}) {
2543
+ return new _Transaction(options);
2544
+ }
2545
+ async function _openDocument(absolutePath, options = {}) {
2546
+ const tx = _beginTransaction(options);
2547
+ return tx.open(absolutePath);
2548
+ }
2549
+
2550
+ // src/file-editor/schema.ts
2551
+ async function _validateWithSchema(schema, value) {
2552
+ const result = await schema["~standard"].validate(value);
2553
+ if (result.issues) {
2554
+ const summary = result.issues.map(formatIssue).join("; ");
2555
+ throw new _SchemaValidationError(summary, result.issues);
2556
+ }
2557
+ return result.value;
2558
+ }
2559
+ function _tryValidateWithSchema(schema, value) {
2560
+ return Promise.resolve(schema["~standard"].validate(value));
2561
+ }
2562
+ var _SchemaValidationError = class extends Error {
2563
+ issues;
2564
+ constructor(message, issues) {
2565
+ super(message);
2566
+ this.name = "_SchemaValidationError";
2567
+ this.issues = issues;
2568
+ }
2569
+ };
2570
+ function formatIssue(issue) {
2571
+ if (!issue.path || issue.path.length === 0) return issue.message;
2572
+ const path = issue.path.map((seg) => typeof seg === "object" ? String(seg.key) : String(seg)).join(".");
2573
+ return `${path}: ${issue.message}`;
2574
+ }
2575
+
2576
+ // src/file-editor/state/fs-manifest.ts
2577
+ var import_promises4 = require("fs/promises");
2578
+ var import_node_path5 = require("path");
2579
+ function _FsCentralManifestStore(options = {}) {
2580
+ const root = (0, import_node_path5.resolve)(options.rootDir ?? process.cwd());
2581
+ const manifestPath = (0, import_node_path5.resolve)(root, options.filename ?? ".file-editor/state.json");
2582
+ const keepBase = options.keepBaseSnapshots ?? false;
2583
+ const toKey = (p) => {
2584
+ const abs = (0, import_node_path5.isAbsolute)(p) ? p : (0, import_node_path5.resolve)(root, p);
2585
+ const rel = (0, import_node_path5.relative)(root, abs);
2586
+ return rel.split(import_node_path5.sep).join("/");
2587
+ };
2588
+ const load = async () => {
2589
+ try {
2590
+ const text = await (0, import_promises4.readFile)(manifestPath, "utf8");
2591
+ const raw = JSON.parse(text);
2592
+ const version = raw !== null && typeof raw === "object" && "version" in raw ? raw.version : void 0;
2593
+ if (version !== 1) {
2594
+ throw new Error(`Unsupported state manifest version: ${String(version)}`);
2595
+ }
2596
+ return raw;
2597
+ } catch (err) {
2598
+ if (isNodeNotFound2(err)) return { version: 1, records: {} };
2599
+ throw err;
2600
+ }
2601
+ };
2602
+ const save = async (manifest) => {
2603
+ await (0, import_promises4.mkdir)((0, import_node_path5.dirname)(manifestPath), { recursive: true });
2604
+ const text = JSON.stringify(manifest, null, 2) + "\n";
2605
+ await (0, import_promises4.writeFile)(manifestPath, text, "utf8");
2606
+ };
2607
+ const stripBase = (record) => {
2608
+ if (keepBase) return record;
2609
+ const { baseText: _baseText, ...rest } = record;
2610
+ return rest;
2611
+ };
2612
+ return {
2613
+ async read(path) {
2614
+ const manifest = await load();
2615
+ return manifest.records[toKey(path)] ?? null;
2616
+ },
2617
+ async write(path, record) {
2618
+ const manifest = await load();
2619
+ const next = {
2620
+ version: 1,
2621
+ records: { ...manifest.records, [toKey(path)]: stripBase(record) }
2622
+ };
2623
+ await save(next);
2624
+ },
2625
+ async delete(path) {
2626
+ const manifest = await load();
2627
+ const key = toKey(path);
2628
+ if (!(key in manifest.records)) return;
2629
+ const { [key]: _removed, ...nextRecords } = manifest.records;
2630
+ await save({ version: 1, records: nextRecords });
2631
+ },
2632
+ async list() {
2633
+ const manifest = await load();
2634
+ return Object.entries(manifest.records).map(([key, record]) => ({
2635
+ path: (0, import_node_path5.resolve)(root, key),
2636
+ record
2637
+ }));
2638
+ }
2639
+ };
2640
+ }
2641
+ function isNodeNotFound2(err) {
2642
+ return err !== null && typeof err === "object" && "code" in err && err.code === "ENOENT";
2643
+ }
2644
+
2645
+ // src/file-editor/facades/package-json.ts
2646
+ var _PackageJsonDoc = class extends _Document {
2647
+ setScript(name, command, opts) {
2648
+ return this.set(scriptPointer(name), command, opts);
2649
+ }
2650
+ requireScript(name, command) {
2651
+ if (command !== void 0) this.addIfMissing(scriptPointer(name), command);
2652
+ this.assert(_check.exists(scriptPointer(name)));
2653
+ return this;
2654
+ }
2655
+ addDependency(name, range, kind = "dependencies", opts) {
2656
+ return this.set(depPointer(kind, name), range, opts);
2657
+ }
2658
+ updateDependency(name, range, kind = "dependencies", opts) {
2659
+ return this.updateIfPresent(depPointer(kind, name), () => range, opts);
2660
+ }
2661
+ removeDependency(name, kind = "dependencies", opts) {
2662
+ return this.remove(depPointer(kind, name), opts);
2663
+ }
2664
+ ensureDependency(name, range, kind = "dependencies", opts) {
2665
+ return this.addIfMissing(depPointer(kind, name), range, opts);
2666
+ }
2667
+ mustHaveScript(name, opts = {}) {
2668
+ const checks = [_check.exists(scriptPointer(name))];
2669
+ if (opts.equals !== void 0)
2670
+ checks.push(_check.equals(scriptPointer(name), opts.equals));
2671
+ if (opts.matches !== void 0)
2672
+ checks.push(_check.matches(scriptPointer(name), opts.matches));
2673
+ return this.assert(...checks);
2674
+ }
2675
+ mustHaveDependency(name, opts = {}) {
2676
+ const kind = opts.kind ?? "dependencies";
2677
+ const checks = [_check.exists(depPointer(kind, name))];
2678
+ if (opts.range !== void 0) {
2679
+ checks.push(_check.semverSatisfies(depPointer(kind, name), opts.range));
2680
+ }
2681
+ return this.assert(...checks);
2682
+ }
2683
+ };
2684
+ function scriptPointer(name) {
2685
+ return _compilePointer(["scripts", name]);
2686
+ }
2687
+ function depPointer(kind, name) {
2688
+ return "/" + kind + "/" + _escapeToken(name);
2689
+ }
2690
+ async function _openPackageJson(absolutePath, options = {}) {
2691
+ const openOpts = {
2692
+ format: options.format ?? "json"
2693
+ };
2694
+ if (options.schema) openOpts.schema = options.schema;
2695
+ const base = await _openDocumentFromDisk(absolutePath, openOpts);
2696
+ return new _PackageJsonDoc({
2697
+ absolutePath: base.absolutePath,
2698
+ format: base.format,
2699
+ originalText: base.getOriginalText(),
2700
+ meta: base.getMeta(),
2701
+ schema: base.schema
2702
+ });
2703
+ }
2704
+
2705
+ // src/file-editor/facades/tsconfig.ts
2706
+ var _TsConfigDoc = class extends _Document {
2707
+ setCompilerOption(name, value, opts) {
2708
+ return this.set(coPointer(name), value, opts);
2709
+ }
2710
+ ensureCompilerOption(name, value, opts) {
2711
+ return this.addIfMissing(coPointer(name), value, opts);
2712
+ }
2713
+ addInclude(pattern, opts) {
2714
+ return this.arrayUpsert("/include", pattern, (p) => p, opts);
2715
+ }
2716
+ addExclude(pattern, opts) {
2717
+ return this.arrayUpsert("/exclude", pattern, (p) => p, opts);
2718
+ }
2719
+ addReference(path, opts) {
2720
+ return this.arrayUpsert(
2721
+ "/references",
2722
+ { path },
2723
+ (ref) => ref.path,
2724
+ opts
2725
+ );
2726
+ }
2727
+ mustHaveCompilerOption(name, opts = {}) {
2728
+ const checks = [_check.exists(coPointer(name))];
2729
+ if (opts.equals !== void 0)
2730
+ checks.push(_check.equals(coPointer(name), opts.equals));
2731
+ if (opts.oneOf !== void 0) checks.push(_check.oneOf(coPointer(name), opts.oneOf));
2732
+ return this.assert(...checks);
2733
+ }
2734
+ };
2735
+ function coPointer(name) {
2736
+ return _compilePointer(["compilerOptions", name]);
2737
+ }
2738
+ async function _openTsConfig(absolutePath, options = {}) {
2739
+ const opts = {
2740
+ format: options.format ?? "jsonc"
2741
+ };
2742
+ if (options.schema) opts.schema = options.schema;
2743
+ const base = await _openDocumentFromDisk(absolutePath, opts);
2744
+ return new _TsConfigDoc({
2745
+ absolutePath: base.absolutePath,
2746
+ format: base.format,
2747
+ originalText: base.getOriginalText(),
2748
+ meta: base.getMeta(),
2749
+ schema: base.schema
2750
+ });
2751
+ }
2752
+
2753
+ // src/file-editor/facades/api-extractor.ts
2754
+ var _ApiExtractorDoc = class extends _Document {
2755
+ setMainEntryPoint(path, opts) {
2756
+ return this.set("/mainEntryPointFilePath", path, opts);
2757
+ }
2758
+ enableApiReport(folder, fileName, opts) {
2759
+ this.set("/apiReport/enabled", true, opts);
2760
+ this.set("/apiReport/reportFolder", folder, opts);
2761
+ this.set("/apiReport/reportFileName", fileName, opts);
2762
+ return this;
2763
+ }
2764
+ enableDtsRollup(publicPath, opts) {
2765
+ this.set("/dtsRollup/enabled", true, opts);
2766
+ this.set("/dtsRollup/publicTrimmedFilePath", publicPath, opts);
2767
+ return this;
2768
+ }
2769
+ };
2770
+ async function _openApiExtractor(absolutePath, options = {}) {
2771
+ const opts = {
2772
+ format: options.format ?? "jsonc"
2773
+ };
2774
+ if (options.schema)
2775
+ opts.schema = options.schema;
2776
+ const base = await _openDocumentFromDisk(absolutePath, opts);
2777
+ return new _ApiExtractorDoc({
2778
+ absolutePath: base.absolutePath,
2779
+ format: base.format,
2780
+ originalText: base.getOriginalText(),
2781
+ meta: base.getMeta(),
2782
+ schema: base.schema
2783
+ });
2784
+ }
2785
+
2786
+ // src/file-editor/facades/stripe-app-manifest.ts
2787
+ var _StripeAppManifestDoc = class extends _Document {
2788
+ setName(name, opts) {
2789
+ return this.set("/name", name, opts);
2790
+ }
2791
+ setVersion(version, opts) {
2792
+ return this.set("/version", version, opts);
2793
+ }
2794
+ addPermission(permission, opts) {
2795
+ return this.arrayUpsert(
2796
+ "/permissions",
2797
+ permission,
2798
+ (p) => p,
2799
+ opts
2800
+ );
2801
+ }
2802
+ removePermission(permission, opts) {
2803
+ const existing = this.get("/permissions");
2804
+ const permissions = (existing ?? []).filter((p) => p !== permission);
2805
+ return this.set("/permissions", permissions, opts);
2806
+ }
2807
+ /**
2808
+ * Add a custom object definition. Uses arrayUpsert to avoid duplicates
2809
+ * (keyed on `id`). Creates the `custom_object_definitions.definitions`
2810
+ * array if it doesn't exist.
2811
+ */
2812
+ addCustomObject(id, content, type = "typescript", opts) {
2813
+ this.addIfMissing("/custom_object_definitions", {}, opts);
2814
+ this.addIfMissing("/custom_object_definitions/definitions", [], opts);
2815
+ return this.arrayUpsert(
2816
+ "/custom_object_definitions/definitions",
2817
+ { id, specification: { type, content } },
2818
+ (entry) => entry.id,
2819
+ opts
2820
+ );
2821
+ }
2822
+ /**
2823
+ * Check if a custom object definition exists by id.
2824
+ */
2825
+ hasCustomObject(id) {
2826
+ const defs = this.get("/custom_object_definitions");
2827
+ if (!defs?.definitions) return false;
2828
+ return defs.definitions.some((d) => d.id === id);
2829
+ }
2830
+ };
2831
+ async function _openStripeAppManifest(absolutePath, options = {}) {
2832
+ const opts = {};
2833
+ if (options.format !== void 0) opts.format = options.format;
2834
+ if (options.schema)
2835
+ opts.schema = options.schema;
2836
+ const base = await _openDocumentFromDisk(absolutePath, opts);
2837
+ return new _StripeAppManifestDoc({
2838
+ absolutePath: base.absolutePath,
2839
+ format: base.format,
2840
+ originalText: base.getOriginalText(),
2841
+ meta: base.getMeta(),
2842
+ schema: base.schema
2843
+ });
2844
+ }
2845
+
2846
+ // src/file-editor/facades/brands.ts
2847
+ var import_semver2 = __toESM(require("semver"), 1);
2848
+ function _semverRange(input) {
2849
+ const valid = import_semver2.default.validRange(input);
2850
+ if (valid === null) {
2851
+ throw new TypeError(`Invalid semver range: ${JSON.stringify(input)}`);
2852
+ }
2853
+ return input;
2854
+ }
2855
+ function _tryParseSemverRange(input) {
2856
+ return import_semver2.default.validRange(input) === null ? null : input;
2857
+ }
2858
+ function _isSemverRange(input) {
2859
+ return typeof input === "string" && import_semver2.default.validRange(input) !== null;
2860
+ }
2861
+ function _semverVersion(input) {
2862
+ if (import_semver2.default.valid(input) === null) {
2863
+ throw new TypeError(`Invalid semver version: ${JSON.stringify(input)}`);
2864
+ }
2865
+ return input;
2866
+ }
2867
+ function _tryParseSemverVersion(input) {
2868
+ return import_semver2.default.valid(input) === null ? null : input;
2869
+ }
2870
+ function _isSemverVersion(input) {
2871
+ return typeof input === "string" && import_semver2.default.valid(input) !== null;
2872
+ }
2873
+ // Annotate the CommonJS export names for ESM import in node:
2874
+ 0 && (module.exports = {
2875
+ _ApiExtractorDoc,
2876
+ _AssertionError,
2877
+ _CliUx,
2878
+ _ConflictAbortedError,
2879
+ _ConflictError,
2880
+ _Document,
2881
+ _FileEditorError,
2882
+ _FormatNotSupportedError,
2883
+ _FsCentralManifestStore,
2884
+ _GeneratorDefectError,
2885
+ _GeneratorInputError,
2886
+ _GeneratorRunner,
2887
+ _InMemoryStateStore,
2888
+ _POINTER_STRING,
2889
+ _PackageJsonDoc,
2890
+ _ParseError,
2891
+ _PathError,
2892
+ _SchemaValidationError,
2893
+ _SingleTemplateManager,
2894
+ _StripeAppManifestDoc,
2895
+ _TemplateManager,
2896
+ _Transaction,
2897
+ _TsConfigDoc,
2898
+ _TypeMismatchError,
2899
+ _WriteAbortedError,
2900
+ _beginTransaction,
2901
+ _canonicalNormalize,
2902
+ _canonicalStringify,
2903
+ _check,
2904
+ _compilePointer,
2905
+ _compositeGenerator,
2906
+ _createCliContext,
2907
+ _createFilesystemTemplateFS,
2908
+ _createInMemoryTemplateFS,
2909
+ _createLogger,
2910
+ _createSimpleSingleFileTemplate,
2911
+ _createSimpleTemplate,
2912
+ _defaultMeta,
2913
+ _detectFormat,
2914
+ _detectMeta,
2915
+ _diffHunks,
2916
+ _diffUnified,
2917
+ _escapeToken,
2918
+ _fingerprint,
2919
+ _getAdapter,
2920
+ _hasPointer,
2921
+ _inferStripeApiName,
2922
+ _isRecord,
2923
+ _isRecordWithValueType,
2924
+ _isSemverRange,
2925
+ _isSemverVersion,
2926
+ _jsonAdapter,
2927
+ _jsoncAdapter,
2928
+ _looksPlural,
2929
+ _looksSingular,
2930
+ _openApiExtractor,
2931
+ _openDocument,
2932
+ _openDocumentFromDisk,
2933
+ _openPackageJson,
2934
+ _openStripeAppManifest,
2935
+ _openTsConfig,
2936
+ _parseArrayIndex,
2937
+ _parseLogLevel,
2938
+ _parsePointer,
2939
+ _pointer,
2940
+ _registerFormatAdapter,
2941
+ _resolvePointer,
2942
+ _runAssertions,
2943
+ _runAsyncAssertions,
2944
+ _semverRange,
2945
+ _semverVersion,
2946
+ _sha256Hex,
2947
+ _stripeMetadataPolicy,
2948
+ _toCapitalized,
2949
+ _toPascalCase,
2950
+ _toPlural,
2951
+ _toSentenceDisplayName,
2952
+ _toSingular,
2953
+ _toSnakeCase,
2954
+ _toStripeApiCase,
2955
+ _tokenizeIdentifier,
2956
+ _truncateName,
2957
+ _tryParseSemverRange,
2958
+ _tryParseSemverVersion,
2959
+ _tryValidateWithSchema,
2960
+ _unescapeToken,
2961
+ _validateApiName,
2962
+ _validateDisplayName,
2963
+ _validateWithSchema,
2964
+ _workspaceVersion,
2965
+ _writeJsonOutput,
2966
+ _yamlAdapter
2967
+ });