@effected/toml 0.15.0 → 0.17.0

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.
package/Toml.js CHANGED
@@ -58,41 +58,47 @@ var TomlStringifyError = class extends Schema.TaggedError()("TomlStringifyError"
58
58
  }
59
59
  };
60
60
  /**
61
- * Run the parser and semantic pass, materializing the engine's raw carriers
62
- * into the typed error: `RawTomlError` becomes a positioned diagnostic and a
63
- * `GuardExceeded` depth trip becomes a `NestingDepthExceeded` diagnostic
64
- * (never an unhandled defect). Anything else is a genuine defect and rethrows
65
- * — synchronously to a `Result` caller, and as a `Die` through the `Effect`
66
- * forms, which evaluate this inside the effect.
61
+ * Materialize an engine throw into the typed {@link TomlParseError}:
62
+ * `RawTomlError` becomes a positioned diagnostic and a `GuardExceeded` depth
63
+ * trip becomes a `NestingDepthExceeded` diagnostic (never an unhandled
64
+ * defect). Anything else is a genuine defect and rethrows — synchronously to a
65
+ * `Result` caller, and as a `Die` through the `Effect` forms, which evaluate
66
+ * it inside the effect.
67
+ *
68
+ * The one mapping shared by every parse path — `Toml.parse`/`parseResult`,
69
+ * `TomlDocument.parseResult`/`toValueResult` and `TomlVisitor.visit` — so a
70
+ * given input fails identically through each of them.
71
+ *
72
+ * @internal
67
73
  */
