toon-parser 2.2.1 → 3.0.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.
Files changed (66) hide show
  1. package/dist/core.cjs +630 -0
  2. package/dist/core.d.ts +73 -0
  3. package/dist/core.d.ts.map +1 -0
  4. package/dist/core.js +625 -0
  5. package/dist/core.js.map +1 -0
  6. package/dist/csv.cjs +16 -24
  7. package/dist/csv.d.ts +2 -1
  8. package/dist/csv.d.ts.map +1 -1
  9. package/dist/csv.js +8 -21
  10. package/dist/csv.js.map +1 -1
  11. package/dist/html.cjs +9 -7
  12. package/dist/html.d.ts +4 -2
  13. package/dist/html.d.ts.map +1 -1
  14. package/dist/html.js +7 -5
  15. package/dist/html.js.map +1 -1
  16. package/dist/index.cjs +5 -816
  17. package/dist/index.d.ts +2 -51
  18. package/dist/index.d.ts.map +1 -1
  19. package/dist/index.js +5 -875
  20. package/dist/index.js.map +1 -1
  21. package/dist/inferType.cjs +39 -0
  22. package/dist/inferType.d.ts +10 -0
  23. package/dist/inferType.d.ts.map +1 -0
  24. package/dist/inferType.js +21 -0
  25. package/dist/inferType.js.map +1 -0
  26. package/dist/internal/constants.cjs +52 -0
  27. package/dist/internal/constants.d.ts +9 -0
  28. package/dist/internal/constants.d.ts.map +1 -0
  29. package/dist/internal/constants.js +15 -0
  30. package/dist/internal/constants.js.map +1 -0
  31. package/dist/internal/errors.cjs +34 -0
  32. package/dist/internal/errors.d.ts +5 -0
  33. package/dist/internal/errors.d.ts.map +1 -0
  34. package/dist/internal/errors.js +8 -0
  35. package/dist/internal/errors.js.map +1 -0
  36. package/dist/internal/primitives.cjs +304 -0
  37. package/dist/internal/primitives.d.ts +23 -0
  38. package/dist/internal/primitives.d.ts.map +1 -0
  39. package/dist/internal/primitives.js +289 -0
  40. package/dist/internal/primitives.js.map +1 -0
  41. package/dist/internal/security.cjs +118 -0
  42. package/dist/internal/security.d.ts +31 -0
  43. package/dist/internal/security.d.ts.map +1 -0
  44. package/dist/internal/security.js +87 -0
  45. package/dist/internal/security.js.map +1 -0
  46. package/dist/internal/types.cjs +16 -0
  47. package/dist/internal/types.d.ts +37 -0
  48. package/dist/internal/types.d.ts.map +1 -0
  49. package/dist/internal/types.js +2 -0
  50. package/dist/internal/types.js.map +1 -0
  51. package/dist/log.cjs +43 -17
  52. package/dist/log.d.ts +4 -3
  53. package/dist/log.d.ts.map +1 -1
  54. package/dist/log.js +47 -19
  55. package/dist/log.js.map +1 -1
  56. package/dist/url.cjs +22 -21
  57. package/dist/url.d.ts +1 -1
  58. package/dist/url.d.ts.map +1 -1
  59. package/dist/url.js +23 -29
  60. package/dist/url.js.map +1 -1
  61. package/dist/xml.cjs +7 -5
  62. package/dist/xml.d.ts +1 -1
  63. package/dist/xml.d.ts.map +1 -1
  64. package/dist/xml.js +5 -3
  65. package/dist/xml.js.map +1 -1
  66. package/package.json +40 -10
