@hybridlabor-api/aos 4.13.1 → 4.14.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/.agents/AGENTS.md +8 -0
- package/.agents/nodes.json +5 -2
- package/.claude/hooks/conventional-commits.mjs +14 -15
- package/.claude/hooks/env-file-protection.mjs +14 -15
- package/.claude/hooks/go-gate.mjs +157 -13
- package/.claude/hooks/go-token.mjs +55 -0
- package/.claude/hooks/memb-inject.mjs +75 -62
- package/.claude/hooks/trail-autostart.mjs +27 -0
- package/.claude/settings.json +18 -0
- package/.claude/workflows/startcycle-dispatch.mjs +11 -4
- package/.opencode/plugins/bdb-aos.js +98 -121
- package/.opencode/plugins/lib/trail-autostart.js +38 -0
- package/CLAUDE.md +1 -1
- package/README.de.md +6 -10
- package/README.md +6 -10
- package/README.pt.md +6 -10
- package/THIRD_PARTY_NOTICES.md +19 -3
- package/assets/header-v5.png +0 -0
- package/bin/aos-acp.mjs +211 -0
- package/bin/aos-doctor.mjs +1 -1
- package/bin/aos-uninstall.mjs +2 -2
- package/docs/master-session-acp.md +51 -0
- package/installer.js +398 -65
- package/mcps/mcsc/packages/mcp/server.js +6 -7
- package/package.json +4 -3
- package/scripts/validate-skills.mjs +76 -0
- package/skills/basic/bdbmediastorm/SKILL.md +1 -1
- package/skills/basic/godmode-shipping/SKILL.md +3 -0
- package/skills/basic/master-session/SKILL.md +89 -0
- package/skills/basic/startcycle/SKILL.md +1 -1
- package/skills/basic/startcycle-graph/SKILL.md +2 -2
- package/skills/basic/startcycle-graph-user/SKILL.md +1 -1
- package/skills/basic/teamwork-preview/SKILL.md +1 -1
- package/skills/bdbrainstorm/SKILL.md +7 -1
- package/skills/global_config/agentic-harness-patterns/SKILL.md +257 -0
- package/skills/global_config/agentic-harness-patterns/metadata.json +10 -0
- package/skills/global_config/agentic-harness-patterns/references/agent-orchestration-pattern.md +97 -0
- package/skills/global_config/agentic-harness-patterns/references/bootstrap-sequence-pattern.md +106 -0
- package/skills/global_config/agentic-harness-patterns/references/context-engineering/compress-pattern.md +78 -0
- package/skills/global_config/agentic-harness-patterns/references/context-engineering/isolate-pattern.md +82 -0
- package/skills/global_config/agentic-harness-patterns/references/context-engineering/select-pattern.md +86 -0
- package/skills/global_config/agentic-harness-patterns/references/context-engineering-pattern.md +29 -0
- package/skills/global_config/agentic-harness-patterns/references/hook-lifecycle-pattern.md +111 -0
- package/skills/global_config/agentic-harness-patterns/references/memory-persistence-pattern.md +109 -0
- package/skills/global_config/agentic-harness-patterns/references/permission-gate-pattern.md +111 -0
- package/skills/global_config/agentic-harness-patterns/references/skill-runtime-pattern.md +104 -0
- package/skills/global_config/agentic-harness-patterns/references/task-decomposition-pattern.md +92 -0
- package/skills/global_config/agentic-harness-patterns/references/tool-registry-pattern.md +101 -0
- package/skills/global_config/agenttrail/SKILL.md +8 -0
- package/skills/global_config/agenttrail/bin/agenttrail.mjs +14 -0
- package/skills/global_config/agenttrail/bin/ensure.mjs +118 -0
- package/skills/global_config/aos-setup/scripts/aos-doctor.mjs +1 -1
- package/skills/global_config/bdb-memb-mcp/SKILL.md +4 -5
- package/skills/global_config/bdb-visual-edit/SKILL.md +51 -0
- package/skills/global_config/bdb-visual-edit/references/vite-react-source-attr.md +59 -0
- package/skills/global_config/bdb-visual-edit/scripts/pick-snippet.js +27 -0
- package/skills/global_config/bdb-visual-edit/scripts/sanitize-element.mjs +123 -0
- package/skills/global_config/factory-collect/SKILL.md +74 -0
- package/skills/global_config/factory-human-digest/SKILL.md +92 -0
- package/skills/global_config/factory-lookback/SKILL.md +95 -0
- package/skills/global_config/factory-review-prs/SKILL.md +63 -0
- package/skills/global_config/git-pr-review/SKILL.md +3 -0
- package/skills/global_config/grilling/SKILL.md +2 -0
- package/skills/global_config/mcsc/SKILL.md +1 -1
- package/skills/global_config/plan-arbiter/SKILL.md +125 -0
- package/skills/global_config/plan-canvas/SKILL.md +63 -9
- package/skills/global_config/plan-canvas/scripts/lib/loopback-guard.js +19 -3
- package/skills/global_config/plan-canvas/scripts/lib/plan-builder/README.md +285 -0
- package/skills/global_config/plan-canvas/scripts/lib/plan-builder/agent-trail.js +129 -0
- package/skills/global_config/plan-canvas/scripts/lib/plan-builder/board-client.js +124 -0
- package/skills/global_config/plan-canvas/scripts/lib/plan-builder/examples/demo-plan/canvas.mdx +19 -0
- package/skills/global_config/plan-canvas/scripts/lib/plan-builder/examples/demo-plan/plan.mdx +18 -0
- package/skills/global_config/plan-canvas/scripts/lib/plan-builder/examples/recap-demo/plan.mdx +72 -0
- package/skills/global_config/plan-canvas/scripts/lib/plan-builder/examples/showcase/README.md +29 -0
- package/skills/global_config/plan-canvas/scripts/lib/plan-builder/examples/showcase/architecture.json +30 -0
- package/skills/global_config/plan-canvas/scripts/lib/plan-builder/examples/showcase/builder/00_architecture.html +14950 -0
- package/skills/global_config/plan-canvas/scripts/lib/plan-builder/examples/showcase/builder/canvas.mdx +511 -0
- package/skills/global_config/plan-canvas/scripts/lib/plan-builder/examples/showcase/builder/plan.mdx +208 -0
- package/skills/global_config/plan-canvas/scripts/lib/plan-builder/examples/showcase/recap/plan.mdx +102 -0
- package/skills/global_config/plan-canvas/scripts/lib/plan-builder/examples/showcase/standard/plan.md +136 -0
- package/skills/global_config/plan-canvas/scripts/lib/plan-builder/examples/signup-storyboard/canvas.mdx +124 -0
- package/skills/global_config/plan-canvas/scripts/lib/plan-builder/examples/signup-storyboard/plan.mdx +37 -0
- package/skills/global_config/plan-canvas/scripts/lib/plan-builder/index.js +188 -0
- package/skills/global_config/plan-canvas/scripts/lib/plan-builder/kit.js +123 -0
- package/skills/global_config/plan-canvas/scripts/lib/plan-builder/mdx.js +411 -0
- package/skills/global_config/plan-canvas/scripts/lib/plan-builder/render.js +1291 -0
- package/skills/global_config/plan-canvas/scripts/lib/plan-builder/templates/architecture/meta.json +1 -0
- package/skills/global_config/plan-canvas/scripts/lib/plan-builder/templates/architecture/plan.mdx +195 -0
- package/skills/global_config/plan-canvas/scripts/lib/plan-builder/templates/architecture/standard.md +95 -0
- package/skills/global_config/plan-canvas/scripts/lib/plan-builder/templates/bugfix/meta.json +1 -0
- package/skills/global_config/plan-canvas/scripts/lib/plan-builder/templates/bugfix/plan.mdx +105 -0
- package/skills/global_config/plan-canvas/scripts/lib/plan-builder/templates/bugfix/standard.md +76 -0
- package/skills/global_config/plan-canvas/scripts/lib/plan-builder/templates/feature/canvas.mdx +81 -0
- package/skills/global_config/plan-canvas/scripts/lib/plan-builder/templates/feature/meta.json +1 -0
- package/skills/global_config/plan-canvas/scripts/lib/plan-builder/templates/feature/plan.mdx +145 -0
- package/skills/global_config/plan-canvas/scripts/lib/plan-builder/templates/feature/standard.md +76 -0
- package/skills/global_config/plan-canvas/scripts/lib/plan-builder/templates/migration/meta.json +1 -0
- package/skills/global_config/plan-canvas/scripts/lib/plan-builder/templates/migration/plan.mdx +172 -0
- package/skills/global_config/plan-canvas/scripts/lib/plan-builder/templates/migration/standard.md +100 -0
- package/skills/global_config/plan-canvas/scripts/lib/plan-builder/templates/recap/meta.json +1 -0
- package/skills/global_config/plan-canvas/scripts/lib/plan-builder/templates/recap/plan.mdx +67 -0
- package/skills/global_config/plan-canvas/scripts/lib/plan-builder/templates/recap/standard.md +49 -0
- package/skills/global_config/plan-canvas/scripts/lib/plan-builder/templates/recap-board/canvas.mdx +63 -0
- package/skills/global_config/plan-canvas/scripts/lib/plan-builder/templates/recap-board/meta.json +1 -0
- package/skills/global_config/plan-canvas/scripts/lib/plan-builder/templates/recap-board/plan.mdx +49 -0
- package/skills/global_config/plan-canvas/scripts/lib/plan-builder/templates/recap-board/standard.md +39 -0
- package/skills/global_config/plan-canvas/scripts/lib/plan-builder/templates/recap-review/meta.json +1 -0
- package/skills/global_config/plan-canvas/scripts/lib/plan-builder/templates/recap-review/plan.mdx +118 -0
- package/skills/global_config/plan-canvas/scripts/lib/plan-builder/templates/recap-review/standard.md +57 -0
- package/skills/global_config/plan-canvas/scripts/lib/plan-builder/templates/release/meta.json +1 -0
- package/skills/global_config/plan-canvas/scripts/lib/plan-builder/templates/release/plan.mdx +173 -0
- package/skills/global_config/plan-canvas/scripts/lib/plan-builder/templates/release/standard.md +96 -0
- package/skills/global_config/plan-canvas/scripts/lib/plan-builder/templates/research/meta.json +1 -0
- package/skills/global_config/plan-canvas/scripts/lib/plan-builder/templates/research/plan.mdx +91 -0
- package/skills/global_config/plan-canvas/scripts/lib/plan-builder/templates/research/standard.md +54 -0
- package/skills/global_config/plan-canvas/scripts/lib/plan-builder/templates/show-control/canvas.mdx +53 -0
- package/skills/global_config/plan-canvas/scripts/lib/plan-builder/templates/show-control/meta.json +1 -0
- package/skills/global_config/plan-canvas/scripts/lib/plan-builder/templates/show-control/plan.mdx +225 -0
- package/skills/global_config/plan-canvas/scripts/lib/plan-builder/templates/show-control/standard.md +111 -0
- package/skills/global_config/plan-canvas/scripts/lib/plan-builder/theme.css +472 -0
- package/skills/global_config/plan-canvas/scripts/lib/plan-builder/trail.js +216 -0
- package/skills/global_config/plan-canvas/scripts/lib/plan-canvas/markdown.js +1 -1
- package/skills/global_config/plan-canvas/scripts/lib/plan-canvas/server.js +37 -4
- package/skills/global_config/plan-canvas/scripts/lib/plan-canvas/ui.js +125 -29
- package/skills/global_config/plan-canvas/scripts/plan-canvas.js +196 -8
- package/skills/global_config/pr-recap/SKILL.md +47 -0
- package/skills/global_config/pr-recap/scripts/pr-recap.mjs +200 -0
- package/skills/global_config/quick-recap/SKILL.md +55 -0
- package/skills/global_config/stay-within-limits/SKILL.md +85 -0
- package/skills/global_config/triage/SKILL.md +3 -0
- package/skills/global_config/visual-edit/README.md +96 -0
- package/skills/global_config/visual-edit/SKILL.md +615 -0
- package/skills/global_config/visual-plan/README.md +93 -0
- package/skills/global_config/visual-plan/SKILL.md +544 -0
- package/skills/global_config/visual-plan/references/canvas.md +139 -0
- package/skills/global_config/visual-plan/references/connection.md +51 -0
- package/skills/global_config/visual-plan/references/document-quality.md +186 -0
- package/skills/global_config/visual-plan/references/exemplar.md +62 -0
- package/skills/global_config/visual-plan/references/local-files.md +99 -0
- package/skills/global_config/visual-plan/references/wireframe.md +319 -0
- package/skills/global_config/visual-recap/README.md +103 -0
- package/skills/global_config/visual-recap/SKILL.md +560 -0
- package/skills/global_config/visual-recap/references/connection.md +51 -0
- package/skills/global_config/visual-recap/references/local-files.md +99 -0
- package/skills/global_config/visual-recap/references/wireframe.md +319 -0
- package/skills/playbooks/pb-ci-fix/SKILL.md +49 -0
- package/skills/playbooks/pb-event-tracker/SKILL.md +45 -0
- package/skills/playbooks/pb-meeting-actions/SKILL.md +42 -0
- package/skills/playbooks/pb-project-new/SKILL.md +48 -0
- package/skills/playbooks/pb-week-plan/SKILL.md +45 -0
- package/assets/header-v4.jpg +0 -0
|
@@ -0,0 +1,411 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Minimal MDX-subset parser for Agent-Native-style plan folders.
|
|
5
|
+
*
|
|
6
|
+
* Understands: optional YAML-ish frontmatter, Markdown prose (handled by
|
|
7
|
+
* lib/plan-canvas/markdown.js at render time) and JSX-like block tags at the
|
|
8
|
+
* top level — self-closing (`<Code code={"x"} />`) or paired
|
|
9
|
+
* (`<RichText>prose</RichText>`).
|
|
10
|
+
*
|
|
11
|
+
* Everything here is best-effort: a malformed document must degrade into an
|
|
12
|
+
* error block, never throw. Nothing is evaluated — `{...}` attribute values are
|
|
13
|
+
* JSON-parsed, and unparsable ones are kept verbatim as strings with a warning.
|
|
14
|
+
*/
|
|
15
|
+
|
|
16
|
+
const FRONTMATTER_RE = /^---\n([\s\S]*?)\n---\n?/;
|
|
17
|
+
const HEADING_RE = /^(#{1,6})\s+(.+?)\s*#*\s*$/;
|
|
18
|
+
const FENCE_RE = /^\s*```/;
|
|
19
|
+
const NAME_RE = /^[A-Za-z][A-Za-z0-9._-]*/;
|
|
20
|
+
|
|
21
|
+
const ESCAPES = { n: '\n', t: '\t', r: '\r', b: '\b', f: '\f', 0: '\0' };
|
|
22
|
+
const MAX_DEPTH = 100;
|
|
23
|
+
|
|
24
|
+
// Data-only JS literal parser (unquoted keys, single quotes, trailing commas,
|
|
25
|
+
// comments, `'a' + 'b'`). Nothing is evaluated; anything else throws.
|
|
26
|
+
function parseLiteral(src) {
|
|
27
|
+
let i = 0;
|
|
28
|
+
const fail = (message) => { throw new Error(`${message} at ${i}`); };
|
|
29
|
+
const ws = () => {
|
|
30
|
+
for (;;) {
|
|
31
|
+
while (i < src.length && /\s/.test(src[i])) i += 1;
|
|
32
|
+
if (src.startsWith('//', i)) {
|
|
33
|
+
while (i < src.length && src[i] !== '\n') i += 1;
|
|
34
|
+
} else if (src.startsWith('/*', i)) {
|
|
35
|
+
const end = src.indexOf('*/', i + 2);
|
|
36
|
+
if (end < 0) fail('unterminated comment');
|
|
37
|
+
i = end + 2;
|
|
38
|
+
} else return;
|
|
39
|
+
}
|
|
40
|
+
};
|
|
41
|
+
const string = () => {
|
|
42
|
+
const quote = src[i++];
|
|
43
|
+
let out = '';
|
|
44
|
+
while (i < src.length && src[i] !== quote) {
|
|
45
|
+
const ch = src[i++];
|
|
46
|
+
if (ch === '\\') {
|
|
47
|
+
const next = src[i++];
|
|
48
|
+
if (next === 'u') { out += String.fromCharCode(parseInt(src.slice(i, i + 4), 16)); i += 4; }
|
|
49
|
+
else if (next === 'x') { out += String.fromCharCode(parseInt(src.slice(i, i + 2), 16)); i += 2; }
|
|
50
|
+
else if (next !== '\n') out += Object.hasOwn(ESCAPES, next) ? ESCAPES[next] : next;
|
|
51
|
+
} else {
|
|
52
|
+
if (quote === '`' && ch === '$' && src[i] === '{') fail('template interpolation');
|
|
53
|
+
out += ch;
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
if (i >= src.length) fail('unterminated string');
|
|
57
|
+
i += 1;
|
|
58
|
+
return out;
|
|
59
|
+
};
|
|
60
|
+
const key = () => {
|
|
61
|
+
ws();
|
|
62
|
+
if ('"\'`'.includes(src[i])) return string();
|
|
63
|
+
const m = /^[A-Za-z_$][\w$]*|^-?\d+/.exec(src.slice(i));
|
|
64
|
+
if (!m) fail('bad key');
|
|
65
|
+
i += m[0].length;
|
|
66
|
+
return m[0];
|
|
67
|
+
};
|
|
68
|
+
const list = (close, depth, item) => {
|
|
69
|
+
i += 1;
|
|
70
|
+
for (;;) {
|
|
71
|
+
ws();
|
|
72
|
+
if (src[i] === close) { i += 1; return; }
|
|
73
|
+
if (i >= src.length) fail('unterminated literal');
|
|
74
|
+
item(depth);
|
|
75
|
+
ws();
|
|
76
|
+
if (src[i] === ',') i += 1;
|
|
77
|
+
else if (src[i] !== close) fail('expected , or ' + close);
|
|
78
|
+
}
|
|
79
|
+
};
|
|
80
|
+
const value = (depth) => {
|
|
81
|
+
if (depth > MAX_DEPTH) fail('nested too deep');
|
|
82
|
+
ws();
|
|
83
|
+
const ch = src[i];
|
|
84
|
+
if (ch === '[') {
|
|
85
|
+
const out = [];
|
|
86
|
+
list(']', depth, (d) => out.push(value(d + 1)));
|
|
87
|
+
return out;
|
|
88
|
+
}
|
|
89
|
+
if (ch === '{') {
|
|
90
|
+
const out = {};
|
|
91
|
+
list('}', depth, (d) => {
|
|
92
|
+
const name = key();
|
|
93
|
+
ws();
|
|
94
|
+
if (src[i++] !== ':') fail('expected :');
|
|
95
|
+
Object.defineProperty(out, name, { value: value(d + 1), enumerable: true, writable: true, configurable: true });
|
|
96
|
+
});
|
|
97
|
+
return out;
|
|
98
|
+
}
|
|
99
|
+
if ('"\'`'.includes(ch)) {
|
|
100
|
+
let out = string();
|
|
101
|
+
ws();
|
|
102
|
+
while (src[i] === '+') {
|
|
103
|
+
i += 1;
|
|
104
|
+
const more = value(depth + 1);
|
|
105
|
+
if (typeof more !== 'string') fail('only strings can be concatenated');
|
|
106
|
+
out += more;
|
|
107
|
+
ws();
|
|
108
|
+
}
|
|
109
|
+
return out;
|
|
110
|
+
}
|
|
111
|
+
const num = /^-?(\d+\.?\d*|\.\d+)(e[+-]?\d+)?/i.exec(src.slice(i));
|
|
112
|
+
if (num) { i += num[0].length; return Number(num[0]); }
|
|
113
|
+
const word = /^[A-Za-z]+/.exec(src.slice(i));
|
|
114
|
+
if (word && Object.hasOwn({ true: 1, false: 1, null: 1, undefined: 1 }, word[0])) {
|
|
115
|
+
i += word[0].length;
|
|
116
|
+
return { true: true, false: false, null: null, undefined: undefined }[word[0]];
|
|
117
|
+
}
|
|
118
|
+
return fail('unsupported value');
|
|
119
|
+
};
|
|
120
|
+
const result = value(0);
|
|
121
|
+
ws();
|
|
122
|
+
if (i < src.length) fail('trailing characters');
|
|
123
|
+
return result;
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
function decodeExpression(body, warnings, tag, key) {
|
|
127
|
+
const raw = body.trim();
|
|
128
|
+
if (!raw) return '';
|
|
129
|
+
try {
|
|
130
|
+
return JSON.parse(raw);
|
|
131
|
+
} catch {
|
|
132
|
+
try {
|
|
133
|
+
return parseLiteral(raw);
|
|
134
|
+
} catch {
|
|
135
|
+
// fall through to the bare-string and raw-text handling below
|
|
136
|
+
}
|
|
137
|
+
// Bare strings are common in hand-written MDX (`{code='x'}`); keep them.
|
|
138
|
+
if ((raw.startsWith('"') && raw.endsWith('"')) || (raw.startsWith("'") && raw.endsWith("'"))) {
|
|
139
|
+
return raw.slice(1, -1);
|
|
140
|
+
}
|
|
141
|
+
warnings.push(`could not parse {${key}} on <${tag}>; kept the raw text`);
|
|
142
|
+
return raw;
|
|
143
|
+
}
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
// Scan one JSX-like tag starting at `text[start] === '<'`. Quote- and
|
|
147
|
+
// brace-aware so `html='<div>'` and `data={"a": ">"}` do not end the tag early.
|
|
148
|
+
function scanTag(text, start) {
|
|
149
|
+
const nameMatch = NAME_RE.exec(text.slice(start + 1));
|
|
150
|
+
if (!nameMatch) return null;
|
|
151
|
+
const name = nameMatch[0];
|
|
152
|
+
let quote = null;
|
|
153
|
+
let depth = 0;
|
|
154
|
+
for (let i = start + 1 + name.length; i < text.length; i++) {
|
|
155
|
+
const ch = text[i];
|
|
156
|
+
if (quote) {
|
|
157
|
+
if (ch === '\\' && depth > 0) i += 1;
|
|
158
|
+
else if (ch === quote) quote = null;
|
|
159
|
+
continue;
|
|
160
|
+
}
|
|
161
|
+
if (ch === '"' || ch === "'" || ch === '`') {
|
|
162
|
+
quote = ch;
|
|
163
|
+
continue;
|
|
164
|
+
}
|
|
165
|
+
if (ch === '{') depth += 1;
|
|
166
|
+
else if (ch === '}' && depth > 0) depth -= 1;
|
|
167
|
+
else if (ch === '>' && depth === 0) {
|
|
168
|
+
const selfClosing = text[i - 1] === '/';
|
|
169
|
+
return {
|
|
170
|
+
name,
|
|
171
|
+
attrsText: text.slice(start + 1 + name.length, selfClosing ? i - 1 : i),
|
|
172
|
+
selfClosing,
|
|
173
|
+
end: i + 1
|
|
174
|
+
};
|
|
175
|
+
}
|
|
176
|
+
}
|
|
177
|
+
return null;
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
function closingIndex(text, from, name) {
|
|
181
|
+
const open = new RegExp('<' + name + '(?=[\\s/>])', 'g');
|
|
182
|
+
const close = new RegExp('</' + name + '\\s*>', 'g');
|
|
183
|
+
let depth = 0;
|
|
184
|
+
let cursor = from;
|
|
185
|
+
while (cursor < text.length) {
|
|
186
|
+
open.lastIndex = cursor;
|
|
187
|
+
close.lastIndex = cursor;
|
|
188
|
+
const nextOpen = open.exec(text);
|
|
189
|
+
const nextClose = close.exec(text);
|
|
190
|
+
if (!nextClose) return -1;
|
|
191
|
+
if (nextOpen && nextOpen.index < nextClose.index) {
|
|
192
|
+
const scanned = scanTag(text, nextOpen.index);
|
|
193
|
+
if (!scanned) {
|
|
194
|
+
cursor = nextClose.index + nextClose[0].length;
|
|
195
|
+
continue;
|
|
196
|
+
}
|
|
197
|
+
if (!scanned.selfClosing) depth += 1;
|
|
198
|
+
cursor = scanned.end;
|
|
199
|
+
continue;
|
|
200
|
+
}
|
|
201
|
+
if (depth === 0) return nextClose.index;
|
|
202
|
+
depth -= 1;
|
|
203
|
+
cursor = nextClose.index + nextClose[0].length;
|
|
204
|
+
}
|
|
205
|
+
return -1;
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
function parseAttributes(attrsText, warnings, tagName) {
|
|
209
|
+
const props = {};
|
|
210
|
+
// Keys whose {expression} could not be parsed; blocks that must not drop data read this.
|
|
211
|
+
Object.defineProperty(props, '_unparsed', { value: [], enumerable: false });
|
|
212
|
+
let i = 0;
|
|
213
|
+
const text = attrsText;
|
|
214
|
+
const skipSpace = () => {
|
|
215
|
+
while (i < text.length && /\s/.test(text[i])) i += 1;
|
|
216
|
+
};
|
|
217
|
+
while (i < text.length) {
|
|
218
|
+
skipSpace();
|
|
219
|
+
if (i >= text.length) break;
|
|
220
|
+
const nameMatch = /^[A-Za-z_][A-Za-z0-9_:.-]*/.exec(text.slice(i));
|
|
221
|
+
if (!nameMatch) {
|
|
222
|
+
i += 1;
|
|
223
|
+
continue;
|
|
224
|
+
}
|
|
225
|
+
const key = nameMatch[0];
|
|
226
|
+
i += key.length;
|
|
227
|
+
skipSpace();
|
|
228
|
+
if (text[i] !== '=') {
|
|
229
|
+
props[key] = true;
|
|
230
|
+
continue;
|
|
231
|
+
}
|
|
232
|
+
i += 1;
|
|
233
|
+
skipSpace();
|
|
234
|
+
const ch = text[i];
|
|
235
|
+
if (ch === '"' || ch === "'" || ch === '`') {
|
|
236
|
+
const end = text.indexOf(ch, i + 1);
|
|
237
|
+
const raw = end < 0 ? text.slice(i + 1) : text.slice(i + 1, end);
|
|
238
|
+
i = end < 0 ? text.length : end + 1;
|
|
239
|
+
if (ch === '`' && raw.includes('${')) {
|
|
240
|
+
warnings.push(`template interpolation in ${tagName}.${key} is not supported; kept the literal text`);
|
|
241
|
+
}
|
|
242
|
+
props[key] = raw;
|
|
243
|
+
continue;
|
|
244
|
+
}
|
|
245
|
+
if (ch === '{') {
|
|
246
|
+
let depth = 0;
|
|
247
|
+
let end = -1;
|
|
248
|
+
let q = null;
|
|
249
|
+
for (let j = i; j < text.length; j++) {
|
|
250
|
+
if (q) {
|
|
251
|
+
if (text[j] === '\\') j += 1;
|
|
252
|
+
else if (text[j] === q) q = null;
|
|
253
|
+
} else if (text[j] === '"' || text[j] === "'" || text[j] === '`') q = text[j];
|
|
254
|
+
else if (text[j] === '{') depth += 1;
|
|
255
|
+
else if (text[j] === '}') {
|
|
256
|
+
depth -= 1;
|
|
257
|
+
if (depth === 0) {
|
|
258
|
+
end = j;
|
|
259
|
+
break;
|
|
260
|
+
}
|
|
261
|
+
}
|
|
262
|
+
}
|
|
263
|
+
const body = end < 0 ? text.slice(i + 1) : text.slice(i + 1, end);
|
|
264
|
+
i = end < 0 ? text.length : end + 1;
|
|
265
|
+
const before = warnings.length;
|
|
266
|
+
props[key] = decodeExpression(body, warnings, tagName, key);
|
|
267
|
+
if (warnings.length > before) props._unparsed.push(key);
|
|
268
|
+
continue;
|
|
269
|
+
}
|
|
270
|
+
props[key] = true;
|
|
271
|
+
}
|
|
272
|
+
return props;
|
|
273
|
+
}
|
|
274
|
+
|
|
275
|
+
function splitFrontmatter(text, warnings) {
|
|
276
|
+
const match = FRONTMATTER_RE.exec(text);
|
|
277
|
+
if (!match) return { frontmatter: {}, body: text };
|
|
278
|
+
const frontmatter = {};
|
|
279
|
+
for (const line of match[1].split('\n')) {
|
|
280
|
+
const kv = /^([A-Za-z_][A-Za-z0-9_-]*)\s*:\s*(.*)$/.exec(line.trim());
|
|
281
|
+
if (!kv) {
|
|
282
|
+
if (line.trim() && !line.trim().startsWith('#')) warnings.push(`ignored frontmatter line: ${line.trim()}`);
|
|
283
|
+
continue;
|
|
284
|
+
}
|
|
285
|
+
const value = kv[2].trim();
|
|
286
|
+
if (!value) frontmatter[kv[1]] = '';
|
|
287
|
+
else if (/^(true|false)$/i.test(value)) frontmatter[kv[1]] = value.toLowerCase() === 'true';
|
|
288
|
+
else if (/^-?\d+(\.\d+)?$/.test(value)) frontmatter[kv[1]] = Number(value);
|
|
289
|
+
else frontmatter[kv[1]] = value.replace(/^["']|["']$/g, '');
|
|
290
|
+
}
|
|
291
|
+
return { frontmatter, body: text.slice(match[0].length) };
|
|
292
|
+
}
|
|
293
|
+
|
|
294
|
+
/**
|
|
295
|
+
* Parse an MDX source string.
|
|
296
|
+
*
|
|
297
|
+
* Returns the block array (`.frontmatter` and `.warnings` hang off it), so
|
|
298
|
+
* callers that only care about content can iterate it directly.
|
|
299
|
+
*/
|
|
300
|
+
function parseMdx(text, opts = {}) {
|
|
301
|
+
const warnings = [];
|
|
302
|
+
const src = String(text == null ? '' : text).replace(/\r\n?/g, '\n');
|
|
303
|
+
const { frontmatter, body } = splitFrontmatter(src, warnings);
|
|
304
|
+
const blocks = [];
|
|
305
|
+
let prose = [];
|
|
306
|
+
let proseLine = 0;
|
|
307
|
+
let fence = false;
|
|
308
|
+
|
|
309
|
+
// 1-based line in the original source file; every block records where it starts.
|
|
310
|
+
const base = (opts.firstLine || 1) + (src.length - body.length ? src.slice(0, src.length - body.length).split('\n').length - 1 : 0);
|
|
311
|
+
let cur = 0;
|
|
312
|
+
let curLine = base;
|
|
313
|
+
const lineOf = (idx) => {
|
|
314
|
+
if (idx < cur) { cur = 0; curLine = base; }
|
|
315
|
+
for (let k = body.indexOf('\n', cur); k >= 0 && k < idx; k = body.indexOf('\n', cur)) { curLine += 1; cur = k + 1; }
|
|
316
|
+
return curLine;
|
|
317
|
+
};
|
|
318
|
+
const pushProse = (line, at) => {
|
|
319
|
+
if (!proseLine && line.trim()) proseLine = lineOf(at);
|
|
320
|
+
prose.push(line);
|
|
321
|
+
};
|
|
322
|
+
|
|
323
|
+
const flush = () => {
|
|
324
|
+
const chunk = prose.join('\n').trim();
|
|
325
|
+
if (chunk) blocks.push({ type: 'prose', text: chunk, line: proseLine });
|
|
326
|
+
prose = [];
|
|
327
|
+
proseLine = 0;
|
|
328
|
+
};
|
|
329
|
+
|
|
330
|
+
let i = 0;
|
|
331
|
+
while (i < body.length) {
|
|
332
|
+
const lineEnd = body.indexOf('\n', i);
|
|
333
|
+
const line = body.slice(i, lineEnd < 0 ? body.length : lineEnd);
|
|
334
|
+
const next = lineEnd < 0 ? body.length : lineEnd + 1;
|
|
335
|
+
|
|
336
|
+
if (FENCE_RE.test(line)) {
|
|
337
|
+
fence = !fence;
|
|
338
|
+
pushProse(line, i);
|
|
339
|
+
i = next;
|
|
340
|
+
continue;
|
|
341
|
+
}
|
|
342
|
+
if (!fence) {
|
|
343
|
+
const heading = HEADING_RE.exec(line);
|
|
344
|
+
if (heading) {
|
|
345
|
+
flush();
|
|
346
|
+
blocks.push({
|
|
347
|
+
type: 'heading',
|
|
348
|
+
level: heading[1].length,
|
|
349
|
+
text: heading[2].trim(),
|
|
350
|
+
line: lineOf(i)
|
|
351
|
+
});
|
|
352
|
+
i = next;
|
|
353
|
+
continue;
|
|
354
|
+
}
|
|
355
|
+
// Only capitalized tags are block components; lowercase markup stays
|
|
356
|
+
// prose so it renders as (escaped) markdown.
|
|
357
|
+
if (line.trimStart().startsWith('<') && /<[A-Z]/.test(line.trimStart())) {
|
|
358
|
+
const start = i + line.length - line.trimStart().length;
|
|
359
|
+
const scanned = scanTag(body, start);
|
|
360
|
+
if (scanned) {
|
|
361
|
+
flush();
|
|
362
|
+
if (scanned.selfClosing) {
|
|
363
|
+
blocks.push({
|
|
364
|
+
type: 'tag',
|
|
365
|
+
name: scanned.name,
|
|
366
|
+
props: parseAttributes(scanned.attrsText, warnings, scanned.name),
|
|
367
|
+
children: [],
|
|
368
|
+
line: lineOf(start),
|
|
369
|
+
raw: body.slice(start, scanned.end)
|
|
370
|
+
});
|
|
371
|
+
i = scanned.end;
|
|
372
|
+
continue;
|
|
373
|
+
}
|
|
374
|
+
const close = closingIndex(body, scanned.end, scanned.name);
|
|
375
|
+
if (close < 0) {
|
|
376
|
+
warnings.push(`unclosed <${scanned.name}>; the rest of the document was not parsed`);
|
|
377
|
+
blocks.push({
|
|
378
|
+
type: 'malformed',
|
|
379
|
+
line: lineOf(start),
|
|
380
|
+
raw: body.slice(start),
|
|
381
|
+
message: `unclosed <${scanned.name}>`
|
|
382
|
+
});
|
|
383
|
+
i = body.length;
|
|
384
|
+
continue;
|
|
385
|
+
}
|
|
386
|
+
const closeEnd = body.indexOf('>', close) + 1;
|
|
387
|
+
blocks.push({
|
|
388
|
+
type: 'tag',
|
|
389
|
+
name: scanned.name,
|
|
390
|
+
props: parseAttributes(scanned.attrsText, warnings, scanned.name),
|
|
391
|
+
line: lineOf(start),
|
|
392
|
+
children: parseMdx(body.slice(scanned.end, close), { firstLine: lineOf(scanned.end) }),
|
|
393
|
+
childrenRaw: body.slice(scanned.end, close),
|
|
394
|
+
raw: body.slice(start, closeEnd)
|
|
395
|
+
});
|
|
396
|
+
i = closeEnd;
|
|
397
|
+
continue;
|
|
398
|
+
}
|
|
399
|
+
}
|
|
400
|
+
}
|
|
401
|
+
pushProse(line, i);
|
|
402
|
+
i = next;
|
|
403
|
+
}
|
|
404
|
+
flush();
|
|
405
|
+
|
|
406
|
+
blocks.frontmatter = frontmatter;
|
|
407
|
+
blocks.warnings = warnings;
|
|
408
|
+
return blocks;
|
|
409
|
+
}
|
|
410
|
+
|
|
411
|
+
module.exports = { parseMdx, scanTag, parseAttributes, parseLiteral };
|