@flatkit/compiler 0.1.1 → 0.1.2

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.
@@ -0,0 +1,326 @@
1
+ // src/lint.ts
2
+ import { analyzeExpr, STD_CONSTANTS, STD_FUNCTIONS, STD_IDS, STD_OBJECTS } from "@flatkit/engine/expr";
3
+ import { packageFunctionNames } from "@flatkit/engine/stdlib";
4
+ import { parseUnits } from "@flatkit/engine/dsl";
5
+ function collectAssigned(actions, into) {
6
+ for (const a of actions) {
7
+ if (a.do === "setVar") into.add(a.name);
8
+ else if (a.do === "if") {
9
+ collectAssigned(a.then, into);
10
+ if (a.else) collectAssigned(a.else, into);
11
+ } else if (a.do === "repeat") collectAssigned(a.body, into);
12
+ else if (a.do === "repeatRange") {
13
+ into.add(a.var);
14
+ collectAssigned(a.body, into);
15
+ }
16
+ }
17
+ }
18
+ function localVariables(units) {
19
+ const vars = /* @__PURE__ */ new Set();
20
+ for (const u of units) {
21
+ if (u.kind === "declare") vars.add(u.name);
22
+ else if (u.kind === "event" || u.kind === "frameActions") collectAssigned(u.body, vars);
23
+ else if (u.kind === "each") vars.add(u.as);
24
+ else if (u.kind === "interactor") {
25
+ if (u.varX) vars.add(u.varX);
26
+ if (u.varY) vars.add(u.varY);
27
+ } else if (u.kind === "drop") collectAssigned(u.body, vars);
28
+ else if (u.kind === "func") {
29
+ for (const p of u.func.params) vars.add(p);
30
+ if (u.func.kind === "proc") collectAssigned(u.func.body, vars);
31
+ }
32
+ }
33
+ return vars;
34
+ }
35
+ var TEXT_CALL = /\btext\s*\(/;
36
+ function lint(src, ctx = {}) {
37
+ const { units, diagnostics, sites } = parseUnits(src);
38
+ const out = [...diagnostics];
39
+ const variables = localVariables(units);
40
+ for (const v of ctx.variables ?? []) variables.add(v);
41
+ const knownIds = /* @__PURE__ */ new Set([...STD_IDS, ...STD_CONSTANTS, ...variables]);
42
+ const knownFns = new Set(STD_FUNCTIONS);
43
+ for (const u of units) {
44
+ if (u.kind === "func") knownFns.add(u.func.name);
45
+ else if (u.kind === "use") for (const name of packageFunctionNames(u.name)) knownFns.add(name);
46
+ }
47
+ for (const f of ctx.functions ?? []) knownFns.add(f);
48
+ const knownObjs = new Set(STD_OBJECTS);
49
+ for (const o of ctx.objects ?? []) knownObjs.add(o);
50
+ const labels = ctx.labels ? new Set(ctx.labels) : null;
51
+ for (const s of sites) {
52
+ if (s.kind === "expr") {
53
+ if (TEXT_CALL.test(s.text)) {
54
+ out.push({ line: s.line, col: s.col, message: 'text("\u2026") is only allowed as an argument to "send"' });
55
+ continue;
56
+ }
57
+ const a = analyzeExpr(s.text);
58
+ if (!a.ok) {
59
+ out.push({ line: s.line, col: s.col, message: `invalid expression: ${a.error}` });
60
+ continue;
61
+ }
62
+ for (const fn of a.refs.calls) if (!knownFns.has(fn)) out.push({ line: s.line, col: s.col, message: `unknown function "${fn}"` });
63
+ for (const o of a.refs.members)
64
+ if (!knownObjs.has(o)) out.push({ line: s.line, col: s.col, message: `unknown object "${o}" (expected: ${[...knownObjs].join(", ")})` });
65
+ for (const id of a.refs.ids)
66
+ if (!knownIds.has(id))
67
+ out.push({ line: s.line, col: s.col, message: `unknown variable "${id}"${variables.size ? "" : ' \u2014 declare it with "let"'}` });
68
+ } else if (labels && !labels.has(s.name)) {
69
+ out.push({ line: s.line, col: s.col, message: `unknown label "${s.name}"` });
70
+ }
71
+ }
72
+ return out.sort((a, b) => a.line - b.line || a.col - b.col);
73
+ }
74
+ function lintReport(src, ctx = {}) {
75
+ return lint(src, ctx).map((d) => `${d.line}:${d.col}: ${d.message}`).join("\n");
76
+ }
77
+
78
+ // src/scopeProgram.ts
79
+ function matchBrace(s, open) {
80
+ let depth = 0;
81
+ for (let i = open; i < s.length; i++) {
82
+ const c = s[i];
83
+ if (c === '"') {
84
+ i++;
85
+ while (i < s.length && s[i] !== '"') {
86
+ if (s[i] === "\\") i++;
87
+ i++;
88
+ }
89
+ ;
90
+ continue;
91
+ }
92
+ if (c === "/" && s[i + 1] === "/") {
93
+ while (i < s.length && s[i] !== "\n") i++;
94
+ continue;
95
+ }
96
+ if (c === "{") depth++;
97
+ else if (c === "}") {
98
+ depth--;
99
+ if (depth === 0) return i;
100
+ }
101
+ }
102
+ return -1;
103
+ }
104
+ function splitScopeProgram(text) {
105
+ let rest = "";
106
+ let i = 0;
107
+ const objects = [];
108
+ while (i < text.length) {
109
+ const m = /object\s+"((?:[^"\\]|\\.)*)"\s*\{/.exec(text.slice(i));
110
+ if (!m) {
111
+ rest += text.slice(i);
112
+ break;
113
+ }
114
+ const start = i + m.index;
115
+ rest += text.slice(i, start);
116
+ const ob = start + m[0].length - 1;
117
+ const oc = matchBrace(text, ob);
118
+ if (oc < 0) {
119
+ rest += text.slice(start);
120
+ break;
121
+ }
122
+ objects.push({ name: m[1].replace(/\\(.)/g, (_, c) => c === "n" ? "\n" : c), body: text.slice(ob + 1, oc) });
123
+ i = oc + 1;
124
+ }
125
+ return { rest, objects };
126
+ }
127
+ function scopeRegions(text) {
128
+ const lineAt = (off) => text.slice(0, off).split("\n").length;
129
+ const regions = [];
130
+ let i = 0;
131
+ let restStart = 0;
132
+ while (i < text.length) {
133
+ const m = /object\s+"((?:[^"\\]|\\.)*)"\s*\{/.exec(text.slice(i));
134
+ if (!m) break;
135
+ const start = i + m.index;
136
+ const gap = text.slice(restStart, start);
137
+ if (gap.trim()) regions.push({ body: gap, line: lineAt(restStart) });
138
+ const ob = start + m[0].length - 1;
139
+ const oc = matchBrace(text, ob);
140
+ if (oc < 0) break;
141
+ regions.push({ body: text.slice(ob + 1, oc), line: lineAt(ob + 1) });
142
+ i = oc + 1;
143
+ restStart = i;
144
+ }
145
+ const tail = text.slice(restStart);
146
+ if (tail.trim()) regions.push({ body: tail, line: lineAt(restStart) });
147
+ return regions;
148
+ }
149
+ var esc = (s) => s.replace(/\\/g, "\\\\").replace(/"/g, '\\"').replace(/\n/g, "\\n");
150
+ var indent = (body) => body.split("\n").map((l) => l.trim() ? " " + l : l).join("\n");
151
+ function formatObjectBlock(name, body) {
152
+ const inner = body.trim();
153
+ return `object "${esc(name)}" {
154
+ ${inner ? indent(inner) + "\n" : ""}}`;
155
+ }
156
+ function joinScopeProgram(rest, objects) {
157
+ const parts = [];
158
+ if (rest.trim()) parts.push(rest.trim());
159
+ for (const o of objects) if (o.body.trim()) parts.push(formatObjectBlock(o.name, o.body));
160
+ return parts.length ? parts.join("\n\n") + "\n" : "";
161
+ }
162
+
163
+ // src/programDoc.ts
164
+ import { printUnits } from "@flatkit/engine/dsl";
165
+ import { functionsToUnits, importsToUnits, objectToUnits, timelineToUnits, variablesToUnits } from "@flatkit/engine/scriptDoc";
166
+ import { contextLayers, getScopeTimeline, isContainer, isGroup, isText, isImage } from "@flatkit/engine/layers";
167
+ import { importedFunctions } from "@flatkit/engine/stdlib";
168
+ import { objectNames } from "@flatkit/engine/sceneRefs";
169
+ import { itemBBox, dropZoneBounds } from "@flatkit/engine/groups";
170
+ import { bboxIntersects } from "@flatkit/engine/bbox";
171
+ import { parseUnits as parseUnits2 } from "@flatkit/engine/dsl";
172
+ function scopeProgram(doc, editPath = []) {
173
+ const atRoot = editPath.length === 0;
174
+ const items = contextLayers(doc, editPath).flatMap((l) => l.items);
175
+ const nameCount = /* @__PURE__ */ new Map();
176
+ for (const it of items) if (isContainer(it) && it.name) nameCount.set(it.name, (nameCount.get(it.name) ?? 0) + 1);
177
+ const scripted = (it) => !!doc.interactions?.some((i) => i.targetId === it.id) || !!doc.interactors?.some((i) => i.targetId === it.id) || !!(it.expressions && Object.keys(it.expressions).length);
178
+ const objects = items.filter((it) => isContainer(it) && !!it.name && nameCount.get(it.name) === 1 && scripted(it)).map((it) => ({ name: it.name, body: printUnits(objectToUnits(it.id, doc.interactions, it.expressions, doc.interactors)) }));
179
+ const imports = atRoot ? printUnits(importsToUnits(doc.imports)) : "";
180
+ const funcs = atRoot ? printUnits(functionsToUnits(doc.functions)) : "";
181
+ const globals = atRoot ? printUnits(variablesToUnits(doc.variables)) : "";
182
+ const scene = printUnits(timelineToUnits(getScopeTimeline(doc, editPath)));
183
+ const head = [imports, globals, funcs, scene].filter((t) => t.trim()).join("\n\n");
184
+ return joinScopeProgram(head, objects);
185
+ }
186
+ function docLintContext(doc, editPath = [], extraVars) {
187
+ return {
188
+ variables: [...Object.keys(doc.variables ?? {}), ...extraVars ?? []],
189
+ labels: (getScopeTimeline(doc, editPath)?.labels ?? []).map((l) => l.name),
190
+ functions: [...(doc.functions ?? []).map((f) => f.name), ...importedFunctions(doc.imports).map((f) => f.name)],
191
+ objects: objectNames(contextLayers(doc, editPath))
192
+ };
193
+ }
194
+ function scopes(doc) {
195
+ return [{ label: "scene", editPath: [] }, ...doc.symbols.map((s) => ({ label: s.name, editPath: [{ kind: "symbol", symbolId: s.id, name: s.name }] }))];
196
+ }
197
+ function allScopeVariables(doc) {
198
+ const set = /* @__PURE__ */ new Set();
199
+ for (const { editPath } of scopes(doc))
200
+ for (const r of scopeRegions(scopeProgram(doc, editPath)))
201
+ for (const v of localVariables(parseUnits2(r.body).units)) set.add(v);
202
+ return [...set];
203
+ }
204
+ function itemNameById(doc, id) {
205
+ let found = null;
206
+ const walk = (layers) => {
207
+ for (const l of layers) for (const it of l.items) {
208
+ if (found) return;
209
+ if ("name" in it && it.id === id) {
210
+ found = it.name;
211
+ return;
212
+ }
213
+ if (isGroup(it)) walk(it.layers);
214
+ }
215
+ };
216
+ walk(doc.layers);
217
+ return found;
218
+ }
219
+ var escapeRe = (s) => s.replace(/[.*+?^${}()|[\]\\-]/g, "\\$&");
220
+ function docStructureWarnings(doc) {
221
+ const out = [];
222
+ const names = new Set(objectNames(doc.layers));
223
+ for (const it of doc.interactions ?? []) {
224
+ if (it.event === "drop" && it.over && !names.has(it.over)) {
225
+ const who = itemNameById(doc, it.targetId) ?? it.targetId;
226
+ out.push({ scope: "scene", diag: { line: 1, col: 1, severity: "warning", message: `unknown drop zone "${it.over}" (object "${who}") \u2014 no item of the scene carries this name` } });
227
+ }
228
+ }
229
+ const names0 = Object.keys(doc.variables ?? {});
230
+ if (names0.length) {
231
+ const allText = scopes(doc).map(({ editPath }) => scopeProgram(doc, editPath)).join("\n");
232
+ for (const name of names0) {
233
+ const m = allText.match(new RegExp(`(?<![\\w-])${escapeRe(name)}(?![\\w-])`, "g"));
234
+ if ((m?.length ?? 0) <= 1) out.push({ scope: "scene", diag: { line: 1, col: 1, severity: "warning", message: `global variable "${name}" never used (declared, but neither read nor written)` } });
235
+ }
236
+ }
237
+ out.push(...docLayoutWarnings(doc));
238
+ return out;
239
+ }
240
+ var warn = (message) => ({ scope: "scene", diag: { line: 1, col: 1, severity: "warning", message } });
241
+ var itemLabel = (it) => "name" in it && it.name ? it.name : "kind" in it ? it.kind : "path";
242
+ function estTextWidth(t) {
243
+ const advance = (t.weight && t.weight >= 700 ? 0.56 : 0.52) * t.size;
244
+ let longest = 0;
245
+ for (const line of t.content.split("\n")) longest = Math.max(longest, line.length);
246
+ return longest * advance;
247
+ }
248
+ function docLayoutWarnings(doc) {
249
+ const out = [];
250
+ const W = doc.width, H = doc.height;
251
+ const TOL = 8;
252
+ const r0 = (n) => Math.round(n);
253
+ const dynamicPos = (it) => "expressions" in it && it.expressions && (it.expressions.x != null || it.expressions.y != null);
254
+ for (const layer of doc.layers) {
255
+ for (const it of layer.items) {
256
+ if (!(isText(it) || isImage(it)) || dynamicPos(it)) continue;
257
+ const b = itemBBox(doc, it);
258
+ if (!b) continue;
259
+ const visible = b.maxX > 0 && b.minX < W && b.maxY > 0 && b.minY < H;
260
+ const clipped = b.minX < -TOL || b.minY < -TOL || b.maxX > W + TOL || b.maxY > H + TOL;
261
+ if (visible && clipped) {
262
+ out.push(warn(`"${itemLabel(it)}" clipped at the canvas edge: bbox [${r0(b.minX)},${r0(b.minY)} -> ${r0(b.maxX)},${r0(b.maxY)}] outside 0,0 -> ${W},${H}`));
263
+ }
264
+ }
265
+ }
266
+ for (const it of doc.layers.flatMap((l) => l.items)) {
267
+ if (!isText(it) || it.wrap || dynamicPos(it)) continue;
268
+ const estW = estTextWidth(it);
269
+ const left = it.align === "left" ? 0 : it.align === "right" ? it.box.w - estW : it.box.w / 2 - estW / 2;
270
+ const wl = it.transform.e + left, wr = wl + estW;
271
+ if (wr > W + TOL || wl < -TOL) {
272
+ out.push(warn(`text "${it.content.slice(0, 24)}${it.content.length > 24 ? "\u2026" : ""}" overflows the canvas (estimated ~${r0(estW)} px, edge ${r0(wl)}->${r0(wr)} outside 0->${W}) \u2014 add "wrap"`));
273
+ }
274
+ }
275
+ const zones = [];
276
+ const collectZones = (layers) => {
277
+ for (const l of layers) for (const it of l.items) {
278
+ if (isGroup(it) && it.hitbox && it.name) {
279
+ const b = dropZoneBounds(doc, it.name);
280
+ if (b) zones.push({ name: it.name, b });
281
+ }
282
+ if (isGroup(it)) collectZones(it.layers);
283
+ }
284
+ };
285
+ collectZones(doc.layers);
286
+ for (let i = 0; i < zones.length; i++) for (let j = i + 1; j < zones.length; j++) {
287
+ if (bboxIntersects(zones[i].b, zones[j].b)) out.push(warn(`overlapping hitboxes: "${zones[i].name}" and "${zones[j].name}" -> ambiguous drop`));
288
+ }
289
+ return out;
290
+ }
291
+ function lintDoc(doc) {
292
+ const out = [];
293
+ const allVars = allScopeVariables(doc);
294
+ for (const { label, editPath } of scopes(doc)) {
295
+ const ctx = docLintContext(doc, editPath, allVars);
296
+ for (const r of scopeRegions(scopeProgram(doc, editPath))) for (const d of lint(r.body, ctx)) out.push({ scope: label, diag: { ...d, line: d.line + r.line - 1 } });
297
+ }
298
+ out.push(...docStructureWarnings(doc));
299
+ return out;
300
+ }
301
+ var sev = (d) => d.severity === "warning" ? "warning" : "error";
302
+ function lintDocReport(doc) {
303
+ return lintDoc(doc).map(({ scope, diag }) => `[${scope}] ${diag.line}:${diag.col}: ${sev(diag)}: ${diag.message}`).join("\n");
304
+ }
305
+ function docHasErrors(doc) {
306
+ return lintDoc(doc).some(({ diag }) => sev(diag) === "error");
307
+ }
308
+
309
+ export {
310
+ localVariables,
311
+ lint,
312
+ lintReport,
313
+ splitScopeProgram,
314
+ scopeRegions,
315
+ formatObjectBlock,
316
+ joinScopeProgram,
317
+ scopeProgram,
318
+ docLintContext,
319
+ allScopeVariables,
320
+ docStructureWarnings,
321
+ docLayoutWarnings,
322
+ lintDoc,
323
+ lintDocReport,
324
+ docHasErrors
325
+ };
326
+ //# sourceMappingURL=chunk-UADRVB63.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/lint.ts","../src/scopeProgram.ts","../src/programDoc.ts"],"sourcesContent":["// ─────────────────────────────────────────────────────────────────────────────\n// lint.ts — linter for the FlatInk Script language (batch L3).\n//\n// Builds on the parser (dsl.ts) for syntax errors + positions, then adds\n// SEMANTIC validation, ~free and PURE:\n// • each expression is compiled (compileExpr) → expression syntax error;\n// • referenced functions / member objects / identifiers are checked against\n// the standard environment (expr.ts) + the known variables → \"unknown\";\n// • `go to \"label\"` is checked against the set of known labels.\n//\n// Tolerant and kid-friendly: a variable is \"known\" if it is declared\n// (`let`) OR simply assigned (`x = …`) somewhere in the source, or provided\n// by the caller (document global variables). Labels are only checked\n// if the caller provides their list (otherwise we don't know the universe).\n// ─────────────────────────────────────────────────────────────────────────────\nimport { analyzeExpr, STD_CONSTANTS, STD_FUNCTIONS, STD_IDS, STD_OBJECTS } from '@flatkit/engine/expr'\nimport { packageFunctionNames } from '@flatkit/engine/stdlib'\nimport { parseUnits, type Diagnostic, type ScriptUnit } from '@flatkit/engine/dsl'\nimport type { Action } from '@flatkit/engine/actions'\n\nexport type LintContext = {\n /** Known variables in addition to those declared/assigned in the source. */\n variables?: Iterable<string>\n /** Known labels (timeline). Absent = we don't check `go to \"…\"`. */\n labels?: Iterable<string>\n /** Known functions in addition to those defined in the source. */\n functions?: Iterable<string>\n /** Scene objects referenceable by name (`Hero.x`) — in addition to mouse/keys. */\n objects?: Iterable<string>\n}\n\n/** Collects the names of ASSIGNED variables (setVar) + loop vars, inside action bodies. */\nfunction collectAssigned(actions: Action[], into: Set<string>): void {\n for (const a of actions) {\n if (a.do === 'setVar') into.add(a.name)\n else if (a.do === 'if') {\n collectAssigned(a.then, into)\n if (a.else) collectAssigned(a.else, into)\n } else if (a.do === 'repeat') collectAssigned(a.body, into)\n else if (a.do === 'repeatRange') { into.add(a.var); collectAssigned(a.body, into) } // i is known in the body\n }\n}\n\n/** Locally known variables: `let` declarations + every assigned variable. */\nexport function localVariables(units: ScriptUnit[]): Set<string> {\n const vars = new Set<string>()\n for (const u of units) {\n if (u.kind === 'declare') vars.add(u.name)\n else if (u.kind === 'event' || u.kind === 'frameActions') collectAssigned(u.body, vars)\n else if (u.kind === 'each') vars.add(u.as) // the index is known in the each bindings\n else if (u.kind === 'interactor') { if (u.varX) vars.add(u.varX); if (u.varY) vars.add(u.varY) } // drag writes these variables\n else if (u.kind === 'drop') collectAssigned(u.body, vars)\n else if (u.kind === 'func') { for (const p of u.func.params) vars.add(p); if (u.func.kind === 'proc') collectAssigned(u.func.body, vars) } // params + assignments\n }\n return vars\n}\n\n/** Detects a `text(` call (whole word) in an expression → only allowed in a `send` payload. */\nconst TEXT_CALL = /\\btext\\s*\\(/\n\n/** Analyze a DSL source and return all diagnostics (syntax + semantic). */\nexport function lint(src: string, ctx: LintContext = {}): Diagnostic[] {\n const { units, diagnostics, sites } = parseUnits(src)\n const out: Diagnostic[] = [...diagnostics]\n\n const variables = localVariables(units)\n for (const v of ctx.variables ?? []) variables.add(v)\n const knownIds = new Set<string>([...STD_IDS, ...STD_CONSTANTS, ...variables])\n const knownFns = new Set(STD_FUNCTIONS)\n for (const u of units) {\n if (u.kind === 'func') knownFns.add(u.func.name) // user-defined functions\n else if (u.kind === 'use') for (const name of packageFunctionNames(u.name)) knownFns.add(name) // package functions (bare + qualified)\n }\n for (const f of ctx.functions ?? []) knownFns.add(f)\n const knownObjs = new Set(STD_OBJECTS)\n for (const o of ctx.objects ?? []) knownObjs.add(o) // scene objects (Hero.x…)\n const labels = ctx.labels ? new Set(ctx.labels) : null\n\n for (const s of sites) {\n if (s.kind === 'expr') {\n // `text(…)` is valid ONLY as a `send` payload (consumed by the parser, never\n // exposed as an expression site). Seeing it here = out-of-context use → dedicated error.\n if (TEXT_CALL.test(s.text)) {\n out.push({ line: s.line, col: s.col, message: 'text(\"…\") is only allowed as an argument to \"send\"' })\n continue\n }\n const a = analyzeExpr(s.text)\n if (!a.ok) {\n out.push({ line: s.line, col: s.col, message: `invalid expression: ${a.error}` })\n continue\n }\n for (const fn of a.refs.calls) if (!knownFns.has(fn)) out.push({ line: s.line, col: s.col, message: `unknown function \"${fn}\"` })\n for (const o of a.refs.members)\n if (!knownObjs.has(o)) out.push({ line: s.line, col: s.col, message: `unknown object \"${o}\" (expected: ${[...knownObjs].join(', ')})` })\n for (const id of a.refs.ids)\n if (!knownIds.has(id))\n out.push({ line: s.line, col: s.col, message: `unknown variable \"${id}\"${variables.size ? '' : ' — declare it with \"let\"'}` })\n } else if (labels && !labels.has(s.name)) {\n out.push({ line: s.line, col: s.col, message: `unknown label \"${s.name}\"` })\n }\n }\n\n return out.sort((a, b) => a.line - b.line || a.col - b.col)\n}\n\n/**\n * Lint report ready to feed back to a generator (LLM/CLI): `''` = no issue (the code passes),\n * otherwise one `line:col: message` line per diagnostic. Primitive of the \"generate → lint →\n * fix\" loop: the language fits in few tokens, robustness comes from this feedback, not from context.\n */\nexport function lintReport(src: string, ctx: LintContext = {}): string {\n return lint(src, ctx).map((d) => `${d.line}:${d.col}: ${d.message}`).join('\\n')\n}\n","// ─────────────────────────────────────────────────────────────────────────────\n// scopeProgram.ts — the \"unified file\" of a scope (the Scene, or a Symbol).\n//\n// Vision: one file = one scope. All the code of a scope fits in ONE text:\n// var … (global variables, root only)\n// when loaded {} (lifecycle of the scope's timeline)\n// every frame {}\n// object \"name\" {} (one block per scripted object of the scope)\n//\n// This is the `.flatink` structure WITHOUT the geometry (which stays visual). This module\n// ONLY deals with the text: splitting the `object` blocks from the rest, and formatting a block.\n// The wiring to the Doc (a single commit) lives in the store, which reuses these functions\n// + scriptDoc/dsl. Pure, no DOM, testable.\n// ─────────────────────────────────────────────────────────────────────────────\n\n/** End of the block opened at `open` (index of the matching `}`), ignoring strings and comments. -1 if unclosed. */\nfunction matchBrace(s: string, open: number): number {\n let depth = 0\n for (let i = open; i < s.length; i++) {\n const c = s[i]\n if (c === '\"') { i++; while (i < s.length && s[i] !== '\"') { if (s[i] === '\\\\') i++; i++ }; continue }\n if (c === '/' && s[i + 1] === '/') { while (i < s.length && s[i] !== '\\n') i++; continue }\n if (c === '{') depth++\n else if (c === '}') { depth--; if (depth === 0) return i }\n }\n return -1\n}\n\n/** Splits a scope file: the `object \"name\" { … }` blocks on one side, the REST (var + lifecycle) on the other. */\nexport function splitScopeProgram(text: string): { rest: string; objects: { name: string; body: string }[] } {\n let rest = ''\n let i = 0\n const objects: { name: string; body: string }[] = []\n while (i < text.length) {\n const m = /object\\s+\"((?:[^\"\\\\]|\\\\.)*)\"\\s*\\{/.exec(text.slice(i))\n if (!m) { rest += text.slice(i); break }\n const start = i + m.index\n rest += text.slice(i, start)\n const ob = start + m[0].length - 1 // the opening brace\n const oc = matchBrace(text, ob)\n if (oc < 0) { rest += text.slice(start); break } // unclosed block → left to the rest (diagnostic downstream)\n objects.push({ name: m[1].replace(/\\\\(.)/g, (_, c) => (c === 'n' ? '\\n' : c)), body: text.slice(ob + 1, oc) })\n i = oc + 1\n }\n return { rest, objects }\n}\n\n/** LINTABLE regions of a scope file (the \"rest\" gaps + the `object` bodies), with their\n * 1-based start line in the original text — to lint each piece as pure DSL and\n * reproject the positions. The `object` keyword and its braces are NOT DSL: we skip them. */\nexport function scopeRegions(text: string): { body: string; line: number }[] {\n const lineAt = (off: number) => text.slice(0, off).split('\\n').length\n const regions: { body: string; line: number }[] = []\n let i = 0\n let restStart = 0\n while (i < text.length) {\n const m = /object\\s+\"((?:[^\"\\\\]|\\\\.)*)\"\\s*\\{/.exec(text.slice(i))\n if (!m) break\n const start = i + m.index\n const gap = text.slice(restStart, start)\n if (gap.trim()) regions.push({ body: gap, line: lineAt(restStart) })\n const ob = start + m[0].length - 1\n const oc = matchBrace(text, ob)\n if (oc < 0) break\n regions.push({ body: text.slice(ob + 1, oc), line: lineAt(ob + 1) })\n i = oc + 1\n restStart = i\n }\n const tail = text.slice(restStart)\n if (tail.trim()) regions.push({ body: tail, line: lineAt(restStart) })\n return regions\n}\n\nconst esc = (s: string) => s.replace(/\\\\/g, '\\\\\\\\').replace(/\"/g, '\\\\\"').replace(/\\n/g, '\\\\n')\nconst indent = (body: string) => body.split('\\n').map((l) => (l.trim() ? ' ' + l : l)).join('\\n')\n\n/** Formats an `object \"name\" { … }` block (body already in DSL). */\nexport function formatObjectBlock(name: string, body: string): string {\n const inner = body.trim()\n return `object \"${esc(name)}\" {\\n${inner ? indent(inner) + '\\n' : ''}}`\n}\n\n/** Assembles a scope file: rest (var + lifecycle) then the object blocks, separated by a blank line. */\nexport function joinScopeProgram(rest: string, objects: { name: string; body: string }[]): string {\n const parts: string[] = []\n if (rest.trim()) parts.push(rest.trim())\n for (const o of objects) if (o.body.trim()) parts.push(formatObjectBlock(o.name, o.body))\n return parts.length ? parts.join('\\n\\n') + '\\n' : ''\n}\n","// -----------------------------------------------------------------------------\n// programDoc.ts — PURE bridge Doc <-> \"scope program\" (text) + lint of a whole Doc.\n//\n// Both the editor AND the CLI need to rebuild the DSL text of a scope (to display it,\n// or to lint it) and to assemble the lint context (variables/labels/functions/objects)\n// FROM a Doc — without depending on the React store. Everything lives here, pure and testable:\n// • `scopeProgram` : Doc fragments -> program text of a scope (round-trip via printUnits);\n// • `docLintContext` : Doc + scope -> LintContext (known names);\n// • `lintDoc` / report : semantic lint of ALL the scopes of a Doc (scene + symbols).\n// -> the CLI gains a `flatc --check`, the editor stops duplicating the logic.\n// -----------------------------------------------------------------------------\nimport type { Doc, EditFrame, Group, Instance } from '@flatkit/types'\nimport { printUnits, type Diagnostic } from '@flatkit/engine/dsl'\nimport { joinScopeProgram, scopeRegions } from './scopeProgram'\nimport { functionsToUnits, importsToUnits, objectToUnits, timelineToUnits, variablesToUnits } from '@flatkit/engine/scriptDoc'\nimport { contextLayers, getScopeTimeline, isContainer, isGroup, isText, isImage } from '@flatkit/engine/layers'\nimport { importedFunctions } from '@flatkit/engine/stdlib'\nimport { objectNames } from '@flatkit/engine/sceneRefs'\nimport { itemBBox, dropZoneBounds } from '@flatkit/engine/groups'\nimport { bboxIntersects } from '@flatkit/engine/bbox'\nimport { lint, localVariables, type LintContext } from './lint'\nimport { parseUnits } from '@flatkit/engine/dsl'\nimport type { Item, Layer, Text } from '@flatkit/types'\n\n/** Rebuilds the \"program\" text of a scope (imports + variables + functions + scene cycle\n * + one `object \"Name\" { … }` block per scripted container with a unique name). Pure, round-trip via printUnits. */\nexport function scopeProgram(doc: Doc, editPath: EditFrame[] = []): string {\n const atRoot = editPath.length === 0\n const items = contextLayers(doc, editPath).flatMap((l) => l.items)\n const nameCount = new Map<string, number>()\n for (const it of items) if (isContainer(it) && it.name) nameCount.set(it.name, (nameCount.get(it.name) ?? 0) + 1)\n const scripted = (it: Group | Instance) => !!doc.interactions?.some((i) => i.targetId === it.id) || !!doc.interactors?.some((i) => i.targetId === it.id) || !!(it.expressions && Object.keys(it.expressions).length)\n const objects = items\n .filter((it): it is Group | Instance => isContainer(it) && !!it.name && nameCount.get(it.name) === 1 && scripted(it))\n .map((it) => ({ name: it.name, body: printUnits(objectToUnits(it.id, doc.interactions, it.expressions, doc.interactors)) }))\n const imports = atRoot ? printUnits(importsToUnits(doc.imports)) : ''\n const funcs = atRoot ? printUnits(functionsToUnits(doc.functions)) : ''\n const globals = atRoot ? printUnits(variablesToUnits(doc.variables)) : ''\n const scene = printUnits(timelineToUnits(getScopeTimeline(doc, editPath)))\n const head = [imports, globals, funcs, scene].filter((t) => t.trim()).join('\\n\\n')\n return joinScopeProgram(head, objects)\n}\n\n/** Lint context of a scope: known names (variables, labels, functions, objects) drawn from the Doc.\n * `extraVars` = variables assigned in the OTHER scopes (FlatInk variables are GLOBAL -> known\n * everywhere; avoids a false \"unknown variable\" on a var written elsewhere, e.g. generated by `match`). */\nexport function docLintContext(doc: Doc, editPath: EditFrame[] = [], extraVars?: Iterable<string>): LintContext {\n return {\n variables: [...Object.keys(doc.variables ?? {}), ...(extraVars ?? [])],\n labels: (getScopeTimeline(doc, editPath)?.labels ?? []).map((l) => l.name),\n functions: [...(doc.functions ?? []).map((f) => f.name), ...importedFunctions(doc.imports).map((f) => f.name)],\n objects: objectNames(contextLayers(doc, editPath)),\n }\n}\n\n/** All the scopes of a Doc: the scene (root) + each symbol of the library. */\nfunction scopes(doc: Doc): { label: string; editPath: EditFrame[] }[] {\n return [{ label: 'scene', editPath: [] }, ...doc.symbols.map((s) => ({ label: s.name, editPath: [{ kind: 'symbol' as const, symbolId: s.id, name: s.name }] }))]\n}\n\n/** Union of the variables ASSIGNED in ALL the scopes (scene + symbols). FlatInk variables are\n * global -> a var written in one scope is legitimate in the others (cross-scope reads). */\nexport function allScopeVariables(doc: Doc): string[] {\n const set = new Set<string>()\n for (const { editPath } of scopes(doc))\n for (const r of scopeRegions(scopeProgram(doc, editPath)))\n for (const v of localVariables(parseUnits(r.body).units)) set.add(v)\n return [...set]\n}\n\n/** Readable name of an item by id (for the messages). */\nfunction itemNameById(doc: Doc, id: string): string | null {\n let found: string | null = null\n const walk = (layers: { items: import('@flatkit/types').Item[] }[]) => {\n for (const l of layers) for (const it of l.items) {\n if (found) return\n if ('name' in it && it.id === id) { found = it.name; return }\n if (isGroup(it)) walk(it.layers)\n }\n }\n walk(doc.layers)\n return found\n}\n\nconst escapeRe = (s: string) => s.replace(/[.*+?^${}()|[\\]\\\\-]/g, '\\\\$&')\n\n/** STRUCTURAL warnings (non-blocking) of a Doc: phantom drop zones, dead variables. */\nexport function docStructureWarnings(doc: Doc): { scope: string; diag: Diagnostic }[] {\n const out: { scope: string; diag: Diagnostic }[] = []\n // (a) `when dropped on X` whose zone X does not exist (no item named so) -> the handler can never fire.\n const names = new Set(objectNames(doc.layers))\n for (const it of doc.interactions ?? []) {\n if (it.event === 'drop' && it.over && !names.has(it.over)) {\n const who = itemNameById(doc, it.targetId) ?? it.targetId\n out.push({ scope: 'scene', diag: { line: 1, col: 1, severity: 'warning', message: `unknown drop zone \"${it.over}\" (object \"${who}\") — no item of the scene carries this name` } })\n }\n }\n // (b) global variable never referenced (declared but not found in any scope) -> probably dead.\n const names0 = Object.keys(doc.variables ?? {})\n if (names0.length) {\n const allText = scopes(doc).map(({ editPath }) => scopeProgram(doc, editPath)).join('\\n')\n for (const name of names0) {\n const m = allText.match(new RegExp(`(?<![\\\\w-])${escapeRe(name)}(?![\\\\w-])`, 'g'))\n if ((m?.length ?? 0) <= 1) out.push({ scope: 'scene', diag: { line: 1, col: 1, severity: 'warning', message: `global variable \"${name}\" never used (declared, but neither read nor written)` } })\n }\n }\n out.push(...docLayoutWarnings(doc))\n return out\n}\n\nconst warn = (message: string): { scope: string; diag: Diagnostic } => ({ scope: 'scene', diag: { line: 1, col: 1, severity: 'warning', message } })\nconst itemLabel = (it: Item): string => ('name' in it && it.name ? it.name : 'kind' in it ? it.kind : 'path')\n/** Estimated width of the longest line (approximate metric, without canvas). */\nfunction estTextWidth(t: Text): number {\n const advance = (t.weight && t.weight >= 700 ? 0.56 : 0.52) * t.size // mean advance of a proportional glyph\n let longest = 0\n for (const line of t.content.split('\\n')) longest = Math.max(longest, line.length)\n return longest * advance\n}\n\n/** LAYOUT warnings (non-blocking): canvas overflow, text too wide, overlapping hitboxes.\n * Approximate but without canvas — catches \"blind\" positioning bugs before the render. */\nexport function docLayoutWarnings(doc: Doc): { scope: string; diag: Diagnostic }[] {\n const out: { scope: string; diag: Diagnostic }[] = []\n const W = doc.width, H = doc.height\n const TOL = 8 // overflow tolerance (px) before alerting\n const r0 = (n: number) => Math.round(n)\n\n // (c) TEXT/IMAGE clipped at the canvas edge. Precise to avoid noise: we ignore `path`/groups\n // (decor that \"bleeds\" on purpose), objects driven by an expression (dynamic position ->\n // misleading static bbox), and items PARKED entirely off-screen (\"hidden\" pattern).\n const dynamicPos = (it: Item) => 'expressions' in it && it.expressions && (it.expressions.x != null || it.expressions.y != null)\n for (const layer of doc.layers) {\n for (const it of layer.items) {\n if (!(isText(it) || isImage(it)) || dynamicPos(it)) continue\n const b = itemBBox(doc, it)\n if (!b) continue\n const visible = b.maxX > 0 && b.minX < W && b.maxY > 0 && b.minY < H // overlaps the canvas (not parked off-screen)\n const clipped = b.minX < -TOL || b.minY < -TOL || b.maxX > W + TOL || b.maxY > H + TOL\n if (visible && clipped) {\n out.push(warn(`\"${itemLabel(it)}\" clipped at the canvas edge: bbox [${r0(b.minX)},${r0(b.minY)} -> ${r0(b.maxX)},${r0(b.maxY)}] outside 0,0 -> ${W},${H}`))\n }\n }\n }\n\n // (d) Text (without `wrap`) whose estimated width makes it OVERFLOW THE CANVAS -> instruction clipped.\n // (We do NOT compare against `box.w`: without wrap, a text overflows its box harmlessly as long as it\n // fits in the canvas — it is the canvas edge that clips.) Top-level only (x = world).\n for (const it of doc.layers.flatMap((l) => l.items)) {\n if (!isText(it) || it.wrap || dynamicPos(it)) continue\n const estW = estTextWidth(it)\n const left = it.align === 'left' ? 0 : it.align === 'right' ? it.box.w - estW : it.box.w / 2 - estW / 2\n const wl = it.transform.e + left, wr = wl + estW // left/right edge of the content in world coords\n if (wr > W + TOL || wl < -TOL) {\n out.push(warn(`text \"${it.content.slice(0, 24)}${it.content.length > 24 ? '…' : ''}\" overflows the canvas (estimated ~${r0(estW)} px, edge ${r0(wl)}->${r0(wr)} outside 0->${W}) — add \"wrap\"`))\n }\n }\n\n // (e) Drop zones (`hitbox`) that overlap -> ambiguous drop.\n const zones: { name: string; b: NonNullable<ReturnType<typeof dropZoneBounds>> }[] = []\n const collectZones = (layers: Layer[]) => {\n for (const l of layers) for (const it of l.items) {\n if (isGroup(it) && it.hitbox && it.name) { const b = dropZoneBounds(doc, it.name); if (b) zones.push({ name: it.name, b }) }\n if (isGroup(it)) collectZones(it.layers)\n }\n }\n collectZones(doc.layers)\n for (let i = 0; i < zones.length; i++) for (let j = i + 1; j < zones.length; j++) {\n if (bboxIntersects(zones[i].b, zones[j].b)) out.push(warn(`overlapping hitboxes: \"${zones[i].name}\" and \"${zones[j].name}\" -> ambiguous drop`))\n }\n return out\n}\n\n/** Semantic lint of a whole Doc (all the scopes) -> diagnostics labeled by scope.\n * The scope program contains `object` blocks (outside DSL) -> we lint each region\n * separately and re-project the lines (like the editor, cf. CodeEditor.lintFile).\n * Adds the structural warnings (phantom drop zones, dead variables). */\nexport function lintDoc(doc: Doc): { scope: string; diag: Diagnostic }[] {\n const out: { scope: string; diag: Diagnostic }[] = []\n const allVars = allScopeVariables(doc) // computed ONCE (global vars -> known in all the scopes)\n for (const { label, editPath } of scopes(doc)) {\n const ctx = docLintContext(doc, editPath, allVars)\n for (const r of scopeRegions(scopeProgram(doc, editPath))) for (const d of lint(r.body, ctx)) out.push({ scope: label, diag: { ...d, line: d.line + r.line - 1 } })\n }\n out.push(...docStructureWarnings(doc))\n return out\n}\n\nconst sev = (d: Diagnostic) => (d.severity === 'warning' ? 'warning' : 'error')\n\n/** Lint report of a Doc ready to display: `''` = no issue, otherwise `[scope] line:col: level: message`. */\nexport function lintDocReport(doc: Doc): string {\n return lintDoc(doc).map(({ scope, diag }) => `[${scope}] ${diag.line}:${diag.col}: ${sev(diag)}: ${diag.message}`).join('\\n')\n}\n\n/** `true` if the Doc has at least one ERROR (warnings alone do not block). */\nexport function docHasErrors(doc: Doc): boolean {\n return lintDoc(doc).some(({ diag }) => sev(diag) === 'error')\n}\n"],"mappings":"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","names":["parseUnits"]}
package/dist/cli/flatc.js CHANGED
@@ -1,6 +1,8 @@
1
1
  import {
2
2
  run
3
- } from "../chunk-EHQSVWOD.js";
3
+ } from "../chunk-KKHUM55Q.js";
4
+ import "../chunk-UADRVB63.js";
5
+ import "../chunk-TPDP5FDF.js";
4
6
  export {
5
7
  run
6
8
  };
