@dotit/core 1.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 (59) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +229 -0
  3. package/dist/aliases.d.ts +1 -0
  4. package/dist/aliases.js +8 -0
  5. package/dist/ask.d.ts +7 -0
  6. package/dist/ask.js +55 -0
  7. package/dist/browser.d.ts +12 -0
  8. package/dist/browser.js +32 -0
  9. package/dist/diff.d.ts +17 -0
  10. package/dist/diff.js +179 -0
  11. package/dist/document-css.d.ts +1 -0
  12. package/dist/document-css.js +290 -0
  13. package/dist/executor.d.ts +40 -0
  14. package/dist/executor.js +501 -0
  15. package/dist/history.d.ts +10 -0
  16. package/dist/history.js +297 -0
  17. package/dist/html-to-it.d.ts +1 -0
  18. package/dist/html-to-it.js +288 -0
  19. package/dist/index-builder.d.ts +62 -0
  20. package/dist/index-builder.js +228 -0
  21. package/dist/index.d.ts +39 -0
  22. package/dist/index.js +94 -0
  23. package/dist/language-registry.d.ts +39 -0
  24. package/dist/language-registry.js +530 -0
  25. package/dist/markdown.d.ts +1 -0
  26. package/dist/markdown.js +123 -0
  27. package/dist/merge.d.ts +6 -0
  28. package/dist/merge.js +255 -0
  29. package/dist/parser.d.ts +29 -0
  30. package/dist/parser.js +1562 -0
  31. package/dist/query.d.ts +32 -0
  32. package/dist/query.js +293 -0
  33. package/dist/renderer.d.ts +16 -0
  34. package/dist/renderer.js +1286 -0
  35. package/dist/schema.d.ts +47 -0
  36. package/dist/schema.js +574 -0
  37. package/dist/source.d.ts +3 -0
  38. package/dist/source.js +223 -0
  39. package/dist/theme.d.ts +49 -0
  40. package/dist/theme.js +113 -0
  41. package/dist/themes/corporate.json +86 -0
  42. package/dist/themes/dark.json +64 -0
  43. package/dist/themes/editorial.json +54 -0
  44. package/dist/themes/legal.json +57 -0
  45. package/dist/themes/minimal.json +50 -0
  46. package/dist/themes/print.json +54 -0
  47. package/dist/themes/technical.json +59 -0
  48. package/dist/themes/warm.json +53 -0
  49. package/dist/trust.d.ts +66 -0
  50. package/dist/trust.js +200 -0
  51. package/dist/types.d.ts +234 -0
  52. package/dist/types.js +19 -0
  53. package/dist/utils.d.ts +2 -0
  54. package/dist/utils.js +13 -0
  55. package/dist/validate.d.ts +13 -0
  56. package/dist/validate.js +711 -0
  57. package/dist/workflow.d.ts +18 -0
  58. package/dist/workflow.js +160 -0
  59. package/package.json +51 -0
