@effected/toml 0.1.0 → 0.2.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/README.md CHANGED
@@ -3,9 +3,9 @@
3
3
  [![npm](https://img.shields.io/npm/v/@effected%2Ftoml?label=npm&color=cb3837)](https://www.npmjs.com/package/@effected/toml)
4
4
  [![License: MIT](https://img.shields.io/badge/License-MIT-4caf50.svg)](https://opensource.org/licenses/MIT)
5
5
  [![Node.js %3E%3D24.11.0](https://img.shields.io/badge/Node.js-%3E%3D24.11.0-5fa04e.svg)](https://nodejs.org/)
6
- [![TypeScript 6.0](https://img.shields.io/badge/TypeScript-6.0-3178c6.svg)](https://www.typescriptlang.org/)
6
+ [![TypeScript 7.0](https://img.shields.io/badge/TypeScript-7.0-3178c6.svg)](https://www.typescriptlang.org/)
7
7
 
8
- Zero-dependency TOML 1.0.0 parsing, editing and formatting expressed as Effect schemas and pure functions. Parse TOML into plain values or a byte-exact linear CST, compute comment-preserving edits, format, modify by path, walk a document as a `Stream`, and decode straight into a validated domain schema.
8
+ Zero-dependency TOML 1.1.0 parsing, editing and formatting expressed as Effect schemas and pure functions. Parse TOML into plain values or a byte-exact linear CST, compute comment-preserving edits, format, modify by path, walk a document as a `Stream`, and decode straight into a validated domain schema.
9
9
 
10
10
  > **Pre-release.** This package is part of the `@effected/*` kit, in pre-`1.0.0`
11
11
  > development against a single pinned Effect v4 beta. Packages graduate to
@@ -23,7 +23,7 @@ Zero-dependency TOML 1.0.0 parsing, editing and formatting expressed as Effect s
23
23
 
24
24
  TOML is the config format behind `Cargo.toml`, `pyproject.toml` and a large share of the Rust and Python toolchains — files that people maintain by hand and comment heavily. Most JavaScript TOML libraries round-trip through a plain-object model, which loses every comment and blank line the moment you write the file back out.
25
25
 
26
- The engine here is written from scratch against the TOML 1.0.0 spec, and it is the only format package in this repo that vendors no upstream code at all. It parses into a lossless linear CST whose expression spans tile the source byte-exact, so `TomlDocument.parse(text).stringify()` reproduces the original text exactly for any valid document. There is no separate re-serialization path that can drift from the source. Edits are byte-minimal splices against that CST, so formatting and path-based modification preserve every comment and every byte you did not touch.
26
+ The engine here is written from scratch against the TOML 1.1.0 spec, and it is the only format package in this repo that vendors no upstream code at all. Parsing accepts everything 1.1 added — newlines, comments and trailing commas inside inline tables, optional seconds in times, and the `\e` and `\xHH` string escapes — while `Toml.stringify` deliberately emits 1.0 spellings, which are valid 1.1, so documents this package writes stay readable by 1.0-only consumers. It parses into a lossless linear CST whose expression spans tile the source byte-exact, so `TomlDocument.parse(text).stringify()` reproduces the original text exactly for any valid document. There is no separate re-serialization path that can drift from the source. Edits are byte-minimal splices against that CST, so formatting and path-based modification preserve every comment and every byte you did not touch.
27
27
 
28
28
  The value model is honest about TOML's types rather than flattening them into JavaScript's. Integers past ±(2^53 − 1) decode to `bigint` instead of silently losing precision, and TOML's four date-time types decode to four calendar-validated value classes instead of a `Date` that cannot represent a local time. TOML has no null, so `Toml.stringify` on a value containing `null` fails with a structured `UnsupportedValue` diagnostic naming the offending path rather than dropping the key. Every fallible entry point carries a typed error built from `TomlDiagnostic`, and nesting-depth guards on both the parse and the stringify side mean hostile input fails through that channel rather than as a stack overflow.
29
29
 
@@ -39,6 +39,8 @@ pnpm add @effected/toml effect
39
39
 
40
40
  Requires Node.js >=24.11.0. `effect` v4 is a peer dependency; the package itself adds no other runtime dependencies.
41
41
 
42
+ All `@effected/*` packages are ESM-only: the exports maps publish only `import` conditions, so `require()` — including tools that resolve in CJS mode — fails with Node's `ERR_PACKAGE_PATH_NOT_EXPORTED` rather than loading a CJS build that does not exist. Import from an ES module.
43
+
42
44
  ## Quick start
43
45
 
44
46
  Compose your schema with `Toml.schema` to decode TOML straight into a validated domain value, or reach for the pre-bound `Toml.TomlFromString` codec when you just want the plain value:
@@ -133,7 +135,7 @@ Effect.runPromise(Effect.result(Toml.stringify({ a: null }))).then(console.log);
133
135
 
134
136
  ## Conformance
135
137
 
136
- The engine runs the [toml-test](https://github.com/toml-lang/toml-test) 1.0.0 compliance corpus — every valid case and every invalid case — to a 100% pass rate with no skip list, and a differential property suite cross-checks it against an independent TOML parser. Both oracles are devDependency-only; neither reaches your runtime.
138
+ The engine runs the [toml-test](https://github.com/toml-lang/toml-test) compliance corpus — its `files-toml-1.1.0` subset, 214 valid cases and 467 invalid ones — to a 100% pass rate with no skip list, and a differential property suite cross-checks it against an independent TOML parser. Both oracles are devDependency-only; neither reaches your runtime.
137
139
 
138
140
  ## License
139
141
 
package/Toml.js CHANGED
@@ -11,6 +11,13 @@ import { Effect, Option, Schema, SchemaIssue, SchemaTransformation } from "effec
11
11
  * Options controlling stringify behavior. The only knob is `newline` —
12
12
  * omitted, it resolves to `"\n"`.
13
13
  *
14
+ * @remarks
15
+ * Stringify deliberately emits only TOML 1.0.0 spellings — seconds always
16
+ * present in times, no `\e`/`\xHH` escapes, single-line inline tables — even
17
+ * though {@link Toml.parse} accepts the full TOML 1.1.0 grammar. Every 1.0
18
+ * document is valid 1.1, so this conservative-write/liberal-read asymmetry
19
+ * keeps emitted documents readable by 1.0-only consumers.
20
+ *
14
21
  * @public
15
22
  */
16
23
  var TomlStringifyOptions = class extends Schema.Class("TomlStringifyOptions")({ newline: Schema.optionalKey(Schema.Literals(["\n", "\r\n"])) }) {};
@@ -84,7 +91,7 @@ const stringifyOrFail = (value, options) => Effect.try({
84
91
  * channels — including the hardening guards (nesting-depth caps on both
85
92
  * sides, circular-reference detection on encode) that keep malformed or
86
93
  * adversarial input on the typed channel instead of surfacing as an
87
- * unhandled defect. `parse` takes no options: TOML 1.0.0 parsing has no
94
+ * unhandled defect. `parse` takes no options: TOML 1.1.0 parsing has no
88
95
  * knobs.
89
96
  *
90
97
  * @example
@@ -103,7 +110,7 @@ const stringifyOrFail = (value, options) => Effect.try({
103
110
  var Toml = class Toml {
104
111
  constructor() {}
105
112
  /**
106
- * Parse a TOML 1.0.0 document into a plain JavaScript value: tables and
113
+ * Parse a TOML 1.1.0 document into a plain JavaScript value: tables and
107
114
  * inline tables become plain objects (`__proto__` lands as an own data
108
115
  * property), arrays become plain arrays, integers become `number` (or
109
116
  * `bigint` past 2^53) and date-times become the four `TomlDateTime`
@@ -159,6 +166,49 @@ var Toml = class Toml {
159
166
  static schema(target) {
160
167
  return Toml.TomlFromString.pipe(Schema.decodeTo(target));
161
168
  }
169
+ /**
170
+ * Bind a target schema to the TOML codec once, yielding the composed
171
+ * schema plus pre-derived `decode`/`encode` directions — the
172
+ * {@link Toml.schema} composition without the generic `Schema` machinery
173
+ * at every use site. Binds the plain form only: TOML 1.1.0 parsing on
174
+ * decode, default stringify options on encode.
175
+ *
176
+ * Both directions fail with `Schema.SchemaError`, exactly as
177
+ * `Schema.decodeEffect`/`Schema.encodeEffect` over {@link Toml.schema}
178
+ * would; the target's decoding/encoding service requirements flow through.
179
+ *
180
+ * @remarks
181
+ * Schema-producing: each call composes a fresh schema and derives both
182
+ * directions from it. Bind the result to a `const` — that single binding is
183
+ * the point.
184
+ *
185
+ * @example
186
+ * ```ts
187
+ * import { Toml } from "@effected/toml";
188
+ * import { Effect, Schema } from "effect";
189
+ *
190
+ * const Config = Schema.Struct({ name: Schema.String });
191
+ * const config = Toml.bind(Config);
192
+ *
193
+ * const program = Effect.gen(function* () {
194
+ * const value = yield* config.decode('name = "Alice"');
195
+ * const text = yield* config.encode(value);
196
+ * return [value, text] as const;
197
+ * });
198
+ * ```
199
+ *
200
+ * @param target - The domain schema decoded values must satisfy.
201
+ * @returns A {@link TomlBoundCodec} carrying the composed schema and its
202
+ * two pre-bound directions.
203
+ */
204
+ static bind(target) {
205
+ const schema = Toml.schema(target);
206
+ return {
207
+ schema,
208
+ decode: Schema.decodeEffect(schema),
209
+ encode: Schema.encodeEffect(schema)
210
+ };
211
+ }
162
212
  };
163
213
 
164
214
  //#endregion
package/TomlNode.js CHANGED
@@ -98,7 +98,7 @@ var TomlDateTimeLiteral = class extends Schema.TaggedClass()("TomlDateTimeLitera
98
98
  length: Schema.Number
99
99
  }) {};
100
100
  /**
101
- * An array value node. Heterogeneous per TOML 1.0.0; may span multiple lines
101
+ * An array value node. Heterogeneous per TOML; may span multiple lines
102
102
  * (the span covers brackets, inner newlines and inner comments).
103
103
  *
104
104
  * @public
@@ -121,7 +121,8 @@ var TomlInlineEntry = class extends Schema.TaggedClass()("TomlInlineEntry", {
121
121
  length: Schema.Number
122
122
  }) {};
123
123
  /**
124
- * An inline table value node (`{ k = v, ... }`). Single-line by grammar.
124
+ * An inline table value node (`{ k = v, ... }`). May span multiple lines
125
+ * since TOML 1.1 (the span covers braces, inner newlines and inner comments).
125
126
  *
126
127
  * @public
127
128
  */
package/index.d.ts CHANGED
@@ -17,7 +17,7 @@ type TomlLexErrorCode = typeof TomlLexErrorCode.Type;
17
17
  *
18
18
  * @public
19
19
  */
20
- declare const TomlParseErrorCode: Schema.Literals<readonly ["ExpectedKey", "ExpectedEquals", "ExpectedValue", "ExpectedNewline", "ExpectedTableHeaderClose", "UnterminatedArray", "UnterminatedInlineTable", "TrailingCommaInInlineTable", "NewlineInInlineTable", "InvalidValue", "InvalidNumber", "IntegerOutOfRange", "InvalidDateTime", "NestingDepthExceeded"]>;
20
+ declare const TomlParseErrorCode: Schema.Literals<readonly ["ExpectedKey", "ExpectedEquals", "ExpectedValue", "ExpectedNewline", "ExpectedTableHeaderClose", "UnterminatedArray", "UnterminatedInlineTable", "InvalidValue", "InvalidNumber", "IntegerOutOfRange", "InvalidDateTime", "NestingDepthExceeded"]>;
21
21
  /**
22
22
  * The union of all parser-stage error code string literals.
23
23
  *
@@ -54,7 +54,7 @@ type TomlStringifyErrorCode = typeof TomlStringifyErrorCode.Type;
54
54
  *
55
55
  * @public
56
56
  */
57
- declare const TomlErrorCode: Schema.Union<readonly [Schema.Literals<readonly ["InvalidUtf8", "UnterminatedString", "InvalidEscape", "InvalidUnicodeEscape", "ControlCharacterInString", "ControlCharacterInComment", "InvalidCharacter", "BareCarriageReturn"]>, Schema.Literals<readonly ["ExpectedKey", "ExpectedEquals", "ExpectedValue", "ExpectedNewline", "ExpectedTableHeaderClose", "UnterminatedArray", "UnterminatedInlineTable", "TrailingCommaInInlineTable", "NewlineInInlineTable", "InvalidValue", "InvalidNumber", "IntegerOutOfRange", "InvalidDateTime", "NestingDepthExceeded"]>, Schema.Literals<readonly ["DuplicateKey", "TableRedefined", "ArrayOfTablesConflict", "DottedKeyConflict", "InlineTableExtended"]>, Schema.Literals<readonly ["CircularReference", "UnsupportedValue", "IntegerOutOfRange", "NestingDepthExceeded"]>]>;
57
+ declare const TomlErrorCode: Schema.Union<readonly [Schema.Literals<readonly ["InvalidUtf8", "UnterminatedString", "InvalidEscape", "InvalidUnicodeEscape", "ControlCharacterInString", "ControlCharacterInComment", "InvalidCharacter", "BareCarriageReturn"]>, Schema.Literals<readonly ["ExpectedKey", "ExpectedEquals", "ExpectedValue", "ExpectedNewline", "ExpectedTableHeaderClose", "UnterminatedArray", "UnterminatedInlineTable", "InvalidValue", "InvalidNumber", "IntegerOutOfRange", "InvalidDateTime", "NestingDepthExceeded"]>, Schema.Literals<readonly ["DuplicateKey", "TableRedefined", "ArrayOfTablesConflict", "DottedKeyConflict", "InlineTableExtended"]>, Schema.Literals<readonly ["CircularReference", "UnsupportedValue", "IntegerOutOfRange", "NestingDepthExceeded"]>]>;
58
58
  /**
59
59
  * The union of all TOML error code string literals.
60
60
  *
@@ -62,7 +62,7 @@ declare const TomlErrorCode: Schema.Union<readonly [Schema.Literals<readonly ["I
62
62
  */
63
63
  type TomlErrorCode = typeof TomlErrorCode.Type;
64
64
  declare const TomlDiagnostic_base: Schema.Class<TomlDiagnostic, Schema.Struct<{
65
- readonly code: Schema.Union<readonly [Schema.Literals<readonly ["InvalidUtf8", "UnterminatedString", "InvalidEscape", "InvalidUnicodeEscape", "ControlCharacterInString", "ControlCharacterInComment", "InvalidCharacter", "BareCarriageReturn"]>, Schema.Literals<readonly ["ExpectedKey", "ExpectedEquals", "ExpectedValue", "ExpectedNewline", "ExpectedTableHeaderClose", "UnterminatedArray", "UnterminatedInlineTable", "TrailingCommaInInlineTable", "NewlineInInlineTable", "InvalidValue", "InvalidNumber", "IntegerOutOfRange", "InvalidDateTime", "NestingDepthExceeded"]>, Schema.Literals<readonly ["DuplicateKey", "TableRedefined", "ArrayOfTablesConflict", "DottedKeyConflict", "InlineTableExtended"]>, Schema.Literals<readonly ["CircularReference", "UnsupportedValue", "IntegerOutOfRange", "NestingDepthExceeded"]>]>;
65
+ readonly code: Schema.Union<readonly [Schema.Literals<readonly ["InvalidUtf8", "UnterminatedString", "InvalidEscape", "InvalidUnicodeEscape", "ControlCharacterInString", "ControlCharacterInComment", "InvalidCharacter", "BareCarriageReturn"]>, Schema.Literals<readonly ["ExpectedKey", "ExpectedEquals", "ExpectedValue", "ExpectedNewline", "ExpectedTableHeaderClose", "UnterminatedArray", "UnterminatedInlineTable", "InvalidValue", "InvalidNumber", "IntegerOutOfRange", "InvalidDateTime", "NestingDepthExceeded"]>, Schema.Literals<readonly ["DuplicateKey", "TableRedefined", "ArrayOfTablesConflict", "DottedKeyConflict", "InlineTableExtended"]>, Schema.Literals<readonly ["CircularReference", "UnsupportedValue", "IntegerOutOfRange", "NestingDepthExceeded"]>]>;
66
66
  readonly message: Schema.String;
67
67
  readonly offset: Schema.Number;
68
68
  readonly length: Schema.Number;
@@ -104,6 +104,13 @@ declare const TomlStringifyOptions_base: Schema.Class<TomlStringifyOptions, Sche
104
104
  * Options controlling stringify behavior. The only knob is `newline` —
105
105
  * omitted, it resolves to `"\n"`.
106
106
  *
107
+ * @remarks
108
+ * Stringify deliberately emits only TOML 1.0.0 spellings — seconds always
109
+ * present in times, no `\e`/`\xHH` escapes, single-line inline tables — even
110
+ * though {@link Toml.parse} accepts the full TOML 1.1.0 grammar. Every 1.0
111
+ * document is valid 1.1, so this conservative-write/liberal-read asymmetry
112
+ * keeps emitted documents readable by 1.0-only consumers.
113
+ *
107
114
  * @public
108
115
  */
109
116
  declare class TomlStringifyOptions extends TomlStringifyOptions_base {}
@@ -135,6 +142,22 @@ declare const TomlStringifyError_base: Schema.Class<TomlStringifyError, Schema.T
135
142
  declare class TomlStringifyError extends TomlStringifyError_base {
136
143
  get message(): string;
137
144
  }
145
+ /**
146
+ * A domain codec pre-bound to its two directions, returned by
147
+ * {@link Toml.bind}: the composed `schema` (what {@link Toml.schema} returns)
148
+ * plus `decode` and `encode` functions derived from it once, so callers need
149
+ * no generic `Schema` machinery at the use site.
150
+ *
151
+ * @public
152
+ */
153
+ interface TomlBoundCodec<T, RD = never, RE = never> {
154
+ /** The composed codec decoding a TOML `string` straight into `T`. */
155
+ readonly schema: Schema.Codec<T, string, RD, RE>;
156
+ /** Decode TOML text into a validated `T`. */
157
+ readonly decode: (text: string) => Effect.Effect<T, Schema.SchemaError, RD>;
158
+ /** Encode a `T` back to canonical TOML text. */
159
+ readonly encode: (value: T) => Effect.Effect<string, Schema.SchemaError, RE>;
160
+ }
138
161
  /**
139
162
  * Static entry points for TOML parsing, stringification and the schema
140
163
  * factories. Not instantiable.
@@ -144,7 +167,7 @@ declare class TomlStringifyError extends TomlStringifyError_base {
144
167
  * channels — including the hardening guards (nesting-depth caps on both
145
168
  * sides, circular-reference detection on encode) that keep malformed or
146
169
  * adversarial input on the typed channel instead of surfacing as an
147
- * unhandled defect. `parse` takes no options: TOML 1.0.0 parsing has no
170
+ * unhandled defect. `parse` takes no options: TOML 1.1.0 parsing has no
148
171
  * knobs.
149
172
  *
150
173
  * @example
@@ -163,7 +186,7 @@ declare class TomlStringifyError extends TomlStringifyError_base {
163
186
  declare class Toml {
164
187
  private constructor();
165
188
  /**
166
- * Parse a TOML 1.0.0 document into a plain JavaScript value: tables and
189
+ * Parse a TOML 1.1.0 document into a plain JavaScript value: tables and
167
190
  * inline tables become plain objects (`__proto__` lands as an own data
168
191
  * property), arrays become plain arrays, integers become `number` (or
169
192
  * `bigint` past 2^53) and date-times become the four `TomlDateTime`
@@ -208,6 +231,42 @@ declare class Toml {
208
231
  * {@link Toml.fromString}).
209
232
  */
210
233
  static schema<T, E, RD = never, RE = never>(target: Schema.Codec<T, E, RD, RE>): Schema.Codec<T, string, RD, RE>;
234
+ /**
235
+ * Bind a target schema to the TOML codec once, yielding the composed
236
+ * schema plus pre-derived `decode`/`encode` directions — the
237
+ * {@link Toml.schema} composition without the generic `Schema` machinery
238
+ * at every use site. Binds the plain form only: TOML 1.1.0 parsing on
239
+ * decode, default stringify options on encode.
240
+ *
241
+ * Both directions fail with `Schema.SchemaError`, exactly as
242
+ * `Schema.decodeEffect`/`Schema.encodeEffect` over {@link Toml.schema}
243
+ * would; the target's decoding/encoding service requirements flow through.
244
+ *
245
+ * @remarks
246
+ * Schema-producing: each call composes a fresh schema and derives both
247
+ * directions from it. Bind the result to a `const` — that single binding is
248
+ * the point.
249
+ *
250
+ * @example
251
+ * ```ts
252
+ * import { Toml } from "@effected/toml";
253
+ * import { Effect, Schema } from "effect";
254
+ *
255
+ * const Config = Schema.Struct({ name: Schema.String });
256
+ * const config = Toml.bind(Config);
257
+ *
258
+ * const program = Effect.gen(function* () {
259
+ * const value = yield* config.decode('name = "Alice"');
260
+ * const text = yield* config.encode(value);
261
+ * return [value, text] as const;
262
+ * });
263
+ * ```
264
+ *
265
+ * @param target - The domain schema decoded values must satisfy.
266
+ * @returns A {@link TomlBoundCodec} carrying the composed schema and its
267
+ * two pre-bound directions.
268
+ */
269
+ static bind<T, E, RD = never, RE = never>(target: Schema.Codec<T, E, RD, RE>): TomlBoundCodec<T, RD, RE>;
211
270
  }
212
271
  //#endregion
213
272
  //#region src/TomlDateTime.d.ts
@@ -385,7 +444,7 @@ declare const TomlArray_base: Schema.Class<TomlArray, Schema.TaggedStruct<"TomlA
385
444
  readonly length: Schema.Number;
386
445
  }>, {}>;
387
446
  /**
388
- * An array value node. Heterogeneous per TOML 1.0.0; may span multiple lines
447
+ * An array value node. Heterogeneous per TOML; may span multiple lines
389
448
  * (the span covers brackets, inner newlines and inner comments).
390
449
  *
391
450
  * @public
@@ -410,7 +469,8 @@ declare const TomlInlineTable_base: Schema.Class<TomlInlineTable, Schema.TaggedS
410
469
  readonly length: Schema.Number;
411
470
  }>, {}>;
412
471
  /**
413
- * An inline table value node (`{ k = v, ... }`). Single-line by grammar.
472
+ * An inline table value node (`{ k = v, ... }`). May span multiple lines
473
+ * since TOML 1.1 (the span covers braces, inner newlines and inner comments).
414
474
  *
415
475
  * @public
416
476
  */
@@ -893,5 +953,5 @@ declare class TomlVisitor {
893
953
  static visit(text: string): Stream.Stream<TomlVisitorEvent, TomlParseError>;
894
954
  }
895
955
  //#endregion
896
- export { Toml, TomlArray, TomlArrayTableHeader, TomlBoolean, TomlDateTimeLiteral, TomlDiagnostic, TomlDocument, TomlEdit, TomlErrorCode, TomlExpression, TomlFloat, TomlFormat, TomlFormattingOptions, TomlInlineEntry, TomlInlineTable, TomlInteger, TomlKey, TomlKeyKind, TomlKeyValue, TomlLexErrorCode, TomlLocalDate, TomlLocalDateTime, TomlLocalTime, TomlModificationError, TomlOffsetDateTime, TomlParseError, TomlParseErrorCode, type TomlPath, TomlRange, type TomlRangeLike, type TomlSegment, TomlSemanticErrorCode, TomlString, TomlStringStyle, TomlStringifyError, TomlStringifyErrorCode, TomlStringifyOptions, TomlTableHeader, TomlTrivia, TomlValueNode, TomlVisitor, TomlVisitorEvent };
956
+ export { Toml, TomlArray, TomlArrayTableHeader, TomlBoolean, type TomlBoundCodec, TomlDateTimeLiteral, TomlDiagnostic, TomlDocument, TomlEdit, TomlErrorCode, TomlExpression, TomlFloat, TomlFormat, TomlFormattingOptions, TomlInlineEntry, TomlInlineTable, TomlInteger, TomlKey, TomlKeyKind, TomlKeyValue, TomlLexErrorCode, TomlLocalDate, TomlLocalDateTime, TomlLocalTime, TomlModificationError, TomlOffsetDateTime, TomlParseError, TomlParseErrorCode, type TomlPath, TomlRange, type TomlRangeLike, type TomlSegment, TomlSemanticErrorCode, TomlString, TomlStringStyle, TomlStringifyError, TomlStringifyErrorCode, TomlStringifyOptions, TomlTableHeader, TomlTrivia, TomlValueNode, TomlVisitor, TomlVisitorEvent };
897
957
  //# sourceMappingURL=index.d.ts.map
@@ -17,8 +17,6 @@ const TOML_PARSE_ERROR_CODES = [
17
17
  "ExpectedTableHeaderClose",
18
18
  "UnterminatedArray",
19
19
  "UnterminatedInlineTable",
20
- "TrailingCommaInInlineTable",
21
- "NewlineInInlineTable",
22
20
  "InvalidValue",
23
21
  "InvalidNumber",
24
22
  "IntegerOutOfRange",
@@ -105,8 +105,12 @@ const parseLineEnd = (source, pos) => {
105
105
  end: after
106
106
  };
107
107
  };
108
- /** Skip whitespace, newlines and comments inside array brackets. */
109
- const skipArrayGap = (source, pos) => {
108
+ /**
109
+ * Skip whitespace, newlines and comments inside array brackets or inline-table
110
+ * braces — the grammar's `ws-comment-newline`, shared by both container
111
+ * parsers since TOML 1.1 let inline tables span lines.
112
+ */
113
+ const skipValueGap = (source, pos) => {
110
114
  let i = pos;
111
115
  for (;;) {
112
116
  i = scanWhitespace(source, i);
@@ -122,12 +126,6 @@ const skipArrayGap = (source, pos) => {
122
126
  return i;
123
127
  }
124
128
  };
125
- /** Guard the inline-table single-line rule and unterminated end-of-input. */
126
- const checkInsideInlineTable = (source, pos, openPos) => {
127
- if (pos >= source.length) raise("UnterminatedInlineTable", "inline table not closed before end of input", openPos, pos - openPos);
128
- const code = source.charCodeAt(pos);
129
- if (code === LF || code === CR) raise("NewlineInInlineTable", "inline tables must fit on a single line", pos, 1);
130
- };
131
129
  /** Whether a token that classified as `number` spells a float (never called on hex/oct/bin). */
132
130
  const isFloatToken = (token) => !/^0[xob]/.test(token) && (/[.eE]/.test(token) || token.includes("inf") || token.includes("nan"));
133
131
  /** An array value starting at `[`; `depth` is this array's own nesting count. */
@@ -136,7 +134,7 @@ const parseArray = (source, openPos, depth) => {
136
134
  const items = [];
137
135
  let i = openPos + 1;
138
136
  for (;;) {
139
- i = skipArrayGap(source, i);
137
+ i = skipValueGap(source, i);
140
138
  if (i >= source.length) raise("UnterminatedArray", "array not closed before end of input", openPos, i - openPos);
141
139
  if (source.charCodeAt(i) === RIGHT_BRACKET) {
142
140
  i += 1;
@@ -144,7 +142,7 @@ const parseArray = (source, openPos, depth) => {
144
142
  }
145
143
  const item = parseValue(source, i, depth);
146
144
  items.push(item.node);
147
- i = skipArrayGap(source, item.end);
145
+ i = skipValueGap(source, item.end);
148
146
  const code = source.charCodeAt(i);
149
147
  if (code === COMMA) {
150
148
  i += 1;
@@ -166,28 +164,29 @@ const parseArray = (source, openPos, depth) => {
166
164
  end: i
167
165
  };
168
166
  };
169
- /** An inline table starting at `{`; `depth` is this table's own nesting count. */
167
+ /**
168
+ * An inline table starting at `{`; `depth` is this table's own nesting count.
169
+ * TOML 1.1 grammar: `ws-comment-newline` may sit before each key-value,
170
+ * before each separator and before the closing brace, and a trailing comma is
171
+ * allowed — but `keyval-sep` is still ws-only, so no newline may split a key
172
+ * from its `=` or the `=` from its value.
173
+ */
170
174
  const parseInlineTable = (source, openPos, depth) => {
171
175
  if (depth > 256) throw new GuardExceeded("NestingDepthExceeded", 256, depth, openPos);
172
176
  const entries = [];
173
- let i = scanWhitespace(source, openPos + 1);
174
- checkInsideInlineTable(source, i, openPos);
175
- if (source.charCodeAt(i) === RIGHT_BRACE) return {
176
- node: new TomlInlineTable({
177
- entries,
178
- offset: openPos,
179
- length: i + 1 - openPos
180
- }),
181
- end: i + 1
182
- };
177
+ let i = openPos + 1;
183
178
  for (;;) {
179
+ i = skipValueGap(source, i);
180
+ if (i >= source.length) raise("UnterminatedInlineTable", "inline table not closed before end of input", openPos, i - openPos);
181
+ if (source.charCodeAt(i) === RIGHT_BRACE) {
182
+ i += 1;
183
+ break;
184
+ }
184
185
  const entryStart = i;
185
186
  const keyPath = parseKeyPath(source, i);
186
187
  i = scanWhitespace(source, keyPath.end);
187
- checkInsideInlineTable(source, i, openPos);
188
188
  if (source.charCodeAt(i) !== EQUALS) raise("ExpectedEquals", "expected = after key", i, 1);
189
189
  i = scanWhitespace(source, i + 1);
190
- checkInsideInlineTable(source, i, openPos);
191
190
  const value = parseValue(source, i, depth);
192
191
  entries.push(new TomlInlineEntry({
193
192
  keyPath: keyPath.node,
@@ -195,20 +194,17 @@ const parseInlineTable = (source, openPos, depth) => {
195
194
  offset: entryStart,
196
195
  length: value.end - entryStart
197
196
  }));
198
- i = scanWhitespace(source, value.end);
199
- checkInsideInlineTable(source, i, openPos);
197
+ i = skipValueGap(source, value.end);
200
198
  const code = source.charCodeAt(i);
201
199
  if (code === RIGHT_BRACE) {
202
200
  i += 1;
203
201
  break;
204
202
  }
205
203
  if (code === COMMA) {
206
- const commaPos = i;
207
- i = scanWhitespace(source, i + 1);
208
- checkInsideInlineTable(source, i, openPos);
209
- if (source.charCodeAt(i) === RIGHT_BRACE) raise("TrailingCommaInInlineTable", "inline tables may not end with a trailing comma", commaPos, 1);
204
+ i += 1;
210
205
  continue;
211
206
  }
207
+ if (i >= source.length) raise("UnterminatedInlineTable", "inline table not closed before end of input", openPos, i - openPos);
212
208
  raise("UnterminatedInlineTable", "expected , or } in inline table", i, 1);
213
209
  }
214
210
  return {
@@ -17,6 +17,7 @@ const RIGHT_BRACKET = 93;
17
17
  const RIGHT_BRACE = 125;
18
18
  const LOWER_U = 117;
19
19
  const UPPER_U = 85;
20
+ const LOWER_X = 120;
20
21
  const BOM = 65279;
21
22
  const raise = (code, message, offset, length) => {
22
23
  throw new RawTomlError({
@@ -36,7 +37,7 @@ const isBareKeyChar = (code) => code >= 97 && code <= 122 || code >= 65 && code
36
37
  /** The number of code units to skip for a single leading U+FEFF BOM. */
37
38
  const skipBom = (source) => source.charCodeAt(0) === BOM ? 1 : 0;
38
39
  /**
39
- * Reject a document carrying U+FFFD REPLACEMENT CHARACTER. TOML 1.0.0 requires
40
+ * Reject a document carrying U+FFFD REPLACEMENT CHARACTER. TOML requires
40
41
  * a valid UTF-8 document, but this engine receives an already-decoded JS
41
42
  * string — after Node's lossy utf8 decode, U+FFFD is the only surviving
42
43
  * evidence of a malformed byte sequence. The grammar technically admits
@@ -106,20 +107,29 @@ const simpleEscape = (code) => {
106
107
  case 110: return "\n";
107
108
  case 102: return "\f";
108
109
  case 114: return "\r";
110
+ case 101: return "\x1B";
109
111
  case QUOTE: return "\"";
110
112
  case BACKSLASH: return "\\";
111
113
  default: return;
112
114
  }
113
115
  };
114
116
  const HEX_DIGITS = /^[0-9A-Fa-f]+$/;
117
+ const ESCAPE_LETTERS = {
118
+ 2: "x",
119
+ 4: "u",
120
+ 8: "U"
121
+ };
115
122
  /**
116
- * Decode a `\uXXXX` / `\UXXXXXXXX` escape at `backslash`. The code point must
117
- * be a Unicode scalar value: surrogates and values above U+10FFFF are errors.
123
+ * Decode a `\xHH` / `\uXXXX` / `\UXXXXXXXX` escape at `backslash`. The code
124
+ * point must be a Unicode scalar value: surrogates and values above U+10FFFF
125
+ * are errors. `\xHH` (TOML 1.1) covers U+0000–U+00FF — the escape is a
126
+ * grammar-sibling of `basic-unescaped`, so the decoded character bypasses the
127
+ * control-character ban by design.
118
128
  */
119
129
  const decodeUnicodeEscape = (source, backslash, width) => {
120
130
  const start = backslash + 2;
121
131
  const hex = source.slice(start, start + width);
122
- if (hex.length < width || !HEX_DIGITS.test(hex)) return raise("InvalidUnicodeEscape", `\\${width === 4 ? "u" : "U"} escape requires ${width} hexadecimal digits`, backslash, width + 2);
132
+ if (hex.length < width || !HEX_DIGITS.test(hex)) return raise("InvalidUnicodeEscape", `\\${ESCAPE_LETTERS[width]} escape requires ${width} hexadecimal digits`, backslash, width + 2);
123
133
  const codePoint = Number.parseInt(hex, 16);
124
134
  if (codePoint >= 55296 && codePoint <= 57343 || codePoint > 1114111) return raise("InvalidUnicodeEscape", `${codeLabel(codePoint)} is not a Unicode scalar value`, backslash, width + 2);
125
135
  return {
@@ -127,6 +137,8 @@ const decodeUnicodeEscape = (source, backslash, width) => {
127
137
  end: start + width
128
138
  };
129
139
  };
140
+ /** The escape width for a unicode-style escape letter, or undefined. */
141
+ const unicodeEscapeWidth = (code) => code === LOWER_X ? 2 : code === LOWER_U ? 4 : code === UPPER_U ? 8 : void 0;
130
142
  /** Scan a single-line basic string starting at the opening `"`. */
131
143
  const scanBasicString = (source, pos) => {
132
144
  let out = "";
@@ -143,11 +155,12 @@ const scanBasicString = (source, pos) => {
143
155
  out += source.slice(chunkStart, i);
144
156
  const next = source.charCodeAt(i + 1);
145
157
  const mapped = simpleEscape(next);
158
+ const width = unicodeEscapeWidth(next);
146
159
  if (mapped !== void 0) {
147
160
  out += mapped;
148
161
  i += 2;
149
- } else if (next === LOWER_U || next === UPPER_U) {
150
- const decoded = decodeUnicodeEscape(source, i, next === LOWER_U ? 4 : 8);
162
+ } else if (width !== void 0) {
163
+ const decoded = decodeUnicodeEscape(source, i, width);
151
164
  out += String.fromCodePoint(decoded.codePoint);
152
165
  i = decoded.end;
153
166
  } else return raise("InvalidEscape", `invalid escape sequence \\${source[i + 1] ?? ""}`, i, 2);
@@ -234,8 +247,9 @@ const scanMultilineBasicString = (source, pos) => {
234
247
  chunkStart = i;
235
248
  continue;
236
249
  }
237
- if (next === LOWER_U || next === UPPER_U) {
238
- const decoded = decodeUnicodeEscape(source, i, next === LOWER_U ? 4 : 8);
250
+ const width = unicodeEscapeWidth(next);
251
+ if (width !== void 0) {
252
+ const decoded = decodeUnicodeEscape(source, i, width);
239
253
  out += String.fromCodePoint(decoded.codePoint);
240
254
  i = decoded.end;
241
255
  chunkStart = i;
@@ -319,10 +333,10 @@ const scanTokenSpan = (source, pos) => {
319
333
  }
320
334
  return i;
321
335
  };
322
- const OFFSET_DATE_TIME = /^([0-9]{4})-([0-9]{2})-([0-9]{2})[Tt ]([0-9]{2}):([0-9]{2}):([0-9]{2})(?:\.([0-9]+))?([Zz]|[+-][0-9]{2}:[0-9]{2})$/;
323
- const LOCAL_DATE_TIME = /^([0-9]{4})-([0-9]{2})-([0-9]{2})[Tt ]([0-9]{2}):([0-9]{2}):([0-9]{2})(?:\.([0-9]+))?$/;
336
+ const OFFSET_DATE_TIME = /^([0-9]{4})-([0-9]{2})-([0-9]{2})[Tt ]([0-9]{2}):([0-9]{2})(?::([0-9]{2})(?:\.([0-9]+))?)?([Zz]|[+-][0-9]{2}:[0-9]{2})$/;
337
+ const LOCAL_DATE_TIME = /^([0-9]{4})-([0-9]{2})-([0-9]{2})[Tt ]([0-9]{2}):([0-9]{2})(?::([0-9]{2})(?:\.([0-9]+))?)?$/;
324
338
  const LOCAL_DATE = /^([0-9]{4})-([0-9]{2})-([0-9]{2})$/;
325
- const LOCAL_TIME = /^([0-9]{2}):([0-9]{2}):([0-9]{2})(?:\.([0-9]+))?$/;
339
+ const LOCAL_TIME = /^([0-9]{2}):([0-9]{2})(?::([0-9]{2})(?:\.([0-9]+))?)?$/;
326
340
  /**
327
341
  * Scan a non-string scalar value token. Stops at whitespace, newlines, `,`,
328
342
  * `]`, `}` and `#` — with the G5 extension: a token that scanned as a full
@@ -409,13 +423,13 @@ const classifyValueToken = (token, offset) => {
409
423
  const length = Math.max(token.length, 1);
410
424
  let match = OFFSET_DATE_TIME.exec(token);
411
425
  if (match !== null) {
412
- const [, y = "", mo = "", d = "", h = "", mi = "", s = "", fraction, offsetText = ""] = match;
426
+ const [, y = "", mo = "", d = "", h = "", mi = "", s, fraction, offsetText = ""] = match;
413
427
  const year = Number(y);
414
428
  const month = Number(mo);
415
429
  const day = Number(d);
416
430
  const hour = Number(h);
417
431
  const minute = Number(mi);
418
- const second = Number(s);
432
+ const second = s === void 0 ? 0 : Number(s);
419
433
  validateDate(year, month, day, offset, length);
420
434
  validateTime(hour, minute, second, offset, length);
421
435
  const offsetMinutes = decodeOffsetMinutes(offsetText, offset, length);
@@ -432,13 +446,13 @@ const classifyValueToken = (token, offset) => {
432
446
  }
433
447
  match = LOCAL_DATE_TIME.exec(token);
434
448
  if (match !== null) {
435
- const [, y = "", mo = "", d = "", h = "", mi = "", s = "", fraction] = match;
449
+ const [, y = "", mo = "", d = "", h = "", mi = "", s, fraction] = match;
436
450
  const year = Number(y);
437
451
  const month = Number(mo);
438
452
  const day = Number(d);
439
453
  const hour = Number(h);
440
454
  const minute = Number(mi);
441
- const second = Number(s);
455
+ const second = s === void 0 ? 0 : Number(s);
442
456
  validateDate(year, month, day, offset, length);
443
457
  validateTime(hour, minute, second, offset, length);
444
458
  return new TomlLocalDateTime({
@@ -466,10 +480,10 @@ const classifyValueToken = (token, offset) => {
466
480
  }
467
481
  match = LOCAL_TIME.exec(token);
468
482
  if (match !== null) {
469
- const [, h = "", mi = "", s = "", fraction] = match;
483
+ const [, h = "", mi = "", s, fraction] = match;
470
484
  const hour = Number(h);
471
485
  const minute = Number(mi);
472
- const second = Number(s);
486
+ const second = s === void 0 ? 0 : Number(s);
473
487
  validateTime(hour, minute, second, offset, length);
474
488
  return new TomlLocalTime({
475
489
  hour,
package/package.json CHANGED
@@ -1,8 +1,8 @@
1
1
  {
2
2
  "name": "@effected/toml",
3
- "version": "0.1.0",
3
+ "version": "0.2.0",
4
4
  "private": false,
5
- "description": "TOML 1.0.0 parsing, editing and formatting as Effect schemas: typed diagnostics, a lossless CST and first-class date-time values.",
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": [
7
7
  "toml",
8
8
  "parser",
@@ -32,12 +32,13 @@
32
32
  "exports": {
33
33
  ".": {
34
34
  "types": "./index.d.ts",
35
- "import": "./index.js"
35
+ "import": "./index.js",
36
+ "default": "./index.js"
36
37
  },
37
38
  "./package.json": "./package.json"
38
39
  },
39
40
  "peerDependencies": {
40
- "effect": "4.0.0-beta.98"
41
+ "effect": "4.0.0-beta.99"
41
42
  },
42
43
  "engines": {
43
44
  "node": ">=24.11.0"
@@ -5,7 +5,7 @@
5
5
  "toolPackages": [
6
6
  {
7
7
  "packageName": "@microsoft/api-extractor",
8
- "packageVersion": "7.58.9"
8
+ "packageVersion": "7.58.11"
9
9
  }
10
10
  ]
11
11
  }