@@ -0,0 +1,18 @@
1
+ import { Doc } from '@flatkit/types';
2
+
3
+ /** Source of a media (base64 data-URI) by declared path (`asset … "path" …`). */
4
+ type MediaMap = Record<string, {
5
+ mime: string;
6
+ data: string;
7
+ }>;
8
+ /**
9
+ * Compile a `.flatink` program + its `.flat` assets → playable `Doc` (the ".flatpack").
10
+ * `assetSrcs` = the text of each `.flat` lib. `media` = the content of the referenced media files
11
+ * (by path). Symbol refs (by name) are resolved across the whole set of libs; declared media
12
+ * are EMBEDDED (path → data-URI).
13
+ */
14
+ declare function compileFlatpack(programSrc: string, assetSrcs?: string[], media?: MediaMap): Doc;
15
+ /** Serialize the `.flatpack` v1 (JSON of the compiled Doc). */
16
+ declare const packToJSON: (doc: Doc) => string;
17
+
18
+ export { type MediaMap, compileFlatpack, packToJSON };
@@ -0,0 +1,9 @@
1
+ import {
2
+ compileFlatpack,
3
+ packToJSON
4
+ } from "./chunk-TPDP5FDF.js";
5
+ export {
6
+ compileFlatpack,
7
+ packToJSON
8
+ };
9
+ //# sourceMappingURL=compile.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":[],"sourcesContent":[],"mappings":"","names":[]}
package/dist/index.d.ts CHANGED
@@ -1,79 +1,10 @@
1
- import { Doc, EditFrame } from '@flatkit/types';
2
- import { Diagnostic, ScriptUnit } from '@flatkit/engine/dsl';
3
- export { parseUnits, printUnits } from '@flatkit/engine/dsl';
1
+ export { MediaMap, compileFlatpack, packToJSON } from './compile.js';
2
+ export { LintContext, allScopeVariables, docHasErrors, docLayoutWarnings, docLintContext, docStructureWarnings, formatObjectBlock, joinScopeProgram, lint, lintDoc, lintDocReport, lintReport, localVariables, scopeProgram, scopeRegions, splitScopeProgram } from './analysis.js';
3
+ import { Doc } from '@flatkit/types';
4
4
  export { run } from './cli/flatc.js';