package/dist/parser.js ADDED
@@ -0,0 +1,1562 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.DEFAULT_SAFE_PARSE_OPTIONS = void 0;
4
+ exports._resetIdCounter = _resetIdCounter;
5
+ exports.detectHistoryBoundary = detectHistoryBoundary;
6
+ exports.parseIntentText = parseIntentText;
7
+ exports.parseIntentTextSafe = parseIntentTextSafe;
8
+ const types_1 = require("./types");
9
+ const aliases_1 = require("./aliases");
10
+ const language_registry_1 = require("./language-registry");
11
+ let _idCounter = 0;
12
+ function nextId() {
13
+ return `b-${++_idCounter}`;
14
+ }
15
+ function _resetIdCounter() {
16
+ _idCounter = 0;
17
+ }
18
+ const MAX_INPUT_LENGTH = 10000000;
19
+ const MAX_LINE_COUNT = 500000;
20
+ const MAX_INLINE_LENGTH = 100000;
21
+ const DANGEROUS_KEYS = new Set(["__proto__", "constructor", "prototype"]);
22
+ const AGENTIC_BLOCK_TYPES = new Set([
23
+ "step",
24
+ "decision",
25
+ "trigger",
26
+ "loop",
27
+ "checkpoint",
28
+ "audit",
29
+ "error",
30
+ "import",
31
+ "export",
32
+ "progress",
33
+ "context",
34
+ "tool",
35
+ "prompt",
36
+ "memory",
37
+ "result",
38
+ "handoff",
39
+ "wait",
40
+ "parallel",
41
+ "retry",
42
+ "gate",
43
+ "call",
44
+ "signal",
45
+ "policy",
46
+ "assert",
47
+ "secret",
48
+ ]);
49
+ const V21_BLOCK_TYPES = new Set([
50
+ "result",
51
+ "handoff",
52
+ "wait",
53
+ "parallel",
54
+ "retry",
55
+ ]);
56
+ const V22_BLOCK_TYPES = new Set(["gate", "call", "signal"]);
57
+ const DOCGEN_LAYOUT_TYPES = new Set([
58
+ "font",
59
+ "page",
60
+ "break",
61
+ "header",
62
+ "footer",
63
+ "watermark",
64
+ "signline",
65
+ ]);
66
+ const DOCGEN_WRITER_TYPES = new Set([
67
+ "byline",
68
+ "epigraph",
69
+ "caption",
70
+ "footnote",
71
+ "toc",
72
+ "dedication",
73
+ ]);
74
+ const METADATA_KEYWORDS = new Set(["agent", "model"]);
75
+ const TRUST_KEYWORDS = new Set([
76
+ "approve",
77
+ "sign",
78
+ "freeze",
79
+ "amendment",
80
+ "history",
81
+ ]);
82
+ const DOCUMENT_IDENTITY_KEYWORDS = new Set([
83
+ "title",
84
+ "summary",
85
+ "track",
86
+ ]);
87
+ const HISTORY_KEYWORDS = new Set(["revision"]);
88
+ const V211_BLOCK_TYPES = new Set([
89
+ "def",
90
+ "metric",
91
+ "amendment",
92
+ "figure",
93
+ "signline",
94
+ "contact",
95
+ "deadline",
96
+ ]);
97
+ function detectHistoryBoundary(lines) {
98
+ for (let i = 0; i < lines.length; i++) {
99
+ const trimmed = lines[i].trim();
100
+ if (trimmed === "history:" || trimmed === "history: ") {
101
+ return i;
102
+ }
103
+ if (trimmed === "---" && i < lines.length - 1) {
104
+ const next = lines[i + 1]?.trim();
105
+ if (next === "// history" || next?.startsWith("// history")) {
106
+ return i;
107
+ }
108
+ }
109
+ }
110
+ return -1;
111
+ }
112
+ function parseHistorySectionText(raw) {
113
+ const lines = raw.split("\n");
114
+ const registry = [];
115
+ const revisions = [];
116
+ for (const line of lines) {
117
+ const trimmed = line.trim();
118
+ if (!trimmed || trimmed.startsWith("//"))
119
+ continue;
120
+ if (trimmed.startsWith("revision:")) {
121
+ const content = trimmed.replace(/^revision:\s*\|?\s*/, "");
122
+ const props = Object.create(null);
123
+ const segments = content.split(" | ");
124
+ for (const seg of segments) {
125
+ const colonIdx = seg.indexOf(":");
126
+ if (colonIdx > -1) {
127
+ props[seg.slice(0, colonIdx).trim()] = seg.slice(colonIdx + 1).trim();
128
+ }
129
+ }
130
+ revisions.push({
131
+ version: props.version || "",
132
+ at: props.at || "",
133
+ by: props.by || "",
134
+ change: (props.change || "added"),
135
+ id: props.id || "",
136
+ block: props.block || "",
137
+ section: props.section,
138
+ was: props.was,
139
+ now: props.now,
140
+ wasSection: props["was-section"],
141
+ nowSection: props["now-section"],
142
+ });
143
+ }
144
+ else if (/^[a-z0-9]{5}\s*\|/.test(trimmed)) {
145
+ const parts = trimmed.split("|").map((p) => p.trim());
146
+ if (parts.length >= 4) {
147
+ registry.push({
148
+ id: parts[0],
149
+ blockType: parts[1],
150
+ section: parts[2],
151
+ fingerprint: parts[3],
152
+ dead: parts[4] === "dead" || undefined,
153
+ });
154
+ }
155
+ }
156
+ }
157
+ return { registry, revisions, raw };
158
+ }
159
+ function parseContextKeyValuePairs(rawContent) {
160
+ const result = Object.create(null);
161
+ let content = rawContent.trim();
162
+ if (content.startsWith("|")) {
163
+ content = content.substring(1).trim();
164
+ }
165
+ const pairs = splitPipeMetadata(content);
166
+ for (const pair of pairs) {
167
+ const trimmed = pair.trim();
168
+ const cleaned = trimmed.startsWith("|")
169
+ ? trimmed.substring(1).trim()
170
+ : trimmed;
171
+ const kvMatch = cleaned.match(/^([\w]+)\s*=\s*"([^"]*)"/) ||
172
+ cleaned.match(/^([\w]+)\s*=\s*(\S+)/);
173
+ if (kvMatch) {
174
+ const key = kvMatch[1];
175
+ if (!DANGEROUS_KEYS.has(key))
176
+ result[key] = kvMatch[2];
177
+ continue;
178
+ }
179
+ const pipeMatch = cleaned.match(/^([\p{L}\p{N}_][\p{L}\p{N}_-]*):\s*(.*)$/u);
180
+ if (pipeMatch) {
181
+ const key = pipeMatch[1].trim();
182
+ if (!DANGEROUS_KEYS.has(key))
183
+ result[key] = pipeMatch[2].trim();
184
+ }
185
+ }
186
+ return result;
187
+ }
188
+ function detectArabic(text) {
189
+ const arabicRegex = /[\u0600-\u06FF]/;
190
+ return arabicRegex.test(text);
191
+ }
192
+ function splitPipeMetadata(rest) {
193
+ const parts = [];
194
+ let current = "";
195
+ for (let i = 0; i < rest.length; i++) {
196
+ if (i + 2 < rest.length &&
197
+ rest[i] === " " &&
198
+ rest[i + 1] === "|" &&
199
+ rest[i + 2] === " ") {
200
+ let backslashes = 0;
201
+ for (let j = i; j - 1 >= 0 && rest[j - 1] === "\\"; j--)
202
+ backslashes++;
203
+ const escaped = backslashes % 2 === 1;
204
+ if (!escaped) {
205
+ parts.push(current);
206
+ current = "";
207
+ i += 2;
208
+ continue;
209
+ }
210
+ }
211
+ current += rest[i];
212
+ }
213
+ parts.push(current);
214
+ return parts;
215
+ }
216
+ function splitTableRow(text) {
217
+ const cells = [];
218
+ let current = "";
219
+ let escaping = false;
220
+ for (let i = 0; i < text.length; i++) {
221
+ const ch = text[i];
222
+ if (escaping) {
223
+ current += ch;
224
+ escaping = false;
225
+ continue;
226
+ }
227
+ if (ch === "\\") {
228
+ current += ch;
229
+ escaping = true;
230
+ continue;
231
+ }
232
+ if (ch === "|") {
233
+ cells.push(unescapeIntentText(current.trim()));
234
+ current = "";
235
+ continue;
236
+ }
237
+ current += ch;
238
+ }
239
+ cells.push(unescapeIntentText(current.trim()));
240
+ return cells.filter((c) => c !== "");
241
+ }
242
+ function parseInlineNodes(text) {
243
+ if (!text || text.length > MAX_INLINE_LENGTH) {
244
+ return {
245
+ content: text || "",
246
+ inline: [{ type: "text", value: text || "" }],
247
+ };
248
+ }
249
+ const inline = [];
250
+ let content = "";
251
+ let currentText = "";
252
+ const flushText = () => {
253
+ if (currentText) {
254
+ inline.push({ type: "text", value: currentText });
255
+ content += currentText;
256
+ currentText = "";
257
+ }
258
+ };
259
+ const addNode = (node) => {
260
+ flushText();
261
+ inline.push(node);
262
+ content += node.value;
263
+ };
264
+ const resolveDateToken = (token) => {
265
+ const lower = token.toLowerCase();
266
+ if (lower === "today") {
267
+ return new Date().toISOString().slice(0, 10);
268
+ }
269
+ if (lower === "tomorrow") {
270
+ const d = new Date();
271
+ d.setDate(d.getDate() + 1);
272
+ return d.toISOString().slice(0, 10);
273
+ }
274
+ if (/^\d{4}-\d{2}-\d{2}$/.test(token)) {
275
+ return token;
276
+ }
277
+ return null;
278
+ };
279
+ let i = 0;
280
+ while (i < text.length) {
281
+ if (text[i] === "[") {
282
+ const fnRefMatch = text.slice(i).match(/^\[\^(\d+)\]/);
283
+ if (fnRefMatch) {
284
+ addNode({ type: "footnote-ref", value: fnRefMatch[1] });
285
+ i += fnRefMatch[0].length;
286
+ continue;
287
+ }
288
+ const linkEnd = text.indexOf("](", i);
289
+ const urlEnd = linkEnd >= 0 ? text.indexOf(")", linkEnd + 2) : -1;
290
+ if (linkEnd > i && urlEnd > linkEnd) {
291
+ const linkText = text.slice(i + 1, linkEnd);
292
+ const linkUrl = text.slice(linkEnd + 2, urlEnd);
293
+ addNode({ type: "link", value: linkText, href: linkUrl });
294
+ i = urlEnd + 1;
295
+ continue;
296
+ }
297
+ if (text[i + 1] !== "[") {
298
+ const close = text.indexOf("]", i + 1);
299
+ if (close > i && text[close + 1] === "{") {
300
+ const braceEnd = text.indexOf("}", close + 2);
301
+ if (braceEnd > close + 1 && text[braceEnd + 1] !== "}") {
302
+ const props = {};
303
+ for (const seg of text.slice(close + 2, braceEnd).split(";")) {
304
+ const c = seg.indexOf(":");
305
+ if (c > 0) {
306
+ const k = seg.slice(0, c).trim();
307
+ if (k)
308
+ props[k] = seg.slice(c + 1).trim();
309
+ }
310
+ }
311
+ if (Object.keys(props).length > 0) {
312
+ addNode({ type: "styled", value: text.slice(i + 1, close), props });
313
+ i = braceEnd + 1;
314
+ continue;
315
+ }
316
+ }
317
+ }
318
+ }
319
+ }
320
+ if (text.startsWith("[[", i)) {
321
+ const end = text.indexOf("]]", i + 2);
322
+ if (end > i + 2) {
323
+ const noteText = text.slice(i + 2, end).trim();
324
+ const pipeAt = noteText.indexOf("|");
325
+ if (pipeAt > 0 && pipeAt < noteText.length - 1) {
326
+ const label = noteText.slice(0, pipeAt).trim();
327
+ const href = noteText.slice(pipeAt + 1).trim();
328
+ if (label && href) {
329
+ addNode({ type: "link", value: label, href });
330
+ i = end + 2;
331
+ continue;
332
+ }
333
+ }
334
+ addNode({ type: "inline-note", value: noteText });
335
+ i = end + 2;
336
+ continue;
337
+ }
338
+ }
339
+ if (text.startsWith("==", i)) {
340
+ const end = text.indexOf("==", i + 2);
341
+ if (end > i + 2) {
342
+ const quoteText = text.slice(i + 2, end);
343
+ addNode({ type: "inline-quote", value: quoteText });
344
+ i = end + 2;
345
+ continue;
346
+ }
347
+ }
348
+ if (text.startsWith("```", i)) {
349
+ const end = text.indexOf("```", i + 3);
350
+ if (end === -1) {
351
+ currentText += "```";
352
+ i += 3;
353
+ continue;
354
+ }
355
+ const codeText = text.slice(i + 3, end);
356
+ addNode({ type: "code", value: codeText });
357
+ i = end + 3;
358
+ continue;
359
+ }
360
+ if (text[i] === "`") {
361
+ const end = text.indexOf("`", i + 1);
362
+ if (end > i + 1) {
363
+ const labelText = text.slice(i + 1, end);
364
+ addNode({ type: "label", value: labelText });
365
+ i = end + 1;
366
+ continue;
367
+ }
368
+ currentText += "`";
369
+ i++;
370
+ continue;
371
+ }
372
+ const ch = text[i];
373
+ if (ch === "*" || ch === "_" || ch === "~" || ch === "^") {
374
+ const end = text.indexOf(ch, i + 1);
375
+ if (end === -1) {
376
+ currentText += ch;
377
+ i++;
378
+ continue;
379
+ }
380
+ const innerText = text.slice(i + 1, end);
381
+ const type = ch === "*"
382
+ ? "bold"
383
+ : ch === "_"
384
+ ? "italic"
385
+ : ch === "~"
386
+ ? "strike"
387
+ : "highlight";
388
+ addNode({ type, value: innerText });
389
+ i = end + 1;
390
+ continue;
391
+ }
392
+ const dateTokenMatch = text
393
+ .slice(i)
394
+ .match(/^@(today|tomorrow|\d{4}-\d{2}-\d{2})\b/i);
395
+ if (dateTokenMatch) {
396
+ const token = dateTokenMatch[1];
397
+ const iso = resolveDateToken(token);
398
+ if (iso) {
399
+ addNode({ type: "date", value: `@${token}`, iso });
400
+ i += dateTokenMatch[0].length;
401
+ continue;
402
+ }
403
+ }
404
+ const mentionMatch = text.slice(i).match(/^@([A-Za-z0-9_-]+)/);
405
+ if (mentionMatch) {
406
+ addNode({ type: "mention", value: mentionMatch[1] });
407
+ i += mentionMatch[0].length;
408
+ continue;
409
+ }
410
+ const tagMatch = text.slice(i).match(/^#([A-Za-z0-9_-]+)/);
411
+ if (tagMatch) {
412
+ addNode({ type: "tag", value: tagMatch[1] });
413
+ i += tagMatch[0].length;
414
+ continue;
415
+ }
416
+ if (text[i] === "{" &&
417
+ text[i + 1] !== "{" &&
418
+ (i === 0 || text[i - 1] !== "{")) {
419
+ const end = text.indexOf("}", i + 1);
420
+ if (end > i + 1 && text[end + 1] !== "}") {
421
+ const labelText = text.slice(i + 1, end).trim();
422
+ if (labelText && !labelText.includes("{")) {
423
+ addNode({ type: "label", value: labelText });
424
+ i = end + 1;
425
+ continue;
426
+ }
427
+ }
428
+ }
429
+ currentText += text[i];
430
+ i++;
431
+ }
432
+ flushText();
433
+ return { content, inline };
434
+ }
435
+ function expandPropertyShortcuts(content) {
436
+ const shortcuts = Object.create(null);
437
+ content = content.replace(/!low\b/gi, () => {
438
+ shortcuts.priority = "low";
439
+ return "";
440
+ });
441
+ content = content.replace(/!medium\b/gi, () => {
442
+ shortcuts.priority = "medium";
443
+ return "";
444
+ });
445
+ content = content.replace(/!high\b/gi, () => {
446
+ shortcuts.priority = "high";
447
+ return "";
448
+ });
449
+ content = content.replace(/!critical\b/gi, () => {
450
+ shortcuts.priority = "critical";
451
+ return "";
452
+ });
453
+ content = content.replace(/@(\w+)/g, (_match, username) => {
454
+ shortcuts.owner = username;
455
+ return "";
456
+ });
457
+ content = content.replace(/🚨/g, () => {
458
+ shortcuts.priority = "urgent";
459
+ return "";
460
+ });
461
+ content = content.replace(/📅\s*(\S.*)/g, (_match, dateText) => {
462
+ shortcuts.due = dateText.trim();
463
+ return "";
464
+ });
465
+ content = content.replace(/✅/g, () => {
466
+ shortcuts.status = "completed";
467
+ return "";
468
+ });
469
+ content = content.replace(/⏰\s*(\S.*)/g, (_match, timeText) => {
470
+ shortcuts.time = timeText.trim();
471
+ return "";
472
+ });
473
+ content = content.replace(/\s+/g, " ").trim();
474
+ return { content, shortcuts };
475
+ }
476
+ function unescapeIntentText(text) {
477
+ let result = "";
478
+ for (let i = 0; i < text.length; i++) {
479
+ const ch = text[i];
480
+ if (ch === "\\" && i + 1 < text.length) {
481
+ const next = text[i + 1];
482
+ if (next === "\\" || next === "|") {
483
+ result += next;
484
+ i++;
485
+ continue;
486
+ }
487
+ }
488
+ result += ch;
489
+ }
490
+ return result;
491
+ }
492
+ function parseLine(line, ctx) {
493
+ const trimmed = line.trim();
494
+ if (!trimmed)
495
+ return null;
496
+ const xNsMatch = trimmed.match(/^x-([a-z]+):\s*(.*)$/);
497
+ const fullXKeyword = xNsMatch ? `x-${xNsMatch[1]}` : "";
498
+ const extensionOwnsKeyword = ctx.extensions.some((ext) => (ext.keywords || []).some((k) => k.toLowerCase() === fullXKeyword));
499
+ if (xNsMatch && !extensionOwnsKeyword) {
500
+ const namespace = xNsMatch[1];
501
+ const afterColon = xNsMatch[2];
502
+ const parts = splitPipeMetadata(afterColon);
503
+ const xType = (parts[0] || "").trim();
504
+ const properties = Object.create(null);
505
+ let blockContent = "";
506
+ for (let i = 1; i < parts.length; i++) {
507
+ const segment = parts[i];
508
+ const propMatch = segment.match(/^([^:]+):\s*(.*)$/);
509
+ if (propMatch) {
510
+ const key = propMatch[1].trim();
511
+ const rawValue = propMatch[2].trim();
512
+ if (!DANGEROUS_KEYS.has(key)) {
513
+ if (key === "content") {
514
+ blockContent = unescapeIntentText(rawValue);
515
+ }
516
+ else {
517
+ properties[key] = unescapeIntentText(rawValue);
518
+ }
519
+ }
520
+ }
521
+ }
522
+ const { content: cleanContent, inline } = ctx.parseInline(blockContent);
523
+ const isKnownExt = language_registry_1.EXTENSION_KEYWORDS.has(xType);
524
+ if (isKnownExt) {
525
+ return {
526
+ id: nextId(),
527
+ type: xType,
528
+ content: cleanContent,
529
+ originalContent: afterColon,
530
+ properties: Object.keys(properties).length > 0 ? properties : undefined,
531
+ inline,
532
+ };
533
+ }
534
+ properties["x-type"] = xType;
535
+ properties["x-ns"] = namespace;
536
+ return {
537
+ id: nextId(),
538
+ type: "extension",
539
+ content: cleanContent,
540
+ originalContent: afterColon,
541
+ properties,
542
+ inline,
543
+ };
544
+ }
545
+ const keywordMatch = trimmed.match(/^(\p{L}[\p{L}\p{N}-]*):\s*(.*)$/u);
546
+ if (keywordMatch) {
547
+ const keyword = keywordMatch[1].toLowerCase();
548
+ const rest = keywordMatch[2];
549
+ const isKnown = ctx.keywords.has(keyword);
550
+ const isCoreKeyword = types_1.KEYWORDS.includes(keyword);
551
+ const looksLikeExtension = keyword.startsWith("x-") || keyword.startsWith("ext-");
552
+ if (!isKnown) {
553
+ if (looksLikeExtension) {
554
+ ctx.diagnostics.push({
555
+ severity: "warning",
556
+ code: "UNKNOWN_EXTENSION_KEYWORD",
557
+ message: `Unknown extension keyword '${keyword}:'`,
558
+ line: ctx.lineNumber,
559
+ column: 1,
560
+ });
561
+ const { content: cleanContent, inline } = ctx.parseInline(trimmed);
562
+ return {
563
+ id: nextId(),
564
+ type: "extension",
565
+ content: cleanContent,
566
+ originalContent: trimmed,
567
+ inline,
568
+ properties: { keyword },
569
+ };
570
+ }
571
+ const customParts = splitPipeMetadata(rest);
572
+ const customContent = unescapeIntentText(customParts[0] || "");
573
+ const customProps = Object.create(null);
574
+ customProps["keyword"] = keyword;
575
+ for (let i = 1; i < customParts.length; i++) {
576
+ const seg = customParts[i];
577
+ const pm = seg.match(/^([^:]+):\s*(.*)$/);
578
+ if (pm) {
579
+ const k = pm[1].trim();
580
+ if (!k.includes("\\") && !k.includes("|") && !DANGEROUS_KEYS.has(k)) {
581
+ customProps[k] = unescapeIntentText(pm[2].trim());
582
+ }
583
+ }
584
+ }
585
+ const { content: parsedContent, inline } = ctx.parseInline(customContent);
586
+ return {
587
+ id: nextId(),
588
+ type: "custom",
589
+ content: parsedContent,
590
+ originalContent: trimmed,
591
+ inline,
592
+ properties: customProps,
593
+ };
594
+ }
595
+ let content;
596
+ const properties = Object.create(null);
597
+ const tableResolved = (aliases_1.ALIASES[keyword] ?? keyword);
598
+ if (keyword === "headers" ||
599
+ tableResolved === "columns" ||
600
+ tableResolved === "row") {
601
+ content = rest;
602
+ }
603
+ else {
604
+ const parts = splitPipeMetadata(rest);
605
+ content = unescapeIntentText(parts[0] || "");
606
+ let propStart = 1;
607
+ const trimmedContent = content.trim();
608
+ if (trimmedContent.startsWith("|")) {
609
+ const firstProp = trimmedContent.substring(1).trim();
610
+ const propMatch = firstProp.match(/^([^:]+):\s*(.*)$/);
611
+ if (propMatch) {
612
+ const key = propMatch[1].trim();
613
+ const rawValue = propMatch[2].trim();
614
+ if (!key.includes("\\") &&
615
+ !key.includes("|") &&
616
+ !DANGEROUS_KEYS.has(key)) {
617
+ properties[key] = unescapeIntentText(rawValue);
618
+ content = "";
619
+ }
620
+ }
621
+ }
622
+ for (let i = 1; i < parts.length; i++) {
623
+ const segment = parts[i];
624
+ const propMatch = segment.match(/^([^:]+):\s*(.*)$/);
625
+ if (propMatch) {
626
+ const key = propMatch[1].trim();
627
+ const rawValue = propMatch[2].trim();
628
+ if (key.includes("\\") ||
629
+ key.includes("|") ||
630
+ DANGEROUS_KEYS.has(key)) {
631
+ ctx.diagnostics.push({
632
+ severity: "warning",
633
+ code: "INVALID_PROPERTY_SEGMENT",
634
+ message: `Invalid property key '${key}'. Property keys must not contain escapes.`,
635
+ line: ctx.lineNumber,
636
+ column: 1,
637
+ });
638
+ content += ` | ${unescapeIntentText(segment)}`;
639
+ continue;
640
+ }
641
+ properties[key] = unescapeIntentText(rawValue);
642
+ }
643
+ else {
644
+ ctx.diagnostics.push({
645
+ severity: "warning",
646
+ code: "INVALID_PROPERTY_SEGMENT",
647
+ message: `Invalid property segment '${segment.trim()}'. Expected 'key: value'.`,
648
+ line: ctx.lineNumber,
649
+ column: 1,
650
+ });
651
+ content += ` | ${unescapeIntentText(segment)}`;
652
+ }
653
+ }
654
+ }
655
+ const { content: cleanContent, inline } = ctx.parseInline(content);
656
+ let handledByExtension = false;
657
+ for (const ext of ctx.extensions) {
658
+ const wantsKeyword = (ext.keywords || []).some((k) => k.toLowerCase() === keyword);
659
+ if (!wantsKeyword || !ext.parseBlock)
660
+ continue;
661
+ const overridden = ext.parseBlock({
662
+ keyword,
663
+ content,
664
+ properties: Object.keys(properties).length > 0 ? properties : undefined,
665
+ line: ctx.lineNumber,
666
+ column: 1,
667
+ parseInline: ctx.parseInline,
668
+ });
669
+ handledByExtension = true;
670
+ if (overridden)
671
+ return overridden;
672
+ if (overridden === null)
673
+ return null;
674
+ }
675
+ if (!isCoreKeyword && (handledByExtension || looksLikeExtension)) {
676
+ return {
677
+ id: nextId(),
678
+ type: "extension",
679
+ content: cleanContent,
680
+ originalContent: content,
681
+ properties: {
682
+ ...(Object.keys(properties).length > 0 ? properties : {}),
683
+ keyword,
684
+ },
685
+ inline,
686
+ };
687
+ }
688
+ const resolvedType = (aliases_1.ALIASES[keyword] ?? keyword);
689
+ if (language_registry_1.DEPRECATED_ALIASES.has(keyword)) {
690
+ ctx.diagnostics.push({
691
+ severity: "warning",
692
+ code: "DEPRECATED_KEYWORD",
693
+ message: `'${keyword}:' is deprecated. Use '${resolvedType}:' instead.`,
694
+ line: ctx.lineNumber,
695
+ column: 1,
696
+ });
697
+ }
698
+ if (language_registry_1.CALLOUT_ALIAS_MAP[keyword] && resolvedType === "info") {
699
+ properties.type = language_registry_1.CALLOUT_ALIAS_MAP[keyword];
700
+ }
701
+ if (keyword === "done" || resolvedType === "done") {
702
+ properties.status = "done";
703
+ }
704
+ if (resolvedType === "context") {
705
+ const kvPairs = parseContextKeyValuePairs(rest);
706
+ for (const [k, v] of Object.entries(kvPairs)) {
707
+ properties[k] = v;
708
+ }
709
+ }
710
+ if (resolvedType === "step" && !properties.status) {
711
+ properties.status = "pending";
712
+ }
713
+ if (resolvedType === "retry") {
714
+ if (properties.max)
715
+ properties.max = Number(properties.max);
716
+ if (properties.delay)
717
+ properties.delay = Number(properties.delay);
718
+ if (properties.retries)
719
+ properties.retries = Number(properties.retries);
720
+ }
721
+ if (resolvedType === "wait" && !properties.status) {
722
+ properties.status = "waiting";
723
+ }
724
+ if (resolvedType === "result" && !properties.status) {
725
+ properties.status = "success";
726
+ }
727
+ if (resolvedType === "gate" && !properties.status) {
728
+ properties.status = "blocked";
729
+ }
730
+ if (resolvedType === "parallel" && !properties.join) {
731
+ properties.join = "all";
732
+ }
733
+ if (resolvedType === "call" && !properties.status) {
734
+ properties.status = "pending";
735
+ }
736
+ if (resolvedType === "signal") {
737
+ if (!properties.level) {
738
+ properties.level = "info";
739
+ }
740
+ }
741
+ if (resolvedType === "image" && properties.at && !properties.src) {
742
+ ctx.diagnostics.push({
743
+ severity: "warning",
744
+ code: "DEPRECATED_PROPERTY",
745
+ message: "'at:' is deprecated on image: blocks. Use 'src:' instead.",
746
+ line: ctx.lineNumber,
747
+ column: 1,
748
+ });
749
+ properties.src = properties.at;
750
+ delete properties.at;
751
+ }
752
+ if (properties.timeout && !isNaN(Number(properties.timeout))) {
753
+ properties.timeout = Number(properties.timeout);
754
+ }
755
+ if (properties.priority && !isNaN(Number(properties.priority))) {
756
+ properties.priority = Number(properties.priority);
757
+ }
758
+ if (properties.retries && !isNaN(Number(properties.retries))) {
759
+ properties.retries = Number(properties.retries);
760
+ }
761
+ if (properties.delay && !isNaN(Number(properties.delay))) {
762
+ properties.delay = Number(properties.delay);
763
+ }
764
+ if (resolvedType === "font") {
765
+ if (properties.leading)
766
+ properties.leading = Number(properties.leading);
767
+ }
768
+ if (resolvedType === "page") {
769
+ if (properties.columns)
770
+ properties.columns = Number(properties.columns);
771
+ if (properties.numbering !== undefined) {
772
+ properties.numbering = properties.numbering === "true" ? 1 : 0;
773
+ }
774
+ }
775
+ if (resolvedType === "toc") {
776
+ if (properties.depth) {
777
+ properties.depth = Number(properties.depth);
778
+ }
779
+ else {
780
+ properties.depth = 2;
781
+ }
782
+ if (!properties.title) {
783
+ properties.title = "Contents";
784
+ }
785
+ }
786
+ if (resolvedType === "break") {
787
+ return {
788
+ id: nextId(),
789
+ type: "break",
790
+ content: "",
791
+ properties: Object.keys(properties).length > 0 ? properties : undefined,
792
+ };
793
+ }
794
+ return {
795
+ id: nextId(),
796
+ type: resolvedType,
797
+ ...(keyword !== resolvedType ? { keywordAlias: keyword } : {}),
798
+ content: cleanContent,
799
+ originalContent: content,
800
+ properties: Object.keys(properties).length > 0 ? properties : undefined,
801
+ inline,
802
+ };
803
+ }
804
+ const checkboxMatch = trimmed.match(/^(\[ \]|\[x\])\s*(.+)$/);
805
+ if (checkboxMatch) {
806
+ const isDone = checkboxMatch[1] === "[x]";
807
+ let content = unescapeIntentText(checkboxMatch[2]);
808
+ const { content: cleanContent, shortcuts } = expandPropertyShortcuts(content);
809
+ content = cleanContent;
810
+ const { content: finalContent, inline } = ctx.parseInline(content);
811
+ if (isDone)
812
+ shortcuts.status = "done";
813
+ return {
814
+ id: nextId(),
815
+ type: "task",
816
+ content: finalContent,
817
+ originalContent: content,
818
+ properties: Object.keys(shortcuts).length > 0 ? shortcuts : undefined,
819
+ inline,
820
+ };
821
+ }
822
+ if (trimmed.startsWith("- ") || trimmed.startsWith("* ")) {
823
+ const payload = trimmed.substring(2);
824
+ const depth = ctx.embedDepth ?? 0;
825
+ const embedded = depth < 2
826
+ ? parseLine(payload, {
827
+ ...ctx,
828
+ embedDepth: depth + 1,
829
+ })
830
+ : null;
831
+ if (embedded &&
832
+ embedded.type !== "list-item" &&
833
+ embedded.type !== "step-item") {
834
+ return {
835
+ id: nextId(),
836
+ type: "list-item",
837
+ content: embedded.content,
838
+ originalContent: embedded.originalContent,
839
+ properties: embedded.properties,
840
+ inline: embedded.inline,
841
+ children: [embedded],
842
+ };
843
+ }
844
+ const unescaped = unescapeIntentText(payload);
845
+ const { content: cleanContent, inline } = ctx.parseInline(unescaped);
846
+ return {
847
+ id: nextId(),
848
+ type: "list-item",
849
+ content: cleanContent,
850
+ originalContent: unescaped,
851
+ inline,
852
+ };
853
+ }
854
+ const orderedMatch = trimmed.match(/^(\d+)\.\s+(.+)$/);
855
+ if (orderedMatch) {
856
+ const content = unescapeIntentText(orderedMatch[2]);
857
+ const { content: cleanContent, inline } = ctx.parseInline(content);
858
+ return {
859
+ id: nextId(),
860
+ type: "step-item",
861
+ content: cleanContent,
862
+ originalContent: content,
863
+ inline,
864
+ };
865
+ }
866
+ const unescaped = unescapeIntentText(trimmed);
867
+ const { content: cleanContent, inline } = ctx.parseInline(unescaped);
868
+ return {
869
+ id: nextId(),
870
+ type: "text",
871
+ content: cleanContent,
872
+ originalContent: unescaped,
873
+ inline,
874
+ };
875
+ }
876
+ function parseIntentText(fileContent, options) {
877
+ if (typeof fileContent !== "string") {
878
+ return { version: "1.4", blocks: [], metadata: {}, diagnostics: [] };
879
+ }
880
+ if (fileContent.length === 0) {
881
+ return { version: "1.4", blocks: [], metadata: {}, diagnostics: [] };
882
+ }
883
+ if (fileContent.length > MAX_INPUT_LENGTH) {
884
+ return {
885
+ version: "1.4",
886
+ blocks: [],
887
+ metadata: {},
888
+ diagnostics: [
889
+ {
890
+ severity: "error",
891
+ code: "UNTERMINATED_CODE_BLOCK",
892
+ message: `Input exceeds maximum allowed length of ${MAX_INPUT_LENGTH} characters.`,
893
+ line: 1,
894
+ column: 1,
895
+ },
896
+ ],
897
+ };
898
+ }
899
+ _resetIdCounter();
900
+ const lines = fileContent.split(/\r?\n/);
901
+ if (lines.length > MAX_LINE_COUNT) {
902
+ return {
903
+ version: "1.4",
904
+ blocks: [],
905
+ metadata: {},
906
+ diagnostics: [
907
+ {
908
+ severity: "error",
909
+ code: "UNTERMINATED_CODE_BLOCK",
910
+ message: `Input exceeds maximum allowed line count of ${MAX_LINE_COUNT}.`,
911
+ line: 1,
912
+ column: 1,
913
+ },
914
+ ],
915
+ };
916
+ }
917
+ const blocks = [];
918
+ const diagnostics = [];
919
+ let currentSection = null;
920
+ let codeCaptureMode = false;
921
+ let codeCaptureType = "keyword";
922
+ let codeContent = [];
923
+ let codeStartLine = 0;
924
+ let pendingCodeProperties = null;
925
+ const historyBoundaryIdx = detectHistoryBoundary(lines);
926
+ const parseLines = historyBoundaryIdx === -1 ? lines : lines.slice(0, historyBoundaryIdx);
927
+ let historySection;
928
+ if (options?.includeHistorySection && historyBoundaryIdx !== -1) {
929
+ const historyRaw = lines.slice(historyBoundaryIdx).join("\n");
930
+ historySection = parseHistorySectionText(historyRaw);
931
+ }
932
+ if (historyBoundaryIdx !== -1 && lines[historyBoundaryIdx].trim() === "---") {
933
+ diagnostics.push({
934
+ severity: "warning",
935
+ code: "LEGACY_HISTORY_BOUNDARY",
936
+ message: "Legacy history boundary detected: '---' followed by '// history'. " +
937
+ "Use 'history:' keyword instead (v2.12+).",
938
+ line: historyBoundaryIdx + 1,
939
+ column: 1,
940
+ });
941
+ }
942
+ const signatureBlocks = [];
943
+ let trackingMeta;
944
+ let freezeMeta;
945
+ let stepAutoIdCounter = 0;
946
+ let seenSectionBlock = false;
947
+ const agenticMetadata = {};
948
+ const extensions = options?.extensions || [];
949
+ const keywords = new Set(types_1.KEYWORDS);
950
+ for (const alias of Object.keys(aliases_1.ALIASES)) {
951
+ keywords.add(alias);
952
+ }
953
+ for (const ext of extensions) {
954
+ for (const k of ext.keywords || []) {
955
+ keywords.add(k.toLowerCase());
956
+ }
957
+ }
958
+ const metaAccumulator = Object.create(null);
959
+ const defaultParseInline = (text) => parseInlineNodes(text);
960
+ const parseInline = (text) => {
961
+ for (const ext of extensions) {
962
+ if (!ext.parseInline)
963
+ continue;
964
+ const result = ext.parseInline({ text, defaultParseInline });
965
+ if (result)
966
+ return result;
967
+ }
968
+ return defaultParseInline(text);
969
+ };
970
+ let pendingTable = null;
971
+ let previousLineWasBlank = false;
972
+ function appendBlockWithProseMerge(target, block) {
973
+ const last = target[target.length - 1];
974
+ if (!previousLineWasBlank &&
975
+ last &&
976
+ (last.type === "text" || last.type === "body-text") &&
977
+ (block.type === "text" || block.type === "body-text")) {
978
+ const mergedOriginal = `${last.originalContent || last.content} ${block.originalContent || block.content}`;
979
+ const parsed = parseInline(mergedOriginal);
980
+ last.originalContent = mergedOriginal;
981
+ last.content = parsed.content;
982
+ last.inline = parsed.inline;
983
+ return;
984
+ }
985
+ target.push(block);
986
+ }
987
+ function flushPendingTable() {
988
+ if (!pendingTable)
989
+ return;
990
+ if (pendingTable.headers &&
991
+ pendingTable.headers.length > 0 &&
992
+ pendingTable.rows.length === 0) {
993
+ diagnostics.push({
994
+ severity: "warning",
995
+ code: "HEADERS_WITHOUT_ROWS",
996
+ message: "Table headers found with no following rows.",
997
+ line: pendingTable.headerLine || 1,
998
+ column: 1,
999
+ });
1000
+ }
1001
+ const block = {
1002
+ id: nextId(),
1003
+ type: "table",
1004
+ content: pendingTable.originalHeaders || "",
1005
+ table: {
1006
+ headers: pendingTable.headers,
1007
+ rows: pendingTable.rows,
1008
+ headersKeyword: pendingTable.headersKeyword,
1009
+ rowKeyword: pendingTable.rowKeyword,
1010
+ },
1011
+ };
1012
+ if (currentSection && currentSection.children) {
1013
+ currentSection.children.push(block);
1014
+ }
1015
+ else {
1016
+ blocks.push(block);
1017
+ }
1018
+ pendingTable = null;
1019
+ }
1020
+ for (let i = 0; i < parseLines.length; i++) {
1021
+ const line = parseLines[i];
1022
+ const trimmed = line.trim();
1023
+ if (codeCaptureMode) {
1024
+ const isEndFence = codeCaptureType === "fence" && trimmed.startsWith("```");
1025
+ if (isEndFence) {
1026
+ const afterClosingFence = trimmed.replace(/^```/, "").trim();
1027
+ if (afterClosingFence.startsWith("|")) {
1028
+ if (!pendingCodeProperties)
1029
+ pendingCodeProperties = Object.create(null);
1030
+ const fenceProps = splitPipeMetadata(afterClosingFence.substring(1).trim());
1031
+ for (const fp of fenceProps) {
1032
+ const fpm = fp.trim().match(/^([\p{L}\p{N}_][\p{L}\p{N}_-]*):\s*(.*)$/u);
1033
+ if (fpm && !pendingCodeProperties[fpm[1].trim()]) {
1034
+ pendingCodeProperties[fpm[1].trim()] = fpm[2].trim();
1035
+ }
1036
+ }
1037
+ }
1038
+ const codeBlock = {
1039
+ id: nextId(),
1040
+ type: "code",
1041
+ content: codeContent.join("\n"),
1042
+ ...(pendingCodeProperties
1043
+ ? { properties: pendingCodeProperties }
1044
+ : {}),
1045
+ };
1046
+ if (currentSection && currentSection.children) {
1047
+ currentSection.children.push(codeBlock);
1048
+ }
1049
+ else {
1050
+ blocks.push(codeBlock);
1051
+ }
1052
+ codeCaptureMode = false;
1053
+ codeCaptureType = "keyword";
1054
+ codeContent = [];
1055
+ codeStartLine = 0;
1056
+ pendingCodeProperties = null;
1057
+ }
1058
+ else {
1059
+ codeContent.push(line);
1060
+ }
1061
+ previousLineWasBlank = false;
1062
+ continue;
1063
+ }
1064
+ if (trimmed.startsWith("//"))
1065
+ continue;
1066
+ if (!trimmed) {
1067
+ previousLineWasBlank = true;
1068
+ continue;
1069
+ }
1070
+ const isMdPipeRow = /^\|.+\|$/.test(trimmed);
1071
+ const rowKwMatch = trimmed.match(/^(\p{L}[\p{L}\p{N}-]*):/u);
1072
+ const isRowLine = !!rowKwMatch &&
1073
+ (aliases_1.ALIASES[rowKwMatch[1].toLowerCase()] ?? rowKwMatch[1].toLowerCase()) ===
1074
+ "row";
1075
+ if (pendingTable && !isRowLine && !isMdPipeRow) {
1076
+ flushPendingTable();
1077
+ }
1078
+ if (trimmed.startsWith("```")) {
1079
+ const fenceLang = trimmed.replace(/^```/, "").trim();
1080
+ if (fenceLang) {
1081
+ pendingCodeProperties = { lang: fenceLang };
1082
+ }
1083
+ codeCaptureMode = true;
1084
+ codeCaptureType = "fence";
1085
+ codeStartLine = i + 1;
1086
+ previousLineWasBlank = false;
1087
+ continue;
1088
+ }
1089
+ if (trimmed === "---") {
1090
+ const dividerBlock = {
1091
+ id: nextId(),
1092
+ type: "divider",
1093
+ content: "",
1094
+ };
1095
+ if (currentSection && currentSection.children) {
1096
+ currentSection.children.push(dividerBlock);
1097
+ }
1098
+ else {
1099
+ blocks.push(dividerBlock);
1100
+ currentSection = null;
1101
+ }
1102
+ previousLineWasBlank = false;
1103
+ continue;
1104
+ }
1105
+ const codeMatch = trimmed.match(/^code:\s*(.*)$/);
1106
+ if (codeMatch) {
1107
+ const afterCode = codeMatch[1].trim();
1108
+ if (afterCode.startsWith("```")) {
1109
+ const afterOpen = afterCode.substring(3);
1110
+ const closeIdx = afterOpen.indexOf("```");
1111
+ if (closeIdx !== -1) {
1112
+ const codeValue = afterOpen.substring(0, closeIdx);
1113
+ const afterClose = afterOpen.substring(closeIdx + 3).trim();
1114
+ const props = Object.create(null);
1115
+ if (afterClose.startsWith("|")) {
1116
+ const parts = splitPipeMetadata(afterClose.substring(1).trim());
1117
+ for (const part of parts) {
1118
+ const pm = part.trim().match(/^([\p{L}\p{N}_][\p{L}\p{N}_-]*):\s*(.*)$/u);
1119
+ if (pm)
1120
+ props[pm[1].trim()] = pm[2].trim();
1121
+ }
1122
+ }
1123
+ const codeBlock = {
1124
+ id: nextId(),
1125
+ type: "code",
1126
+ content: codeValue,
1127
+ ...(Object.keys(props).length > 0 ? { properties: props } : {}),
1128
+ };
1129
+ if (currentSection && currentSection.children) {
1130
+ currentSection.children.push(codeBlock);
1131
+ }
1132
+ else {
1133
+ blocks.push(codeBlock);
1134
+ }
1135
+ previousLineWasBlank = false;
1136
+ continue;
1137
+ }
1138
+ else {
1139
+ if (afterOpen.trim()) {
1140
+ codeContent.push(afterOpen);
1141
+ }
1142
+ codeCaptureMode = true;
1143
+ codeCaptureType = "fence";
1144
+ codeStartLine = i + 1;
1145
+ previousLineWasBlank = false;
1146
+ continue;
1147
+ }
1148
+ }
1149
+ if (!afterCode || afterCode.startsWith("|")) {
1150
+ const props = Object.create(null);
1151
+ if (afterCode.startsWith("|")) {
1152
+ const parts = splitPipeMetadata(afterCode.substring(1).trim());
1153
+ for (const part of parts) {
1154
+ const propMatch = part.trim().match(/^([\p{L}\p{N}_][\p{L}\p{N}_-]*):\s*(.*)$/u);
1155
+ if (propMatch) {
1156
+ props[propMatch[1].trim()] = propMatch[2].trim();
1157
+ }
1158
+ }
1159
+ }
1160
+ let nextIdx = i + 1;
1161
+ while (nextIdx < lines.length && !lines[nextIdx].trim())
1162
+ nextIdx++;
1163
+ if (nextIdx < lines.length && lines[nextIdx].trim().startsWith("```")) {
1164
+ const fenceLang = lines[nextIdx].trim().replace(/^```/, "").trim();
1165
+ if (fenceLang && !props.lang) {
1166
+ props.lang = fenceLang;
1167
+ }
1168
+ pendingCodeProperties = Object.keys(props).length > 0 ? props : null;
1169
+ codeCaptureMode = true;
1170
+ codeCaptureType = "fence";
1171
+ codeStartLine = i + 1;
1172
+ i = nextIdx;
1173
+ previousLineWasBlank = false;
1174
+ continue;
1175
+ }
1176
+ }
1177
+ const codeBlock = {
1178
+ id: nextId(),
1179
+ type: "code",
1180
+ content: afterCode,
1181
+ };
1182
+ if (currentSection && currentSection.children) {
1183
+ currentSection.children.push(codeBlock);
1184
+ }
1185
+ else {
1186
+ blocks.push(codeBlock);
1187
+ }
1188
+ previousLineWasBlank = false;
1189
+ continue;
1190
+ }
1191
+ const block = parseLine(line, {
1192
+ keywords,
1193
+ extensions,
1194
+ lineNumber: i + 1,
1195
+ diagnostics,
1196
+ parseInline,
1197
+ });
1198
+ if (!block)
1199
+ continue;
1200
+ const mdTableMatch = trimmed.match(/^\|(.+)\|$/);
1201
+ if (mdTableMatch && trimmed.includes("|")) {
1202
+ const cells = trimmed
1203
+ .split("|")
1204
+ .slice(1, -1)
1205
+ .map((cell) => cell.trim())
1206
+ .filter((cell) => cell !== "");
1207
+ const isSeparator = cells.every((cell) => /^[-:]+$/.test(cell));
1208
+ if (!isSeparator && cells.length > 0) {
1209
+ if (!pendingTable) {
1210
+ pendingTable = {
1211
+ headers: cells,
1212
+ rows: [],
1213
+ originalHeaders: trimmed,
1214
+ headerLine: i + 1,
1215
+ };
1216
+ }
1217
+ else {
1218
+ pendingTable.rows.push(cells);
1219
+ }
1220
+ continue;
1221
+ }
1222
+ else if (isSeparator && pendingTable) {
1223
+ continue;
1224
+ }
1225
+ }
1226
+ if (block.type === "columns") {
1227
+ flushPendingTable();
1228
+ pendingTable = {
1229
+ headers: splitTableRow(block.originalContent || block.content),
1230
+ rows: [],
1231
+ originalHeaders: block.originalContent || block.content,
1232
+ headerLine: i + 1,
1233
+ headersKeyword: block.keywordAlias,
1234
+ };
1235
+ previousLineWasBlank = false;
1236
+ continue;
1237
+ }
1238
+ if (block.type === "row") {
1239
+ const rowCells = splitTableRow(block.originalContent || block.content);
1240
+ if (pendingTable) {
1241
+ pendingTable.rows.push(rowCells);
1242
+ if (block.keywordAlias && !pendingTable.rowKeyword)
1243
+ pendingTable.rowKeyword = block.keywordAlias;
1244
+ }
1245
+ else {
1246
+ diagnostics.push({
1247
+ severity: "warning",
1248
+ code: "ROW_WITHOUT_HEADERS",
1249
+ message: "Table row found without preceding headers.",
1250
+ line: i + 1,
1251
+ column: 1,
1252
+ });
1253
+ pendingTable = { rows: [rowCells] };
1254
+ flushPendingTable();
1255
+ }
1256
+ previousLineWasBlank = false;
1257
+ continue;
1258
+ }
1259
+ if (block.type === "section") {
1260
+ seenSectionBlock = true;
1261
+ }
1262
+ if (block.type === "meta") {
1263
+ if (!seenSectionBlock) {
1264
+ const metaProps = block.properties || {};
1265
+ for (const [k, v] of Object.entries(metaProps)) {
1266
+ metaAccumulator[k] = String(v);
1267
+ }
1268
+ continue;
1269
+ }
1270
+ }
1271
+ const xType = block.type === "extension" ? block.properties?.["x-type"] : undefined;
1272
+ if ((METADATA_KEYWORDS.has(block.type) ||
1273
+ xType === "agent" ||
1274
+ xType === "model") &&
1275
+ !seenSectionBlock) {
1276
+ if (block.type === "agent" || xType === "agent") {
1277
+ agenticMetadata.agent = block.content;
1278
+ if (block.properties?.model) {
1279
+ agenticMetadata.model = String(block.properties.model);
1280
+ }
1281
+ }
1282
+ else if (block.type === "model" || xType === "model") {
1283
+ agenticMetadata.model = block.content;
1284
+ }
1285
+ continue;
1286
+ }
1287
+ if (block.type === "context" && !seenSectionBlock) {
1288
+ const ctxProps = block.properties || {};
1289
+ if (!agenticMetadata.context)
1290
+ agenticMetadata.context = {};
1291
+ for (const [k, v] of Object.entries(ctxProps)) {
1292
+ agenticMetadata.context[k] = String(v);
1293
+ }
1294
+ }
1295
+ if (block.type === "track") {
1296
+ trackingMeta = {
1297
+ version: block.properties?.version
1298
+ ? String(block.properties.version)
1299
+ : "1.0",
1300
+ by: block.properties?.by ? String(block.properties.by) : "",
1301
+ active: true,
1302
+ };
1303
+ continue;
1304
+ }
1305
+ if (block.type === "history") {
1306
+ continue;
1307
+ }
1308
+ if (block.type === "sign") {
1309
+ signatureBlocks.push({
1310
+ signer: block.content,
1311
+ role: block.properties?.role
1312
+ ? String(block.properties.role)
1313
+ : undefined,
1314
+ at: block.properties?.at ? String(block.properties.at) : "",
1315
+ hash: block.properties?.hash ? String(block.properties.hash) : "",
1316
+ });
1317
+ }
1318
+ if (block.type === "freeze") {
1319
+ freezeMeta = {
1320
+ at: block.properties?.at ? String(block.properties.at) : "",
1321
+ hash: block.properties?.hash ? String(block.properties.hash) : "",
1322
+ status: "locked",
1323
+ };
1324
+ }
1325
+ if (block.type === "step") {
1326
+ stepAutoIdCounter++;
1327
+ if (block.properties?.id) {
1328
+ block.id = String(block.properties.id);
1329
+ }
1330
+ else {
1331
+ block.id = `step-${stepAutoIdCounter}`;
1332
+ if (!block.properties)
1333
+ block.properties = {};
1334
+ block.properties.id = `step-${stepAutoIdCounter}`;
1335
+ }
1336
+ }
1337
+ if (block.type === "section") {
1338
+ currentSection = block;
1339
+ currentSection.children = [];
1340
+ blocks.push(currentSection);
1341
+ }
1342
+ else if (block.type === "sub") {
1343
+ if (currentSection) {
1344
+ block.children = [];
1345
+ appendBlockWithProseMerge(currentSection.children, block);
1346
+ }
1347
+ else {
1348
+ appendBlockWithProseMerge(blocks, block);
1349
+ }
1350
+ }
1351
+ else if (block.type === "title" || block.type === "summary") {
1352
+ appendBlockWithProseMerge(blocks, block);
1353
+ }
1354
+ else if (block.type === "divider" ||
1355
+ block.type === "image" ||
1356
+ block.type === "link" ||
1357
+ block.type === "code" ||
1358
+ block.type === "table" ||
1359
+ block.type === "body-text" ||
1360
+ block.type === "text" ||
1361
+ block.type === "page" ||
1362
+ block.type === "break" ||
1363
+ block.type === "toc" ||
1364
+ (block.type === "extension" &&
1365
+ (DOCGEN_LAYOUT_TYPES.has(String(block.properties?.["x-type"])) ||
1366
+ String(block.properties?.["x-type"]) === "ref" ||
1367
+ String(block.properties?.["x-type"]) === "embed" ||
1368
+ String(block.properties?.["x-type"]) === "dedication" ||
1369
+ String(block.properties?.["x-type"]) === "toc"))) {
1370
+ appendBlockWithProseMerge(blocks, block);
1371
+ currentSection = null;
1372
+ }
1373
+ else {
1374
+ let target = currentSection;
1375
+ if (target) {
1376
+ const lastSub = target.children?.[target.children.length - 1];
1377
+ if (lastSub?.type === "sub") {
1378
+ target = lastSub;
1379
+ }
1380
+ if (!target.children)
1381
+ target.children = [];
1382
+ appendBlockWithProseMerge(target.children, block);
1383
+ }
1384
+ else {
1385
+ appendBlockWithProseMerge(blocks, block);
1386
+ }
1387
+ }
1388
+ previousLineWasBlank = false;
1389
+ }
1390
+ flushPendingTable();
1391
+ if (codeCaptureMode) {
1392
+ diagnostics.push({
1393
+ severity: "error",
1394
+ code: "UNTERMINATED_CODE_BLOCK",
1395
+ message: "Unterminated code block. Expected closing ``` before end of file.",
1396
+ line: codeStartLine || lines.length,
1397
+ column: 1,
1398
+ });
1399
+ const codeBlock = {
1400
+ id: nextId(),
1401
+ type: "code",
1402
+ content: codeContent.join("\n"),
1403
+ ...(pendingCodeProperties ? { properties: pendingCodeProperties } : {}),
1404
+ };
1405
+ pendingCodeProperties = null;
1406
+ if (currentSection && currentSection.children) {
1407
+ currentSection.children.push(codeBlock);
1408
+ }
1409
+ else {
1410
+ blocks.push(codeBlock);
1411
+ }
1412
+ }
1413
+ const titleBlock = blocks.find((b) => b.type === "title");
1414
+ const summaryBlock = blocks.find((b) => b.type === "summary");
1415
+ const hasArabic = blocks.some((b) => detectArabic(b.content));
1416
+ const allBlocks = blocks.flatMap(function collect(b) {
1417
+ return [b, ...(b.children ?? []).flatMap(collect)];
1418
+ });
1419
+ const hasDocgenContent = allBlocks.some((b) => DOCGEN_LAYOUT_TYPES.has(b.type) || DOCGEN_WRITER_TYPES.has(b.type));
1420
+ const hasTrustContent = trackingMeta != null ||
1421
+ signatureBlocks.length > 0 ||
1422
+ freezeMeta != null ||
1423
+ allBlocks.some((b) => TRUST_KEYWORDS.has(b.type));
1424
+ const hasV22Content = allBlocks.some((b) => V22_BLOCK_TYPES.has(b.type));
1425
+ const hasV21Content = allBlocks.some((b) => V21_BLOCK_TYPES.has(b.type));
1426
+ const hasAgenticContent = allBlocks.some((b) => AGENTIC_BLOCK_TYPES.has(b.type)) ||
1427
+ agenticMetadata.agent != null ||
1428
+ agenticMetadata.model != null ||
1429
+ agenticMetadata.context != null;
1430
+ const metadata = {
1431
+ title: titleBlock?.content,
1432
+ summary: summaryBlock?.content,
1433
+ language: hasArabic ? "rtl" : "ltr",
1434
+ ...(agenticMetadata.agent != null && { agent: agenticMetadata.agent }),
1435
+ ...(agenticMetadata.model != null && { model: agenticMetadata.model }),
1436
+ ...(agenticMetadata.context != null && {
1437
+ context: agenticMetadata.context,
1438
+ }),
1439
+ ...(trackingMeta != null && { tracking: trackingMeta }),
1440
+ ...(signatureBlocks.length > 0 && { signatures: signatureBlocks }),
1441
+ ...(freezeMeta != null && { freeze: freezeMeta }),
1442
+ ...(Object.keys(metaAccumulator).length > 0 && { meta: metaAccumulator }),
1443
+ };
1444
+ const hasV212Content = (historyBoundaryIdx !== -1 &&
1445
+ lines[historyBoundaryIdx].trim().startsWith("history:")) ||
1446
+ allBlocks.some((b) => b.type === "history");
1447
+ const document = {
1448
+ version: hasV212Content
1449
+ ? "2.12"
1450
+ : allBlocks.some((b) => V211_BLOCK_TYPES.has(b.type))
1451
+ ? "2.11"
1452
+ : hasTrustContent
1453
+ ? "2.8"
1454
+ : hasDocgenContent
1455
+ ? "2.5"
1456
+ : hasV22Content
1457
+ ? "2.2"
1458
+ : hasV21Content
1459
+ ? "2.1"
1460
+ : hasAgenticContent
1461
+ ? "2.0"
1462
+ : "1.4",
1463
+ blocks,
1464
+ metadata,
1465
+ diagnostics: diagnostics.length > 0 ? diagnostics : undefined,
1466
+ ...(historySection != null && { history: historySection }),
1467
+ };
1468
+ for (const ext of extensions) {
1469
+ if (!ext.validate)
1470
+ continue;
1471
+ const extDiags = ext.validate(document);
1472
+ if (extDiags && extDiags.length > 0) {
1473
+ if (!document.diagnostics)
1474
+ document.diagnostics = [];
1475
+ document.diagnostics.push(...extDiags);
1476
+ }
1477
+ }
1478
+ return document;
1479
+ }
1480
+ exports.DEFAULT_SAFE_PARSE_OPTIONS = {
1481
+ unknownKeyword: "note",
1482
+ maxBlocks: 10000,
1483
+ maxLineLength: 50000,
1484
+ strict: false,
1485
+ };
1486
+ function parseIntentTextSafe(source, options) {
1487
+ const opts = { ...exports.DEFAULT_SAFE_PARSE_OPTIONS, ...options };
1488
+ const warnings = [];
1489
+ const errors = [];
1490
+ if (typeof source !== "string" || source.length === 0) {
1491
+ return {
1492
+ document: { version: "1.4", blocks: [], metadata: {}, diagnostics: [] },
1493
+ warnings: [],
1494
+ errors: [],
1495
+ };
1496
+ }
1497
+ try {
1498
+ const rawLines = source.split(/\r?\n/);
1499
+ const processedLines = [];
1500
+ const knownKeywords = new Set(types_1.KEYWORDS);
1501
+ for (let i = 0; i < rawLines.length; i++) {
1502
+ let line = rawLines[i];
1503
+ if (line.length > opts.maxLineLength) {
1504
+ warnings.push({
1505
+ line: i + 1,
1506
+ message: `Line truncated from ${line.length} to ${opts.maxLineLength} characters`,
1507
+ code: "LINE_TRUNCATED",
1508
+ original: line.slice(0, 200) + "...",
1509
+ });
1510
+ line = line.slice(0, opts.maxLineLength);
1511
+ }
1512
+ const kwMatch = line.match(/^(\s*)([a-zA-Z_-]+)\s*:/);
1513
+ if (kwMatch) {
1514
+ const kw = kwMatch[2].toLowerCase();
1515
+ if (!knownKeywords.has(kw) && !line.trim().startsWith("//")) {
1516
+ const entry = {
1517
+ line: i + 1,
1518
+ message: `Unknown keyword: "${kw}"`,
1519
+ code: "UNKNOWN_KEYWORD",
1520
+ original: line,
1521
+ };
1522
+ if (opts.strict || opts.unknownKeyword === "throw") {
1523
+ errors.push(entry);
1524
+ }
1525
+ else {
1526
+ warnings.push(entry);
1527
+ }
1528
+ if (opts.unknownKeyword === "skip") {
1529
+ continue;
1530
+ }
1531
+ }
1532
+ }
1533
+ processedLines.push(line);
1534
+ }
1535
+ const processedSource = processedLines.join("\n");
1536
+ const document = parseIntentText(processedSource);
1537
+ if (document.blocks.length > opts.maxBlocks) {
1538
+ warnings.push({
1539
+ line: 0,
1540
+ message: `Document has ${document.blocks.length} blocks, truncated to ${opts.maxBlocks}`,
1541
+ code: "MAX_BLOCKS_REACHED",
1542
+ original: "",
1543
+ });
1544
+ document.blocks = document.blocks.slice(0, opts.maxBlocks);
1545
+ }
1546
+ return { document, warnings, errors };
1547
+ }
1548
+ catch (e) {
1549
+ const message = e instanceof Error ? e.message : "Unknown parser error";
1550
+ errors.push({
1551
+ line: 0,
1552
+ message,
1553
+ code: "PARSE_EXCEPTION",
1554
+ original: "",
1555
+ });
1556
+ return {
1557
+ document: { version: "1.4", blocks: [], metadata: {}, diagnostics: [] },
1558
+ warnings,
1559
+ errors,
1560
+ };
1561
+ }
1562
+ }