@savvy-web/silk 2.1.1 → 2.1.3

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.
@@ -1218,7 +1218,7 @@ var YamlToken = class extends effect.Schema.Class("YamlToken")({
1218
1218
  *
1219
1219
  * @public
1220
1220
  */
1221
- function createScanner$1(text) {
1221
+ function createScanner$2(text) {
1222
1222
  let pos = 0;
1223
1223
  let line = 0;
1224
1224
  let col = 0;
@@ -2169,7 +2169,7 @@ function createScanner$1(text) {
2169
2169
  * @public
2170
2170
  */
2171
2171
  function lex(text) {
2172
- return effect.Stream.unfold(createScanner$1(text), (scanner) => {
2172
+ return effect.Stream.unfold(createScanner$2(text), (scanner) => {
2173
2173
  const kind = scanner.scan();
2174
2174
  if (kind === null) return effect.Option.none();
2175
2175
  const token = new YamlToken({
@@ -6313,7 +6313,7 @@ function parseDocument(text, options) {
6313
6313
  *
6314
6314
  * @public
6315
6315
  */
6316
- function parse$2(text, options) {
6316
+ function parse$3(text, options) {
6317
6317
  const uniqueKeys = options?.uniqueKeys ?? true;
6318
6318
  return parseDocument(text, options).pipe(effect.Effect.flatMap((doc) => {
6319
6319
  if (uniqueKeys) {
@@ -6335,7 +6335,7 @@ function parse$2(text, options) {
6335
6335
  *
6336
6336
  * @public
6337
6337
  */
6338
- const workspaceManifestFromYaml = (content) => parse$2(content).pipe(effect.Effect.mapError((e) => new CatalogAssemblyError({
6338
+ const workspaceManifestFromYaml = (content) => parse$3(content).pipe(effect.Effect.mapError((e) => new CatalogAssemblyError({
6339
6339
  source: "manifest",
6340
6340
  reason: `invalid yaml: ${String(e)}`
6341
6341
  })), effect.Effect.map((parsed) => {
@@ -6654,7 +6654,7 @@ var CatalogSet = class CatalogSet extends effect.Schema.Class("CatalogSet")({ en
6654
6654
  *
6655
6655
  * @internal
6656
6656
  */
6657
- const lockfileCatalogsFromText = (text) => parse$2(text).pipe(effect.Effect.map((parsed) => CatalogSet.fromLockfileCatalogs(parsed?.catalogs)), effect.Effect.orElseSucceed(() => CatalogSet.empty()));
6657
+ const lockfileCatalogsFromText = (text) => parse$3(text).pipe(effect.Effect.map((parsed) => CatalogSet.fromLockfileCatalogs(parsed?.catalogs)), effect.Effect.orElseSucceed(() => CatalogSet.empty()));
6658
6658
  /**
6659
6659
  * Read the working tree's catalog state at `root`.
6660
6660
  *
@@ -8444,6 +8444,1285 @@ effect.Schema.Class("WorkspaceInfo")({
8444
8444
  patterns: effect.Schema.Array(effect.Schema.String)
8445
8445
  });
8446
8446
  //#endregion
8447
+ //#region ../../node_modules/.pnpm/jsonc-effect@0.3.1_effect@3.21.4/node_modules/jsonc-effect/errors.js
8448
+ /**
8449
+ * JSONC error types using Effect's Data.TaggedError pattern.
8450
+ *
8451
+ * @packageDocumentation
8452
+ */
8453
+ /**
8454
+ * Error codes representing specific JSONC parse failures.
8455
+ *
8456
+ * @remarks
8457
+ * Each code maps to a distinct syntactic error the parser can encounter,
8458
+ * from invalid symbols and number formats to missing delimiters and
8459
+ * unexpected end-of-input conditions.
8460
+ *
8461
+ * @see {@link JsoncParseErrorDetail} — carries one of these codes alongside
8462
+ * position information
8463
+ *
8464
+ * @public
8465
+ */
8466
+ const JsoncParseErrorCode = effect.Schema.Literal("InvalidSymbol", "InvalidNumberFormat", "PropertyNameExpected", "ValueExpected", "ColonExpected", "CommaExpected", "CloseBraceExpected", "CloseBracketExpected", "EndOfFileExpected", "InvalidCommentToken", "UnexpectedEndOfComment", "UnexpectedEndOfString", "UnexpectedEndOfNumber", "InvalidUnicode", "InvalidEscapeCharacter", "InvalidCharacter");
8467
+ /**
8468
+ * Detail for a single parse error, including the error code, a human-readable
8469
+ * message, and the exact position within the source document.
8470
+ *
8471
+ * @remarks
8472
+ * - `code` — a {@link (JsoncParseErrorCode:type)} identifying the error kind.
8473
+ * - `message` — a descriptive message suitable for display.
8474
+ * - `offset` — zero-based character offset where the error occurred.
8475
+ * - `length` — character length of the problematic span.
8476
+ * - `startLine` — zero-based line number of the error.
8477
+ * - `startCharacter` — zero-based column within `startLine`.
8478
+ *
8479
+ * @see {@link JsoncParseError} — aggregates an array of these details
8480
+ *
8481
+ * @example
8482
+ * ```ts
8483
+ * import { JsoncParseErrorDetail } from "jsonc-effect";
8484
+ *
8485
+ * const detail = new JsoncParseErrorDetail({
8486
+ * code: "ValueExpected",
8487
+ * message: "Value expected",
8488
+ * offset: 5,
8489
+ * length: 1,
8490
+ * startLine: 0,
8491
+ * startCharacter: 5,
8492
+ * });
8493
+ *
8494
+ * console.log(detail.code); // "ValueExpected"
8495
+ * console.log(detail.offset); // 5
8496
+ * ```
8497
+ *
8498
+ * @public
8499
+ */
8500
+ var JsoncParseErrorDetail = class extends effect.Schema.Class("JsoncParseErrorDetail")({
8501
+ code: JsoncParseErrorCode,
8502
+ message: effect.Schema.String,
8503
+ offset: effect.Schema.Number,
8504
+ length: effect.Schema.Number,
8505
+ startLine: effect.Schema.Number,
8506
+ startCharacter: effect.Schema.Number
8507
+ }) {};
8508
+ /**
8509
+ * Base class for {@link JsoncParseError}; not intended to be constructed or
8510
+ * caught directly — use `JsoncParseError` instead.
8511
+ *
8512
+ * @privateRemarks
8513
+ * The `*Base` pattern is required because `Data.TaggedError` produces complex
8514
+ * type signatures involving intersection types and branded generics that
8515
+ * api-extractor cannot roll up into a single `.d.ts` bundle. Exporting the
8516
+ * base separately lets the public `JsoncParseError` class extend it with
8517
+ * concrete fields, giving api-extractor a simple class declaration to work
8518
+ * with. It is tagged `@public` (rather than `@internal`) because it appears
8519
+ * in `JsoncParseError`'s heritage clause in the public `.d.ts`, and API
8520
+ * Extractor requires release tags to be compatible across a signature.
8521
+ *
8522
+ * @public
8523
+ */
8524
+ const JsoncParseErrorBase = effect.Data.TaggedError("JsoncParseError");
8525
+ /**
8526
+ * Error raised when JSONC parsing encounters one or more syntax errors.
8527
+ *
8528
+ * @remarks
8529
+ * Contains the full source `text`, the `options` used for parsing, and an
8530
+ * `errors` array of {@link JsoncParseErrorDetail} instances with precise
8531
+ * position information for each problem found.
8532
+ *
8533
+ * @see {@link parse} — may fail with this error
8534
+ * @see {@link parseTree} — may fail with this error
8535
+ *
8536
+ * @example Catching with `Effect.catchTag`
8537
+ * ```ts
8538
+ * import { Effect } from "effect";
8539
+ * import { parse } from "jsonc-effect";
8540
+ *
8541
+ * const program = parse("{ invalid }").pipe(
8542
+ * Effect.catchTag("JsoncParseError", (e) => {
8543
+ * console.error(e.errors); // Array of JsoncParseErrorDetail
8544
+ * return Effect.succeed({});
8545
+ * }),
8546
+ * );
8547
+ * ```
8548
+ *
8549
+ * @example Inspecting error details
8550
+ * ```ts
8551
+ * import { Effect } from "effect";
8552
+ * import { parse } from "jsonc-effect";
8553
+ *
8554
+ * const program = parse("{ invalid }").pipe(
8555
+ * Effect.catchTag("JsoncParseError", (e) => {
8556
+ * for (const detail of e.errors) {
8557
+ * console.error(
8558
+ * `[${detail.code}] ${detail.message} at line ${detail.startLine}:${detail.startCharacter}`,
8559
+ * );
8560
+ * }
8561
+ * return Effect.succeed({});
8562
+ * }),
8563
+ * );
8564
+ * ```
8565
+ *
8566
+ * @public
8567
+ */
8568
+ var JsoncParseError = class extends JsoncParseErrorBase {
8569
+ get message() {
8570
+ const count = this.errors.length;
8571
+ return `JSONC parse failed with ${count} error${count !== 1 ? "s" : ""}: ${this.errors.map((e) => e.message).join("; ")}`;
8572
+ }
8573
+ };
8574
+ effect.Data.TaggedError("JsoncNodeNotFoundError");
8575
+ /**
8576
+ * Base class for {@link JsoncModificationError}; not intended to be
8577
+ * constructed or caught directly — use `JsoncModificationError` instead.
8578
+ *
8579
+ * @privateRemarks
8580
+ * Uses the same `*Base` pattern as {@link JsoncParseErrorBase} to work
8581
+ * around api-extractor's inability to roll up the complex type produced
8582
+ * by `Data.TaggedError` into a single `.d.ts` declaration. Tagged `@public`
8583
+ * for the same heritage-clause-compatibility reason as `JsoncParseErrorBase`.
8584
+ *
8585
+ * @public
8586
+ */
8587
+ const JsoncModificationErrorBase = effect.Data.TaggedError("JsoncModificationError");
8588
+ /**
8589
+ * Error raised when {@link modify} produces invalid edits or encounters
8590
+ * an unsupported modification scenario.
8591
+ *
8592
+ * @remarks
8593
+ * Contains the `path` where modification was attempted and a `reason`
8594
+ * string explaining why it failed.
8595
+ *
8596
+ * @see {@link modify} — may fail with this error
8597
+ *
8598
+ * @example
8599
+ * ```ts
8600
+ * import { Effect } from "effect";
8601
+ * import { modify } from "jsonc-effect";
8602
+ *
8603
+ * const program = modify("{}", ["deep", "path"], 42).pipe(
8604
+ * Effect.catchTag("JsoncModificationError", (e) => {
8605
+ * console.error(`Failed at [${e.path.join(", ")}]: ${e.reason}`);
8606
+ * return Effect.succeed([]);
8607
+ * }),
8608
+ * );
8609
+ * ```
8610
+ *
8611
+ * @public
8612
+ */
8613
+ var JsoncModificationError = class extends JsoncModificationErrorBase {
8614
+ get message() {
8615
+ return `Modification failed at path [${this.path.join(", ")}]: ${this.reason}`;
8616
+ }
8617
+ };
8618
+ //#endregion
8619
+ //#region ../../node_modules/.pnpm/jsonc-effect@0.3.1_effect@3.21.4/node_modules/jsonc-effect/scanner.js
8620
+ const isWhitespace = (ch) => ch === 32 || ch === 9 || ch === 11 || ch === 12 || ch === 160 || ch === 65279;
8621
+ const isLineBreak$1 = (ch) => ch === 10 || ch === 13 || ch === 8232 || ch === 8233;
8622
+ const isDigit$1 = (ch) => ch >= 48 && ch <= 57;
8623
+ /**
8624
+ * Create a stateful {@link JsoncScanner} for the given JSONC string.
8625
+ *
8626
+ * @param text - JSONC string to tokenize
8627
+ * @param ignoreTrivia - If `true`, the scanner automatically skips whitespace,
8628
+ * line-break, and comment tokens so that only structural tokens are returned
8629
+ * (default: `false`).
8630
+ * @returns A stateful {@link JsoncScanner} positioned before the first token.
8631
+ *
8632
+ * @remarks
8633
+ * When `ignoreTrivia` is `true` the scanner is suitable for building parsers
8634
+ * that only care about structural tokens (`OpenBrace`, `String`, `Number`,
8635
+ * etc.). Set it to `false` (the default) when you need to preserve comments
8636
+ * or whitespace — for example in a formatter or a comment-stripping pass.
8637
+ *
8638
+ * @see {@link JsoncScanner} — the interface returned by this factory
8639
+ * @see {@link parse} — higher-level API that uses a scanner internally
8640
+ *
8641
+ * @example
8642
+ * Tokenizing a JSONC string and printing each token:
8643
+ * ```ts
8644
+ * import type { JsoncSyntaxKind } from "jsonc-effect";
8645
+ * import { createScanner } from "jsonc-effect";
8646
+ *
8647
+ * const scanner = createScanner('{ "name": "jsonc" }', true);
8648
+ * let kind: JsoncSyntaxKind;
8649
+ * do {
8650
+ * kind = scanner.scan();
8651
+ * console.log(kind, scanner.getTokenValue());
8652
+ * } while (kind !== "EOF");
8653
+ * ```
8654
+ *
8655
+ * @privateRemarks
8656
+ * Ported from Microsoft's jsonc-parser (MIT), adapted to use string literal
8657
+ * token types instead of numeric enums.
8658
+ *
8659
+ * @public
8660
+ */
8661
+ const createScanner$1 = (text, ignoreTrivia = false) => {
8662
+ const len = text.length;
8663
+ let pos = 0;
8664
+ let tokenOffset = 0;
8665
+ let token = "Unknown";
8666
+ let tokenValue = "";
8667
+ let tokenError = "None";
8668
+ let lineNumber = 0;
8669
+ let lineStartOffset = 0;
8670
+ let tokenStartLine = 0;
8671
+ let tokenStartCharacter = 0;
8672
+ const scanHexDigits = (count) => {
8673
+ let value = 0;
8674
+ for (let i = 0; i < count; i++) {
8675
+ if (pos >= len) return -1;
8676
+ const ch = text.charCodeAt(pos);
8677
+ if (ch >= 48 && ch <= 57) value = value * 16 + (ch - 48);
8678
+ else if (ch >= 65 && ch <= 70) value = value * 16 + (ch - 65 + 10);
8679
+ else if (ch >= 97 && ch <= 102) value = value * 16 + (ch - 97 + 10);
8680
+ else return -1;
8681
+ pos++;
8682
+ }
8683
+ return value;
8684
+ };
8685
+ const scanString = () => {
8686
+ let result = "";
8687
+ pos++;
8688
+ let start = pos;
8689
+ while (pos < len) {
8690
+ const ch = text.charCodeAt(pos);
8691
+ if (ch === 34) {
8692
+ result += text.substring(start, pos);
8693
+ pos++;
8694
+ return result;
8695
+ }
8696
+ if (ch === 92) {
8697
+ result += text.substring(start, pos);
8698
+ pos++;
8699
+ if (pos >= len) {
8700
+ tokenError = "UnexpectedEndOfString";
8701
+ return result;
8702
+ }
8703
+ const escaped = text.charCodeAt(pos);
8704
+ pos++;
8705
+ switch (escaped) {
8706
+ case 34:
8707
+ result += "\"";
8708
+ break;
8709
+ case 92:
8710
+ result += "\\";
8711
+ break;
8712
+ case 47:
8713
+ result += "/";
8714
+ break;
8715
+ case 98:
8716
+ result += "\b";
8717
+ break;
8718
+ case 102:
8719
+ result += "\f";
8720
+ break;
8721
+ case 110:
8722
+ result += "\n";
8723
+ break;
8724
+ case 114:
8725
+ result += "\r";
8726
+ break;
8727
+ case 116:
8728
+ result += " ";
8729
+ break;
8730
+ case 117: {
8731
+ const value = scanHexDigits(4);
8732
+ if (value >= 0) result += String.fromCharCode(value);
8733
+ else tokenError = "InvalidUnicode";
8734
+ break;
8735
+ }
8736
+ default:
8737
+ tokenError = "InvalidEscapeCharacter";
8738
+ break;
8739
+ }
8740
+ start = pos;
8741
+ } else if (isLineBreak$1(ch)) {
8742
+ tokenError = "UnexpectedEndOfString";
8743
+ return result + text.substring(start, pos);
8744
+ } else pos++;
8745
+ }
8746
+ tokenError = "UnexpectedEndOfString";
8747
+ return result + text.substring(start, pos);
8748
+ };
8749
+ const scanNumber = () => {
8750
+ const start = pos;
8751
+ if (text.charCodeAt(pos) === 45) pos++;
8752
+ if (text.charCodeAt(pos) === 48) pos++;
8753
+ else {
8754
+ if (!isDigit$1(text.charCodeAt(pos))) {
8755
+ tokenError = "UnexpectedEndOfNumber";
8756
+ return text.substring(start, pos);
8757
+ }
8758
+ pos++;
8759
+ while (pos < len && isDigit$1(text.charCodeAt(pos))) pos++;
8760
+ }
8761
+ if (pos < len && text.charCodeAt(pos) === 46) {
8762
+ pos++;
8763
+ if (!isDigit$1(text.charCodeAt(pos))) {
8764
+ tokenError = "UnexpectedEndOfNumber";
8765
+ return text.substring(start, pos);
8766
+ }
8767
+ pos++;
8768
+ while (pos < len && isDigit$1(text.charCodeAt(pos))) pos++;
8769
+ }
8770
+ if (pos < len && (text.charCodeAt(pos) === 69 || text.charCodeAt(pos) === 101)) {
8771
+ pos++;
8772
+ if (pos < len && (text.charCodeAt(pos) === 43 || text.charCodeAt(pos) === 45)) pos++;
8773
+ if (!isDigit$1(text.charCodeAt(pos))) {
8774
+ tokenError = "UnexpectedEndOfNumber";
8775
+ return text.substring(start, pos);
8776
+ }
8777
+ pos++;
8778
+ while (pos < len && isDigit$1(text.charCodeAt(pos))) pos++;
8779
+ }
8780
+ return text.substring(start, pos);
8781
+ };
8782
+ const scan = () => {
8783
+ tokenValue = "";
8784
+ tokenError = "None";
8785
+ if (pos >= len) {
8786
+ tokenOffset = len;
8787
+ tokenStartLine = lineNumber;
8788
+ tokenStartCharacter = pos - lineStartOffset;
8789
+ token = "EOF";
8790
+ return token;
8791
+ }
8792
+ let ch = text.charCodeAt(pos);
8793
+ if (isWhitespace(ch)) {
8794
+ tokenOffset = pos;
8795
+ tokenStartLine = lineNumber;
8796
+ tokenStartCharacter = pos - lineStartOffset;
8797
+ do {
8798
+ pos++;
8799
+ ch = pos < len ? text.charCodeAt(pos) : 0;
8800
+ } while (isWhitespace(ch));
8801
+ tokenValue = text.substring(tokenOffset, pos);
8802
+ if (ignoreTrivia) return scan();
8803
+ token = "Trivia";
8804
+ return token;
8805
+ }
8806
+ if (isLineBreak$1(ch)) {
8807
+ tokenOffset = pos;
8808
+ tokenStartLine = lineNumber;
8809
+ tokenStartCharacter = pos - lineStartOffset;
8810
+ pos++;
8811
+ if (ch === 13 && pos < len && text.charCodeAt(pos) === 10) pos++;
8812
+ lineNumber++;
8813
+ lineStartOffset = pos;
8814
+ tokenValue = text.substring(tokenOffset, pos);
8815
+ if (ignoreTrivia) return scan();
8816
+ token = "LineBreak";
8817
+ return token;
8818
+ }
8819
+ tokenOffset = pos;
8820
+ tokenStartLine = lineNumber;
8821
+ tokenStartCharacter = pos - lineStartOffset;
8822
+ switch (ch) {
8823
+ case 123:
8824
+ pos++;
8825
+ tokenValue = "{";
8826
+ token = "OpenBrace";
8827
+ return token;
8828
+ case 125:
8829
+ pos++;
8830
+ tokenValue = "}";
8831
+ token = "CloseBrace";
8832
+ return token;
8833
+ case 91:
8834
+ pos++;
8835
+ tokenValue = "[";
8836
+ token = "OpenBracket";
8837
+ return token;
8838
+ case 93:
8839
+ pos++;
8840
+ tokenValue = "]";
8841
+ token = "CloseBracket";
8842
+ return token;
8843
+ case 58:
8844
+ pos++;
8845
+ tokenValue = ":";
8846
+ token = "Colon";
8847
+ return token;
8848
+ case 44:
8849
+ pos++;
8850
+ tokenValue = ",";
8851
+ token = "Comma";
8852
+ return token;
8853
+ case 34:
8854
+ tokenValue = scanString();
8855
+ token = "String";
8856
+ return token;
8857
+ case 47: {
8858
+ const nextCh = pos + 1 < len ? text.charCodeAt(pos + 1) : 0;
8859
+ if (nextCh === 47) {
8860
+ pos += 2;
8861
+ while (pos < len && !isLineBreak$1(text.charCodeAt(pos))) pos++;
8862
+ tokenValue = text.substring(tokenOffset, pos);
8863
+ if (ignoreTrivia) return scan();
8864
+ token = "LineComment";
8865
+ return token;
8866
+ }
8867
+ if (nextCh === 42) {
8868
+ pos += 2;
8869
+ const safeLen = len - 1;
8870
+ let commentClosed = false;
8871
+ while (pos < safeLen) {
8872
+ const cch = text.charCodeAt(pos);
8873
+ if (isLineBreak$1(cch)) {
8874
+ if (cch === 13 && pos + 1 < len && text.charCodeAt(pos + 1) === 10) pos++;
8875
+ pos++;
8876
+ lineNumber++;
8877
+ lineStartOffset = pos;
8878
+ } else if (cch === 42 && text.charCodeAt(pos + 1) === 47) {
8879
+ pos += 2;
8880
+ commentClosed = true;
8881
+ break;
8882
+ } else pos++;
8883
+ }
8884
+ if (!commentClosed) {
8885
+ pos = len;
8886
+ tokenError = "UnexpectedEndOfComment";
8887
+ }
8888
+ tokenValue = text.substring(tokenOffset, pos);
8889
+ if (ignoreTrivia) return scan();
8890
+ token = "BlockComment";
8891
+ return token;
8892
+ }
8893
+ pos++;
8894
+ tokenValue = text.substring(tokenOffset, pos);
8895
+ token = "Unknown";
8896
+ tokenError = "InvalidCharacter";
8897
+ return token;
8898
+ }
8899
+ case 45:
8900
+ if (pos + 1 < len && isDigit$1(text.charCodeAt(pos + 1))) {
8901
+ tokenValue = scanNumber();
8902
+ token = "Number";
8903
+ return token;
8904
+ }
8905
+ pos++;
8906
+ tokenValue = "-";
8907
+ token = "Unknown";
8908
+ tokenError = "InvalidSymbol";
8909
+ return token;
8910
+ default:
8911
+ if (isDigit$1(ch)) {
8912
+ tokenValue = scanNumber();
8913
+ token = "Number";
8914
+ return token;
8915
+ }
8916
+ if (ch >= 97 && ch <= 122) {
8917
+ const start = pos;
8918
+ pos++;
8919
+ while (pos < len) {
8920
+ const kch = text.charCodeAt(pos);
8921
+ if (kch >= 97 && kch <= 122) pos++;
8922
+ else break;
8923
+ }
8924
+ tokenValue = text.substring(start, pos);
8925
+ switch (tokenValue) {
8926
+ case "true":
8927
+ token = "True";
8928
+ return token;
8929
+ case "false":
8930
+ token = "False";
8931
+ return token;
8932
+ case "null":
8933
+ token = "Null";
8934
+ return token;
8935
+ default:
8936
+ token = "Unknown";
8937
+ tokenError = "InvalidSymbol";
8938
+ return token;
8939
+ }
8940
+ }
8941
+ pos++;
8942
+ tokenValue = text.substring(tokenOffset, pos);
8943
+ token = "Unknown";
8944
+ tokenError = "InvalidCharacter";
8945
+ return token;
8946
+ }
8947
+ };
8948
+ return {
8949
+ scan,
8950
+ getToken: () => token,
8951
+ getTokenValue: () => tokenValue,
8952
+ getTokenOffset: () => tokenOffset,
8953
+ getTokenLength: () => pos - tokenOffset,
8954
+ getTokenStartLine: () => tokenStartLine,
8955
+ getTokenStartCharacter: () => tokenStartCharacter,
8956
+ getTokenError: () => tokenError,
8957
+ getPosition: () => pos,
8958
+ setPosition: (newPos) => {
8959
+ pos = newPos;
8960
+ tokenValue = "";
8961
+ token = "Unknown";
8962
+ tokenError = "None";
8963
+ }
8964
+ };
8965
+ };
8966
+ //#endregion
8967
+ //#region ../../node_modules/.pnpm/jsonc-effect@0.3.1_effect@3.21.4/node_modules/jsonc-effect/parse.js
8968
+ /**
8969
+ * JSONC Parser — converts token stream into JavaScript values or AST nodes.
8970
+ *
8971
+ * Pure Effect implementation using recursive descent parsing.
8972
+ * Reference: Microsoft's jsonc-parser parser design (MIT).
8973
+ *
8974
+ * @packageDocumentation
8975
+ */
8976
+ /**
8977
+ * Parse a JSONC string into a JavaScript value.
8978
+ *
8979
+ * @param text - JSONC string to parse
8980
+ * @param options - Optional {@link JsoncParseOptions} controlling comment and
8981
+ * trailing-comma handling.
8982
+ * @returns `Effect<unknown, JsoncParseError>` — succeeds with the parsed value
8983
+ * or fails with a {@link JsoncParseError} containing every error encountered.
8984
+ *
8985
+ * @remarks
8986
+ * The return type is `unknown` (not `any`) so consumers are forced to narrow
8987
+ * the result, which is safer in Effect pipelines. By default
8988
+ * `allowTrailingComma` is `true`, matching common JSONC conventions used in
8989
+ * VS Code settings and `tsconfig.json`.
8990
+ *
8991
+ * @see {@link parseTree} — parse into an AST instead of a plain value
8992
+ * @see {@link JsoncParseOptions} — available parse options
8993
+ * @see {@link JsoncParseError} — the tagged error type on the failure channel
8994
+ *
8995
+ * @example
8996
+ * Basic parsing:
8997
+ * ```ts
8998
+ * import { Effect } from "effect";
8999
+ * import { parse } from "jsonc-effect";
9000
+ *
9001
+ * const value = Effect.runSync(parse('{ "key": 42 }'));
9002
+ * console.log(value); // { key: 42 }
9003
+ * ```
9004
+ *
9005
+ * @example
9006
+ * Parsing with options:
9007
+ * ```ts
9008
+ * import { Effect } from "effect";
9009
+ * import { parse } from "jsonc-effect";
9010
+ *
9011
+ * const value = Effect.runSync(
9012
+ * parse('{ "key": 42 }', { disallowComments: true }),
9013
+ * );
9014
+ * ```
9015
+ *
9016
+ * @example
9017
+ * Error handling with `catchTag`:
9018
+ * ```ts
9019
+ * import { Effect } from "effect";
9020
+ * import { parse } from "jsonc-effect";
9021
+ *
9022
+ * const program = parse("{ bad }").pipe(
9023
+ * Effect.catchTag("JsoncParseError", (err) =>
9024
+ * Effect.succeed({ fallback: true, errors: err.errors }),
9025
+ * ),
9026
+ * );
9027
+ *
9028
+ * const result = Effect.runSync(program);
9029
+ * console.log(result);
9030
+ * ```
9031
+ *
9032
+ * @example
9033
+ * Using `Effect.gen`:
9034
+ * ```ts
9035
+ * import { Effect } from "effect";
9036
+ * import { parse } from "jsonc-effect";
9037
+ *
9038
+ * const program = Effect.gen(function* () {
9039
+ * const config = yield* parse('{ "port": 3000 }');
9040
+ * return config;
9041
+ * });
9042
+ *
9043
+ * const result = Effect.runSync(program);
9044
+ * console.log(result); // { port: 3000 }
9045
+ * ```
9046
+ *
9047
+ * @privateRemarks
9048
+ * Uses {@link createScanner} internally with a recursive descent parser.
9049
+ * The scanner is created with `ignoreTrivia = false` so the parser can
9050
+ * report comment-related errors when `disallowComments` is set.
9051
+ *
9052
+ * @public
9053
+ */
9054
+ const parse$2 = (text, options) => effect.Effect.sync(() => parseInternal(text, options ?? {}, false)).pipe(effect.Effect.flatMap(({ value, errors }) => {
9055
+ if (errors.length > 0) return effect.Effect.fail(new JsoncParseError({
9056
+ errors,
9057
+ text,
9058
+ ...options !== void 0 ? { options } : {}
9059
+ }));
9060
+ return effect.Effect.succeed(value);
9061
+ }));
9062
+ function parseInternal(text, options, buildTree) {
9063
+ const scanner = createScanner$1(text, false);
9064
+ const errors = [];
9065
+ const disallowComments = options.disallowComments ?? false;
9066
+ const allowTrailingComma = options.allowTrailingComma ?? true;
9067
+ const allowEmptyContent = options.allowEmptyContent ?? false;
9068
+ let currentToken = "Unknown";
9069
+ function token() {
9070
+ return currentToken;
9071
+ }
9072
+ function scanNext() {
9073
+ for (;;) {
9074
+ currentToken = scanner.scan();
9075
+ switch (scanner.getTokenError()) {
9076
+ case "InvalidUnicode":
9077
+ handleError("InvalidUnicode");
9078
+ break;
9079
+ case "InvalidEscapeCharacter":
9080
+ handleError("InvalidEscapeCharacter");
9081
+ break;
9082
+ case "UnexpectedEndOfNumber":
9083
+ handleError("InvalidNumberFormat");
9084
+ break;
9085
+ case "UnexpectedEndOfComment":
9086
+ handleError("UnexpectedEndOfComment");
9087
+ break;
9088
+ case "UnexpectedEndOfString":
9089
+ handleError("UnexpectedEndOfString");
9090
+ break;
9091
+ case "InvalidCharacter":
9092
+ handleError("InvalidCharacter");
9093
+ break;
9094
+ }
9095
+ switch (currentToken) {
9096
+ case "LineComment":
9097
+ case "BlockComment":
9098
+ if (disallowComments) handleError("InvalidCommentToken");
9099
+ break;
9100
+ case "Trivia":
9101
+ case "LineBreak": break;
9102
+ default: return currentToken;
9103
+ }
9104
+ }
9105
+ }
9106
+ function tokenEnd() {
9107
+ return scanner.getTokenOffset() + scanner.getTokenLength();
9108
+ }
9109
+ function handleError(code, skipUntilAfter = [], skipUntil = []) {
9110
+ errors.push(new JsoncParseErrorDetail({
9111
+ code,
9112
+ message: formatError(code, scanner.getTokenOffset()),
9113
+ offset: scanner.getTokenOffset(),
9114
+ length: scanner.getTokenLength(),
9115
+ startLine: scanner.getTokenStartLine(),
9116
+ startCharacter: scanner.getTokenStartCharacter()
9117
+ }));
9118
+ if (skipUntilAfter.length > 0 || skipUntil.length > 0) {
9119
+ let t = token();
9120
+ while (t !== "EOF") {
9121
+ if (skipUntilAfter.includes(t)) {
9122
+ scanNext();
9123
+ break;
9124
+ }
9125
+ if (skipUntil.includes(t)) break;
9126
+ t = scanNext();
9127
+ }
9128
+ }
9129
+ }
9130
+ function parseValue() {
9131
+ switch (token()) {
9132
+ case "OpenBracket": return parseArray();
9133
+ case "OpenBrace": return parseObject();
9134
+ case "String": return parseString();
9135
+ case "Number": return parseNumber();
9136
+ case "True":
9137
+ scanNext();
9138
+ return true;
9139
+ case "False":
9140
+ scanNext();
9141
+ return false;
9142
+ case "Null":
9143
+ scanNext();
9144
+ return null;
9145
+ default: return;
9146
+ }
9147
+ }
9148
+ function parseString() {
9149
+ const value = scanner.getTokenValue();
9150
+ scanNext();
9151
+ return value;
9152
+ }
9153
+ function parseNumber() {
9154
+ const value = Number.parseFloat(scanner.getTokenValue());
9155
+ scanNext();
9156
+ return value;
9157
+ }
9158
+ function parseArray() {
9159
+ scanNext();
9160
+ const arr = [];
9161
+ let needsComma = false;
9162
+ while (token() !== "CloseBracket" && token() !== "EOF") {
9163
+ if (token() === "Comma") {
9164
+ if (!needsComma) handleError("ValueExpected");
9165
+ scanNext();
9166
+ if (token() === "CloseBracket" && allowTrailingComma) break;
9167
+ } else if (needsComma) handleError("CommaExpected");
9168
+ const value = parseValue();
9169
+ if (value === void 0) handleError("ValueExpected", [], ["CloseBracket", "Comma"]);
9170
+ else arr.push(value);
9171
+ needsComma = true;
9172
+ }
9173
+ if (token() !== "CloseBracket") handleError("CloseBracketExpected");
9174
+ else scanNext();
9175
+ return arr;
9176
+ }
9177
+ function parseObject() {
9178
+ scanNext();
9179
+ const obj = {};
9180
+ let needsComma = false;
9181
+ while (token() !== "CloseBrace" && token() !== "EOF") {
9182
+ if (token() === "Comma") {
9183
+ if (!needsComma) handleError("PropertyNameExpected");
9184
+ scanNext();
9185
+ if (token() === "CloseBrace" && allowTrailingComma) break;
9186
+ } else if (needsComma) handleError("CommaExpected");
9187
+ if (token() !== "String") {
9188
+ handleError("PropertyNameExpected", [], ["CloseBrace", "Comma"]);
9189
+ continue;
9190
+ }
9191
+ const key = scanner.getTokenValue();
9192
+ scanNext();
9193
+ if (token() !== "Colon") {
9194
+ handleError("ColonExpected", [], ["CloseBrace", "Comma"]);
9195
+ continue;
9196
+ }
9197
+ scanNext();
9198
+ const value = parseValue();
9199
+ if (value === void 0) handleError("ValueExpected", [], ["CloseBrace", "Comma"]);
9200
+ else obj[key] = value;
9201
+ needsComma = true;
9202
+ }
9203
+ if (token() !== "CloseBrace") handleError("CloseBraceExpected");
9204
+ else scanNext();
9205
+ return obj;
9206
+ }
9207
+ function parseValueTree() {
9208
+ switch (token()) {
9209
+ case "OpenBracket": return parseArrayTree();
9210
+ case "OpenBrace": return parseObjectTree();
9211
+ case "String": {
9212
+ const node = {
9213
+ type: "string",
9214
+ offset: scanner.getTokenOffset(),
9215
+ length: 0,
9216
+ value: scanner.getTokenValue()
9217
+ };
9218
+ const end = tokenEnd();
9219
+ scanNext();
9220
+ node.length = end - node.offset;
9221
+ return node;
9222
+ }
9223
+ case "Number": {
9224
+ const node = {
9225
+ type: "number",
9226
+ offset: scanner.getTokenOffset(),
9227
+ length: 0,
9228
+ value: Number.parseFloat(scanner.getTokenValue())
9229
+ };
9230
+ const end = tokenEnd();
9231
+ scanNext();
9232
+ node.length = end - node.offset;
9233
+ return node;
9234
+ }
9235
+ case "True": {
9236
+ const node = {
9237
+ type: "boolean",
9238
+ offset: scanner.getTokenOffset(),
9239
+ length: 0,
9240
+ value: true
9241
+ };
9242
+ const end = tokenEnd();
9243
+ scanNext();
9244
+ node.length = end - node.offset;
9245
+ return node;
9246
+ }
9247
+ case "False": {
9248
+ const node = {
9249
+ type: "boolean",
9250
+ offset: scanner.getTokenOffset(),
9251
+ length: 0,
9252
+ value: false
9253
+ };
9254
+ const end = tokenEnd();
9255
+ scanNext();
9256
+ node.length = end - node.offset;
9257
+ return node;
9258
+ }
9259
+ case "Null": {
9260
+ const node = {
9261
+ type: "null",
9262
+ offset: scanner.getTokenOffset(),
9263
+ length: 0,
9264
+ value: null
9265
+ };
9266
+ const end = tokenEnd();
9267
+ scanNext();
9268
+ node.length = end - node.offset;
9269
+ return node;
9270
+ }
9271
+ default: return;
9272
+ }
9273
+ }
9274
+ function parseArrayTree() {
9275
+ const node = {
9276
+ type: "array",
9277
+ offset: scanner.getTokenOffset(),
9278
+ length: 0,
9279
+ children: []
9280
+ };
9281
+ scanNext();
9282
+ let needsComma = false;
9283
+ while (token() !== "CloseBracket" && token() !== "EOF") {
9284
+ if (token() === "Comma") {
9285
+ if (!needsComma) handleError("ValueExpected");
9286
+ scanNext();
9287
+ if (token() === "CloseBracket" && allowTrailingComma) break;
9288
+ } else if (needsComma) handleError("CommaExpected");
9289
+ const child = parseValueTree();
9290
+ if (child) node.children.push(child);
9291
+ else handleError("ValueExpected", [], ["CloseBracket", "Comma"]);
9292
+ needsComma = true;
9293
+ }
9294
+ let end;
9295
+ if (token() !== "CloseBracket") {
9296
+ handleError("CloseBracketExpected");
9297
+ end = scanner.getTokenOffset();
9298
+ } else {
9299
+ end = tokenEnd();
9300
+ scanNext();
9301
+ }
9302
+ node.length = end - node.offset;
9303
+ return node;
9304
+ }
9305
+ function parseObjectTree() {
9306
+ const node = {
9307
+ type: "object",
9308
+ offset: scanner.getTokenOffset(),
9309
+ length: 0,
9310
+ children: []
9311
+ };
9312
+ scanNext();
9313
+ let needsComma = false;
9314
+ while (token() !== "CloseBrace" && token() !== "EOF") {
9315
+ if (token() === "Comma") {
9316
+ if (!needsComma) handleError("PropertyNameExpected");
9317
+ scanNext();
9318
+ if (token() === "CloseBrace" && allowTrailingComma) break;
9319
+ } else if (needsComma) handleError("CommaExpected");
9320
+ if (token() !== "String") {
9321
+ handleError("PropertyNameExpected", [], ["CloseBrace", "Comma"]);
9322
+ continue;
9323
+ }
9324
+ const property = {
9325
+ type: "property",
9326
+ offset: scanner.getTokenOffset(),
9327
+ length: 0,
9328
+ children: []
9329
+ };
9330
+ const keyNode = {
9331
+ type: "string",
9332
+ offset: scanner.getTokenOffset(),
9333
+ length: 0,
9334
+ value: scanner.getTokenValue()
9335
+ };
9336
+ const keyEnd = tokenEnd();
9337
+ scanNext();
9338
+ keyNode.length = keyEnd - keyNode.offset;
9339
+ property.children.push(keyNode);
9340
+ if (token() !== "Colon") {
9341
+ handleError("ColonExpected", [], ["CloseBrace", "Comma"]);
9342
+ property.length = scanner.getTokenOffset() - property.offset;
9343
+ node.children.push(property);
9344
+ continue;
9345
+ }
9346
+ property.colonOffset = scanner.getTokenOffset();
9347
+ scanNext();
9348
+ const valueNode = parseValueTree();
9349
+ if (valueNode) {
9350
+ property.children.push(valueNode);
9351
+ property.length = valueNode.offset + valueNode.length - property.offset;
9352
+ } else {
9353
+ handleError("ValueExpected", [], ["CloseBrace", "Comma"]);
9354
+ property.length = scanner.getTokenOffset() - property.offset;
9355
+ }
9356
+ node.children.push(property);
9357
+ needsComma = true;
9358
+ }
9359
+ let end;
9360
+ if (token() !== "CloseBrace") {
9361
+ handleError("CloseBraceExpected");
9362
+ end = scanner.getTokenOffset();
9363
+ } else {
9364
+ end = tokenEnd();
9365
+ scanNext();
9366
+ }
9367
+ node.length = end - node.offset;
9368
+ return node;
9369
+ }
9370
+ scanNext();
9371
+ if (buildTree) {
9372
+ const root = parseValueTree();
9373
+ if (token() !== "EOF") handleError("EndOfFileExpected");
9374
+ if (!root && !allowEmptyContent) handleError("ValueExpected");
9375
+ return {
9376
+ value: void 0,
9377
+ root,
9378
+ errors
9379
+ };
9380
+ }
9381
+ const value = parseValue();
9382
+ if (token() !== "EOF") handleError("EndOfFileExpected");
9383
+ if (value === void 0 && !allowEmptyContent) handleError("ValueExpected");
9384
+ return {
9385
+ value,
9386
+ root: void 0,
9387
+ errors
9388
+ };
9389
+ }
9390
+ function formatError(code, offset) {
9391
+ switch (code) {
9392
+ case "InvalidSymbol": return `Invalid symbol at offset ${offset}`;
9393
+ case "InvalidNumberFormat": return `Invalid number format at offset ${offset}`;
9394
+ case "PropertyNameExpected": return `Property name expected at offset ${offset}`;
9395
+ case "ValueExpected": return `Value expected at offset ${offset}`;
9396
+ case "ColonExpected": return `Colon expected at offset ${offset}`;
9397
+ case "CommaExpected": return `Comma expected at offset ${offset}`;
9398
+ case "CloseBraceExpected": return `Close brace expected at offset ${offset}`;
9399
+ case "CloseBracketExpected": return `Close bracket expected at offset ${offset}`;
9400
+ case "EndOfFileExpected": return `End of file expected at offset ${offset}`;
9401
+ case "InvalidCommentToken": return `Comments not allowed at offset ${offset}`;
9402
+ case "UnexpectedEndOfComment": return `Unexpected end of comment at offset ${offset}`;
9403
+ case "UnexpectedEndOfString": return `Unexpected end of string at offset ${offset}`;
9404
+ case "UnexpectedEndOfNumber": return `Unexpected end of number at offset ${offset}`;
9405
+ case "InvalidUnicode": return `Invalid unicode escape at offset ${offset}`;
9406
+ case "InvalidEscapeCharacter": return `Invalid escape character at offset ${offset}`;
9407
+ case "InvalidCharacter": return `Invalid character at offset ${offset}`;
9408
+ default: return `Parse error at offset ${offset}`;
9409
+ }
9410
+ }
9411
+ //#endregion
9412
+ //#region ../../node_modules/.pnpm/jsonc-effect@0.3.1_effect@3.21.4/node_modules/jsonc-effect/format.js
9413
+ /**
9414
+ * Apply an array of text edits to JSONC source text.
9415
+ *
9416
+ * This is a {@link https://effect.website/docs/function-dual | Function.dual}
9417
+ * that supports both data-first and data-last (pipeline) usage.
9418
+ *
9419
+ * @param text - The original JSONC source text.
9420
+ * @param edits - A read-only array of {@link JsoncEdit} objects, typically
9421
+ * produced by {@link format} or {@link modify}.
9422
+ * @returns An `Effect` that succeeds with the edited string.
9423
+ *
9424
+ * @remarks
9425
+ * Edits are sorted in reverse offset order before application so that
9426
+ * earlier edits do not shift the offsets of later ones. The original `edits`
9427
+ * array is not mutated.
9428
+ *
9429
+ * @see {@link format} to compute formatting edits.
9430
+ * @see {@link modify} to compute structural edits (insert, replace, remove).
9431
+ *
9432
+ * @example Data-first usage
9433
+ * ```ts
9434
+ * import { Effect } from "effect";
9435
+ * import { format, applyEdits } from "jsonc-effect";
9436
+ *
9437
+ * const input = '{"a":1}';
9438
+ * const edits = Effect.runSync(format(input));
9439
+ * const result: string = Effect.runSync(applyEdits(input, edits));
9440
+ * ```
9441
+ *
9442
+ * @example Pipeline with modify
9443
+ * ```ts
9444
+ * import { Effect, pipe } from "effect";
9445
+ * import { modify, applyEdits } from "jsonc-effect";
9446
+ *
9447
+ * const input = '{ "a": 1 }';
9448
+ * const result = pipe(
9449
+ * input,
9450
+ * modify(["a"], 42),
9451
+ * Effect.flatMap((edits) => applyEdits(input, edits)),
9452
+ * Effect.runSync,
9453
+ * );
9454
+ * ```
9455
+ *
9456
+ * @public
9457
+ */
9458
+ const applyEdits$1 = effect.Function.dual(2, (text, edits) => effect.Effect.sync(() => {
9459
+ const sorted = [...edits].sort((a, b) => b.offset - a.offset);
9460
+ let result = text;
9461
+ for (const edit of sorted) result = result.substring(0, edit.offset) + edit.content + result.substring(edit.offset + edit.length);
9462
+ return result;
9463
+ }));
9464
+ /**
9465
+ * Compute edits to insert, replace, or remove a value at a JSON path.
9466
+ *
9467
+ * This is a {@link https://effect.website/docs/function-dual | Function.dual}
9468
+ * that supports both data-first and data-last (pipeline) usage.
9469
+ *
9470
+ * @param text - The JSONC source text to modify.
9471
+ * @param path - A {@link (JsoncPath:type)} (array of string keys and numeric indices)
9472
+ * identifying the target location in the JSON structure.
9473
+ * @param value - The value to set. Pass `undefined` to remove the
9474
+ * property or array element at the given path.
9475
+ * @param options - Optional object with `formattingOptions` controlling
9476
+ * indent size, tabs vs. spaces, and EOL style for generated text.
9477
+ * @returns An `Effect` that succeeds with a read-only array of
9478
+ * {@link JsoncEdit} objects, or fails with a
9479
+ * {@link JsoncModificationError} if the path cannot be navigated.
9480
+ *
9481
+ * @remarks
9482
+ * Setting `value` to `undefined` removes the targeted property or element,
9483
+ * including its surrounding comma. When inserting a new property into an
9484
+ * object, it is appended after the last existing property.
9485
+ *
9486
+ * @see {@link applyEdits} to apply the returned edits to the source text.
9487
+ * @see {@link JsoncModificationError} for the error type on navigation failure.
9488
+ *
9489
+ * @example Update an existing property
9490
+ * ```ts
9491
+ * import { Effect } from "effect";
9492
+ * import { modify, applyEdits } from "jsonc-effect";
9493
+ *
9494
+ * const input = '{ "a": 1 }';
9495
+ * const edits = Effect.runSync(modify(input, ["a"], 2));
9496
+ * const result: string = Effect.runSync(applyEdits(input, edits));
9497
+ * ```
9498
+ *
9499
+ * @example Insert a new property
9500
+ * ```ts
9501
+ * import { Effect } from "effect";
9502
+ * import { modify, applyEdits } from "jsonc-effect";
9503
+ *
9504
+ * const input = '{ "a": 1 }';
9505
+ * const edits = Effect.runSync(modify(input, ["b"], "hello"));
9506
+ * const result: string = Effect.runSync(applyEdits(input, edits));
9507
+ * ```
9508
+ *
9509
+ * @example Remove a property
9510
+ * ```ts
9511
+ * import { Effect } from "effect";
9512
+ * import { modify, applyEdits } from "jsonc-effect";
9513
+ *
9514
+ * const input = '{ "a": 1, "b": 2 }';
9515
+ * const edits = Effect.runSync(modify(input, ["a"], undefined));
9516
+ * const result: string = Effect.runSync(applyEdits(input, edits));
9517
+ * ```
9518
+ *
9519
+ * @example Pipeline (data-last) usage
9520
+ * ```ts
9521
+ * import { Effect, pipe } from "effect";
9522
+ * import { modify, applyEdits } from "jsonc-effect";
9523
+ *
9524
+ * const input = '{ "a": 1 }';
9525
+ * const result = pipe(
9526
+ * input,
9527
+ * modify(["a"], 42),
9528
+ * Effect.flatMap((edits) => applyEdits(input, edits)),
9529
+ * Effect.runSync,
9530
+ * );
9531
+ * ```
9532
+ *
9533
+ * @privateRemarks
9534
+ * Uses its own scanner-based navigation to locate the target path rather
9535
+ * than building a full AST via `parseTree`. This keeps the implementation
9536
+ * lightweight and avoids an intermediate allocation.
9537
+ *
9538
+ * @public
9539
+ */
9540
+ const modify = effect.Function.dual((args) => typeof args[0] === "string" && Array.isArray(args[1]), (text, path, value, options) => effect.Effect.try({
9541
+ try: () => modifyImpl(text, path, value, options?.formattingOptions),
9542
+ catch: (e) => new JsoncModificationError({
9543
+ path,
9544
+ reason: String(e)
9545
+ })
9546
+ }));
9547
+ function modifyImpl(text, path, value, formattingOptions) {
9548
+ const opts = {
9549
+ tabSize: formattingOptions?.tabSize ?? 2,
9550
+ insertSpaces: formattingOptions?.insertSpaces ?? true,
9551
+ eol: formattingOptions?.eol ?? "\n"
9552
+ };
9553
+ const indentUnit = opts.insertSpaces ? " ".repeat(opts.tabSize) : " ";
9554
+ if (path.length === 0) {
9555
+ const content = value === void 0 ? "" : JSON.stringify(value, null, opts.tabSize);
9556
+ return [{
9557
+ offset: 0,
9558
+ length: text.length,
9559
+ content
9560
+ }];
9561
+ }
9562
+ const scanner = createScanner$1(text, true);
9563
+ let currentToken = scanner.scan();
9564
+ function tokenEnd() {
9565
+ return scanner.getTokenOffset() + scanner.getTokenLength();
9566
+ }
9567
+ function skipValue() {
9568
+ switch (currentToken) {
9569
+ case "OpenBrace": {
9570
+ let end = tokenEnd();
9571
+ currentToken = scanner.scan();
9572
+ let first = true;
9573
+ while (currentToken !== "CloseBrace" && currentToken !== "EOF") {
9574
+ if (!first && currentToken === "Comma") currentToken = scanner.scan();
9575
+ if (currentToken === "String") {
9576
+ currentToken = scanner.scan();
9577
+ if (currentToken === "Colon") {
9578
+ currentToken = scanner.scan();
9579
+ end = skipValue();
9580
+ }
9581
+ } else {
9582
+ end = tokenEnd();
9583
+ currentToken = scanner.scan();
9584
+ }
9585
+ first = false;
9586
+ }
9587
+ if (currentToken === "CloseBrace") {
9588
+ end = tokenEnd();
9589
+ currentToken = scanner.scan();
9590
+ }
9591
+ return end;
9592
+ }
9593
+ case "OpenBracket": {
9594
+ let end = tokenEnd();
9595
+ currentToken = scanner.scan();
9596
+ let first = true;
9597
+ while (currentToken !== "CloseBracket" && currentToken !== "EOF") {
9598
+ if (!first && currentToken === "Comma") currentToken = scanner.scan();
9599
+ end = skipValue();
9600
+ first = false;
9601
+ }
9602
+ if (currentToken === "CloseBracket") {
9603
+ end = tokenEnd();
9604
+ currentToken = scanner.scan();
9605
+ }
9606
+ return end;
9607
+ }
9608
+ default: {
9609
+ const end = tokenEnd();
9610
+ currentToken = scanner.scan();
9611
+ return end;
9612
+ }
9613
+ }
9614
+ }
9615
+ let depth = 0;
9616
+ for (const segment of path) {
9617
+ depth++;
9618
+ if (typeof segment === "string") {
9619
+ if (currentToken !== "OpenBrace") throw new Error(`Expected object at depth ${depth}`);
9620
+ currentToken = scanner.scan();
9621
+ let found = false;
9622
+ let lastValueEnd = scanner.getTokenOffset();
9623
+ let isFirst = true;
9624
+ while (currentToken !== "CloseBrace" && currentToken !== "EOF") {
9625
+ if (!isFirst && currentToken === "Comma") currentToken = scanner.scan();
9626
+ if (currentToken === "String") {
9627
+ const key = scanner.getTokenValue();
9628
+ currentToken = scanner.scan();
9629
+ if (currentToken === "Colon") currentToken = scanner.scan();
9630
+ if (key === segment) {
9631
+ found = true;
9632
+ if (depth === path.length) {
9633
+ const valueStart = scanner.getTokenOffset();
9634
+ const prevEnd = valueStart;
9635
+ const valueEnd = skipValue();
9636
+ if (value === void 0) {
9637
+ let removeStart = valueStart;
9638
+ let removeEnd = valueEnd;
9639
+ const keyStart = text.substring(0, valueStart).lastIndexOf(`"${segment}"`);
9640
+ if (keyStart >= 0) removeStart = keyStart;
9641
+ const commaPosBefore = text.substring(0, removeStart).trimEnd().lastIndexOf(",");
9642
+ if (commaPosBefore >= 0) removeStart = commaPosBefore;
9643
+ else {
9644
+ const trimmedAfter = text.substring(removeEnd).match(/^(\s*,)/);
9645
+ if (trimmedAfter) removeEnd += trimmedAfter[1].length;
9646
+ }
9647
+ return [{
9648
+ offset: removeStart,
9649
+ length: removeEnd - removeStart,
9650
+ content: ""
9651
+ }];
9652
+ }
9653
+ const serialized = JSON.stringify(value, null, opts.tabSize);
9654
+ return [{
9655
+ offset: prevEnd,
9656
+ length: valueEnd - prevEnd,
9657
+ content: serialized
9658
+ }];
9659
+ }
9660
+ break;
9661
+ }
9662
+ lastValueEnd = skipValue();
9663
+ } else {
9664
+ currentToken = scanner.scan();
9665
+ lastValueEnd = scanner.getTokenOffset();
9666
+ }
9667
+ isFirst = false;
9668
+ }
9669
+ if (!found && depth === path.length && value !== void 0) {
9670
+ const indent = indentUnit.repeat(depth);
9671
+ const serialized = JSON.stringify(value, null, opts.tabSize);
9672
+ const insertText = isFirst ? `${opts.eol}${indent}"${segment}": ${serialized}${opts.eol}${indentUnit.repeat(depth - 1)}` : `,${opts.eol}${indent}"${segment}": ${serialized}`;
9673
+ return [{
9674
+ offset: lastValueEnd,
9675
+ length: 0,
9676
+ content: insertText
9677
+ }];
9678
+ }
9679
+ } else {
9680
+ if (currentToken !== "OpenBracket") throw new Error(`Expected array at depth ${depth}`);
9681
+ currentToken = scanner.scan();
9682
+ let idx = 0;
9683
+ let lastEnd = scanner.getTokenOffset();
9684
+ while (currentToken !== "CloseBracket" && currentToken !== "EOF") {
9685
+ if (idx > 0 && currentToken === "Comma") currentToken = scanner.scan();
9686
+ if (idx === segment) {
9687
+ if (depth === path.length) {
9688
+ const valueStart = scanner.getTokenOffset();
9689
+ const valueEnd = skipValue();
9690
+ if (value === void 0) {
9691
+ let removeEnd = valueEnd;
9692
+ if (text.substring(removeEnd).trimStart().startsWith(",")) removeEnd = text.indexOf(",", removeEnd) + 1;
9693
+ return [{
9694
+ offset: valueStart,
9695
+ length: removeEnd - valueStart,
9696
+ content: ""
9697
+ }];
9698
+ }
9699
+ const serialized = JSON.stringify(value, null, opts.tabSize);
9700
+ return [{
9701
+ offset: valueStart,
9702
+ length: valueEnd - valueStart,
9703
+ content: serialized
9704
+ }];
9705
+ }
9706
+ break;
9707
+ }
9708
+ lastEnd = skipValue();
9709
+ idx++;
9710
+ }
9711
+ if (idx <= segment && depth === path.length && value !== void 0) {
9712
+ const indent = indentUnit.repeat(depth);
9713
+ const serialized = JSON.stringify(value, null, opts.tabSize);
9714
+ const insertText = idx === 0 ? `${opts.eol}${indent}${serialized}${opts.eol}${indentUnit.repeat(depth - 1)}` : `,${opts.eol}${indent}${serialized}`;
9715
+ return [{
9716
+ offset: lastEnd,
9717
+ length: 0,
9718
+ content: insertText
9719
+ }];
9720
+ }
9721
+ }
9722
+ }
9723
+ return [];
9724
+ }
9725
+ //#endregion
8447
9726
  //#region ../../node_modules/.pnpm/workspaces-effect@2.0.2_@effect+platform@0.96.2_effect@3.21.4__effect@3.21.4/node_modules/workspaces-effect/schemas/WorkspaceStateSnapshot.js
8448
9727
  const DepRecord = effect.Schema.optionalWith(effect.Schema.Record({
8449
9728
  key: effect.Schema.String,
@@ -8730,7 +10009,7 @@ const packageSnapshotFromJson = (text, relativePath) => {
8730
10009
  *
8731
10010
  * @internal
8732
10011
  */
8733
- const lockfileCatalogsAtRef = (text) => effect.Option.isNone(text) ? effect.Effect.succeed(CatalogSet.empty()) : parse$2(text.value).pipe(effect.Effect.map((parsed) => CatalogSet.fromLockfileCatalogs(parsed?.catalogs)), effect.Effect.orElseSucceed(() => CatalogSet.empty()));
10012
+ const lockfileCatalogsAtRef = (text) => effect.Option.isNone(text) ? effect.Effect.succeed(CatalogSet.empty()) : parse$3(text.value).pipe(effect.Effect.map((parsed) => CatalogSet.fromLockfileCatalogs(parsed?.catalogs)), effect.Effect.orElseSucceed(() => CatalogSet.empty()));
8734
10013
  /**
8735
10014
  * Live layer for the {@link PointInTimeWorkspace} service.
8736
10015
  *
@@ -37463,15 +38742,16 @@ function makeShape$2(inspector) {
37463
38742
  path,
37464
38743
  status: "added"
37465
38744
  }));
38745
+ const isOwnChangeset = (path) => path.startsWith(".changeset/") && path.endsWith(".md");
37466
38746
  const seen = /* @__PURE__ */ new Set();
37467
38747
  const rawEntries = [];
37468
38748
  for (const e of diffEntries) {
37469
- if (seen.has(e.path)) continue;
38749
+ if (seen.has(e.path) || isOwnChangeset(e.path)) continue;
37470
38750
  seen.add(e.path);
37471
38751
  rawEntries.push(e);
37472
38752
  }
37473
38753
  for (const e of untrackedEntries) {
37474
- if (seen.has(e.path)) continue;
38754
+ if (seen.has(e.path) || isOwnChangeset(e.path)) continue;
37475
38755
  seen.add(e.path);
37476
38756
  rawEntries.push(e);
37477
38757
  }
@@ -37604,6 +38884,32 @@ const DEP_TYPE_MAP = [
37604
38884
  */
37605
38885
  const resolveOrRaw = (snapshot, dep, spec) => effect.Option.getOrElse(snapshot.resolve(dep, spec), () => spec);
37606
38886
  /**
38887
+ * Drop no-net-change field moves: the same dependency removed from one field
38888
+ * and added to another with an equal resolved version (e.g. a dep promoted
38889
+ * from `devDependencies` to `dependencies`). A field reclassification is a
38890
+ * contract change worth release-note prose, not a version movement, so it
38891
+ * must not surface as an unrelated removed row plus an added row. Moves that
38892
+ * also change the resolved version keep both rows (the movement is real).
38893
+ */
38894
+ const collapseFieldMoves = (rows) => {
38895
+ const dropped = /* @__PURE__ */ new Set();
38896
+ const byName = /* @__PURE__ */ new Map();
38897
+ for (const row of rows) {
38898
+ const group = byName.get(row.dependency);
38899
+ if (group) group.push(row);
38900
+ else byName.set(row.dependency, [row]);
38901
+ }
38902
+ for (const group of byName.values()) for (const removed of group) {
38903
+ if (removed.action !== "removed" || dropped.has(removed)) continue;
38904
+ const added = group.find((r) => r.action === "added" && !dropped.has(r) && r.type !== removed.type && r.to === removed.from);
38905
+ if (added) {
38906
+ dropped.add(removed);
38907
+ dropped.add(added);
38908
+ }
38909
+ }
38910
+ return rows.filter((r) => !dropped.has(r));
38911
+ };
38912
+ /**
37607
38913
  * Diff two workspace snapshots and return per-package dependency-table rows,
37608
38914
  * comparing already-resolved specifier values per side.
37609
38915
  *
@@ -37659,10 +38965,11 @@ function computeWorkspaceDependencyDiffs(before, after) {
37659
38965
  });
37660
38966
  }
37661
38967
  }
37662
- if (rows.length > 0) result.push({
38968
+ const collapsed = collapseFieldMoves(rows);
38969
+ if (collapsed.length > 0) result.push({
37663
38970
  package: afterPkg.name,
37664
38971
  relativePath: afterPkg.relativePath,
37665
- rows: sortDependencyRows(rows)
38972
+ rows: sortDependencyRows(collapsed)
37666
38973
  });
37667
38974
  }
37668
38975
  return result;
@@ -38002,7 +39309,8 @@ function makeShape$1(pit, inspector, discovery, detector, config, fs) {
38002
39309
  fromRef = yield* gitMergeBase(resolvedCwd, baseBranch);
38003
39310
  }
38004
39311
  const rawDiffs = computeWorkspaceDependencyDiffs(yield* pit.at(fromRef, { cwd: resolvedCwd }), options.to ? yield* pit.at(options.to, { cwd: resolvedCwd }) : yield* pit.worktree({ cwd: resolvedCwd }));
38005
- const targetPkg = options.package;
39312
+ const explicitTargets = /* @__PURE__ */ new Set([...options.packages ?? [], ...options.package ? [options.package] : []]);
39313
+ const excluded = new Set(options.exclude ?? []);
38006
39314
  const livePackages = yield* discovery.listPackages(resolvedCwd);
38007
39315
  const publishable = yield* listPublishablePackageNames(livePackages, resolvedCwd).pipe(effect.Effect.provide(provideDetector));
38008
39316
  const versionPrivate = yield* config.versionPrivate(resolvedCwd);
@@ -38011,9 +39319,15 @@ function makeShape$1(pit, inspector, discovery, detector, config, fs) {
38011
39319
  if (yield* config.isIgnored(pkg.name, resolvedCwd)) continue;
38012
39320
  if (publishable.has(pkg.name) || versionPrivate) inScope.add(pkg.name);
38013
39321
  }
38014
- const targetIgnored = targetPkg ? yield* config.isIgnored(targetPkg, resolvedCwd) : false;
39322
+ const activeTargets = /* @__PURE__ */ new Set();
39323
+ for (const name of explicitTargets) {
39324
+ if (excluded.has(name)) continue;
39325
+ if (yield* config.isIgnored(name, resolvedCwd)) continue;
39326
+ activeTargets.add(name);
39327
+ }
39328
+ const inScopeFor = (name) => explicitTargets.size > 0 ? activeTargets.has(name) : inScope.has(name) && !excluded.has(name);
38015
39329
  const keepDevDeps = options.includeDevDeps === true;
38016
- const scoped = targetPkg ? targetIgnored ? [] : rawDiffs.filter((d) => d.package === targetPkg) : rawDiffs.filter((d) => inScope.has(d.package));
39330
+ const scoped = rawDiffs.filter((d) => inScopeFor(d.package));
38017
39331
  const resolved = [];
38018
39332
  for (const diff of scoped) {
38019
39333
  const rows = keepDevDeps ? [...diff.rows] : diff.rows.filter((r) => r.type !== "devDependency");
@@ -38024,7 +39338,7 @@ function makeShape$1(pit, inspector, discovery, detector, config, fs) {
38024
39338
  }
38025
39339
  const existingPure = yield* findPureDependencyChangesets(fs, changesetDir);
38026
39340
  const skippedMixed = yield* findMixedDependencyChangesets(fs, changesetDir);
38027
- const toDelete = targetPkg ? targetIgnored ? [] : existingPure.filter((p) => p.package === targetPkg) : existingPure.filter((p) => inScope.has(p.package));
39341
+ const toDelete = existingPure.filter((p) => inScopeFor(p.package));
38028
39342
  const chosenFilenames = /* @__PURE__ */ new Set();
38029
39343
  const toWrite = [];
38030
39344
  for (const diff of resolved) {
@@ -38240,23 +39554,46 @@ function parseJsonPath(path) {
38240
39554
  * @internal
38241
39555
  */
38242
39556
  function jsonPathGet(obj, path) {
39557
+ return walkJsonPath(obj, path).map((entry) => entry.node);
39558
+ }
39559
+ /**
39560
+ * Shared breadth-first traversal behind {@link jsonPathGet} and
39561
+ * {@link jsonPathResolve}: each segment fans out the current set of matched
39562
+ * entries, carrying both the node and the concrete path taken to reach it.
39563
+ * The two public functions differ only in which half of the entry they keep.
39564
+ */
39565
+ function walkJsonPath(obj, path) {
38243
39566
  const segments = parseJsonPath(path);
38244
- let current = [obj];
39567
+ let current = [{
39568
+ node: obj,
39569
+ path: []
39570
+ }];
38245
39571
  for (const segment of segments) {
38246
39572
  const next = [];
38247
- for (const node of current) {
39573
+ for (const { node, path: nodePath } of current) {
38248
39574
  if (node === null || node === void 0 || typeof node !== "object") continue;
38249
39575
  switch (segment.type) {
38250
39576
  case "property": {
38251
39577
  const value = node[segment.key];
38252
- if (value !== void 0) next.push(value);
39578
+ if (value !== void 0) next.push({
39579
+ node: value,
39580
+ path: [...nodePath, segment.key]
39581
+ });
38253
39582
  break;
38254
39583
  }
38255
39584
  case "index":
38256
- if (Array.isArray(node) && segment.index < node.length) next.push(node[segment.index]);
39585
+ if (Array.isArray(node) && segment.index < node.length) next.push({
39586
+ node: node[segment.index],
39587
+ path: [...nodePath, segment.index]
39588
+ });
38257
39589
  break;
38258
39590
  case "wildcard":
38259
- if (Array.isArray(node)) next.push(...node);
39591
+ if (Array.isArray(node)) node.forEach((element, index) => {
39592
+ next.push({
39593
+ node: element,
39594
+ path: [...nodePath, index]
39595
+ });
39596
+ });
38260
39597
  break;
38261
39598
  }
38262
39599
  }
@@ -38265,81 +39602,37 @@ function jsonPathGet(obj, path) {
38265
39602
  return current;
38266
39603
  }
38267
39604
  /**
38268
- * Mutate all matching locations in an object in-place.
39605
+ * Resolve a JSONPath expression to the concrete paths of every existing match.
38269
39606
  *
38270
39607
  * @remarks
38271
- * Walks to the parent(s) of the final segment, then sets the value
38272
- * at each matching location. Only updates existing keys/indices;
38273
- * does not create new properties or extend arrays. Returns the count
38274
- * of locations actually updated.
39608
+ * Uses the same breadth-first expansion as {@link jsonPathGet}, but instead of
39609
+ * collecting the matched *values* it records the concrete `(string | number)[]`
39610
+ * path taken to reach each one. Wildcards and indices are materialized into the
39611
+ * numeric array index actually traversed, so the returned paths are directly
39612
+ * consumable by structural editors such as `jsonc-effect`'s `modify`, which
39613
+ * require a fully concrete path (no wildcards).
38275
39614
  *
38276
- * @param obj - The object to modify in-place
39615
+ * Only existing locations are returned; nothing is created. A path with no
39616
+ * matches yields an empty array, and the empty path (`"$."`) yields a single
39617
+ * empty concrete path (the document root).
39618
+ *
39619
+ * @param obj - The object to query
38277
39620
  * @param path - JSONPath string (e.g., `"$.packages[*].version"`)
38278
- * @param value - The value to set at each matching location
38279
- * @returns The number of locations updated (0 if no matches or empty path)
39621
+ * @returns Array of concrete paths, each an array of string keys / numeric indices
38280
39622
  *
38281
39623
  * @example
38282
39624
  * ```typescript
38283
- * import { jsonPathSet } from "../utils/jsonpath.js";
39625
+ * import { jsonPathResolve } from "../utils/jsonpath.js";
38284
39626
  *
38285
- * const obj = { version: "1.0.0" };
38286
- * const count = jsonPathSet(obj, "$.version", "2.0.0");
38287
- * // count === 1, obj.version === "2.0.0"
39627
+ * const obj = { packages: [{ version: "1.0.0" }, { version: "2.0.0" }] };
39628
+ * const paths = jsonPathResolve(obj, "$.packages[*].version");
39629
+ * // [["packages", 0, "version"], ["packages", 1, "version"]]
38288
39630
  * ```
38289
39631
  *
38290
39632
  * @internal
38291
39633
  */
38292
- function jsonPathSet(obj, path, value) {
38293
- const segments = parseJsonPath(path);
38294
- if (segments.length === 0) return 0;
38295
- const lastSegment = segments[segments.length - 1];
38296
- const parentSegments = segments.slice(0, -1);
38297
- let parents = [obj];
38298
- for (const segment of parentSegments) {
38299
- const next = [];
38300
- for (const node of parents) {
38301
- if (node === null || node === void 0 || typeof node !== "object") continue;
38302
- switch (segment.type) {
38303
- case "property": {
38304
- const child = node[segment.key];
38305
- if (child !== void 0) next.push(child);
38306
- break;
38307
- }
38308
- case "index":
38309
- if (Array.isArray(node) && segment.index < node.length) next.push(node[segment.index]);
38310
- break;
38311
- case "wildcard":
38312
- if (Array.isArray(node)) next.push(...node);
38313
- break;
38314
- }
38315
- }
38316
- parents = next;
38317
- }
38318
- let count = 0;
38319
- for (const parent of parents) {
38320
- if (parent === null || parent === void 0 || typeof parent !== "object") continue;
38321
- switch (lastSegment.type) {
38322
- case "property":
38323
- if (lastSegment.key in parent) {
38324
- parent[lastSegment.key] = value;
38325
- count++;
38326
- }
38327
- break;
38328
- case "index":
38329
- if (Array.isArray(parent) && lastSegment.index < parent.length) {
38330
- parent[lastSegment.index] = value;
38331
- count++;
38332
- }
38333
- break;
38334
- case "wildcard":
38335
- if (Array.isArray(parent)) for (let i = 0; i < parent.length; i++) {
38336
- parent[i] = value;
38337
- count++;
38338
- }
38339
- break;
38340
- }
38341
- }
38342
- return count;
39634
+ function jsonPathResolve(obj, path) {
39635
+ return walkJsonPath(obj, path).map((entry) => entry.path);
38343
39636
  }
38344
39637
  //#endregion
38345
39638
  //#region ../silk-effects/dist/dev/pkg/changesets/utils/version-files.js
@@ -38530,31 +39823,39 @@ var VersionFiles = class VersionFiles {
38530
39823
  return content.match(/^(\s+)"/m)?.[1] ?? " ";
38531
39824
  }
38532
39825
  /**
38533
- * Update JSON file at specified JSONPath locations.
39826
+ * Update a JSON (or JSONC) file at specified JSONPath locations,
39827
+ * preserving the original formatting byte-for-byte.
38534
39828
  *
38535
39829
  * @remarks
38536
- * Reads the file, detects its indentation style and trailing newline
38537
- * preference, applies all JSONPath updates via {@link jsonPathSet},
38538
- * and writes the result back preserving the original formatting.
38539
- * Returns `undefined` if no JSONPath locations matched (no write occurs).
39830
+ * The write is performed with `jsonc-effect`'s format-preserving
39831
+ * `modify`/`applyEdits` rather than a `JSON.parse`/`JSON.stringify`
39832
+ * round-trip (which always explodes inline arrays one-element-per-line and
39833
+ * drops comments). Each JSONPath expression is resolved to concrete
39834
+ * `(string | number)[]` paths against the parsed document, and each
39835
+ * concrete path becomes a minimal text edit that touches only the target
39836
+ * value's span — so inline arrays, comments,
39837
+ * indentation, and the trailing-newline preference all survive; a one-line
39838
+ * version bump produces a one-line diff.
39839
+ *
39840
+ * Insertion semantics: a concrete, wildcard-free JSONPath whose leaf
39841
+ * property does not exist yet is inserted after the last sibling using the
39842
+ * document's detected indent (the one case where indent detection still
39843
+ * matters). Wildcard expressions only ever update existing matches. Returns
39844
+ * `undefined` (no write) when nothing was updated or inserted.
38540
39845
  *
38541
39846
  * @param filePath - Absolute path to the JSON file
38542
39847
  * @param jsonPaths - JSONPath expressions to update
38543
39848
  * @param version - New version string
38544
39849
  * @returns Update result, or `undefined` if no changes were made
39850
+ *
39851
+ * @see {@link jsonPathResolve} for concrete-path enumeration
39852
+ * @see {@link VersionFiles.applyVersionEdit} for the per-path edit
38545
39853
  */
38546
39854
  static updateFile(filePath, jsonPaths, version) {
38547
- const content = (0, node_fs.readFileSync)(filePath, "utf-8");
38548
- const indent = VersionFiles.detectIndent(content);
38549
- const trailingNewline = content.endsWith("\n");
38550
- const obj = JSON.parse(content);
38551
- const previousValues = jsonPaths.flatMap((jp) => jsonPathGet(obj, jp));
38552
- let totalUpdated = 0;
38553
- for (const jp of jsonPaths) totalUpdated += jsonPathSet(obj, jp, version);
38554
- if (totalUpdated === 0) return;
38555
- let output = JSON.stringify(obj, null, indent);
38556
- if (trailingNewline) output += "\n";
38557
- (0, node_fs.writeFileSync)(filePath, output, "utf-8");
39855
+ const original = (0, node_fs.readFileSync)(filePath, "utf-8");
39856
+ const { content, previousValues, totalChanged } = VersionFiles.computeUpdate(original, jsonPaths, version);
39857
+ if (totalChanged === 0) return;
39858
+ (0, node_fs.writeFileSync)(filePath, content, "utf-8");
38558
39859
  return {
38559
39860
  filePath,
38560
39861
  jsonPaths,
@@ -38563,6 +39864,83 @@ var VersionFiles = class VersionFiles {
38563
39864
  };
38564
39865
  }
38565
39866
  /**
39867
+ * Compute the full update for a document without touching the filesystem:
39868
+ * the edited content, the previous values at every matched path, and how
39869
+ * many locations actually changed.
39870
+ *
39871
+ * @remarks
39872
+ * This is the single decision path shared by {@link VersionFiles.updateFile}
39873
+ * and the dry-run branches of the two process methods, so a preview reports
39874
+ * exactly the files a real run would write — including pending inserts of a
39875
+ * not-yet-existing wildcard-free leaf, and excluding same-value no-ops.
39876
+ *
39877
+ * @param original - Document text as read from disk
39878
+ * @param jsonPaths - JSONPath expressions to update
39879
+ * @param version - New version string
39880
+ * @returns The updated content, previous values, and changed-location count
39881
+ */
39882
+ static computeUpdate(original, jsonPaths, version) {
39883
+ let content = original;
39884
+ const obj = effect.Effect.runSync(parse$2(content));
39885
+ const previousValues = jsonPaths.flatMap((jp) => jsonPathGet(obj, jp));
39886
+ const indent = VersionFiles.detectIndent(content);
39887
+ const eol = content.includes("\r\n") ? "\r\n" : "\n";
39888
+ let totalChanged = 0;
39889
+ for (const jp of jsonPaths) {
39890
+ const segments = parseJsonPath(jp);
39891
+ if (segments.length === 0) continue;
39892
+ const hasWildcard = segments.some((segment) => segment.type === "wildcard");
39893
+ let concretePaths;
39894
+ if (hasWildcard) concretePaths = jsonPathResolve(obj, jp);
39895
+ else {
39896
+ const direct = segments.map((segment) => segment.type === "property" ? segment.key : segment.index);
39897
+ concretePaths = jsonPathResolve(obj, jp).length > 0 || typeof direct[direct.length - 1] === "string" ? [direct] : [];
39898
+ }
39899
+ for (const concretePath of concretePaths) {
39900
+ const updated = VersionFiles.applyVersionEdit(content, concretePath, version, indent, eol);
39901
+ if (updated !== void 0) {
39902
+ content = updated;
39903
+ totalChanged += 1;
39904
+ }
39905
+ }
39906
+ }
39907
+ return {
39908
+ content,
39909
+ previousValues,
39910
+ totalChanged
39911
+ };
39912
+ }
39913
+ /**
39914
+ * Compute the format-preserving edit for a single concrete path, returning
39915
+ * the updated document, or `undefined` when nothing changed.
39916
+ *
39917
+ * @remarks
39918
+ * Delegates to `jsonc-effect`'s {@link modify} + {@link applyEdits}
39919
+ * (requires `jsonc-effect >= 0.3.1`, whose edit spans touch only the target
39920
+ * value), so every other byte of the document is preserved. When the leaf
39921
+ * of a wildcard-free path does not exist, `modify` inserts the property
39922
+ * after the last sibling using the supplied formatting options — the only
39923
+ * case where the detected indent matters. A path whose parent is missing or
39924
+ * not an object cannot be navigated; the resulting modification error is
39925
+ * caught and reported as "no change" so the file is left alone.
39926
+ *
39927
+ * @param content - Current document text
39928
+ * @param concretePath - A wildcard-free `(string | number)[]` path
39929
+ * @param version - New version string
39930
+ * @param indentUnit - One indentation level, for inserted text
39931
+ * @param eol - End-of-line sequence, for inserted text
39932
+ * @returns The updated document, or `undefined` if the path was unchanged
39933
+ */
39934
+ static applyVersionEdit(content, concretePath, version, indentUnit, eol) {
39935
+ const insertSpaces = !indentUnit.includes(" ");
39936
+ const program = modify(content, [...concretePath], version, { formattingOptions: {
39937
+ insertSpaces,
39938
+ tabSize: insertSpaces ? indentUnit.length : 1,
39939
+ eol
39940
+ } }).pipe(effect.Effect.flatMap((edits) => applyEdits$1(content, edits)), effect.Effect.map((updated) => updated === content ? void 0 : updated), effect.Effect.catchTag("JsoncModificationError", () => effect.Effect.succeed(void 0)));
39941
+ return effect.Effect.runSync(program);
39942
+ }
39943
+ /**
38566
39944
  * Orchestrate the full version file update flow.
38567
39945
  *
38568
39946
  * @remarks
@@ -38588,9 +39966,8 @@ var VersionFiles = class VersionFiles {
38588
39966
  try {
38589
39967
  if (dryRun) {
38590
39968
  const content = (0, node_fs.readFileSync)(filePath, "utf-8");
38591
- const obj = JSON.parse(content);
38592
- const previousValues = jsonPaths.flatMap((jp) => jsonPathGet(obj, jp));
38593
- if (previousValues.length > 0) updates.push({
39969
+ const { previousValues, totalChanged } = VersionFiles.computeUpdate(content, jsonPaths, version);
39970
+ if (totalChanged > 0) updates.push({
38594
39971
  filePath,
38595
39972
  jsonPaths,
38596
39973
  version,
@@ -38634,9 +40011,8 @@ var VersionFiles = class VersionFiles {
38634
40011
  for (const filePath of vf.matchedFiles) try {
38635
40012
  if (dryRun) {
38636
40013
  const content = (0, node_fs.readFileSync)(filePath, "utf-8");
38637
- const obj = JSON.parse(content);
38638
- const previousValues = jsonPaths.flatMap((jp) => jsonPathGet(obj, jp));
38639
- if (previousValues.length > 0) updates.push({
40014
+ const { previousValues, totalChanged } = VersionFiles.computeUpdate(content, jsonPaths, scope.version);
40015
+ if (totalChanged > 0) updates.push({
38640
40016
  filePath,
38641
40017
  jsonPaths,
38642
40018
  version: scope.version,