5
+ export { parseUnits, printUnits } from '@flatkit/engine/dsl';
5
6
  export { exportFlatProject, parseFlat, parseFlatLib, parseProgram, parseProgramFull, printProgram, printProgramFull } from '@flatkit/engine/flatFormat';
6
7
 
7
- /** Source of a media (base64 data-URI) by declared path (`asset … "path" …`). */
8
- type MediaMap = Record<string, {
9
- mime: string;
10
- data: string;
11
- }>;
12
- /**
13
- * Compile a `.flatink` program + its `.flat` assets → playable `Doc` (the ".flatpack").
14
- * `assetSrcs` = the text of each `.flat` lib. `media` = the content of the referenced media files
15
- * (by path). Symbol refs (by name) are resolved across the whole set of libs; declared media
16
- * are EMBEDDED (path → data-URI).
17
- */
18
- declare function compileFlatpack(programSrc: string, assetSrcs?: string[], media?: MediaMap): Doc;
19
- /** Serialize the `.flatpack` v1 (JSON of the compiled Doc). */
20
- declare const packToJSON: (doc: Doc) => string;
21
-
22
- type LintContext = {
23
- /** Known variables in addition to those declared/assigned in the source. */
24
- variables?: Iterable<string>;
25
- /** Known labels (timeline). Absent = we don't check `go to "…"`. */
26
- labels?: Iterable<string>;
27
- /** Known functions in addition to those defined in the source. */
28
- functions?: Iterable<string>;
29
- /** Scene objects referenceable by name (`Hero.x`) — in addition to mouse/keys. */
30
- objects?: Iterable<string>;
31
- };
32
- /** Locally known variables: `let` declarations + every assigned variable. */
33
- declare function localVariables(units: ScriptUnit[]): Set<string>;
34
- /** Analyze a DSL source and return all diagnostics (syntax + semantic). */
35
- declare function lint(src: string, ctx?: LintContext): Diagnostic[];
36
- /**
37
- * Lint report ready to feed back to a generator (LLM/CLI): `''` = no issue (the code passes),
38
- * otherwise one `line:col: message` line per diagnostic. Primitive of the "generate → lint →
39
- * fix" loop: the language fits in few tokens, robustness comes from this feedback, not from context.
40
- */
41
- declare function lintReport(src: string, ctx?: LintContext): string;
42
-
43
- /** Rebuilds the "program" text of a scope (imports + variables + functions + scene cycle
44
- * + one `object "Name" { … }` block per scripted container with a unique name). Pure, round-trip via printUnits. */
45
- declare function scopeProgram(doc: Doc, editPath?: EditFrame[]): string;
46
- /** Lint context of a scope: known names (variables, labels, functions, objects) drawn from the Doc.
47
- * `extraVars` = variables assigned in the OTHER scopes (FlatInk variables are GLOBAL -> known
48
- * everywhere; avoids a false "unknown variable" on a var written elsewhere, e.g. generated by `match`). */
49
- declare function docLintContext(doc: Doc, editPath?: EditFrame[], extraVars?: Iterable<string>): LintContext;
50
- /** Union of the variables ASSIGNED in ALL the scopes (scene + symbols). FlatInk variables are
51
- * global -> a var written in one scope is legitimate in the others (cross-scope reads). */
52
- declare function allScopeVariables(doc: Doc): string[];
53
- /** STRUCTURAL warnings (non-blocking) of a Doc: phantom drop zones, dead variables. */
54
- declare function docStructureWarnings(doc: Doc): {
55
- scope: string;
56
- diag: Diagnostic;
57
- }[];
58
- /** LAYOUT warnings (non-blocking): canvas overflow, text too wide, overlapping hitboxes.
59
- * Approximate but without canvas — catches "blind" positioning bugs before the render. */
60
- declare function docLayoutWarnings(doc: Doc): {
61
- scope: string;
62
- diag: Diagnostic;
63
- }[];
64
- /** Semantic lint of a whole Doc (all the scopes) -> diagnostics labeled by scope.
65
- * The scope program contains `object` blocks (outside DSL) -> we lint each region
66
- * separately and re-project the lines (like the editor, cf. CodeEditor.lintFile).
67
- * Adds the structural warnings (phantom drop zones, dead variables). */
68
- declare function lintDoc(doc: Doc): {
69
- scope: string;
70
- diag: Diagnostic;
71
- }[];
72
- /** Lint report of a Doc ready to display: `''` = no issue, otherwise `[scope] line:col: level: message`. */
73
- declare function lintDocReport(doc: Doc): string;
74
- /** `true` if the Doc has at least one ERROR (warnings alone do not block). */
75
- declare function docHasErrors(doc: Doc): boolean;
76
-
77
8
  type ManifestObject = {
78
9
  name: string;
79
10
  kind: string;
@@ -91,27 +22,4 @@ declare function llmContext(doc: Doc): string;
91
22
  /** Language reference card (static, kept in sync with the engine via STD_*). */
92
23
  declare function languageCard(): string;
93
24
 
94
- /** Splits a scope file: the `object "name" { … }` blocks on one side, the REST (var + lifecycle) on the other. */
95
- declare function splitScopeProgram(text: string): {
96
- rest: string;
97
- objects: {
98
- name: string;
99
- body: string;
100
- }[];
101
- };
102
- /** LINTABLE regions of a scope file (the "rest" gaps + the `object` bodies), with their
103
- * 1-based start line in the original text — to lint each piece as pure DSL and
104
- * reproject the positions. The `object` keyword and its braces are NOT DSL: we skip them. */
105
- declare function scopeRegions(text: string): {
106
- body: string;
107
- line: number;
108
- }[];
109
- /** Formats an `object "name" { … }` block (body already in DSL). */
110
- declare function formatObjectBlock(name: string, body: string): string;
111
- /** Assembles a scope file: rest (var + lifecycle) then the object blocks, separated by a blank line. */
112
- declare function joinScopeProgram(rest: string, objects: {
113
- name: string;
114
- body: string;
115
- }[]): string;
116
-
117
- export { type LintContext, type ManifestObject, type MediaMap, allScopeVariables, compileFlatpack, docHasErrors, docLayoutWarnings, docLintContext, docStructureWarnings, docToManifest, formatObjectBlock, joinScopeProgram, languageCard, lint, lintDoc, lintDocReport, lintReport, llmContext, localVariables, manifestObjects, packToJSON, scopeProgram, scopeRegions, splitScopeProgram };
25
+ export { type ManifestObject, docToManifest, languageCard, llmContext, manifestObjects };
package/dist/index.js CHANGED
@@ -1,6 +1,8 @@
1
+ import {
2
+ run
3
+ } from "./chunk-KKHUM55Q.js";
1
4
  import {
2
5
  allScopeVariables,
3
- compileFlatpack,
4
6
  docHasErrors,
5
7
  docLayoutWarnings,
6
8
  docLintContext,
@@ -12,12 +14,14 @@ import {
12
14
  lintDocReport,
13
15
  lintReport,
14
16
  localVariables,
15
- packToJSON,
16
- run,
17
17
  scopeProgram,
18
18
  scopeRegions,
19
19
  splitScopeProgram
20
- } from "./chunk-EHQSVWOD.js";
20
+ } from "./chunk-UADRVB63.js";
21
+ import {
22
+ compileFlatpack,
23
+ packToJSON
24
+ } from "./chunk-TPDP5FDF.js";
21
25
 
22
26
  // src/manifest.ts
23
27
  import { EXPR_CHANNELS as EXPR_CHANNELS2 } from "@flatkit/engine/timeline";