@effected/toml 0.1.0 → 0.3.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
@@ -4,13 +4,20 @@ import { parseExpressions } from "./internal/parser.js";
4
4
  import { buildValue } from "./internal/semantic.js";
5
5
  import { stringifyValue } from "./internal/stringifyValue.js";
6
6
  import { TomlDiagnostic } from "./TomlDiagnostic.js";
7
- import { Effect, Option, Schema, SchemaIssue, SchemaTransformation } from "effect";
7
+ import { Effect, Option, Result, Schema, SchemaIssue, SchemaTransformation } from "effect";
8
8
 
9
9
  //#region src/Toml.ts
10
10
  /**
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"])) }) {};
@@ -47,34 +54,38 @@ var TomlStringifyError = class extends Schema.TaggedErrorClass()("TomlStringifyE
47
54
  * Run the parser and semantic pass, materializing the engine's raw carriers
48
55
  * into the typed error: `RawTomlError` becomes a positioned diagnostic and a
49
56
  * `GuardExceeded` depth trip becomes a `NestingDepthExceeded` diagnostic
50
- * (never an unhandled defect). Anything else is a genuine defect and rethrows.
57
+ * (never an unhandled defect). Anything else is a genuine defect and rethrows
58
+ * — synchronously to a `Result` caller, and as a `Die` through the `Effect`
59
+ * forms, which evaluate this inside the effect.
51
60
  */