package/dist/core.cjs ADDED
@@ -0,0 +1,630 @@
1
+ "use strict";
2
+ var __defProp = Object.defineProperty;
3
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
4
+ var __getOwnPropNames = Object.getOwnPropertyNames;
5
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
6
+ var __export = (target, all) => {
7
+ for (var name in all)
8
+ __defProp(target, name, { get: all[name], enumerable: true });
9
+ };
10
+ var __copyProps = (to, from, except, desc) => {
11
+ if (from && typeof from === "object" || typeof from === "function") {
12
+ for (let key of __getOwnPropNames(from))
13
+ if (!__hasOwnProp.call(to, key) && key !== except)
14
+ __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
15
+ }
16
+ return to;
17
+ };
18
+ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
19
+ var core_exports = {};
20
+ __export(core_exports, {
21
+ ToonError: () => import_errors2.ToonError,
22
+ enforceInputLength: () => import_security2.enforceInputLength,
23
+ jsonToToon: () => jsonToToon,
24
+ toonToJson: () => toonToJson
25
+ });
26
+ module.exports = __toCommonJS(core_exports);
27
+ var import_constants = require("./internal/constants.cjs");
28
+ var import_errors = require("./internal/errors.cjs");
29
+ var import_security = require("./internal/security.cjs");
30
+ var import_primitives = require("./internal/primitives.cjs");
31
+ var import_errors2 = require("./internal/errors.cjs");
32
+ var import_security2 = require("./internal/security.cjs");
33
+ function jsonToToon(value, options = {}) {
34
+ const indentSize = options.indent ?? 2;
35
+ if (!Number.isInteger(indentSize) || indentSize <= 0) {
36
+ throw new import_errors.ToonError("Indent must be a positive integer.");
37
+ }
38
+ const delimiter = options.delimiter ?? import_constants.DEFAULT_DELIMITER;
39
+ const limits = (0, import_security.applyLimits)(options);
40
+ const state = { nodes: 0 };
41
+ const lines = [];
42
+ const indentUnit = " ".repeat(indentSize);
43
+ const keyFolding = options.keyFolding ?? "off";
44
+ const flattenDepth = options.flattenDepth ?? Infinity;
45
+ const tryFoldChain = (firstKey, firstValue, siblingKeys) => {
46
+ if (keyFolding !== "safe") return null;
47
+ if (!import_constants.IDENTIFIER_SEGMENT_RE.test(firstKey)) return null;
48
+ if (limits.disallowedKeys.includes(firstKey)) return null;
49
+ const segments = [firstKey];
50
+ const valuesAtEachStep = [firstValue];
51
+ let current = firstValue;
52
+ while ((0, import_security.isPlainObject)(current) && Object.keys(current).length === 1) {
53
+ const onlyKey = Object.keys(current)[0];
54
+ if (!import_constants.IDENTIFIER_SEGMENT_RE.test(onlyKey)) break;
55
+ if (limits.disallowedKeys.includes(onlyKey)) break;
56
+ const nextValue = current[onlyKey];
57
+ segments.push(onlyKey);
58
+ valuesAtEachStep.push(nextValue);
59
+ current = nextValue;
60
+ }
61
+ const endpointIsEmptyObject = (0, import_security.isPlainObject)(current) && Object.keys(current).length === 0;
62
+ const endpointFoldable = (0, import_security.isPrimitive)(current) || Array.isArray(current) || current instanceof Date || endpointIsEmptyObject;
63
+ if (!endpointFoldable) return null;
64
+ if (segments.length < 2) return null;
65
+ const cap = Math.max(2, Math.min(segments.length, flattenDepth));
66
+ const usedSegments = segments.slice(0, cap);
67
+ const leaf = valuesAtEachStep[cap - 1];
68
+ const path = usedSegments.join(".");
69
+ if (siblingKeys.has(path)) return null;
70
+ return { path, leaf };
71
+ };
72
+ const encodeValue = (input, depth, key, activeDelimiter) => {
73
+ (0, import_security.enforceLimits)(depth, limits, state);
74
+ if ((0, import_security.isPrimitive)(input)) {
75
+ const line = (0, import_primitives.primitiveLine)(key, input, indentUnit.repeat(depth), activeDelimiter, limits);
76
+ lines.push(line);
77
+ return;
78
+ }
79
+ if (Array.isArray(input)) {
80
+ encodeArray(key, input, depth, activeDelimiter);
81
+ return;
82
+ }
83
+ if ((0, import_security.isPlainObject)(input)) {
84
+ encodeObject(key, input, depth, activeDelimiter);
85
+ return;
86
+ }
87
+ if (input instanceof Date) {
88
+ const line = (0, import_primitives.primitiveLine)(key, input.toISOString(), indentUnit.repeat(depth), activeDelimiter, limits);
89
+ lines.push(line);
90
+ return;
91
+ }
92
+ throw new import_errors.ToonError(`Unsupported value type: ${typeof input}`);
93
+ };
94
+ const encodeObject = (key, obj, depth, activeDelimiter) => {
95
+ (0, import_security.enforceDepth)(depth, limits);
96
+ const entries = Object.entries(obj);
97
+ const sortedEntries = options.sortKeys ? [...entries].sort(([a], [b]) => a.localeCompare(b)) : entries;
98
+ const prefix = indentUnit.repeat(depth);
99
+ if (key !== null) {
100
+ (0, import_security.validateKeySafety)(key, limits);
101
+ if (sortedEntries.length === 0) {
102
+ lines.push(`${prefix}${(0, import_primitives.encodeKey)(key, activeDelimiter)}:`);
103
+ return;
104
+ }
105
+ lines.push(`${prefix}${(0, import_primitives.encodeKey)(key, activeDelimiter)}:`);
106
+ } else if (depth > 0 && sortedEntries.length === 0) {
107
+ return;
108
+ }
109
+ const siblingKeys = new Set(sortedEntries.map(([k]) => k));
110
+ for (const [childKey, childValue] of sortedEntries) {
111
+ const nextDepth = key === null ? depth : depth + 1;
112
+ (0, import_security.enforceDepth)(nextDepth, limits);
113
+ const folded = tryFoldChain(childKey, childValue, siblingKeys);
114
+ if (folded) {
115
+ encodeValue(folded.leaf, nextDepth, folded.path, activeDelimiter);
116
+ } else {
117
+ encodeValue(childValue, nextDepth, childKey, activeDelimiter);
118
+ }
119
+ }
120
+ };
121
+ const encodeArray = (key, arr, depth, activeDelimiter) => {
122
+ (0, import_security.enforceDepth)(depth, limits);
123
+ if (key !== null) {
124
+ (0, import_security.validateKeySafety)(key, limits);
125
+ }
126
+ if (arr.length > limits.maxArrayLength) {
127
+ throw new import_errors.ToonError(`Array length ${arr.length} exceeds limit ${limits.maxArrayLength}.`);
128
+ }
129
+ const prefix = indentUnit.repeat(depth);
130
+ const headerKey = key === null ? "" : (0, import_primitives.encodeKey)(key, activeDelimiter);
131
+ if (arr.every(import_security.isPrimitive)) {
132
+ const encoded = arr.map((v) => (0, import_primitives.encodePrimitive)(v, activeDelimiter, activeDelimiter)).join(activeDelimiter);
133
+ const spacing = arr.length > 0 ? " " : "";
134
+ lines.push(`${prefix}${headerKey}[${arr.length}]:${spacing}${encoded}`);
135
+ (0, import_security.bumpNodes)(state, limits, arr.length);
136
+ return;
137
+ }
138
+ const tabular = (0, import_security.detectTabular)(arr);
139
+ if (tabular) {
140
+ const { fields, rows } = tabular;
141
+ const encodedFields = fields.map((f) => (0, import_primitives.encodeKey)(f, activeDelimiter)).join(activeDelimiter);
142
+ lines.push(`${prefix}${headerKey}[${arr.length}]{${encodedFields}}:`);
143
+ for (const row of rows) {
144
+ const rowValues = fields.map((f) => (0, import_primitives.encodePrimitive)(row[f], activeDelimiter, activeDelimiter)).join(activeDelimiter);
145
+ lines.push(`${indentUnit.repeat(depth + 1)}${rowValues}`);
146
+ }
147
+ (0, import_security.bumpNodes)(state, limits, arr.length * fields.length);
148
+ return;
149
+ }
150
+ lines.push(`${prefix}${headerKey}[${arr.length}]:`);
151
+ const itemIndent = depth + 1;
152
+ const itemPrefix = indentUnit.repeat(itemIndent);
153
+ for (const item of arr) {
154
+ (0, import_security.enforceDepth)(itemIndent, limits);
155
+ if ((0, import_security.isPrimitive)(item)) {
156
+ lines.push(`${itemPrefix}- ${(0, import_primitives.encodePrimitive)(item, activeDelimiter, activeDelimiter)}`);
157
+ (0, import_security.bumpNodes)(state, limits, 1);
158
+ } else if (Array.isArray(item)) {
159
+ (0, import_security.bumpNodes)(state, limits, 1);
160
+ const inline = item.every(import_security.isPrimitive);
161
+ if (inline) {
162
+ const encoded = item.map((v) => (0, import_primitives.encodePrimitive)(v, activeDelimiter, activeDelimiter)).join(activeDelimiter);
163
+ const spacing = item.length > 0 ? " " : "";
164
+ lines.push(`${itemPrefix}- [${item.length}]:${spacing}${encoded}`);
165
+ (0, import_security.bumpNodes)(state, limits, item.length);
166
+ } else {
167
+ lines.push(`${itemPrefix}-`);
168
+ encodeArray(null, item, itemIndent + 1, activeDelimiter);
169
+ }
170
+ } else if ((0, import_security.isPlainObject)(item)) {
171
+ (0, import_security.bumpNodes)(state, limits, 1);
172
+ const objEntries = Object.entries(item);
173
+ if (objEntries.length === 0) {
174
+ lines.push(`${itemPrefix}-`);
175
+ continue;
176
+ }
177
+ lines.push(`${itemPrefix}-`);
178
+ for (const [childKey, childValue] of objEntries) {
179
+ encodeValue(childValue, itemIndent + 1, childKey, activeDelimiter);
180
+ }
181
+ } else if (item instanceof Date) {
182
+ lines.push(`${itemPrefix}- ${(0, import_primitives.encodePrimitive)(item.toISOString(), activeDelimiter, activeDelimiter)}`);
183
+ (0, import_security.bumpNodes)(state, limits, 1);
184
+ } else {
185
+ throw new import_errors.ToonError(`Unsupported array item type: ${typeof item}`);
186
+ }
187
+ }
188
+ };
189
+ encodeValue(value, 0, null, delimiter);
190
+ return lines.join("\n");
191
+ }
192
+ function toonToJson(text, options = {}) {
193
+ const limits = (0, import_security.applyLimits)(options);
194
+ if (text.length > limits.maxInputLength) {
195
+ throw new import_errors.ToonError(`Input length ${text.length} exceeds limit ${limits.maxInputLength}.`);
196
+ }
197
+ const strict = options.strict ?? true;
198
+ const expandPaths = options.expandPaths ?? "off";
199
+ const delimiterFallback = import_constants.DEFAULT_DELIMITER;
200
+ const trySplitDottedKey = (key, lineNo) => {
201
+ if (expandPaths !== "safe") return null;
202
+ if (!key.includes(".")) return null;
203
+ const segments = key.split(".");
204
+ for (const seg of segments) {
205
+ if (!import_constants.IDENTIFIER_SEGMENT_RE.test(seg)) return null;
206
+ if (limits.disallowedKeys.includes(seg)) {
207
+ throw new import_errors.ToonError(
208
+ `Disallowed key segment "${seg}" in expanded path "${key}".`,
209
+ lineNo
210
+ );
211
+ }
212
+ }
213
+ return segments;
214
+ };
215
+ const assignExpandedPath = (target, segments, value, lineNo) => {
216
+ let cursor = target;
217
+ for (let i = 0; i < segments.length - 1; i++) {
218
+ const seg = segments[i];
219
+ const existing2 = cursor[seg];
220
+ if (existing2 === void 0) {
221
+ const next2 = (0, import_security.createSafeObject)();
222
+ cursor[seg] = next2;
223
+ cursor = next2;
224
+ continue;
225
+ }
226
+ if ((0, import_security.isPlainObject)(existing2)) {
227
+ cursor = existing2;
228
+ continue;
229
+ }
230
+ if (strict) {
231
+ throw new import_errors.ToonError(
232
+ `Expansion conflict at path "${segments.slice(0, i + 1).join(".")}" (object vs primitive).`,
233
+ lineNo
234
+ );
235
+ }
236
+ const next = (0, import_security.createSafeObject)();
237
+ cursor[seg] = next;
238
+ cursor = next;
239
+ }
240
+ const leafKey = segments[segments.length - 1];
241
+ const existing = cursor[leafKey];
242
+ if (existing === void 0) {
243
+ cursor[leafKey] = value;
244
+ return;
245
+ }
246
+ if ((0, import_security.isPlainObject)(existing) && (0, import_security.isPlainObject)(value)) {
247
+ Object.assign(existing, value);
248
+ return;
249
+ }
250
+ if (strict) {
251
+ throw new import_errors.ToonError(`Expansion conflict at path "${segments.join(".")}".`, lineNo);
252
+ }
253
+ cursor[leafKey] = value;
254
+ };
255
+ const lines = text.split(/\r?\n/);
256
+ const contexts = [];
257
+ let rootContainer = null;
258
+ let indentStep = null;
259
+ let root = null;
260
+ const state = { nodes: 0 };
261
+ const finalizeContainer = (container, lineNo) => {
262
+ if (container.type === "tabular") {
263
+ if (strict && container.value.length !== container.expectedLength) {
264
+ throw new import_errors.ToonError(
265
+ `Tabular array length mismatch: expected ${container.expectedLength}, got ${container.value.length}.`,
266
+ lineNo
267
+ );
268
+ }
269
+ } else if (container.type === "list") {
270
+ if (strict && container.expectedLength !== null && container.value.length !== container.expectedLength) {
271
+ throw new import_errors.ToonError(`List length mismatch: expected ${container.expectedLength}, got ${container.value.length}.`, lineNo);
272
+ }
273
+ } else if (container.type === "placeholder") {
274
+ if (!container.filled) {
275
+ container.assign((0, import_security.createSafeObject)());
276
+ container.filled = true;
277
+ }
278
+ }
279
+ };
280
+ const attachValue = (value, parent, key, lineNo) => {
281
+ const ensureRootObject = () => {
282
+ if (rootContainer) {
283
+ return rootContainer;
284
+ }
285
+ const obj = { type: "object", value: (0, import_security.createSafeObject)(), indent: 0 };
286
+ rootContainer = obj;
287
+ root = obj.value;
288
+ contexts.push(obj);
289
+ return obj;
290
+ };
291
+ if (!parent) {
292
+ if (key !== null) {
293
+ const target = ensureRootObject();
294
+ const segments = trySplitDottedKey(key, lineNo);
295
+ if (segments) {
296
+ assignExpandedPath(target.value, segments, value, lineNo);
297
+ return;
298
+ }
299
+ if (expandPaths === "safe" && Object.prototype.hasOwnProperty.call(target.value, key)) {
300
+ assignExpandedPath(target.value, [key], value, lineNo);
301
+ return;
302
+ }
303
+ target.value[key] = value;
304
+ return;
305
+ }
306
+ if (root !== null) {
307
+ throw new import_errors.ToonError("Multiple root values detected.", lineNo);
308
+ }
309
+ root = value;
310
+ return;
311
+ }
312
+ if (parent.type === "object") {
313
+ if (key === null) {
314
+ throw new import_errors.ToonError("Missing key for object assignment.");
315
+ }
316
+ const segments = trySplitDottedKey(key, lineNo);
317
+ if (segments) {
318
+ assignExpandedPath(parent.value, segments, value, lineNo);
319
+ return;
320
+ }
321
+ if (expandPaths === "safe" && Object.prototype.hasOwnProperty.call(parent.value, key)) {
322
+ assignExpandedPath(parent.value, [key], value, lineNo);
323
+ return;
324
+ }
325
+ parent.value[key] = value;
326
+ return;
327
+ }
328
+ if (parent.type === "list") {
329
+ parent.value.push(value);
330
+ return;
331
+ }
332
+ if (parent.type === "placeholder") {
333
+ if (parent.filled) {
334
+ throw new import_errors.ToonError("List item already filled.", lineNo);
335
+ }
336
+ parent.assign(value);
337
+ parent.filled = true;
338
+ return;
339
+ }
340
+ throw new import_errors.ToonError("Invalid parent container.");
341
+ };
342
+ const parseArrayHeader = (token, lineNo) => {
343
+ const match = token.match(/^\[(\d+)([,\|\t])?\](\{(.+)\})?$/);
344
+ if (!match) {
345
+ throw new import_errors.ToonError(`Invalid array header "${token}".`, lineNo);
346
+ }
347
+ const length = parseInt(match[1], 10);
348
+ if (!Number.isFinite(length)) {
349
+ throw new import_errors.ToonError("Invalid array length.", lineNo);
350
+ }
351
+ const delimiter = match[2] ?? delimiterFallback;
352
+ const fieldsRaw = match[4];
353
+ if (fieldsRaw === void 0) {
354
+ return { length, delimiter };
355
+ }
356
+ const fields = (0, import_primitives.splitDelimited)(fieldsRaw, delimiter, lineNo).map((f) => (0, import_primitives.decodeKey)(f, lineNo));
357
+ if (fields.length === 0 && strict) {
358
+ throw new import_errors.ToonError("Tabular arrays require at least one field.", lineNo);
359
+ }
360
+ return { length, delimiter, fields };
361
+ };
362
+ const processKeyValueLine = (indentLevel, keyToken, valueToken, lineNo, parent) => {
363
+ const { rawKey, header } = (0, import_primitives.splitKeyHeader)(keyToken);
364
+ const key = rawKey === "" ? null : (0, import_primitives.decodeKey)(rawKey, lineNo);
365
+ if (key !== null) {
366
+ (0, import_security.validateKeySafety)(key, limits, lineNo);
367
+ }
368
+ if (header) {
369
+ const { length, delimiter, fields } = parseArrayHeader(header, lineNo);
370
+ if (length > limits.maxArrayLength) {
371
+ throw new import_errors.ToonError(`Array length ${length} exceeds limit ${limits.maxArrayLength}.`, lineNo);
372
+ }
373
+ if (fields) {
374
+ if (valueToken !== "") {
375
+ throw new import_errors.ToonError("Tabular array header must not have inline values.", lineNo);
376
+ }
377
+ const arr = [];
378
+ (0, import_security.bumpNodes)(state, limits, 1, lineNo);
379
+ attachValue(arr, parent, key, lineNo);
380
+ contexts.push({
381
+ type: "tabular",
382
+ value: arr,
383
+ indent: indentLevel + 1,
384
+ expectedLength: length,
385
+ delimiter,
386
+ fields
387
+ });
388
+ return;
389
+ }
390
+ if (valueToken === "") {
391
+ const arr = [];
392
+ (0, import_security.bumpNodes)(state, limits, 1, lineNo);
393
+ attachValue(arr, parent, key, lineNo);
394
+ contexts.push({
395
+ type: "list",
396
+ value: arr,
397
+ indent: indentLevel + 1,
398
+ expectedLength: length,
399
+ delimiter
400
+ });
401
+ return;
402
+ }
403
+ const values = (0, import_primitives.splitDelimited)(valueToken, delimiter, lineNo).map((t) => (0, import_primitives.parsePrimitiveToken)(t, delimiter, lineNo, strict));
404
+ if (strict && values.length !== length) {
405
+ throw new import_errors.ToonError(`Inline array length mismatch: expected ${length}, got ${values.length}.`, lineNo);
406
+ }
407
+ attachValue(values, parent, key, lineNo);
408
+ (0, import_security.bumpNodes)(state, limits, values.length, lineNo);
409
+ return;
410
+ }
411
+ if (valueToken === "") {
412
+ const obj = (0, import_security.createSafeObject)();
413
+ attachValue(obj, parent, key, lineNo);
414
+ contexts.push({ type: "object", value: obj, indent: indentLevel + 1 });
415
+ return;
416
+ }
417
+ const value = (0, import_primitives.parsePrimitiveToken)(valueToken, delimiterFallback, lineNo, strict);
418
+ attachValue(value, parent, key, lineNo);
419
+ (0, import_security.bumpNodes)(state, limits, 1, lineNo);
420
+ };
421
+ lines.forEach((rawLine, index) => {
422
+ const lineNo = index + 1;
423
+ const trimmedEnd = rawLine.replace(/[ \t]+$/, "");
424
+ if (trimmedEnd.trim() === "") {
425
+ return;
426
+ }
427
+ const indentSpaces = (0, import_primitives.countLeadingSpaces)(trimmedEnd, lineNo);
428
+ if (indentStep === null) {
429
+ indentStep = indentSpaces === 0 ? 2 : indentSpaces;
430
+ }
431
+ if (indentSpaces % indentStep !== 0) {
432
+ throw new import_errors.ToonError(`Inconsistent indentation: expected multiples of ${indentStep} spaces.`, lineNo);
433
+ }
434
+ const indentLevel = indentSpaces / indentStep;
435
+ if (indentLevel > limits.maxDepth) {
436
+ throw new import_errors.ToonError(`Maximum depth ${limits.maxDepth} exceeded.`, lineNo);
437
+ }
438
+ const line = trimmedEnd.slice(indentSpaces);
439
+ while (true) {
440
+ const top = contexts[contexts.length - 1];
441
+ if (!top || indentLevel >= top.indent) {
442
+ break;
443
+ }
444
+ contexts.pop();
445
+ finalizeContainer(top, lineNo);
446
+ }
447
+ let handled = false;
448
+ let consumed = false;
449
+ while (!handled) {
450
+ const top = contexts[contexts.length - 1];
451
+ if (top && top.type === "tabular" && indentLevel === top.indent) {
452
+ const classification = (0, import_primitives.classifyTabularLine)(line, top.delimiter);
453
+ if (classification === "row") {
454
+ const cells = (0, import_primitives.splitDelimited)(line, top.delimiter, lineNo);
455
+ if (strict && cells.length !== top.fields.length) {
456
+ throw new import_errors.ToonError(
457
+ `Tabular row width mismatch: expected ${top.fields.length}, got ${cells.length}.`,
458
+ lineNo
459
+ );
460
+ }
461
+ const obj = (0, import_security.createSafeObject)();
462
+ top.fields.forEach((field, idx) => {
463
+ (0, import_security.validateKeySafety)(field, limits, lineNo);
464
+ const token = cells[idx] ?? "";
465
+ obj[field] = (0, import_primitives.parsePrimitiveToken)(token, top.delimiter, lineNo, strict);
466
+ (0, import_security.bumpNodes)(state, limits, 1, lineNo);
467
+ });
468
+ top.value.push(obj);
469
+ if (top.value.length > limits.maxArrayLength) {
470
+ throw new import_errors.ToonError(
471
+ `Tabular array length exceeds limit ${limits.maxArrayLength}.`,
472
+ lineNo
473
+ );
474
+ }
475
+ handled = true;
476
+ consumed = true;
477
+ break;
478
+ }
479
+ finalizeContainer(top, lineNo);
480
+ contexts.pop();
481
+ continue;
482
+ }
483
+ handled = true;
484
+ }
485
+ if (consumed) {
486
+ return;
487
+ }
488
+ const parent = contexts[contexts.length - 1];
489
+ if (parent && parent.type === "list") {
490
+ if (indentLevel !== parent.indent) {
491
+ throw new import_errors.ToonError("List items must align under their header.", lineNo);
492
+ }
493
+ parseListItem(line, parent, indentLevel, lineNo, processKeyValueLine, contexts, state, limits, strict);
494
+ return;
495
+ }
496
+ if (parent && parent.type === "placeholder") {
497
+ if (indentLevel !== parent.indent) {
498
+ throw new import_errors.ToonError('List item body must indent one level below "-".', lineNo);
499
+ }
500
+ }
501
+ if (indentLevel !== (parent ? parent.indent : 0)) {
502
+ throw new import_errors.ToonError("Unexpected indentation level.", lineNo);
503
+ }
504
+ const colonIndex = (0, import_primitives.findUnquotedColon)(line);
505
+ if (colonIndex === -1) {
506
+ if (root === null && !parent) {
507
+ const value = (0, import_primitives.parsePrimitiveToken)(line.trim(), delimiterFallback, lineNo, strict);
508
+ root = value;
509
+ (0, import_security.bumpNodes)(state, limits, 1, lineNo);
510
+ return;
511
+ }
512
+ throw new import_errors.ToonError("Expected key-value pair.", lineNo);
513
+ }
514
+ const keyToken = line.slice(0, colonIndex).trim();
515
+ const valueToken = line.slice(colonIndex + 1).trim();
516
+ if (parent && parent.type === "placeholder") {
517
+ if (!keyToken.startsWith("[")) {
518
+ if (!parent.current) {
519
+ const obj = (0, import_security.createSafeObject)();
520
+ parent.assign(obj);
521
+ parent.current = { value: obj, indent: indentLevel + 1 };
522
+ parent.filled = true;
523
+ }
524
+ const objContext = { type: "object", value: parent.current.value, indent: indentLevel + 1 };
525
+ processKeyValueLine(indentLevel, keyToken, valueToken, lineNo, objContext);
526
+ return;
527
+ }
528
+ }
529
+ processKeyValueLine(indentLevel, keyToken, valueToken, lineNo, parent);
530
+ });
531
+ while (contexts.length > 0) {
532
+ const container = contexts.pop();
533
+ finalizeContainer(container, lines.length);
534
+ }
535
+ if (root === null) {
536
+ return (0, import_security.createSafeObject)();
537
+ }
538
+ return root;
539
+ }
540
+ function parseListItem(line, list, indentLevel, lineNo, processKeyValueLine, contexts, state, limits, strict) {
541
+ const trimmed = line.trim();
542
+ if (!trimmed.startsWith("-")) {
543
+ throw new import_errors.ToonError('List items must start with "-".', lineNo);
544
+ }
545
+ const content = trimmed.slice(1).trim();
546
+ if (content === "") {
547
+ const placeholder = {
548
+ type: "placeholder",
549
+ indent: indentLevel + 1,
550
+ filled: false,
551
+ current: void 0,
552
+ assign: function(value2) {
553
+ if (this.current && typeof value2 === "object" && value2 !== null && !Array.isArray(value2) && Object.keys(value2).length === 0) {
554
+ return;
555
+ }
556
+ list.value.push(value2);
557
+ }
558
+ };
559
+ contexts.push(placeholder);
560
+ return;
561
+ }
562
+ if (content.startsWith("[")) {
563
+ const colonIndex2 = (0, import_primitives.findUnquotedColon)(content);
564
+ const headerToken = colonIndex2 === -1 ? content : content.slice(0, colonIndex2).trim();
565
+ const valueToken = colonIndex2 === -1 ? "" : content.slice(colonIndex2 + 1).trim();
566
+ const { length, delimiter, fields } = (0, import_primitives.parseArrayHeaderFromList)(headerToken, lineNo);
567
+ if (length > limits.maxArrayLength) {
568
+ throw new import_errors.ToonError(`Array length ${length} exceeds limit ${limits.maxArrayLength}.`, lineNo);
569
+ }
570
+ if (fields) {
571
+ if (valueToken !== "") {
572
+ throw new import_errors.ToonError("Tabular header in list item cannot have inline values.", lineNo);
573
+ }
574
+ const arr = [];
575
+ (0, import_security.bumpNodes)(state, limits, 1, lineNo);
576
+ list.value.push(arr);
577
+ contexts.push({
578
+ type: "tabular",
579
+ value: arr,
580
+ indent: indentLevel + 1,
581
+ expectedLength: length,
582
+ delimiter,
583
+ fields
584
+ });
585
+ return;
586
+ }
587
+ if (valueToken === "") {
588
+ const arr = [];
589
+ (0, import_security.bumpNodes)(state, limits, 1, lineNo);
590
+ list.value.push(arr);
591
+ contexts.push({
592
+ type: "list",
593
+ value: arr,
594
+ indent: indentLevel + 1,
595
+ expectedLength: length,
596
+ delimiter
597
+ });
598
+ return;
599
+ }
600
+ const values = (0, import_primitives.splitDelimited)(valueToken, delimiter, lineNo).map((t) => (0, import_primitives.parsePrimitiveToken)(t, delimiter, lineNo, strict));
601
+ if (strict && values.length !== length) {
602
+ throw new import_errors.ToonError(`Inline array length mismatch: expected ${length}, got ${values.length}.`, lineNo);
603
+ }
604
+ (0, import_security.bumpNodes)(state, limits, values.length, lineNo);
605
+ list.value.push(values);
606
+ return;
607
+ }
608
+ const colonIndex = (0, import_primitives.findUnquotedColon)(content);
609
+ if (colonIndex !== -1) {
610
+ const keyToken = content.slice(0, colonIndex).trim();
611
+ const valueToken = content.slice(colonIndex + 1).trim();
612
+ const obj = (0, import_security.createSafeObject)();
613
+ (0, import_security.bumpNodes)(state, limits, 1, lineNo);
614
+ list.value.push(obj);
615
+ const objContext = { type: "object", value: obj, indent: indentLevel + 1 };
616
+ contexts.push(objContext);
617
+ processKeyValueLine(indentLevel + 1, keyToken, valueToken, lineNo, objContext);
618
+ return;
619
+ }
620
+ const value = (0, import_primitives.parsePrimitiveToken)(content, list.delimiter, lineNo, strict);
621
+ (0, import_security.bumpNodes)(state, limits, 1, lineNo);
622
+ list.value.push(value);
623
+ }
624
+ // Annotate the CommonJS export names for ESM import in node:
625
+ 0 && (module.exports = {
626
+ ToonError,
627
+ enforceInputLength,
628
+ jsonToToon,
629
+ toonToJson
630
+ });