68
- const parseToResult = (text) => {
69
- try {
70
- return Result.succeed(buildValue(parseExpressions(text)));
71
- } catch (defect) {
72
- if (isRawTomlError(defect)) return Result.fail(new TomlParseError({ diagnostics: [TomlDiagnostic.fromRaw(text, defect.diagnostic)] }));
73
- if (isGuardExceeded(defect)) return Result.fail(new TomlParseError({ diagnostics: [TomlDiagnostic.fromRaw(text, {
74
- code: "NestingDepthExceeded",
75
- message: defect.message,
76
- offset: defect.offset,
77
- length: 0
78
- })] }));
79
- throw defect;
80
- }
74
+ const toTomlParseError = (text, defect) => {
75
+ if (isRawTomlError(defect)) return new TomlParseError({ diagnostics: [TomlDiagnostic.fromRaw(text, defect.diagnostic)] });
76
+ if (isGuardExceeded(defect)) return new TomlParseError({ diagnostics: [TomlDiagnostic.fromRaw(text, {
77
+ code: "NestingDepthExceeded",
78
+ message: defect.message,
79
+ offset: defect.offset,
80
+ length: 0
81
+ })] });
82
+ throw defect;
81
83
  };
82
- const stringifyToResult = (value, options) => {
83
- try {
84
- return Result.succeed(stringifyValue(value, options?.newline ?? "\n"));
85
- } catch (defect) {
86
- if (isRawTomlError(defect)) return Result.fail(new TomlStringifyError({ diagnostic: TomlDiagnostic.fromRaw("", defect.diagnostic) }));
87
- if (isGuardExceeded(defect)) return Result.fail(new TomlStringifyError({ diagnostic: TomlDiagnostic.fromRaw("", {
84
+ /** Run the parser and semantic pass, materializing failures via {@link toTomlParseError}. */
85
+ const parseToResult = (text) => Result.try({
86
+ try: () => buildValue(parseExpressions(text)),
87
+ catch: (defect) => toTomlParseError(text, defect)
88
+ });
89
+ const stringifyToResult = (value, options) => Result.try({
90
+ try: () => stringifyValue(value, options?.newline ?? "\n"),
91
+ catch: (defect) => {
92
+ if (isRawTomlError(defect)) return new TomlStringifyError({ diagnostic: TomlDiagnostic.fromRaw("", defect.diagnostic) });
93
+ if (isGuardExceeded(defect)) return new TomlStringifyError({ diagnostic: TomlDiagnostic.fromRaw("", {
88
94
  code: "NestingDepthExceeded",
89
95
  message: defect.message,
90
96
  offset: 0,
91
97
  length: 0
92
- }) }));
98
+ }) });
93
99
  throw defect;
94
100
  }
95
- };
101
+ });
96
102
  /**
97
103
  * Static entry points for TOML parsing, stringification and the schema
98
104
  * factories. Not instantiable.
@@ -303,4 +309,4 @@ var Toml = class Toml {
303
309
  }
304
310
  };
305
311
 
306
- export { Toml, TomlParseError, TomlStringifyError, TomlStringifyOptions };
312
+ export { Toml, TomlParseError, TomlStringifyError, TomlStringifyOptions, toTomlParseError };
package/TomlDiagnostic.js CHANGED
@@ -1,5 +1,6 @@
1
1
  import { TOML_LEX_ERROR_CODES, TOML_PARSE_ERROR_CODES, TOML_SEMANTIC_ERROR_CODES, TOML_STRINGIFY_ERROR_CODES } from "./internal/diagnostics.js";
2
2
  import { Schema } from "effect";
3
+ import { LineIndex } from "@effected/source";
3
4
 
4
5
  // src/TomlDiagnostic.ts
5
6
  /**
@@ -49,6 +50,15 @@ const TomlErrorCode = Schema.Union([
49
50
  * and `@effected/yaml`'s `YamlDiagnostic`; `message` is this package's
50
51
  * additive extra.
51
52
  *
53
+ * With `message` it satisfies `@effected/source`'s `SourceDiagnostic`
54
+ * structurally, so a consumer written over that interface (an LSP server, a
55
+ * CLI printing annotations) reads it directly. `line` and `character` come
56
+ * from `@effected/source`'s `LineIndex` under its default `"lsp"` rule: a line
57
+ * ends at `\n`, `\r\n` or a bare `\r`, as an LSP client counts. TOML's own
58
+ * newline is `\n` or `\r\n` only, so a bare `\r` is invalid TOML; it is
59
+ * counted as a line break only so a diagnostic about it positions LSP-style.
60
+ * An offset at the `\n` of a `\r\n` reads as the end of its line.
61
+ *
52
62
  * @public
53
63
  */
54
64
  var TomlDiagnostic = class TomlDiagnostic extends Schema.Class("TomlDiagnostic")({
@@ -69,7 +79,7 @@ var TomlDiagnostic = class TomlDiagnostic extends Schema.Class("TomlDiagnostic")
69
79
  * @returns The diagnostic with `line` and `character` filled in.
70
80
  */
71
81
  static fromRaw(source, raw) {
72
- const { line, character } = lineChar(source, raw.offset);
82
+ const { line, character } = LineIndex.make(source).positionAt(raw.offset);
73
83
  return TomlDiagnostic.make({
74
84
  code: raw.code,
75
85
  message: raw.message,
@@ -80,30 +90,5 @@ var TomlDiagnostic = class TomlDiagnostic extends Schema.Class("TomlDiagnostic")
80
90
  });
81
91
  }
82
92
  };
83
- /**
84
- * Compute the zero-based line/character position of `offset` within `text`.
85
- * Recognizes `\n`, `\r` and `\r\n` as line breaks (TOML's newline grammar);
86
- * a CRLF pair counts as a single newline.
87
- */
88
- function lineChar(text, offset) {
89
- let line = 0;
90
- let lineStart = 0;
91
- const limit = Math.min(offset, text.length);
92
- for (let i = 0; i < limit; i++) {
93
- const ch = text.charCodeAt(i);
94
- if (ch === 10) {
95
- line++;
96
- lineStart = i + 1;
97
- } else if (ch === 13) {
98
- if (i + 1 < text.length && text.charCodeAt(i + 1) === 10) i++;
99
- line++;
100
- lineStart = i + 1;
101
- }
102
- }
103
- return {
104
- line,
105
- character: offset - lineStart
106
- };
107
- }
108
93
 
109
94
  export { TomlDiagnostic, TomlErrorCode, TomlLexErrorCode, TomlParseErrorCode, TomlSemanticErrorCode, TomlStringifyErrorCode };
package/TomlDocument.js CHANGED
@@ -1,30 +1,13 @@
1
1
  import { isRawTomlError } from "./internal/diagnostics.js";
2
- import { isGuardExceeded } from "./internal/limits.js";
3
2
  import { TomlExpression } from "./TomlNode.js";
4
3
  import { parseExpressions } from "./internal/parser.js";
5
4
  import { analyze, buildValue } from "./internal/semantic.js";
6
5
  import { TomlDiagnostic } from "./TomlDiagnostic.js";
7
- import { TomlParseError } from "./Toml.js";
8
- import { Effect, Schema, SchemaIssue, SchemaTransformation } from "effect";
6
+ import { TomlParseError, toTomlParseError } from "./Toml.js";
7
+ import { Effect, Result, Schema, SchemaIssue, SchemaTransformation } from "effect";
9
8
 
10
9
  // src/TomlDocument.ts
11
10
  /**
12
- * Materialize an engine throw into the typed error: `RawTomlError` becomes a
13
- * positioned diagnostic and a `GuardExceeded` depth trip becomes a
14
- * `NestingDepthExceeded` diagnostic. Anything else is a genuine defect and
15
- * rethrows.
16
- */
17
- const materializeError = (text, defect) => {
18
- if (isRawTomlError(defect)) return new TomlParseError({ diagnostics: [TomlDiagnostic.fromRaw(text, defect.diagnostic)] });
19
- if (isGuardExceeded(defect)) return new TomlParseError({ diagnostics: [TomlDiagnostic.fromRaw(text, {
20
- code: "NestingDepthExceeded",
21
- message: defect.message,
22
- offset: defect.offset,
23
- length: 0
24
- })] });
25
- throw defect;
26
- };
27
- /**
28
11
  * A parsed TOML document that never loses a byte: the `source` text, the
29
12
  * linear {@link (TomlExpression:type)} CST whose spans tile the source
30
13
  * exactly, and
@@ -61,36 +44,64 @@ var TomlDocument = class TomlDocument extends Schema.Class("TomlDocument")({
61
44
  expressions: Schema.Array(TomlExpression),
62
45
  diagnostics: Schema.Array(TomlDiagnostic)
63
46
  }) {
47
+ /**
48
+ * Synchronous {@link TomlDocument.parse}, returning a `Result` instead of
49
+ * an `Effect` — the primitive `parse` is defined in terms of, so a
50
+ * synchronous host can call it without an Effect runtime. Fails with
51
+ * {@link TomlParseError} only on lex/parse errors — including a
52
+ * nesting-depth bomb, which surfaces as a `NestingDepthExceeded`
53
+ * diagnostic, never a throw. Semantic violations do not fail: they land in
54
+ * `diagnostics` as data (first violation wins, so there is at most one
55
+ * today; the array shape is the contract).
56
+ *
57
+ * @example
58
+ * ```ts
59
+ * import { TomlDocument } from "@effected/toml";
60
+ * import { Result } from "effect";
61
+ *
62
+ * const result = TomlDocument.parseResult('name = "Alice"\n');
63
+ * if (Result.isSuccess(result)) {
64
+ * console.log(result.success.stringify()); // => 'name = "Alice"\n'
65
+ * }
66
+ * ```
67
+ *
68
+ * @param text - The TOML source to parse.
69
+ * @returns A `Result` succeeding with the {@link TomlDocument}, or failing
70
+ * with {@link TomlParseError}.
71
+ */
72
+ static parseResult(text) {
73
+ return Result.map(Result.try({
74
+ try: () => parseExpressions(text),
75
+ catch: (defect) => toTomlParseError(text, defect)
76
+ }), (expressions) => {
77
+ const diagnostics = [];
78
+ try {
79
+ analyze(expressions);
80
+ } catch (defect) {
81
+ if (!isRawTomlError(defect)) throw defect;
82
+ diagnostics.push(TomlDiagnostic.fromRaw(text, defect.diagnostic));
83
+ }
84
+ return TomlDocument.make({
85
+ source: text,
86
+ expressions,
87
+ diagnostics
88
+ });
89
+ });
90
+ }
64
91
  /**
65
92
  * Parse TOML text into a lossless document. Fails with
66
93
  * {@link TomlParseError} only on lex/parse errors — including a
67
94
  * nesting-depth bomb, which surfaces as a `NestingDepthExceeded`
68
95
  * diagnostic, never an unhandled defect. Semantic violations do not fail:
69
- * they land in `diagnostics` as data (first violation wins, so there is at
70
- * most one today; the array shape is the contract).
96
+ * they land in `diagnostics` as data. Defined in terms of
97
+ * {@link TomlDocument.parseResult} — synchronous callers can use that
98
+ * variant directly.
71
99
  *
72
100
  * @param text - The TOML source to parse.
73
101
  * @returns An `Effect` that succeeds with the {@link TomlDocument}, or fails
74
102
  * with {@link TomlParseError}.
75
103
  */
76
- static parse = Effect.fn("TomlDocument.parse")(function* (text) {
77
- const expressions = yield* Effect.try({
78
- try: () => parseExpressions(text),
79
- catch: (defect) => materializeError(text, defect)
80
- });
81
- const diagnostics = [];
82
- try {
83
- analyze(expressions);
84
- } catch (defect) {
85
- if (!isRawTomlError(defect)) throw defect;
86
- diagnostics.push(TomlDiagnostic.fromRaw(text, defect.diagnostic));
87
- }
88
- return TomlDocument.make({
89
- source: text,
90
- expressions,
91
- diagnostics
92
- });
93
- });
104
+ static parse = Effect.fn("TomlDocument.parse")((text) => Effect.fromResult(TomlDocument.parseResult(text)));
94
105
  /**
95
106
  * A `Schema<TomlDocument, string>` decoding TOML text into a full document
96
107
  * (source, expressions, diagnostics) and encoding a document back to its
@@ -111,13 +122,43 @@ var TomlDocument = class TomlDocument extends Schema.Class("TomlDocument")({
111
122
  * {@link TomlParseError} carrying the stored `diagnostics` when the parse
112
123
  * recorded semantic violations; otherwise builds the value from the
113
124
  * expression list (already validated, so the defensive materialization
114
- * wrapper is belt-and-suspenders).
125
+ * wrapper is belt-and-suspenders). Defined in terms of
126
+ * {@link TomlDocument.toValueResult} — synchronous callers can use that
127
+ * variant directly.
128
+ *
129
+ * @returns An `Effect` that succeeds with the plain value, or fails with
130
+ * {@link TomlParseError}.
115
131
  */
116
132
  toValue() {
117
- if (this.diagnostics.length > 0) return Effect.fail(new TomlParseError({ diagnostics: this.diagnostics }));
118
- return Effect.try({
133
+ return Effect.suspend(() => Effect.fromResult(this.toValueResult()));
134
+ }
135
+ /**
136
+ * Synchronous {@link TomlDocument.toValue}, returning a `Result` — the
137
+ * second half of a synchronous document round-trip that starts at
138
+ * {@link TomlDocument.parseResult}. Fails with {@link TomlParseError}
139
+ * carrying the stored `diagnostics` when the parse recorded semantic
140
+ * violations.
141
+ *
142
+ * @example
143
+ * ```ts
144
+ * import { TomlDocument } from "@effected/toml";
145
+ * import { Result } from "effect";
146
+ *
147
+ * const parsed = TomlDocument.parseResult("a = 1\na = 2\n");
148
+ * if (Result.isSuccess(parsed)) {
149
+ * const value = parsed.success.toValueResult();
150
+ * console.log(Result.isFailure(value)); // => true — DuplicateKey
151
+ * }
152
+ * ```
153
+ *
154
+ * @returns A `Result` succeeding with the plain value, or failing with
155
+ * {@link TomlParseError}.
156
+ */
157
+ toValueResult() {
158
+ if (this.diagnostics.length > 0) return Result.fail(new TomlParseError({ diagnostics: this.diagnostics }));
159
+ return Result.try({
119
160
  try: () => buildValue(this.expressions),
120
- catch: (defect) => materializeError(this.source, defect)
161
+ catch: (defect) => toTomlParseError(this.source, defect)
121
162
  });
122
163
  }
123
164
  /**
package/TomlFormat.js CHANGED
@@ -7,7 +7,7 @@ import { renderInlineValue, renderKey } from "./internal/stringifyValue.js";
7
7
  import { TomlDiagnostic } from "./TomlDiagnostic.js";
8
8
  import { TomlDocument } from "./TomlDocument.js";
9
9
  import { TomlEdit } from "./TomlEdit.js";
10
- import { Effect, Schema } from "effect";
10
+ import { Effect, Result, Schema } from "effect";
11
11
 
12
12
  // src/TomlFormat.ts
13
13
  /**
@@ -579,7 +579,9 @@ var TomlFormat = class TomlFormat {
579
579
  }
580
580
  /**
581
581
  * Compute the edits that replace, delete, or insert a value at `path`,
582
- * resolved through the document's semantic view. Every segment but the
582
+ * synchronously — the primitive {@link TomlFormat.modify} is defined in
583
+ * terms of, so a synchronous host can call it without an Effect runtime.
584
+ * Paths resolve through the document's semantic view. Every segment but the
583
585
  * last must resolve — intermediate tables are never auto-created. A
584
586
  * `value` of `undefined` deletes: a key-value's whole line, an inline
585
587
  * entry, or an array item, splicing separators. A new key inserts per the
@@ -596,22 +598,24 @@ var TomlFormat = class TomlFormat {
596
598
  * target instead.
597
599
  * @param options - Optional {@link TomlFormattingOptions}; `newline` overrides
598
600
  * the newline used for inserted lines.
599
- * @returns An `Effect` that succeeds with the edits to apply (via
600
- * `TomlEdit.applyAll`), or fails with {@link TomlParseError} when the
601
+ * @returns A `Result` succeeding with the edits to apply (via
602
+ * `TomlEdit.applyAll`), or failing with {@link TomlParseError} when the
601
603
  * source does not parse, or {@link TomlModificationError} when `path`
602
604
  * cannot be resolved or the insertion target is not allowed.
603
605
  */
604
- static modify = Effect.fn("TomlFormat.modify")(function* (text, path, value, options) {
605
- const failWith = (code, message, offset = 0, length = 0) => new TomlModificationError({ diagnostic: TomlDiagnostic.fromRaw(text, {
606
+ static modifyResult(text, path, value, options) {
607
+ const failWith = (code, message, offset = 0, length = 0) => Result.fail(new TomlModificationError({ diagnostic: TomlDiagnostic.fromRaw(text, {
606
608
  code,
607
609
  message,
608
610
  offset,
609
611
  length
610
- }) });
611
- if (path.length === 0) return yield* failWith("DottedKeyConflict", "an empty path does not address a value");
612
- if (path.length > 256) return yield* failWith("NestingDepthExceeded", `path depth ${path.length} exceeds the ${256} cap`);
613
- const doc = yield* TomlDocument.parse(text);
614
- if (doc.diagnostics.length > 0) return yield* new TomlModificationError({ diagnostic: doc.diagnostics[0] });
612
+ }) }));
613
+ if (path.length === 0) return failWith("DottedKeyConflict", "an empty path does not address a value");
614
+ if (path.length > 256) return failWith("NestingDepthExceeded", `path depth ${path.length} exceeds the ${256} cap`);
615
+ const parsed = TomlDocument.parseResult(text);
616
+ if (Result.isFailure(parsed)) return Result.fail(parsed.failure);
617
+ const doc = parsed.success;
618
+ if (doc.diagnostics.length > 0) return Result.fail(new TomlModificationError({ diagnostic: doc.diagnostics[0] }));
615
619
  const { root, sections } = buildSemanticIndex(doc.expressions);
616
620
  const ctx = {
617
621
  source: text,
@@ -627,20 +631,73 @@ var TomlFormat = class TomlFormat {
627
631
  for (let i = 0; i < path.length - 1; i++) cursor = step(cursor, path[i]);
628
632
  raw = terminal(cursor, path[path.length - 1], value, ctx);
629
633
  } catch (defect) {
630
- if (defect instanceof ModifyFailure) return yield* failWith(defect.code, defect.message, defect.offset, defect.len);
631
- if (isRawTomlError(defect)) return yield* new TomlModificationError({ diagnostic: TomlDiagnostic.fromRaw(text, defect.diagnostic) });
632
- if (isGuardExceeded(defect)) return yield* failWith("NestingDepthExceeded", defect.message, defect.offset);
634
+ if (defect instanceof ModifyFailure) return failWith(defect.code, defect.message, defect.offset, defect.len);
635
+ if (isRawTomlError(defect)) return Result.fail(new TomlModificationError({ diagnostic: TomlDiagnostic.fromRaw(text, defect.diagnostic) }));
636
+ if (isGuardExceeded(defect)) return failWith("NestingDepthExceeded", defect.message, defect.offset);
633
637
  throw defect;
634
638
  }
635
- return raw.map((edit) => TomlEdit.make({
639
+ return Result.succeed(raw.map((edit) => TomlEdit.make({
636
640
  offset: edit.offset,
637
641
  length: edit.length,
638
642
  content: edit.newText
639
- }));
640
- });
643
+ })));
644
+ }
645
+ /**
646
+ * Compute the edits that replace, delete, or insert a value at `path`.
647
+ * Defined in terms of {@link TomlFormat.modifyResult}, which carries the
648
+ * full contract — semantic path resolution, deletion splicing and the
649
+ * pinned insertion placement rules — behind a `TomlFormat.modify` span;
650
+ * synchronous callers can use that variant directly.
651
+ *
652
+ * @param text - The TOML source to modify.
653
+ * @param path - The location to set, replace or delete.
654
+ * @param value - The plain JavaScript value to write; `undefined` deletes the
655
+ * target instead.
656
+ * @param options - Optional {@link TomlFormattingOptions}; `newline` overrides
657
+ * the newline used for inserted lines.
658
+ * @returns An `Effect` that succeeds with the edits to apply (via
659
+ * `TomlEdit.applyAll`), or fails with {@link TomlParseError} when the
660
+ * source does not parse, or {@link TomlModificationError} when `path`
661
+ * cannot be resolved or the insertion target is not allowed.
662
+ *
663
+ * @remarks
664
+ * The source is parsed through `TomlDocument.parseResult`, so this span no
665
+ * longer opens a nested `TomlDocument.parse` child span.
666
+ */
667
+ static modify = Effect.fn("TomlFormat.modify")((text, path, value, options) => Effect.fromResult(TomlFormat.modifyResult(text, path, value, options)));
668
+ /**
669
+ * Synchronous {@link TomlFormat.modifyToString}: modify `text` and apply the
670
+ * resulting edits in one step (`TomlEdit.applyAll ∘ modifyResult`),
671
+ * returning a `Result`. Inherits the {@link TomlFormat.modifyResult} error
672
+ * channel.
673
+ *
674
+ * @example
675
+ * ```ts
676
+ * import { TomlFormat } from "@effected/toml";
677
+ * import { Result } from "effect";
678
+ *
679
+ * const result = TomlFormat.modifyToStringResult("port = 3000 # dev\n", ["port"], 8080);
680
+ * if (Result.isSuccess(result)) {
681
+ * console.log(result.success); // => "port = 8080 # dev\n"
682
+ * }
683
+ * ```
684
+ *
685
+ * @param text - The TOML source to modify.
686
+ * @param path - The location to set, replace or delete.
687
+ * @param value - The plain JavaScript value to write; `undefined` deletes the
688
+ * target instead.
689
+ * @param options - Optional {@link TomlFormattingOptions}.
690
+ * @returns A `Result` succeeding with the modified text, or failing with
691
+ * {@link TomlParseError} or {@link TomlModificationError}.
692
+ */
693
+ static modifyToStringResult(text, path, value, options) {
694
+ return Result.map(TomlFormat.modifyResult(text, path, value, options), (edits) => TomlEdit.applyAll(text, edits));
695
+ }
641
696
  /**
642
697
  * Modify `text` and apply the resulting edits in one step
643
- * (`TomlEdit.applyAll ∘ modify`).
698
+ * (`TomlEdit.applyAll ∘ modify`). Defined in terms of
699
+ * {@link TomlFormat.modifyToStringResult}; it opens its own span and no
700
+ * longer a nested `TomlFormat.modify` child span.
644
701
  *
645
702
  * @param text - The TOML source to modify.
646
703
  * @param path - The location to set, replace or delete.
@@ -651,10 +708,7 @@ var TomlFormat = class TomlFormat {
651
708
  * {@link TomlParseError} or {@link TomlModificationError}, as
652
709
  * {@link TomlFormat.modify} does.
653
710
  */
654
- static modifyToString = Effect.fn("TomlFormat.modifyToString")(function* (text, path, value, options) {
655
- const edits = yield* TomlFormat.modify(text, path, value, options);
656
- return TomlEdit.applyAll(text, edits);
657
- });
711
+ static modifyToString = Effect.fn("TomlFormat.modifyToString")((text, path, value, options) => Effect.fromResult(TomlFormat.modifyToStringResult(text, path, value, options)));
658
712
  };
659
713
 
660
714
  export { TomlFormat, TomlFormattingOptions, TomlModificationError };
package/TomlVisitor.js CHANGED
@@ -1,12 +1,9 @@
1
- import { isRawTomlError } from "./internal/diagnostics.js";
2
- import { isGuardExceeded } from "./internal/limits.js";
3
1
  import { TomlKeyValue, TomlTrivia } from "./TomlNode.js";
4
2
  import { scanComment, scanNewline, scanWhitespace } from "./internal/scanner.js";
5
3
  import { parseExpressions } from "./internal/parser.js";
6
4
  import { analyze } from "./internal/semantic.js";
7
- import { TomlDiagnostic } from "./TomlDiagnostic.js";
8
- import { TomlParseError } from "./Toml.js";
9
- import { Data, Effect, Stream } from "effect";
5
+ import { toTomlParseError } from "./Toml.js";
6
+ import { Data, Effect, Result, Stream } from "effect";
10
7
 
11
8
  // src/TomlVisitor.ts
12
9
  /**
@@ -108,22 +105,14 @@ const collectEvents = (text) => {
108
105
  };
109
106
  /**
110
107
  * Run `collectEvents`, materializing the engine's raw carriers into
111
- * {@link TomlParseError} exactly like `Toml.parse` — a lex/parse error and a
112
- * semantic-pass error (e.g. `DuplicateKey`) both fail the same way.
108
+ * {@link TomlParseError} through the same `toTomlParseError` mapping
109
+ * `Toml.parse` uses — a lex/parse error and a semantic-pass error (e.g.
110
+ * `DuplicateKey`) both fail the same way.
113
111
  */
114
- const collectEventsOrFail = (text) => Effect.try({
112
+ const collectEventsOrFail = (text) => Effect.suspend(() => Effect.fromResult(Result.try({
115
113
  try: () => collectEvents(text),
116
- catch: (defect) => {
117
- if (isRawTomlError(defect)) return new TomlParseError({ diagnostics: [TomlDiagnostic.fromRaw(text, defect.diagnostic)] });
118
- if (isGuardExceeded(defect)) return new TomlParseError({ diagnostics: [TomlDiagnostic.fromRaw(text, {
119
- code: "NestingDepthExceeded",
120
- message: defect.message,
121
- offset: defect.offset,
122
- length: 0
123
- })] });
124
- throw defect;
125
- }
126
- });
114
+ catch: (defect) => toTomlParseError(text, defect)
115
+ })));
127
116
  /**
128
117
  * Walks TOML text as a `Stream` of table, array-table, key-value and comment
129
118
  * events in document order, without building a value. Not instantiable.
package/index.d.ts CHANGED
@@ -79,6 +79,15 @@ declare const TomlDiagnostic_base: Schema.Class<TomlDiagnostic, Schema.Struct<{
79
79
  * and `@effected/yaml`'s `YamlDiagnostic`; `message` is this package's
80
80
  * additive extra.
81
81
  *
82
+ * With `message` it satisfies `@effected/source`'s `SourceDiagnostic`
83
+ * structurally, so a consumer written over that interface (an LSP server, a
84
+ * CLI printing annotations) reads it directly. `line` and `character` come
85
+ * from `@effected/source`'s `LineIndex` under its default `"lsp"` rule: a line
86
+ * ends at `\n`, `\r\n` or a bare `\r`, as an LSP client counts. TOML's own
87
+ * newline is `\n` or `\r\n` only, so a bare `\r` is invalid TOML; it is
88
+ * counted as a line break only so a diagnostic about it positions LSP-style.
89
+ * An offset at the `\n` of a `\r\n` reads as the end of its line.
90
+ *
82
91
  * @public
83
92
  */
84
93
  export declare class TomlDiagnostic extends TomlDiagnostic_base {
@@ -678,13 +687,40 @@ declare const TomlDocument_base: Schema.Class<TomlDocument, Schema.Struct<{
678
687
  * @public
679
688
  */
680
689
  export declare class TomlDocument extends TomlDocument_base {
690
+ /**
691
+ * Synchronous {@link TomlDocument.parse}, returning a `Result` instead of
692
+ * an `Effect` — the primitive `parse` is defined in terms of, so a
693
+ * synchronous host can call it without an Effect runtime. Fails with
694
+ * {@link TomlParseError} only on lex/parse errors — including a
695
+ * nesting-depth bomb, which surfaces as a `NestingDepthExceeded`
696
+ * diagnostic, never a throw. Semantic violations do not fail: they land in
697
+ * `diagnostics` as data (first violation wins, so there is at most one
698
+ * today; the array shape is the contract).
699
+ *
700
+ * @example
701
+ * ```ts
702
+ * import { TomlDocument } from "@effected/toml";
703
+ * import { Result } from "effect";
704
+ *
705
+ * const result = TomlDocument.parseResult('name = "Alice"\n');
706
+ * if (Result.isSuccess(result)) {
707
+ * console.log(result.success.stringify()); // => 'name = "Alice"\n'
708
+ * }
709
+ * ```
710
+ *
711
+ * @param text - The TOML source to parse.
712
+ * @returns A `Result` succeeding with the {@link TomlDocument}, or failing
713
+ * with {@link TomlParseError}.
714
+ */
715
+ static parseResult(text: string): Result.Result<TomlDocument, TomlParseError>;
681
716
  /**
682
717
  * Parse TOML text into a lossless document. Fails with
683
718
  * {@link TomlParseError} only on lex/parse errors — including a
684
719
  * nesting-depth bomb, which surfaces as a `NestingDepthExceeded`
685
720
  * diagnostic, never an unhandled defect. Semantic violations do not fail:
686
- * they land in `diagnostics` as data (first violation wins, so there is at
687
- * most one today; the array shape is the contract).
721
+ * they land in `diagnostics` as data. Defined in terms of
722
+ * {@link TomlDocument.parseResult} — synchronous callers can use that
723
+ * variant directly.
688
724
  *
689
725
  * @param text - The TOML source to parse.
690
726
  * @returns An `Effect` that succeeds with the {@link TomlDocument}, or fails
@@ -706,9 +742,37 @@ export declare class TomlDocument extends TomlDocument_base {
706
742
  * {@link TomlParseError} carrying the stored `diagnostics` when the parse
707
743
  * recorded semantic violations; otherwise builds the value from the
708
744
  * expression list (already validated, so the defensive materialization
709
- * wrapper is belt-and-suspenders).
745
+ * wrapper is belt-and-suspenders). Defined in terms of
746
+ * {@link TomlDocument.toValueResult} — synchronous callers can use that
747
+ * variant directly.
748
+ *
749
+ * @returns An `Effect` that succeeds with the plain value, or fails with
750
+ * {@link TomlParseError}.
710
751
  */
711
752
  toValue(): Effect.Effect<unknown, TomlParseError>;
753
+ /**
754
+ * Synchronous {@link TomlDocument.toValue}, returning a `Result` — the
755
+ * second half of a synchronous document round-trip that starts at
756
+ * {@link TomlDocument.parseResult}. Fails with {@link TomlParseError}
757
+ * carrying the stored `diagnostics` when the parse recorded semantic
758
+ * violations.
759
+ *
760
+ * @example
761
+ * ```ts
762
+ * import { TomlDocument } from "@effected/toml";
763
+ * import { Result } from "effect";
764
+ *
765
+ * const parsed = TomlDocument.parseResult("a = 1\na = 2\n");
766
+ * if (Result.isSuccess(parsed)) {
767
+ * const value = parsed.success.toValueResult();
768
+ * console.log(Result.isFailure(value)); // => true — DuplicateKey
769
+ * }
770
+ * ```
771
+ *
772
+ * @returns A `Result` succeeding with the plain value, or failing with
773
+ * {@link TomlParseError}.
774
+ */
775
+ toValueResult(): Result.Result<unknown, TomlParseError>;
712
776
  /**
713
777
  * Reconstruct the document text by concatenating each expression's source
714
778
  * span in order. The expression spans tile the source exactly, so the
@@ -874,7 +938,9 @@ export declare class TomlFormat {
874
938
  static formatToString(text: string, range?: TomlRangeLike, options?: TomlFormattingOptions): string;
875
939
  /**
876
940
  * Compute the edits that replace, delete, or insert a value at `path`,
877
- * resolved through the document's semantic view. Every segment but the
941
+ * synchronously — the primitive {@link TomlFormat.modify} is defined in
942
+ * terms of, so a synchronous host can call it without an Effect runtime.
943
+ * Paths resolve through the document's semantic view. Every segment but the
878
944
  * last must resolve — intermediate tables are never auto-created. A
879
945
  * `value` of `undefined` deletes: a key-value's whole line, an inline
880
946
  * entry, or an array item, splicing separators. A new key inserts per the
@@ -891,15 +957,66 @@ export declare class TomlFormat {
891
957
  * target instead.
892
958
  * @param options - Optional {@link TomlFormattingOptions}; `newline` overrides
893
959
  * the newline used for inserted lines.
960
+ * @returns A `Result` succeeding with the edits to apply (via
961
+ * `TomlEdit.applyAll`), or failing with {@link TomlParseError} when the
962
+ * source does not parse, or {@link TomlModificationError} when `path`
963
+ * cannot be resolved or the insertion target is not allowed.
964
+ */
965
+ static modifyResult(text: string, path: TomlPath, value: unknown, options?: TomlFormattingOptions): Result.Result<ReadonlyArray<TomlEdit>, TomlParseError | TomlModificationError>;
966
+ /**
967
+ * Compute the edits that replace, delete, or insert a value at `path`.
968
+ * Defined in terms of {@link TomlFormat.modifyResult}, which carries the
969
+ * full contract — semantic path resolution, deletion splicing and the
970
+ * pinned insertion placement rules — behind a `TomlFormat.modify` span;
971
+ * synchronous callers can use that variant directly.
972
+ *
973
+ * @param text - The TOML source to modify.
974
+ * @param path - The location to set, replace or delete.
975
+ * @param value - The plain JavaScript value to write; `undefined` deletes the
976
+ * target instead.
977
+ * @param options - Optional {@link TomlFormattingOptions}; `newline` overrides
978
+ * the newline used for inserted lines.
894
979
  * @returns An `Effect` that succeeds with the edits to apply (via
895
980
  * `TomlEdit.applyAll`), or fails with {@link TomlParseError} when the
896
981
  * source does not parse, or {@link TomlModificationError} when `path`
897
982
  * cannot be resolved or the insertion target is not allowed.
983
+ *
984
+ * @remarks
985
+ * The source is parsed through `TomlDocument.parseResult`, so this span no
986
+ * longer opens a nested `TomlDocument.parse` child span.
898
987
  */
899
988
  static readonly modify: (text: string, path: TomlPath, value: unknown, options?: TomlFormattingOptions | undefined) => Effect.Effect<readonly TomlEdit[], TomlModificationError | TomlParseError, never>;
989
+ /**
990
+ * Synchronous {@link TomlFormat.modifyToString}: modify `text` and apply the
991
+ * resulting edits in one step (`TomlEdit.applyAll ∘ modifyResult`),
992
+ * returning a `Result`. Inherits the {@link TomlFormat.modifyResult} error
993
+ * channel.
994
+ *
995
+ * @example
996
+ * ```ts
997
+ * import { TomlFormat } from "@effected/toml";
998
+ * import { Result } from "effect";
999
+ *
1000
+ * const result = TomlFormat.modifyToStringResult("port = 3000 # dev\n", ["port"], 8080);
1001
+ * if (Result.isSuccess(result)) {
1002
+ * console.log(result.success); // => "port = 8080 # dev\n"
1003
+ * }
1004
+ * ```
1005
+ *
1006
+ * @param text - The TOML source to modify.
1007
+ * @param path - The location to set, replace or delete.
1008
+ * @param value - The plain JavaScript value to write; `undefined` deletes the
1009
+ * target instead.
1010
+ * @param options - Optional {@link TomlFormattingOptions}.
1011
+ * @returns A `Result` succeeding with the modified text, or failing with
1012
+ * {@link TomlParseError} or {@link TomlModificationError}.
1013
+ */
1014
+ static modifyToStringResult(text: string, path: TomlPath, value: unknown, options?: TomlFormattingOptions): Result.Result<string, TomlParseError | TomlModificationError>;
900
1015
  /**
901
1016
  * Modify `text` and apply the resulting edits in one step
902
- * (`TomlEdit.applyAll ∘ modify`).
1017
+ * (`TomlEdit.applyAll ∘ modify`). Defined in terms of
1018
+ * {@link TomlFormat.modifyToStringResult}; it opens its own span and no
1019
+ * longer a nested `TomlFormat.modify` child span.
903
1020
  *
904
1021
  * @param text - The TOML source to modify.
905
1022
  * @param path - The location to set, replace or delete.
@@ -947,125 +1064,22 @@ export type TomlVisitorEvent = Data.TaggedEnum<{
947
1064
  *
948
1065
  * @public
949
1066
  */
950
- export declare const TomlVisitorEvent: {
951
- readonly $is: <Tag extends "ArrayTableStart" | "Comment" | "KeyValue" | "TableStart">(tag: Tag) => (u: unknown) => u is Extract<{
952
- readonly _tag: "ArrayTableStart";
953
- readonly path: ReadonlyArray<string>;
954
- readonly index: number;
955
- }, {
956
- readonly _tag: Tag;
957
- }> | Extract<{
958
- readonly _tag: "Comment";
959
- readonly text: string;
960
- readonly offset: number;
961
- }, {
962
- readonly _tag: Tag;
963
- }> | Extract<{
964
- readonly _tag: "KeyValue";
965
- readonly path: ReadonlyArray<string>;
966
- readonly node: TomlKeyValue;
967
- }, {
968
- readonly _tag: Tag;
969
- }> | Extract<{
970
- readonly _tag: "TableStart";
971
- readonly path: ReadonlyArray<string>;
972
- }, {
973
- readonly _tag: Tag;
974
- }>;
975
- readonly $match: {
976
- <Cases extends {
977
- readonly ArrayTableStart: (args: {
978
- readonly _tag: "ArrayTableStart";
979
- readonly path: ReadonlyArray<string>;
980
- readonly index: number;
981
- }) => any;
982
- readonly Comment: (args: {
983
- readonly _tag: "Comment";
984
- readonly text: string;
985
- readonly offset: number;
986
- }) => any;
987
- readonly KeyValue: (args: {
988
- readonly _tag: "KeyValue";
989
- readonly path: ReadonlyArray<string>;
990
- readonly node: TomlKeyValue;
991
- }) => any;
992
- readonly TableStart: (args: {
993
- readonly _tag: "TableStart";
994
- readonly path: ReadonlyArray<string>;
995
- }) => any;
996
- }>(cases: Cases): (value: {
997
- readonly _tag: "ArrayTableStart";
998
- readonly path: ReadonlyArray<string>;
999
- readonly index: number;
1000
- } | {
1001
- readonly _tag: "Comment";
1002
- readonly text: string;
1003
- readonly offset: number;
1004
- } | {
1005
- readonly _tag: "KeyValue";
1006
- readonly path: ReadonlyArray<string>;
1007
- readonly node: TomlKeyValue;
1008
- } | {
1009
- readonly _tag: "TableStart";
1010
- readonly path: ReadonlyArray<string>;
1011
- }) => import("effect/Unify").Unify<ReturnType<Cases["ArrayTableStart" | "Comment" | "KeyValue" | "TableStart"]>>;
1012
- <Cases extends {
1013
- readonly ArrayTableStart: (args: {
1014
- readonly _tag: "ArrayTableStart";
1015
- readonly path: ReadonlyArray<string>;
1016
- readonly index: number;
1017
- }) => any;
1018
- readonly Comment: (args: {
1019
- readonly _tag: "Comment";
1020
- readonly text: string;
1021
- readonly offset: number;
1022
- }) => any;
1023
- readonly KeyValue: (args: {
1024
- readonly _tag: "KeyValue";
1025
- readonly path: ReadonlyArray<string>;
1026
- readonly node: TomlKeyValue;
1027
- }) => any;
1028
- readonly TableStart: (args: {
1029
- readonly _tag: "TableStart";
1030
- readonly path: ReadonlyArray<string>;
1031
- }) => any;
1032
- }>(value: {
1033
- readonly _tag: "ArrayTableStart";
1034
- readonly path: ReadonlyArray<string>;
1035
- readonly index: number;
1036
- } | {
1037
- readonly _tag: "Comment";
1038
- readonly text: string;
1039
- readonly offset: number;
1040
- } | {
1041
- readonly _tag: "KeyValue";
1042
- readonly path: ReadonlyArray<string>;
1043
- readonly node: TomlKeyValue;
1044
- } | {
1045
- readonly _tag: "TableStart";
1046
- readonly path: ReadonlyArray<string>;
1047
- }, cases: Cases): import("effect/Unify").Unify<ReturnType<Cases["ArrayTableStart" | "Comment" | "KeyValue" | "TableStart"]>>;
1048
- };
1049
- readonly ArrayTableStart: Data.TaggedEnum.ConstructorFrom<{
1050
- readonly _tag: "ArrayTableStart";
1051
- readonly path: ReadonlyArray<string>;
1052
- readonly index: number;
1053
- }, "_tag">;
1054
- readonly Comment: Data.TaggedEnum.ConstructorFrom<{
1055
- readonly _tag: "Comment";
1056
- readonly text: string;
1057
- readonly offset: number;
1058
- }, "_tag">;
1059
- readonly KeyValue: Data.TaggedEnum.ConstructorFrom<{
1060
- readonly _tag: "KeyValue";
1061
- readonly path: ReadonlyArray<string>;
1062
- readonly node: TomlKeyValue;
1063
- }, "_tag">;
1064
- readonly TableStart: Data.TaggedEnum.ConstructorFrom<{
1065
- readonly _tag: "TableStart";
1066
- readonly path: ReadonlyArray<string>;
1067
- }, "_tag">;
1068
- };
1067
+ export declare const TomlVisitorEvent: Data.TaggedEnum.Constructor<{
1068
+ readonly _tag: "ArrayTableStart";
1069
+ readonly path: ReadonlyArray<string>;
1070
+ readonly index: number;
1071
+ } | {
1072
+ readonly _tag: "Comment";
1073
+ readonly text: string;
1074
+ readonly offset: number;
1075
+ } | {
1076
+ readonly _tag: "KeyValue";
1077
+ readonly path: ReadonlyArray<string>;
1078
+ readonly node: TomlKeyValue;
1079
+ } | {
1080
+ readonly _tag: "TableStart";
1081
+ readonly path: ReadonlyArray<string>;
1082
+ }>;
1069
1083
  /**
1070
1084
  * Walks TOML text as a `Stream` of table, array-table, key-value and comment
1071
1085
  * events in document order, without building a value. Not instantiable.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@effected/toml",
3
- "version": "0.15.0",
3
+ "version": "0.17.0",
4
4
  "private": false,
5
5
  "description": "TOML 1.1.0 parsing, editing and formatting as Effect schemas: typed diagnostics, a lossless CST and first-class date-time values.",
6
6
  "keywords": [
@@ -37,6 +37,9 @@
37
37
  },
38
38
  "./package.json": "./package.json"
39
39
  },
40
+ "dependencies": {
41
+ "@effected/source": "^0.1.0"
42
+ },
40
43
  "peerDependencies": {
41
44
  "effect": "^4.0.0"
42
45
  },
@@ -5,7 +5,7 @@
5
5
  "toolPackages": [
6
6
  {
7
7
  "packageName": "@microsoft/api-extractor",
8
- "packageVersion": "7.59.1"
8
+ "packageVersion": "7.59.4"
9
9
  }
10
10
  ]
11
11
  }