52
- const parseOrFail = (text) => Effect.try({
53
- try: () => buildValue(parseExpressions(text)),
54
- catch: (defect) => {
55
- if (isRawTomlError(defect)) return new TomlParseError({ diagnostics: [TomlDiagnostic.fromRaw(text, defect.diagnostic)] });
56
- if (isGuardExceeded(defect)) return new TomlParseError({ diagnostics: [TomlDiagnostic.fromRaw(text, {
61
+ const parseToResult = (text) => {
62
+ try {
63
+ return Result.succeed(buildValue(parseExpressions(text)));
64
+ } catch (defect) {
65
+ if (isRawTomlError(defect)) return Result.fail(new TomlParseError({ diagnostics: [TomlDiagnostic.fromRaw(text, defect.diagnostic)] }));
66
+ if (isGuardExceeded(defect)) return Result.fail(new TomlParseError({ diagnostics: [TomlDiagnostic.fromRaw(text, {
57
67
  code: "NestingDepthExceeded",
58
68
  message: defect.message,
59
69
  offset: defect.offset,
60
70
  length: 0
61
- })] });
71
+ })] }));
62
72
  throw defect;
63
73
  }
64
- });
65
- const stringifyOrFail = (value, options) => Effect.try({
66
- try: () => stringifyValue(value, options?.newline ?? "\n"),
67
- catch: (defect) => {
68
- if (isRawTomlError(defect)) return new TomlStringifyError({ diagnostic: TomlDiagnostic.fromRaw("", defect.diagnostic) });
69
- if (isGuardExceeded(defect)) return new TomlStringifyError({ diagnostic: TomlDiagnostic.fromRaw("", {
74
+ };
75
+ const stringifyToResult = (value, options) => {
76
+ try {
77
+ return Result.succeed(stringifyValue(value, options?.newline ?? "\n"));
78
+ } catch (defect) {
79
+ if (isRawTomlError(defect)) return Result.fail(new TomlStringifyError({ diagnostic: TomlDiagnostic.fromRaw("", defect.diagnostic) }));
80
+ if (isGuardExceeded(defect)) return Result.fail(new TomlStringifyError({ diagnostic: TomlDiagnostic.fromRaw("", {
70
81
  code: "NestingDepthExceeded",
71
82
  message: defect.message,
72
83
  offset: 0,
73
84
  length: 0
74
- }) });
85
+ }) }));
75
86
  throw defect;
76
87
  }
77
- });
88
+ };
78
89
  /**
79
90
  * Static entry points for TOML parsing, stringification and the schema
80
91
  * factories. Not instantiable.
@@ -84,7 +95,7 @@ const stringifyOrFail = (value, options) => Effect.try({
84
95
  * channels — including the hardening guards (nesting-depth caps on both
85
96
  * sides, circular-reference detection on encode) that keep malformed or
86
97
  * 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
98
+ * unhandled defect. `parse` takes no options: TOML 1.1.0 parsing has no
88
99
  * knobs.
89
100
  *
90
101
  * @example
@@ -103,32 +114,110 @@ const stringifyOrFail = (value, options) => Effect.try({
103
114
  var Toml = class Toml {
104
115
  constructor() {}
105
116
  /**
106
- * Parse a TOML 1.0.0 document into a plain JavaScript value: tables and
107
- * inline tables become plain objects (`__proto__` lands as an own data
108
- * property), arrays become plain arrays, integers become `number` (or
109
- * `bigint` past 2^53) and date-times become the four `TomlDateTime`
110
- * classes. Fails with {@link TomlParseError} at the first violation;
111
- * returns `unknown`, never `any`.
117
+ * Parse a TOML 1.1.0 document into a plain JavaScript value, synchronously,
118
+ * returning a `Result` instead of an `Effect`: tables and inline tables
119
+ * become plain objects (`__proto__` lands as an own data property), arrays
120
+ * become plain arrays, integers become `number` (or `bigint` past 2^53) and
121
+ * date-times become the four `TomlDateTime` classes. Fails with
122
+ * {@link TomlParseError} at the first violation; returns `unknown`, never
123
+ * `any`.
112
124
  *
113
125
  * A nesting-depth bomb (arrays or inline tables past the engine cap) also
114
126
  * fails through {@link TomlParseError} with a `NestingDepthExceeded`
115
127
  * diagnostic, never as an unhandled defect.
128
+ *
129
+ * @remarks
130
+ * {@link Toml.parse} is defined in terms of this function; the two never
131
+ * diverge. Reach for the `Effect` variant inside Effect code — it carries
132
+ * the `Toml.parse` tracing span — and for this one at synchronous
133
+ * boundaries such as a lint-staged handler.
134
+ *
135
+ * @example
136
+ * ```ts
137
+ * import { Toml } from "@effected/toml";
138
+ * import { Result } from "effect";
139
+ *
140
+ * const ok = Toml.parseResult('name = "Alice"');
141
+ * if (Result.isSuccess(ok)) {
142
+ * console.log(ok.success); // => { name: "Alice" }
143
+ * }
144
+ *
145
+ * const bad = Toml.parseResult("name = ");
146
+ * if (Result.isFailure(bad)) {
147
+ * console.log(bad.failure._tag); // => "TomlParseError"
148
+ * }
149
+ * ```
150
+ *
151
+ * @param text - The TOML source to parse.
152
+ * @returns A `Result` succeeding with the decoded value (`unknown`, never
153
+ * `any`), or failing with {@link TomlParseError}.
154
+ */
155
+ static parseResult(text) {
156
+ return parseToResult(text);
157
+ }
158
+ /**
159
+ * Parse a TOML 1.1.0 document into a plain JavaScript value. Defined in
160
+ * terms of {@link Toml.parseResult} — synchronous callers can use that
161
+ * variant directly.
162
+ *
163
+ * @param text - The TOML source to parse.
164
+ * @returns An `Effect` that succeeds with the decoded value, or fails with
165
+ * {@link TomlParseError}.
116
166
  */
117
- static parse = Effect.fn("Toml.parse")(function* (text) {
118
- return yield* parseOrFail(text);
119
- });
167
+ static parse = Effect.fn("Toml.parse")((text) => Effect.fromResult(Toml.parseResult(text)));
120
168
  /**
121
- * Stringify a plain JavaScript value as a canonical TOML document: within
122
- * a table, non-table pairs first, then sub-tables as `[dotted.header]`
169
+ * Stringify a plain JavaScript value as a canonical TOML document,
170
+ * synchronously, returning a `Result` instead of an `Effect`: within a
171
+ * table, non-table pairs first, then sub-tables as `[dotted.header]`
123
172
  * sections depth-first, then arrays of tables as `[[dotted.header]]`
124
173
  * sections, a blank line before every header except at document start.
125
174
  * Fails with {@link TomlStringifyError} on unsupported values (TOML has no
126
175
  * null), out-of-int64-range `bigint`s, circular references and
127
176
  * depth-guard trips — all on the typed channel.
177
+ *
178
+ * @remarks
179
+ * {@link Toml.stringify} is defined in terms of this function; the two
180
+ * never diverge. Reach for the `Effect` variant inside Effect code — it
181
+ * carries the `Toml.stringify` tracing span — and for this one at
182
+ * synchronous boundaries.
183
+ *
184
+ * @example
185
+ * ```ts
186
+ * import { Toml } from "@effected/toml";
187
+ * import { Result } from "effect";
188
+ *
189
+ * const ok = Toml.stringifyResult({ name: "Alice" });
190
+ * if (Result.isSuccess(ok)) {
191
+ * console.log(ok.success); // => 'name = "Alice"\n'
192
+ * }
193
+ *
194
+ * const bad = Toml.stringifyResult({ nope: null });
195
+ * if (Result.isFailure(bad)) {
196
+ * console.log(bad.failure._tag); // => "TomlStringifyError"
197
+ * }
198
+ * ```
199
+ *
200
+ * @param value - The plain JavaScript value to stringify.
201
+ * @param options - Optional {@link TomlStringifyOptions}; `newline`
202
+ * defaults to `"\n"`.
203
+ * @returns A `Result` succeeding with the TOML text, or failing with
204
+ * {@link TomlStringifyError}.
205
+ */
206
+ static stringifyResult(value, options) {
207
+ return stringifyToResult(value, options);
208
+ }
209
+ /**
210
+ * Stringify a plain JavaScript value as a canonical TOML document. Defined
211
+ * in terms of {@link Toml.stringifyResult} — synchronous callers can use
212
+ * that variant directly.
213
+ *
214
+ * @param value - The plain JavaScript value to stringify.
215
+ * @param options - Optional {@link TomlStringifyOptions}; `newline`
216
+ * defaults to `"\n"`.
217
+ * @returns An `Effect` that succeeds with the TOML text, or fails with
218
+ * {@link TomlStringifyError}.
128
219
  */
129
- static stringify = Effect.fn("Toml.stringify")(function* (value, options) {
130
- return yield* stringifyOrFail(value, options);
131
- });
220
+ static stringify = Effect.fn("Toml.stringify")((value, options) => Effect.fromResult(Toml.stringifyResult(value, options)));
132
221
  /**
133
222
  * A `Schema<unknown, string>` decoding a TOML document and encoding values
134
223
  * back to canonical TOML text.
@@ -140,7 +229,7 @@ var Toml = class Toml {
140
229
  static fromString() {
141
230
  return Schema.String.pipe(Schema.decodeTo(Schema.Unknown, SchemaTransformation.transformOrFail({
142
231
  decode: (input) => Toml.parse(input).pipe(Effect.mapError((error) => new SchemaIssue.InvalidValue(Option.some(input), { message: error.message }))),
143
- encode: (value) => stringifyOrFail(value).pipe(Effect.mapError((error) => new SchemaIssue.InvalidValue(Option.some(value), { message: error.message })))
232
+ encode: (value) => Effect.fromResult(Toml.stringifyResult(value)).pipe(Effect.mapError((error) => new SchemaIssue.InvalidValue(Option.some(value), { message: error.message })))
144
233
  })));
145
234
  }
146
235
  /**
@@ -159,6 +248,49 @@ var Toml = class Toml {
159
248
  static schema(target) {
160
249
  return Toml.TomlFromString.pipe(Schema.decodeTo(target));
161
250
  }
251
+ /**
252
+ * Bind a target schema to the TOML codec once, yielding the composed
253
+ * schema plus pre-derived `decode`/`encode` directions — the
254
+ * {@link Toml.schema} composition without the generic `Schema` machinery
255
+ * at every use site. Binds the plain form only: TOML 1.1.0 parsing on
256
+ * decode, default stringify options on encode.
257
+ *
258
+ * Both directions fail with `Schema.SchemaError`, exactly as
259
+ * `Schema.decodeEffect`/`Schema.encodeEffect` over {@link Toml.schema}
260
+ * would; the target's decoding/encoding service requirements flow through.
261
+ *
262
+ * @remarks
263
+ * Schema-producing: each call composes a fresh schema and derives both
264
+ * directions from it. Bind the result to a `const` — that single binding is
265
+ * the point.
266
+ *
267
+ * @example
268
+ * ```ts
269
+ * import { Toml } from "@effected/toml";
270
+ * import { Effect, Schema } from "effect";
271
+ *
272
+ * const Config = Schema.Struct({ name: Schema.String });
273
+ * const config = Toml.bind(Config);
274
+ *
275
+ * const program = Effect.gen(function* () {
276
+ * const value = yield* config.decode('name = "Alice"');
277
+ * const text = yield* config.encode(value);
278
+ * return [value, text] as const;
279
+ * });
280
+ * ```
281
+ *
282
+ * @param target - The domain schema decoded values must satisfy.
283
+ * @returns A {@link TomlBoundCodec} carrying the composed schema and its
284
+ * two pre-bound directions.
285
+ */
286
+ static bind(target) {
287
+ const schema = Toml.schema(target);
288
+ return {
289
+ schema,
290
+ decode: Schema.decodeEffect(schema),
291
+ encode: Schema.encodeEffect(schema)
292
+ };
293
+ }
162
294
  };
163
295
 
164
296
  //#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
@@ -1,4 +1,4 @@
1
- import { Data, Effect, Schema, Stream } from "effect";
1
+ import { Data, Effect, Result, Schema, Stream } from "effect";
2
2
  //#region src/TomlDiagnostic.d.ts
3
3
  /**
4
4
  * Error codes emitted by the lexer stage.
@@ -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,26 +186,104 @@ 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
167
- * inline tables become plain objects (`__proto__` lands as an own data
168
- * property), arrays become plain arrays, integers become `number` (or
169
- * `bigint` past 2^53) and date-times become the four `TomlDateTime`
170
- * classes. Fails with {@link TomlParseError} at the first violation;
171
- * returns `unknown`, never `any`.
189
+ * Parse a TOML 1.1.0 document into a plain JavaScript value, synchronously,
190
+ * returning a `Result` instead of an `Effect`: tables and inline tables
191
+ * become plain objects (`__proto__` lands as an own data property), arrays
192
+ * become plain arrays, integers become `number` (or `bigint` past 2^53) and
193
+ * date-times become the four `TomlDateTime` classes. Fails with
194
+ * {@link TomlParseError} at the first violation; returns `unknown`, never
195
+ * `any`.
172
196
  *
173
197
  * A nesting-depth bomb (arrays or inline tables past the engine cap) also
174
198
  * fails through {@link TomlParseError} with a `NestingDepthExceeded`
175
199
  * diagnostic, never as an unhandled defect.
200
+ *
201
+ * @remarks
202
+ * {@link Toml.parse} is defined in terms of this function; the two never
203
+ * diverge. Reach for the `Effect` variant inside Effect code — it carries
204
+ * the `Toml.parse` tracing span — and for this one at synchronous
205
+ * boundaries such as a lint-staged handler.
206
+ *
207
+ * @example
208
+ * ```ts
209
+ * import { Toml } from "@effected/toml";
210
+ * import { Result } from "effect";
211
+ *
212
+ * const ok = Toml.parseResult('name = "Alice"');
213
+ * if (Result.isSuccess(ok)) {
214
+ * console.log(ok.success); // => { name: "Alice" }
215
+ * }
216
+ *
217
+ * const bad = Toml.parseResult("name = ");
218
+ * if (Result.isFailure(bad)) {
219
+ * console.log(bad.failure._tag); // => "TomlParseError"
220
+ * }
221
+ * ```
222
+ *
223
+ * @param text - The TOML source to parse.
224
+ * @returns A `Result` succeeding with the decoded value (`unknown`, never
225
+ * `any`), or failing with {@link TomlParseError}.
226
+ */
227
+ static parseResult(text: string): Result.Result<unknown, TomlParseError>;
228
+ /**
229
+ * Parse a TOML 1.1.0 document into a plain JavaScript value. Defined in
230
+ * terms of {@link Toml.parseResult} — synchronous callers can use that
231
+ * variant directly.
232
+ *
233
+ * @param text - The TOML source to parse.
234
+ * @returns An `Effect` that succeeds with the decoded value, or fails with
235
+ * {@link TomlParseError}.
176
236
  */
177
237
  static readonly parse: (text: string) => Effect.Effect<unknown, TomlParseError, never>;
178
238
  /**
179
- * Stringify a plain JavaScript value as a canonical TOML document: within
180
- * a table, non-table pairs first, then sub-tables as `[dotted.header]`
239
+ * Stringify a plain JavaScript value as a canonical TOML document,
240
+ * synchronously, returning a `Result` instead of an `Effect`: within a
241
+ * table, non-table pairs first, then sub-tables as `[dotted.header]`
181
242
  * sections depth-first, then arrays of tables as `[[dotted.header]]`
182
243
  * sections, a blank line before every header except at document start.
183
244
  * Fails with {@link TomlStringifyError} on unsupported values (TOML has no
184
245
  * null), out-of-int64-range `bigint`s, circular references and
185
246
  * depth-guard trips — all on the typed channel.
247
+ *
248
+ * @remarks
249
+ * {@link Toml.stringify} is defined in terms of this function; the two
250
+ * never diverge. Reach for the `Effect` variant inside Effect code — it
251
+ * carries the `Toml.stringify` tracing span — and for this one at
252
+ * synchronous boundaries.
253
+ *
254
+ * @example
255
+ * ```ts
256
+ * import { Toml } from "@effected/toml";
257
+ * import { Result } from "effect";
258
+ *
259
+ * const ok = Toml.stringifyResult({ name: "Alice" });
260
+ * if (Result.isSuccess(ok)) {
261
+ * console.log(ok.success); // => 'name = "Alice"\n'
262
+ * }
263
+ *
264
+ * const bad = Toml.stringifyResult({ nope: null });
265
+ * if (Result.isFailure(bad)) {
266
+ * console.log(bad.failure._tag); // => "TomlStringifyError"
267
+ * }
268
+ * ```
269
+ *
270
+ * @param value - The plain JavaScript value to stringify.
271
+ * @param options - Optional {@link TomlStringifyOptions}; `newline`
272
+ * defaults to `"\n"`.
273
+ * @returns A `Result` succeeding with the TOML text, or failing with
274
+ * {@link TomlStringifyError}.
275
+ */
276
+ static stringifyResult(value: unknown, options?: TomlStringifyOptions): Result.Result<string, TomlStringifyError>;
277
+ /**
278
+ * Stringify a plain JavaScript value as a canonical TOML document. Defined
279
+ * in terms of {@link Toml.stringifyResult} — synchronous callers can use
280
+ * that variant directly.
281
+ *
282
+ * @param value - The plain JavaScript value to stringify.
283
+ * @param options - Optional {@link TomlStringifyOptions}; `newline`
284
+ * defaults to `"\n"`.
285
+ * @returns An `Effect` that succeeds with the TOML text, or fails with
286
+ * {@link TomlStringifyError}.
186
287
  */
187
288
  static readonly stringify: (value: unknown, options?: TomlStringifyOptions | undefined) => Effect.Effect<string, TomlStringifyError, never>;
188
289
  /**
@@ -208,6 +309,42 @@ declare class Toml {
208
309
  * {@link Toml.fromString}).
209
310
  */
210
311
  static schema<T, E, RD = never, RE = never>(target: Schema.Codec<T, E, RD, RE>): Schema.Codec<T, string, RD, RE>;
312
+ /**
313
+ * Bind a target schema to the TOML codec once, yielding the composed
314
+ * schema plus pre-derived `decode`/`encode` directions — the
315
+ * {@link Toml.schema} composition without the generic `Schema` machinery
316
+ * at every use site. Binds the plain form only: TOML 1.1.0 parsing on
317
+ * decode, default stringify options on encode.
318
+ *
319
+ * Both directions fail with `Schema.SchemaError`, exactly as
320
+ * `Schema.decodeEffect`/`Schema.encodeEffect` over {@link Toml.schema}
321
+ * would; the target's decoding/encoding service requirements flow through.
322
+ *
323
+ * @remarks
324
+ * Schema-producing: each call composes a fresh schema and derives both
325
+ * directions from it. Bind the result to a `const` — that single binding is
326
+ * the point.
327
+ *
328
+ * @example
329
+ * ```ts
330
+ * import { Toml } from "@effected/toml";
331
+ * import { Effect, Schema } from "effect";
332
+ *
333
+ * const Config = Schema.Struct({ name: Schema.String });
334
+ * const config = Toml.bind(Config);
335
+ *
336
+ * const program = Effect.gen(function* () {
337
+ * const value = yield* config.decode('name = "Alice"');
338
+ * const text = yield* config.encode(value);
339
+ * return [value, text] as const;
340
+ * });
341
+ * ```
342
+ *
343
+ * @param target - The domain schema decoded values must satisfy.
344
+ * @returns A {@link TomlBoundCodec} carrying the composed schema and its
345
+ * two pre-bound directions.
346
+ */
347
+ static bind<T, E, RD = never, RE = never>(target: Schema.Codec<T, E, RD, RE>): TomlBoundCodec<T, RD, RE>;
211
348
  }
