@flatkit/compiler 0.1.0 → 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,82 @@
1
+ import { Diagnostic, ScriptUnit } from '@flatkit/engine/dsl';
2
+ import { Doc, EditFrame } from '@flatkit/types';
3
+
4
+ type LintContext = {
5
+ /** Known variables in addition to those declared/assigned in the source. */
6
+ variables?: Iterable<string>;
7
+ /** Known labels (timeline). Absent = we don't check `go to "…"`. */
8
+ labels?: Iterable<string>;
9
+ /** Known functions in addition to those defined in the source. */
10
+ functions?: Iterable<string>;
11
+ /** Scene objects referenceable by name (`Hero.x`) — in addition to mouse/keys. */
12
+ objects?: Iterable<string>;
13
+ };
14
+ /** Locally known variables: `let` declarations + every assigned variable. */
15
+ declare function localVariables(units: ScriptUnit[]): Set<string>;
16
+ /** Analyze a DSL source and return all diagnostics (syntax + semantic). */
17
+ declare function lint(src: string, ctx?: LintContext): Diagnostic[];
18
+ /**
19
+ * Lint report ready to feed back to a generator (LLM/CLI): `''` = no issue (the code passes),
20
+ * otherwise one `line:col: message` line per diagnostic. Primitive of the "generate → lint →
21
+ * fix" loop: the language fits in few tokens, robustness comes from this feedback, not from context.
22
+ */
23
+ declare function lintReport(src: string, ctx?: LintContext): string;
24
+
25
+ /** Rebuilds the "program" text of a scope (imports + variables + functions + scene cycle
26
+ * + one `object "Name" { … }` block per scripted container with a unique name). Pure, round-trip via printUnits. */
27
+ declare function scopeProgram(doc: Doc, editPath?: EditFrame[]): string;
28
+ /** Lint context of a scope: known names (variables, labels, functions, objects) drawn from the Doc.
29
+ * `extraVars` = variables assigned in the OTHER scopes (FlatInk variables are GLOBAL -> known
30
+ * everywhere; avoids a false "unknown variable" on a var written elsewhere, e.g. generated by `match`). */
31
+ declare function docLintContext(doc: Doc, editPath?: EditFrame[], extraVars?: Iterable<string>): LintContext;
32
+ /** Union of the variables ASSIGNED in ALL the scopes (scene + symbols). FlatInk variables are
33
+ * global -> a var written in one scope is legitimate in the others (cross-scope reads). */
34
+ declare function allScopeVariables(doc: Doc): string[];
35
+ /** STRUCTURAL warnings (non-blocking) of a Doc: phantom drop zones, dead variables. */
36
+ declare function docStructureWarnings(doc: Doc): {
37
+ scope: string;
38
+ diag: Diagnostic;
39
+ }[];
40
+ /** LAYOUT warnings (non-blocking): canvas overflow, text too wide, overlapping hitboxes.
41
+ * Approximate but without canvas — catches "blind" positioning bugs before the render. */
42
+ declare function docLayoutWarnings(doc: Doc): {
43
+ scope: string;
44
+ diag: Diagnostic;
45
+ }[];
46
+ /** Semantic lint of a whole Doc (all the scopes) -> diagnostics labeled by scope.
47
+ * The scope program contains `object` blocks (outside DSL) -> we lint each region
48
+ * separately and re-project the lines (like the editor, cf. CodeEditor.lintFile).
49
+ * Adds the structural warnings (phantom drop zones, dead variables). */
50
+ declare function lintDoc(doc: Doc): {
51
+ scope: string;
52
+ diag: Diagnostic;
53
+ }[];
54
+ /** Lint report of a Doc ready to display: `''` = no issue, otherwise `[scope] line:col: level: message`. */
55
+ declare function lintDocReport(doc: Doc): string;
56
+ /** `true` if the Doc has at least one ERROR (warnings alone do not block). */
57
+ declare function docHasErrors(doc: Doc): boolean;
58
+
59
+ /** Splits a scope file: the `object "name" { … }` blocks on one side, the REST (var + lifecycle) on the other. */
60
+ declare function splitScopeProgram(text: string): {
61
+ rest: string;
62
+ objects: {
63
+ name: string;
64
+ body: string;
65
+ }[];
66
+ };
67
+ /** LINTABLE regions of a scope file (the "rest" gaps + the `object` bodies), with their
68
+ * 1-based start line in the original text — to lint each piece as pure DSL and
69
+ * reproject the positions. The `object` keyword and its braces are NOT DSL: we skip them. */
70
+ declare function scopeRegions(text: string): {
71
+ body: string;
72
+ line: number;
73
+ }[];
74
+ /** Formats an `object "name" { … }` block (body already in DSL). */
75
+ declare function formatObjectBlock(name: string, body: string): string;
76
+ /** Assembles a scope file: rest (var + lifecycle) then the object blocks, separated by a blank line. */
77
+ declare function joinScopeProgram(rest: string, objects: {
78
+ name: string;
79
+ body: string;
80
+ }[]): string;
81
+
82
+ export { type LintContext, allScopeVariables, docHasErrors, docLayoutWarnings, docLintContext, docStructureWarnings, formatObjectBlock, joinScopeProgram, lint, lintDoc, lintDocReport, lintReport, localVariables, scopeProgram, scopeRegions, splitScopeProgram };
@@ -0,0 +1,35 @@
1
+ import {
2
+ allScopeVariables,
3
+ docHasErrors,
4
+ docLayoutWarnings,
5
+ docLintContext,
6
+ docStructureWarnings,
7
+ formatObjectBlock,
8
+ joinScopeProgram,
9
+ lint,
10
+ lintDoc,
11
+ lintDocReport,
12
+ lintReport,
13
+ localVariables,
14
+ scopeProgram,
15
+ scopeRegions,
16
+ splitScopeProgram
17
+ } from "./chunk-UADRVB63.js";
18
+ export {
19
+ allScopeVariables,
20
+ docHasErrors,
21
+ docLayoutWarnings,
22
+ docLintContext,
23
+ docStructureWarnings,
24
+ formatObjectBlock,
25
+ joinScopeProgram,
26
+ lint,
27
+ lintDoc,
28
+ lintDocReport,
29
+ lintReport,
30
+ localVariables,
31
+ scopeProgram,
32
+ scopeRegions,
33
+ splitScopeProgram
34
+ };
35
+ //# sourceMappingURL=analysis.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":[],"sourcesContent":[],"mappings":"","names":[]}
@@ -0,0 +1,312 @@
1
+ import {
2
+ docHasErrors,
3
+ lintDocReport
4
+ } from "./chunk-UADRVB63.js";
5
+ import {
6
+ compileFlatpack,
7
+ packToJSON
8
+ } from "./chunk-TPDP5FDF.js";
9
+
10
+ // src/cli/flatc.ts
11
+ import { readFileSync, writeFileSync, existsSync, readdirSync, watch, mkdirSync, copyFileSync } from "fs";
12
+ import { resolve, dirname, basename, extname, join, relative, isAbsolute } from "path";
13
+ import { parseProgramFull } from "@flatkit/engine/flatFormat";
14
+ import { hasPackage } from "@flatkit/engine/stdlib";
15
+ import { parseUnits } from "@flatkit/engine/dsl";
16
+ import { sanitizeDoc } from "@flatkit/engine/validateDoc";
17
+ import { unitsToFunctions } from "@flatkit/engine/scriptDoc";
18
+ import { playHeadless } from "@flatkit/player/debug";
19
+ var MIME = {
20
+ ".png": "image/png",
21
+ ".jpg": "image/jpeg",
22
+ ".jpeg": "image/jpeg",
23
+ ".gif": "image/gif",
24
+ ".webp": "image/webp",
25
+ ".svg": "image/svg+xml",
26
+ ".avif": "image/avif",
27
+ ".mp3": "audio/mpeg",
28
+ ".wav": "audio/wav",
29
+ ".ogg": "audio/ogg",
30
+ ".m4a": "audio/mp4",
31
+ // Fonts (RFC 8081 media types) — matches the editor's import (`font/woff2` default, FontFace API).
32
+ ".woff2": "font/woff2",
33
+ ".woff": "font/woff",
34
+ ".ttf": "font/ttf",
35
+ ".otf": "font/otf"
36
+ // NB: video (.mp4/.webm/…) intentionally omitted — no video runtime in the editor or player yet.
37
+ };
38
+ var mimeFor = (path) => MIME[extname(path).toLowerCase()] ?? "application/octet-stream";
39
+ function isWithin(baseDir, target) {
40
+ const rel = relative(baseDir, target);
41
+ return rel === "" || !rel.startsWith("..") && !isAbsolute(rel);
42
+ }
43
+ function printHelp() {
44
+ process.stdout.write(`flatc \u2014 compile a FlatInk program into a .flatpack
45
+
46
+ Usage:
47
+ flatc <program.flatink> [assets.flat \u2026] [-o output.flatpack]
48
+ flatc <program.flatink> --watch
49
+ flatc --play <program.flatink | scene.flatpack> --script <gestures.json>
50
+ flatc --render <program.flatink | scene.flatpack> -o out.png [--frame N] [--at k=v[,k2=v2]] [--scale S]
51
+
52
+ program.flatink the program (composition + logic, text DSL)
53
+ assets.flat visual asset libs (default: every .flat in the program's folder)
54
+ -o, --out output file (default: <program>.flatpack \u2014 the CANONICAL name, JSON inside)
55
+ --assets MODE media baking: 'inline' (default, base64 in the .flatpack) or 'external'
56
+ (sidecar <out>.assets/ folder; asset.data = relative key \u2014 serve the folder and
57
+ play with sameOriginAssetResolver(<flatpackUrl>))
58
+ --check semantic lint only (no .flatpack); exits \u22600 on ERROR (warnings do not stop)
59
+ --watch recompile on every change in the folder (agent \u2192 player loop)
60
+ --play run the file WITHOUT a canvas, replay --script and print { sends, vars } (JSON)
61
+ --trace (with --play) HUMAN-READABLE log per gesture: emitted sends + variable diff (debug)
62
+ --script <f> JSON gesture script: [{ "type": "down|move|up|cancel", "x", "y" }, { "type": "set", "name", "value" }, { "type": "wait", "frames": N }]
63
+ semantic (by NAME, the engine resolves coords): { "type": "drag", "source", "target" } \xB7 { "type": "tap", "target" }
64
+ \xB7 { "type": "scratch", "target" } (sweeps a reveal zone) \xB7 { "type": "connect", "source", "target" } (pulls a link wire)
65
+ "wait" lets the simulation run N fixed steps (60 Hz): "every frame" + playhead advance like in real playback
66
+ "expect" self-verifies: { "type": "expect", "sends": ["done"], "vars": { "score": 3 } } \u2192 exits \u22600 on mismatch
67
+ (sends = sequence of names emitted SINCE the last expect; vars = current state). Great in CI.
68
+ --render render a PNG IMAGE (headless skia): see what we draw (positioning)
69
+ --frame N (with --render) target frame (default 0)
70
+ --at k=v[,k2=v2] (with --render) force variables \u2192 capture a given state (e.g. a step of an escape)
71
+ --steps N (with --render) run N fixed sim steps (60 Hz, every-frame) BEFORE capture \u2192 see a
72
+ stateful act unfold without forcing every derived variable by hand in --at
73
+ --scale S (with --render) resolution factor (default 2)
74
+ -h, --help this help
75
+
76
+ Media referenced by 'asset "id" "path" kind' are embedded (paths relative to the program).
77
+ `);
78
+ }
79
+ function buildDocFromProgram(programPath, explicitFlats = [], assetMode = "inline", assetsDir = "") {
80
+ const baseDir = dirname(programPath);
81
+ const programSrc = readFileSync(programPath, "utf8");
82
+ const prog = parseProgramFull(programSrc);
83
+ const flatPaths = new Set(explicitFlats.map((p) => resolve(p)));
84
+ if (!flatPaths.size) for (const f of readdirSync(baseDir).filter((f2) => f2.endsWith(".flat"))) flatPaths.add(join(baseDir, f));
85
+ const pkgFunctions = [];
86
+ const localResolved = /* @__PURE__ */ new Set();
87
+ for (const name of prog.imports ?? []) {
88
+ if (hasPackage(name)) continue;
89
+ const fink = join(baseDir, name + ".flatink");
90
+ const fflat = join(baseDir, name + ".flat");
91
+ if (!isWithin(baseDir, fink) || !isWithin(baseDir, fflat)) {
92
+ process.stderr.write(`flatc: package outside the program folder (ignored): "${name}"
93
+ `);
94
+ continue;
95
+ }
96
+ let found = false;
97
+ if (existsSync(fink)) {
98
+ for (const f of unitsToFunctions(parseUnits(readFileSync(fink, "utf8")).units)) pkgFunctions.push(f, { ...f, name: `${name}.${f.name}` });
99
+ found = true;
100
+ }
101
+ if (existsSync(fflat)) {
102
+ flatPaths.add(fflat);
103
+ found = true;
104
+ }
105
+ if (found) localResolved.add(name);
106
+ else process.stderr.write(`flatc: package not found: "${name}" (neither stdlib, nor ${name}.flatink / ${name}.flat)
107
+ `);
108
+ }
109
+ const assetSrcs = [...flatPaths].map((p) => readFileSync(p, "utf8"));
110
+ const media = {};
111
+ const mediaCopies = [];
112
+ for (const a of prog.assets ?? []) {
113
+ const mp = resolve(baseDir, a.data);
114
+ if (!isWithin(baseDir, mp)) {
115
+ process.stderr.write(`flatc: media outside the program folder (ignored): ${a.data}
116
+ `);
117
+ continue;
118
+ }
119
+ if (!existsSync(mp)) {
120
+ process.stderr.write(`flatc: missing media (ignored): ${a.data}
121
+ `);
122
+ continue;
123
+ }
124
+ const mime = mimeFor(mp);
125
+ if (assetMode === "external") {
126
+ const key = `${assetsDir}/${a.data.replace(/\\/g, "/")}`;
127
+ media[a.data] = { mime, data: key };
128
+ mediaCopies.push({ src: mp, key });
129
+ } else {
130
+ media[a.data] = { mime, data: `data:${mime};base64,${readFileSync(mp).toString("base64")}` };
131
+ }
132
+ }
133
+ let doc = compileFlatpack(programSrc, assetSrcs, media);
134
+ if (pkgFunctions.length) doc = { ...doc, functions: [...doc.functions ?? [], ...pkgFunctions] };
135
+ const stdImports = (doc.imports ?? []).filter(hasPackage);
136
+ doc = { ...doc, imports: stdImports.length ? stdImports : void 0 };
137
+ return { doc, flatLibs: flatPaths.size, packages: localResolved.size, media: Object.keys(media).length, mediaCopies };
138
+ }
139
+ function compileOnce(programPath, explicitFlats, out, checkOnly, assetMode = "inline") {
140
+ const outPath = out ? resolve(out) : join(dirname(programPath), basename(programPath, extname(programPath)) + ".flatpack");
141
+ const assetsDir = assetMode === "external" ? basename(outPath, extname(outPath)) + ".assets" : "";
142
+ let built;
143
+ try {
144
+ built = buildDocFromProgram(programPath, explicitFlats, assetMode, assetsDir);
145
+ } catch (e) {
146
+ process.stderr.write(`flatc: compile error: ${e.message}
147
+ `);
148
+ return 1;
149
+ }
150
+ const { doc } = built;
151
+ const report = lintDocReport(doc);
152
+ if (checkOnly) {
153
+ if (report) process.stderr.write(report + "\n");
154
+ if (docHasErrors(doc)) return 1;
155
+ process.stdout.write("flatc: no errors \u2713\n");
156
+ return 0;
157
+ }
158
+ if (report) process.stderr.write(report + "\n");
159
+ writeFileSync(outPath, packToJSON(doc));
160
+ const outDir = dirname(outPath);
161
+ for (const c of built.mediaCopies) {
162
+ const dest = join(outDir, ...c.key.split("/"));
163
+ if (!isWithin(outDir, dest)) continue;
164
+ mkdirSync(dirname(dest), { recursive: true });
165
+ copyFileSync(c.src, dest);
166
+ }
167
+ const where = assetMode === "external" ? ` (external \u2192 ${assetsDir}/)` : "";
168
+ process.stdout.write(`flatc: ${basename(outPath)} \u2713 ${doc.symbols.length} symbol(s) \xB7 ${built.flatLibs} lib(s) \xB7 ${built.packages} package(s) \xB7 ${built.media} media${where}
169
+ `);
170
+ return 0;
171
+ }
172
+ function loadDoc(filePath) {
173
+ if (filePath.endsWith(".flatink")) return buildDocFromProgram(filePath).doc;
174
+ return sanitizeDoc(JSON.parse(readFileSync(filePath, "utf8")));
175
+ }
176
+ function playOnce(filePath, scriptPath, trace) {
177
+ if (!scriptPath) {
178
+ process.stderr.write("flatc: --play requires --script <gestures.json>\n");
179
+ return 1;
180
+ }
181
+ if (!existsSync(scriptPath)) {
182
+ process.stderr.write(`flatc: script not found: ${scriptPath}
183
+ `);
184
+ return 1;
185
+ }
186
+ let doc, gestures;
187
+ try {
188
+ doc = loadDoc(filePath);
189
+ } catch (e) {
190
+ process.stderr.write(`flatc: cannot read: ${e.message}
191
+ `);
192
+ return 1;
193
+ }
194
+ try {
195
+ gestures = JSON.parse(readFileSync(scriptPath, "utf8"));
196
+ } catch (e) {
197
+ process.stderr.write(`flatc: invalid JSON script: ${e.message}
198
+ `);
199
+ return 1;
200
+ }
201
+ if (!Array.isArray(gestures)) {
202
+ process.stderr.write("flatc: the script must be an array of gestures\n");
203
+ return 1;
204
+ }
205
+ const res = playHeadless(doc, gestures, { trace });
206
+ if (!trace) process.stdout.write(JSON.stringify(res, null, 2) + "\n");
207
+ else
208
+ for (const s of res.steps ?? []) {
209
+ const sends = s.sends.length ? " sends:[" + s.sends.map((e) => e.value !== void 0 ? `${e.name}=${e.value}` : e.name).join(", ") + "]" : "";
210
+ const vars = Object.entries(s.changed).map(([k, [a, b]]) => `${k}:${JSON.stringify(a)}\u2192${JSON.stringify(b)}`);
211
+ process.stdout.write(`${s.gesture.padEnd(28)}${sends}${vars.length ? " vars{" + vars.join(" ") + "}" : ""}
212
+ `);
213
+ }
214
+ if (res.expectFailures?.length) {
215
+ for (const f of res.expectFailures) process.stderr.write(`flatc: \u2717 ${f}
216
+ `);
217
+ return 1;
218
+ }
219
+ return 0;
220
+ }
221
+ function parseVars(spec, into) {
222
+ for (const pair of spec.split(",")) {
223
+ const i = pair.indexOf("=");
224
+ if (i < 0) continue;
225
+ const k = pair.slice(0, i).trim();
226
+ const v = Number(pair.slice(i + 1).trim());
227
+ if (k && Number.isFinite(v)) into[k] = v;
228
+ }
229
+ }
230
+ async function renderOnce(filePath, out, frame, vars, scale, steps) {
231
+ let doc;
232
+ try {
233
+ doc = loadDoc(filePath);
234
+ } catch (e) {
235
+ process.stderr.write(`flatc: cannot read: ${e.message}
236
+ `);
237
+ return 1;
238
+ }
239
+ const outPath = out ? resolve(out) : join(dirname(filePath), basename(filePath, extname(filePath)) + ".png");
240
+ try {
241
+ const { renderDocToPng } = await import("./cli/render.js");
242
+ const png = await renderDocToPng(doc, { frame, vars: Object.keys(vars).length ? vars : void 0, scale, steps: steps || void 0 });
243
+ writeFileSync(outPath, png);
244
+ } catch (e) {
245
+ process.stderr.write(`flatc: render failed: ${e.message}
246
+ `);
247
+ return 1;
248
+ }
249
+ process.stdout.write(`flatc: ${basename(outPath)} \u2713 ${doc.width}\xD7${doc.height} \xD7${scale}${frame ? ` \xB7 frame ${frame}` : ""}${steps ? ` \xB7 ${steps} step(s)` : ""}${Object.keys(vars).length ? ` \xB7 ${Object.entries(vars).map(([k, v]) => `${k}=${v}`).join(" ")}` : ""}
250
+ `);
251
+ return 0;
252
+ }
253
+ function run(argv) {
254
+ const args = argv.slice(2);
255
+ let out = "", scriptPath = "";
256
+ let checkOnly = false, doWatch = false, doPlay = false, doRender = false, doTrace = false;
257
+ let frame = 0, scale = 2, steps = 0;
258
+ let assetMode = "inline";
259
+ const vars = {};
260
+ const positional = [];
261
+ for (let i = 0; i < args.length; i++) {
262
+ const a = args[i];
263
+ if (a === "-o" || a === "--out") out = args[++i] ?? "";
264
+ else if (a === "--script") scriptPath = resolve(args[++i] ?? "");
265
+ else if (a === "--assets") assetMode = args[++i] === "external" ? "external" : "inline";
266
+ else if (a === "--check") checkOnly = true;
267
+ else if (a === "--watch") doWatch = true;
268
+ else if (a === "--play") doPlay = true;
269
+ else if (a === "--trace") doTrace = true;
270
+ else if (a === "--render") doRender = true;
271
+ else if (a === "--frame") frame = Number(args[++i] ?? "0") || 0;
272
+ else if (a === "--steps") steps = Math.max(0, Number(args[++i] ?? "0") || 0);
273
+ else if (a === "--scale") scale = Number(args[++i] ?? "2") || 2;
274
+ else if (a === "--at") parseVars(args[++i] ?? "", vars);
275
+ else if (a === "-h" || a === "--help") {
276
+ printHelp();
277
+ return 0;
278
+ } else positional.push(a);
279
+ }
280
+ if (!positional.length) {
281
+ printHelp();
282
+ return 1;
283
+ }
284
+ const filePath = resolve(positional[0]);
285
+ if (!existsSync(filePath)) {
286
+ process.stderr.write(`flatc: not found: ${filePath}
287
+ `);
288
+ return 1;
289
+ }
290
+ const explicitFlats = positional.slice(1);
291
+ if (doRender) return renderOnce(filePath, out, frame, vars, scale, steps);
292
+ if (doPlay) return playOnce(filePath, scriptPath, doTrace);
293
+ if (doWatch) {
294
+ const code = compileOnce(filePath, explicitFlats, out, checkOnly, assetMode);
295
+ const baseDir = dirname(filePath);
296
+ let timer;
297
+ watch(baseDir, { recursive: false }, (_e, filename) => {
298
+ if (filename && (filename.endsWith(".flatpack") || filename.endsWith(".flatpack.json"))) return;
299
+ clearTimeout(timer);
300
+ timer = setTimeout(() => compileOnce(filePath, explicitFlats, out, checkOnly, assetMode), 80);
301
+ });
302
+ process.stdout.write(`flatc: watching ${baseDir} \u2026 (Ctrl+C to stop)
303
+ `);
304
+ return code;
305
+ }
306
+ return compileOnce(filePath, explicitFlats, out, checkOnly, assetMode);
307
+ }
308
+
309
+ export {
310
+ run
311
+ };
312
+ //# sourceMappingURL=chunk-KKHUM55Q.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/cli/flatc.ts"],"sourcesContent":["// ─────────────────────────────────────────────────────────────────────────────\n// flatc — the \"modern SWF\" command-line compiler.\n//\n// The VSCode-first flow: you write a `.flatink` program (composition + logic, DSL)\n// + `.flat` asset libs (visuals, exported by the editor) + media, then:\n//\n// flatc game.flatink hero.flat decor.flat -o game.flatpack\n//\n// → a single `.flatpack` (the baked Doc as JSON) that the player runs. Media referenced\n// by `asset \"id\" \"path\" kind` are EMBEDDED (paths relative to the program).\n// ─────────────────────────────────────────────────────────────────────────────\nimport { readFileSync, writeFileSync, existsSync, readdirSync, watch, mkdirSync, copyFileSync } from 'node:fs'\nimport { resolve, dirname, basename, extname, join, relative, isAbsolute } from 'node:path'\nimport { compileFlatpack, packToJSON, type MediaMap } from '../compile'\nimport { parseProgramFull } from '@flatkit/engine/flatFormat'\nimport { hasPackage } from '@flatkit/engine/stdlib'\nimport { parseUnits } from '@flatkit/engine/dsl'\nimport { sanitizeDoc } from '@flatkit/engine/validateDoc'\nimport { unitsToFunctions } from '@flatkit/engine/scriptDoc'\nimport { lintDocReport, docHasErrors } from '../programDoc'\nimport { playHeadless, type Gesture } from '@flatkit/player/debug'\nimport type { FuncDef } from '@flatkit/engine/actions'\nimport type { Doc } from '@flatkit/types'\n\nconst MIME: Record<string, string> = {\n '.png': 'image/png', '.jpg': 'image/jpeg', '.jpeg': 'image/jpeg', '.gif': 'image/gif',\n '.webp': 'image/webp', '.svg': 'image/svg+xml', '.avif': 'image/avif',\n '.mp3': 'audio/mpeg', '.wav': 'audio/wav', '.ogg': 'audio/ogg', '.m4a': 'audio/mp4',\n // Fonts (RFC 8081 media types) — matches the editor's import (`font/woff2` default, FontFace API).\n '.woff2': 'font/woff2', '.woff': 'font/woff', '.ttf': 'font/ttf', '.otf': 'font/otf',\n // NB: video (.mp4/.webm/…) intentionally omitted — no video runtime in the editor or player yet.\n}\nconst mimeFor = (path: string): string => MIME[extname(path).toLowerCase()] ?? 'application/octet-stream'\n\n/**\n * True if `target` resolves to a file inside `baseDir` (or baseDir itself). Guards against a hostile\n * program escaping its folder via `../` in a media path or a `use` package name — compiling an untrusted\n * `.flatink` must never read (and embed) arbitrary host files.\n */\nfunction isWithin(baseDir: string, target: string): boolean {\n const rel = relative(baseDir, target)\n return rel === '' || (!rel.startsWith('..') && !isAbsolute(rel))\n}\n\nfunction printHelp(): void {\n process.stdout.write(`flatc — compile a FlatInk program into a .flatpack\n\nUsage:\n flatc <program.flatink> [assets.flat …] [-o output.flatpack]\n flatc <program.flatink> --watch\n flatc --play <program.flatink | scene.flatpack> --script <gestures.json>\n flatc --render <program.flatink | scene.flatpack> -o out.png [--frame N] [--at k=v[,k2=v2]] [--scale S]\n\n program.flatink the program (composition + logic, text DSL)\n assets.flat visual asset libs (default: every .flat in the program's folder)\n -o, --out output file (default: <program>.flatpack — the CANONICAL name, JSON inside)\n --assets MODE media baking: 'inline' (default, base64 in the .flatpack) or 'external'\n (sidecar <out>.assets/ folder; asset.data = relative key — serve the folder and\n play with sameOriginAssetResolver(<flatpackUrl>))\n --check semantic lint only (no .flatpack); exits ≠0 on ERROR (warnings do not stop)\n --watch recompile on every change in the folder (agent → player loop)\n --play run the file WITHOUT a canvas, replay --script and print { sends, vars } (JSON)\n --trace (with --play) HUMAN-READABLE log per gesture: emitted sends + variable diff (debug)\n --script <f> JSON gesture script: [{ \"type\": \"down|move|up|cancel\", \"x\", \"y\" }, { \"type\": \"set\", \"name\", \"value\" }, { \"type\": \"wait\", \"frames\": N }]\n semantic (by NAME, the engine resolves coords): { \"type\": \"drag\", \"source\", \"target\" } · { \"type\": \"tap\", \"target\" }\n · { \"type\": \"scratch\", \"target\" } (sweeps a reveal zone) · { \"type\": \"connect\", \"source\", \"target\" } (pulls a link wire)\n \"wait\" lets the simulation run N fixed steps (60 Hz): \"every frame\" + playhead advance like in real playback\n \"expect\" self-verifies: { \"type\": \"expect\", \"sends\": [\"done\"], \"vars\": { \"score\": 3 } } → exits ≠0 on mismatch\n (sends = sequence of names emitted SINCE the last expect; vars = current state). Great in CI.\n --render render a PNG IMAGE (headless skia): see what we draw (positioning)\n --frame N (with --render) target frame (default 0)\n --at k=v[,k2=v2] (with --render) force variables → capture a given state (e.g. a step of an escape)\n --steps N (with --render) run N fixed sim steps (60 Hz, every-frame) BEFORE capture → see a\n stateful act unfold without forcing every derived variable by hand in --at\n --scale S (with --render) resolution factor (default 2)\n -h, --help this help\n\nMedia referenced by 'asset \"id\" \"path\" kind' are embedded (paths relative to the program).\n`)\n}\n\n/** How media is baked: `inline` = base64 data-URI in the .flatpack; `external` = relative key + sidecar files. */\ntype AssetMode = 'inline' | 'external'\ntype MediaCopy = { src: string; key: string } // external mode: source file → relative key (forward slashes)\ntype BuildResult = { doc: Doc; flatLibs: number; packages: number; media: number; mediaCopies: MediaCopy[] }\n\n/**\n * Reads a `.flatink`, resolves libs/packages/media, compiles → standalone Doc. Throws on compile error.\n * `assetMode`: `inline` embeds each media as a base64 data-URI (default); `external` keeps `asset.data` as a\n * relative key (`<assetsDir>/<path>`) and returns the files to copy beside the .flatpack (no base64 bloat).\n */\nfunction buildDocFromProgram(programPath: string, explicitFlats: string[] = [], assetMode: AssetMode = 'inline', assetsDir = ''): BuildResult {\n const baseDir = dirname(programPath)\n const programSrc = readFileSync(programPath, 'utf8')\n const prog = parseProgramFull(programSrc)\n\n // .flat libs: explicit if provided, otherwise auto-discover the .flat files in the program's folder.\n const flatPaths = new Set<string>(explicitFlats.map((p) => resolve(p)))\n if (!flatPaths.size) for (const f of readdirSync(baseDir).filter((f) => f.endsWith('.flat'))) flatPaths.add(join(baseDir, f))\n\n // PACKAGES: non-stdlib `use \"x\"` → local files inlined (x.flatink = functions, x.flat = symbols).\n const pkgFunctions: FuncDef[] = []\n const localResolved = new Set<string>()\n for (const name of prog.imports ?? []) {\n if (hasPackage(name)) continue // stdlib → left as a reference\n const fink = join(baseDir, name + '.flatink')\n const fflat = join(baseDir, name + '.flat')\n if (!isWithin(baseDir, fink) || !isWithin(baseDir, fflat)) { process.stderr.write(`flatc: package outside the program folder (ignored): \"${name}\"\\n`); continue }\n let found = false\n if (existsSync(fink)) { for (const f of unitsToFunctions(parseUnits(readFileSync(fink, 'utf8')).units)) pkgFunctions.push(f, { ...f, name: `${name}.${f.name}` }); found = true }\n if (existsSync(fflat)) { flatPaths.add(fflat); found = true }\n if (found) localResolved.add(name)\n else process.stderr.write(`flatc: package not found: \"${name}\" (neither stdlib, nor ${name}.flatink / ${name}.flat)\\n`)\n }\n const assetSrcs = [...flatPaths].map((p) => readFileSync(p, 'utf8'))\n\n // Media: each `asset … \"path\" …` resolved relative to the program. `inline` → base64 data-URI baked in;\n // `external` → `asset.data` becomes a relative key and the file is copied next to the .flatpack.\n const media: MediaMap = {}\n const mediaCopies: MediaCopy[] = []\n for (const a of prog.assets ?? []) {\n const mp = resolve(baseDir, a.data)\n if (!isWithin(baseDir, mp)) { process.stderr.write(`flatc: media outside the program folder (ignored): ${a.data}\\n`); continue }\n if (!existsSync(mp)) { process.stderr.write(`flatc: missing media (ignored): ${a.data}\\n`); continue }\n const mime = mimeFor(mp)\n if (assetMode === 'external') {\n const key = `${assetsDir}/${a.data.replace(/\\\\/g, '/')}` // URL-form relative key (resolved by the host)\n media[a.data] = { mime, data: key }\n mediaCopies.push({ src: mp, key })\n } else {\n media[a.data] = { mime, data: `data:${mime};base64,${readFileSync(mp).toString('base64')}` }\n }\n }\n\n let doc = compileFlatpack(programSrc, assetSrcs, media)\n if (pkgFunctions.length) doc = { ...doc, functions: [...(doc.functions ?? []), ...pkgFunctions] }\n const stdImports = (doc.imports ?? []).filter(hasPackage)\n doc = { ...doc, imports: stdImports.length ? stdImports : undefined }\n return { doc, flatLibs: flatPaths.size, packages: localResolved.size, media: Object.keys(media).length, mediaCopies }\n}\n\n/** Compile once (write or --check). Returns the exit code. */\nfunction compileOnce(programPath: string, explicitFlats: string[], out: string, checkOnly: boolean, assetMode: AssetMode = 'inline'): number {\n const outPath = out ? resolve(out) : join(dirname(programPath), basename(programPath, extname(programPath)) + '.flatpack')\n // External mode: sidecar folder next to the .flatpack, e.g. `game.flatpack` → `game.assets/`.\n const assetsDir = assetMode === 'external' ? basename(outPath, extname(outPath)) + '.assets' : ''\n let built: BuildResult\n try { built = buildDocFromProgram(programPath, explicitFlats, assetMode, assetsDir) }\n catch (e) { process.stderr.write(`flatc: compile error: ${(e as Error).message}\\n`); return 1 }\n const { doc } = built\n const report = lintDocReport(doc)\n if (checkOnly) {\n if (report) process.stderr.write(report + '\\n')\n if (docHasErrors(doc)) return 1\n process.stdout.write('flatc: no errors ✓\\n')\n return 0\n }\n if (report) process.stderr.write(report + '\\n') // compile anyway: diagnostics as warnings\n writeFileSync(outPath, packToJSON(doc))\n const outDir = dirname(outPath)\n for (const c of built.mediaCopies) {\n const dest = join(outDir, ...c.key.split('/'))\n if (!isWithin(outDir, dest)) continue // defensive: never write outside the output folder\n mkdirSync(dirname(dest), { recursive: true })\n copyFileSync(c.src, dest)\n }\n const where = assetMode === 'external' ? ` (external → ${assetsDir}/)` : ''\n process.stdout.write(`flatc: ${basename(outPath)} ✓ ${doc.symbols.length} symbol(s) · ${built.flatLibs} lib(s) · ${built.packages} package(s) · ${built.media} media${where}\\n`)\n return 0\n}\n\n/** Loads a Doc from a `.flatink` (compiled) or a `.flatpack`/`.flatpack.json` (already-baked JSON). */\nfunction loadDoc(filePath: string): Doc {\n if (filePath.endsWith('.flatink')) return buildDocFromProgram(filePath).doc\n // .flatpack (canonical) or .flatpack.json (alias): untrusted JSON → normalize before use.\n return sanitizeDoc(JSON.parse(readFileSync(filePath, 'utf8')))\n}\n\n/** --play: runs the file headless, replays --script, prints { sends, vars }. */\nfunction playOnce(filePath: string, scriptPath: string, trace: boolean): number {\n if (!scriptPath) { process.stderr.write('flatc: --play requires --script <gestures.json>\\n'); return 1 }\n if (!existsSync(scriptPath)) { process.stderr.write(`flatc: script not found: ${scriptPath}\\n`); return 1 }\n let doc: Doc, gestures: Gesture[]\n try { doc = loadDoc(filePath) } catch (e) { process.stderr.write(`flatc: cannot read: ${(e as Error).message}\\n`); return 1 }\n try { gestures = JSON.parse(readFileSync(scriptPath, 'utf8')) as Gesture[] } catch (e) { process.stderr.write(`flatc: invalid JSON script: ${(e as Error).message}\\n`); return 1 }\n if (!Array.isArray(gestures)) { process.stderr.write('flatc: the script must be an array of gestures\\n'); return 1 }\n const res = playHeadless(doc, gestures, { trace })\n if (!trace) process.stdout.write(JSON.stringify(res, null, 2) + '\\n')\n else // --trace: readable log (one gesture per line) → inspection / debug-player.\n for (const s of res.steps ?? []) {\n const sends = s.sends.length ? ' sends:[' + s.sends.map((e) => (e.value !== undefined ? `${e.name}=${e.value}` : e.name)).join(', ') + ']' : ''\n const vars = Object.entries(s.changed).map(([k, [a, b]]) => `${k}:${JSON.stringify(a)}→${JSON.stringify(b)}`)\n process.stdout.write(`${s.gesture.padEnd(28)}${sends}${vars.length ? ' vars{' + vars.join(' ') + '}' : ''}\\n`)\n }\n // `expect`: any detected mismatch → stderr log + exit code ≠0 (CI self-verification).\n if (res.expectFailures?.length) {\n for (const f of res.expectFailures) process.stderr.write(`flatc: ✗ ${f}\\n`)\n return 1\n }\n return 0\n}\n\n/** Parses `--at`: \"k=v[,k2=v2]\" → variable table (state override for rendering). */\nfunction parseVars(spec: string, into: Record<string, number>): void {\n for (const pair of spec.split(',')) {\n const i = pair.indexOf('=')\n if (i < 0) continue\n const k = pair.slice(0, i).trim(); const v = Number(pair.slice(i + 1).trim())\n if (k && Number.isFinite(v)) into[k] = v\n }\n}\n\n/** --render: renders the file to PNG (headless skia). Async (SVG decode + raster). */\nasync function renderOnce(filePath: string, out: string, frame: number, vars: Record<string, number>, scale: number, steps: number): Promise<number> {\n let doc: Doc\n try { doc = loadDoc(filePath) } catch (e) { process.stderr.write(`flatc: cannot read: ${(e as Error).message}\\n`); return 1 }\n const outPath = out ? resolve(out) : join(dirname(filePath), basename(filePath, extname(filePath)) + '.png')\n try {\n const { renderDocToPng } = await import('./render')\n const png = await renderDocToPng(doc, { frame, vars: Object.keys(vars).length ? vars : undefined, scale, steps: steps || undefined })\n writeFileSync(outPath, png)\n } catch (e) { process.stderr.write(`flatc: render failed: ${(e as Error).message}\\n`); return 1 }\n process.stdout.write(`flatc: ${basename(outPath)} ✓ ${doc.width}×${doc.height} ×${scale}${frame ? ` · frame ${frame}` : ''}${steps ? ` · ${steps} step(s)` : ''}${Object.keys(vars).length ? ` · ${Object.entries(vars).map(([k, v]) => `${k}=${v}`).join(' ')}` : ''}\\n`)\n return 0\n}\n\nexport function run(argv: string[]): number | Promise<number> {\n const args = argv.slice(2)\n let out = '', scriptPath = ''\n let checkOnly = false, doWatch = false, doPlay = false, doRender = false, doTrace = false\n let frame = 0, scale = 2, steps = 0\n let assetMode: AssetMode = 'inline'\n const vars: Record<string, number> = {}\n const positional: string[] = []\n for (let i = 0; i < args.length; i++) {\n const a = args[i]\n if (a === '-o' || a === '--out') out = args[++i] ?? ''\n else if (a === '--script') scriptPath = resolve(args[++i] ?? '')\n else if (a === '--assets') assetMode = args[++i] === 'external' ? 'external' : 'inline'\n else if (a === '--check') checkOnly = true\n else if (a === '--watch') doWatch = true\n else if (a === '--play') doPlay = true\n else if (a === '--trace') doTrace = true\n else if (a === '--render') doRender = true\n else if (a === '--frame') frame = Number(args[++i] ?? '0') || 0\n else if (a === '--steps') steps = Math.max(0, Number(args[++i] ?? '0') || 0)\n else if (a === '--scale') scale = Number(args[++i] ?? '2') || 2\n else if (a === '--at') parseVars(args[++i] ?? '', vars)\n else if (a === '-h' || a === '--help') { printHelp(); return 0 }\n else positional.push(a)\n }\n if (!positional.length) { printHelp(); return 1 }\n\n const filePath = resolve(positional[0])\n if (!existsSync(filePath)) { process.stderr.write(`flatc: not found: ${filePath}\\n`); return 1 }\n\n const explicitFlats = positional.slice(1)\n if (doRender) return renderOnce(filePath, out, frame, vars, scale, steps)\n if (doPlay) return playOnce(filePath, scriptPath, doTrace)\n if (doWatch) {\n const code = compileOnce(filePath, explicitFlats, out, checkOnly, assetMode)\n const baseDir = dirname(filePath)\n let timer: ReturnType<typeof setTimeout> | undefined\n // We ignore changes to the OUTPUT (.flatpack) — otherwise writing it would re-trigger the compile in a loop.\n watch(baseDir, { recursive: false }, (_e, filename) => {\n if (filename && (filename.endsWith('.flatpack') || filename.endsWith('.flatpack.json'))) return\n clearTimeout(timer); timer = setTimeout(() => compileOnce(filePath, explicitFlats, out, checkOnly, assetMode), 80)\n })\n process.stdout.write(`flatc: watching ${baseDir} … (Ctrl+C to stop)\\n`)\n return code\n }\n return compileOnce(filePath, explicitFlats, out, checkOnly, assetMode)\n}\n"],"mappings":"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","names":["f"]}
@@ -0,0 +1,47 @@
1
+ // src/compile.ts
2
+ import { parseFlatLib, parseProgramFull } from "@flatkit/engine/flatFormat";
3
+ import { isGroup, isInstance } from "@flatkit/engine/layers";
4
+ function resolveRefs(layers, byName) {
5
+ const walk = (items) => {
6
+ for (const it of items) {
7
+ if (isInstance(it) && it.symbolId.startsWith("@")) it.symbolId = byName.get(it.symbolId.slice(1)) ?? it.symbolId;
8
+ if (isGroup(it)) it.layers.forEach((l) => walk(l.items));
9
+ }
10
+ };
11
+ layers.forEach((l) => walk(l.items));
12
+ }
13
+ function compileFlatpack(programSrc, assetSrcs = [], media = {}) {
14
+ const libs = assetSrcs.map((src) => parseFlatLib(src));
15
+ const symbols = libs.flatMap((l) => l.symbols);
16
+ const folders = libs.flatMap((l) => l.folders);
17
+ const prog = parseProgramFull(programSrc);
18
+ const byName = new Map(symbols.map((s) => [s.name, s.id]));
19
+ symbols.forEach((s) => resolveRefs(s.layers, byName));
20
+ resolveRefs(prog.layers, byName);
21
+ const assets = (prog.assets ?? []).map((a) => {
22
+ const m = media[a.data];
23
+ return m ? { ...a, mime: m.mime, data: m.data } : a;
24
+ });
25
+ return {
26
+ width: prog.width,
27
+ height: prog.height,
28
+ ...prog.background ? { background: prog.background } : {},
29
+ symbols,
30
+ ...folders.length ? { folders } : {},
31
+ layers: prog.layers,
32
+ ...prog.variables && Object.keys(prog.variables).length ? { variables: prog.variables } : {},
33
+ ...prog.imports?.length ? { imports: prog.imports } : {},
34
+ ...prog.functions?.length ? { functions: prog.functions } : {},
35
+ timeline: prog.timeline ?? { fps: 24, durationFrames: 60, tracks: [] },
36
+ ...prog.interactions?.length ? { interactions: prog.interactions } : {},
37
+ ...prog.interactors?.length ? { interactors: prog.interactors } : {},
38
+ ...assets.length ? { assets } : {}
39
+ };
40
+ }
41
+ var packToJSON = (doc) => JSON.stringify(doc);
42
+
43
+ export {
44
+ compileFlatpack,
45
+ packToJSON
46
+ };
47
+ //# sourceMappingURL=chunk-TPDP5FDF.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/compile.ts"],"sourcesContent":["// ─────────────────────────────────────────────────────────────────────────────\n// compile.ts — the \"modern SWF\" compiler (RFC, step P2).\n//\n// Takes the PROGRAM (`.flatink`) + the ASSETS (`.flat`) and produces a resolved `Doc` —\n// the \".flatpack\" v1 (= the baked doc the player already plays). Steps:\n// 1. parse the assets → symbols;\n// 2. parse the program → composition + behavior (`@Name` refs);\n// 3. RESOLVE refs by name (instances → symbol id), across ALL libs;\n// 4. assemble the Doc.\n//\n// v1: no binary optimization nor media embedding (P2b). Pure, no DOM.\n// ─────────────────────────────────────────────────────────────────────────────\nimport type { Doc, Item, Layer } from '@flatkit/types'\nimport { parseFlatLib, parseProgramFull } from '@flatkit/engine/flatFormat'\nimport { isGroup, isInstance } from '@flatkit/engine/layers'\n\n/** Resolves `symbolId: '@Name'` instances into real ids (recursive, across groups). */\nfunction resolveRefs(layers: Layer[], byName: Map<string, string>): void {\n const walk = (items: Item[]) => {\n for (const it of items) {\n if (isInstance(it) && it.symbolId.startsWith('@')) it.symbolId = byName.get(it.symbolId.slice(1)) ?? it.symbolId\n if (isGroup(it)) it.layers.forEach((l) => walk(l.items))\n }\n }\n layers.forEach((l) => walk(l.items))\n}\n\n/** Source of a media (base64 data-URI) by declared path (`asset … \"path\" …`). */\nexport type MediaMap = Record<string, { mime: string; data: string }>\n\n/**\n * Compile a `.flatink` program + its `.flat` assets → playable `Doc` (the \".flatpack\").\n * `assetSrcs` = the text of each `.flat` lib. `media` = the content of the referenced media files\n * (by path). Symbol refs (by name) are resolved across the whole set of libs; declared media\n * are EMBEDDED (path → data-URI).\n */\nexport function compileFlatpack(programSrc: string, assetSrcs: string[] = [], media: MediaMap = {}): Doc {\n const libs = assetSrcs.map((src) => parseFlatLib(src))\n const symbols = libs.flatMap((l) => l.symbols)\n const folders = libs.flatMap((l) => l.folders) // library folders (organization)\n const prog = parseProgramFull(programSrc)\n const byName = new Map(symbols.map((s) => [s.name, s.id]))\n symbols.forEach((s) => resolveRefs(s.layers, byName)) // cross-lib refs\n resolveRefs(prog.layers, byName) // program refs\n\n // Embed declared media (data = path → provided data-URI). Missing = left as is.\n const assets = (prog.assets ?? []).map((a) => { const m = media[a.data]; return m ? { ...a, mime: m.mime, data: m.data } : a })\n\n return {\n width: prog.width,\n height: prog.height,\n ...(prog.background ? { background: prog.background } : {}),\n symbols,\n ...(folders.length ? { folders } : {}),\n layers: prog.layers,\n ...(prog.variables && Object.keys(prog.variables).length ? { variables: prog.variables } : {}),\n ...(prog.imports?.length ? { imports: prog.imports } : {}),\n ...(prog.functions?.length ? { functions: prog.functions } : {}),\n timeline: prog.timeline ?? { fps: 24, durationFrames: 60, tracks: [] },\n ...(prog.interactions?.length ? { interactions: prog.interactions } : {}),\n ...(prog.interactors?.length ? { interactors: prog.interactors } : {}),\n ...(assets.length ? { assets } : {}),\n }\n}\n\n/** Serialize the `.flatpack` v1 (JSON of the compiled Doc). */\nexport const packToJSON = (doc: Doc): string => JSON.stringify(doc)\n"],"mappings":";AAaA,SAAS,cAAc,wBAAwB;AAC/C,SAAS,SAAS,kBAAkB;AAGpC,SAAS,YAAY,QAAiB,QAAmC;AACvE,QAAM,OAAO,CAAC,UAAkB;AAC9B,eAAW,MAAM,OAAO;AACtB,UAAI,WAAW,EAAE,KAAK,GAAG,SAAS,WAAW,GAAG,EAAG,IAAG,WAAW,OAAO,IAAI,GAAG,SAAS,MAAM,CAAC,CAAC,KAAK,GAAG;AACxG,UAAI,QAAQ,EAAE,EAAG,IAAG,OAAO,QAAQ,CAAC,MAAM,KAAK,EAAE,KAAK,CAAC;AAAA,IACzD;AAAA,EACF;AACA,SAAO,QAAQ,CAAC,MAAM,KAAK,EAAE,KAAK,CAAC;AACrC;AAWO,SAAS,gBAAgB,YAAoB,YAAsB,CAAC,GAAG,QAAkB,CAAC,GAAQ;AACvG,QAAM,OAAO,UAAU,IAAI,CAAC,QAAQ,aAAa,GAAG,CAAC;AACrD,QAAM,UAAU,KAAK,QAAQ,CAAC,MAAM,EAAE,OAAO;AAC7C,QAAM,UAAU,KAAK,QAAQ,CAAC,MAAM,EAAE,OAAO;AAC7C,QAAM,OAAO,iBAAiB,UAAU;AACxC,QAAM,SAAS,IAAI,IAAI,QAAQ,IAAI,CAAC,MAAM,CAAC,EAAE,MAAM,EAAE,EAAE,CAAC,CAAC;AACzD,UAAQ,QAAQ,CAAC,MAAM,YAAY,EAAE,QAAQ,MAAM,CAAC;AACpD,cAAY,KAAK,QAAQ,MAAM;AAG/B,QAAM,UAAU,KAAK,UAAU,CAAC,GAAG,IAAI,CAAC,MAAM;AAAE,UAAM,IAAI,MAAM,EAAE,IAAI;AAAG,WAAO,IAAI,EAAE,GAAG,GAAG,MAAM,EAAE,MAAM,MAAM,EAAE,KAAK,IAAI;AAAA,EAAE,CAAC;AAE9H,SAAO;AAAA,IACL,OAAO,KAAK;AAAA,IACZ,QAAQ,KAAK;AAAA,IACb,GAAI,KAAK,aAAa,EAAE,YAAY,KAAK,WAAW,IAAI,CAAC;AAAA,IACzD;AAAA,IACA,GAAI,QAAQ,SAAS,EAAE,QAAQ,IAAI,CAAC;AAAA,IACpC,QAAQ,KAAK;AAAA,IACb,GAAI,KAAK,aAAa,OAAO,KAAK,KAAK,SAAS,EAAE,SAAS,EAAE,WAAW,KAAK,UAAU,IAAI,CAAC;AAAA,IAC5F,GAAI,KAAK,SAAS,SAAS,EAAE,SAAS,KAAK,QAAQ,IAAI,CAAC;AAAA,IACxD,GAAI,KAAK,WAAW,SAAS,EAAE,WAAW,KAAK,UAAU,IAAI,CAAC;AAAA,IAC9D,UAAU,KAAK,YAAY,EAAE,KAAK,IAAI,gBAAgB,IAAI,QAAQ,CAAC,EAAE;AAAA,IACrE,GAAI,KAAK,cAAc,SAAS,EAAE,cAAc,KAAK,aAAa,IAAI,CAAC;AAAA,IACvE,GAAI,KAAK,aAAa,SAAS,EAAE,aAAa,KAAK,YAAY,IAAI,CAAC;AAAA,IACpE,GAAI,OAAO,SAAS,EAAE,OAAO,IAAI,CAAC;AAAA,EACpC;AACF;AAGO,IAAM,aAAa,CAAC,QAAqB,KAAK,UAAU,GAAG;","names":[]}