212
349
  //#endregion
213
350
  //#region src/TomlDateTime.d.ts
@@ -385,7 +522,7 @@ declare const TomlArray_base: Schema.Class<TomlArray, Schema.TaggedStruct<"TomlA
385
522
  readonly length: Schema.Number;
386
523
  }>, {}>;
387
524
  /**
388
- * An array value node. Heterogeneous per TOML 1.0.0; may span multiple lines
525
+ * An array value node. Heterogeneous per TOML; may span multiple lines
389
526
  * (the span covers brackets, inner newlines and inner comments).
390
527
  *
391
528
  * @public
@@ -410,7 +547,8 @@ declare const TomlInlineTable_base: Schema.Class<TomlInlineTable, Schema.TaggedS
410
547
  readonly length: Schema.Number;
411
548
  }>, {}>;
412
549
  /**
413
- * An inline table value node (`{ k = v, ... }`). Single-line by grammar.
550
+ * An inline table value node (`{ k = v, ... }`). May span multiple lines
551
+ * since TOML 1.1 (the span covers braces, inner newlines and inner comments).
414
552
  *
415
553
  * @public
416
554
  */
@@ -893,5 +1031,5 @@ declare class TomlVisitor {
893
1031
  static visit(text: string): Stream.Stream<TomlVisitorEvent, TomlParseError>;
894
1032
  }
895
1033
  //#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 };
1034
+ 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
1035
  //# 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.3.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
  }