@nodegx/export 0.0.0-stage → 0.2.5

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (49) hide show
  1. package/README.md +59 -2
  2. package/dist/analyze/appState.d.ts +296 -0
  3. package/dist/analyze/plan.d.ts +3757 -0
  4. package/dist/analyze/querySettings.d.ts +75 -0
  5. package/dist/analyze/reach.d.ts +41 -0
  6. package/dist/analyze/scriptUses.d.ts +29 -0
  7. package/dist/catalog.d.ts +129 -0
  8. package/dist/cli/args.d.ts +90 -0
  9. package/dist/cli/exitCodes.d.ts +125 -0
  10. package/dist/cli/run.d.ts +30 -0
  11. package/dist/cli.inputs.json +174 -0
  12. package/dist/cli.mjs +53746 -0
  13. package/dist/cli.mjs.map +7 -0
  14. package/dist/emit/animateLib.d.ts +43 -0
  15. package/dist/emit/component.d.ts +144 -0
  16. package/dist/emit/dateLib.d.ts +45 -0
  17. package/dist/emit/emitApp.d.ts +38 -0
  18. package/dist/emit/idLib.d.ts +68 -0
  19. package/dist/emit/kits.d.ts +85 -0
  20. package/dist/emit/mediaLib.d.ts +75 -0
  21. package/dist/emit/naming.d.ts +76 -0
  22. package/dist/emit/parseLib.d.ts +106 -0
  23. package/dist/emit/preflight.d.ts +174 -0
  24. package/dist/emit/readme.d.ts +51 -0
  25. package/dist/emit/report.d.ts +309 -0
  26. package/dist/emit/scaffold.d.ts +67 -0
  27. package/dist/emit/statesLib.d.ts +52 -0
  28. package/dist/emit/style.d.ts +165 -0
  29. package/dist/emit/utilLib.d.ts +78 -0
  30. package/dist/errorMessage.d.ts +28 -0
  31. package/dist/index.cjs +52848 -0
  32. package/dist/index.cjs.map +7 -0
  33. package/dist/index.d.ts +37 -0
  34. package/dist/index.mjs +52769 -0
  35. package/dist/index.mjs.map +7 -0
  36. package/dist/ir/types.d.ts +567 -0
  37. package/dist/ledger.cjs +971 -0
  38. package/dist/ledger.cjs.map +7 -0
  39. package/dist/ledger.d.ts +75 -0
  40. package/dist/ledger.mjs +948 -0
  41. package/dist/ledger.mjs.map +7 -0
  42. package/dist/node-catalog.json +65333 -0
  43. package/dist/parse/kitSource.d.ts +135 -0
  44. package/dist/parse/parseModules.d.ts +42 -0
  45. package/dist/parse/parseProject.d.ts +13 -0
  46. package/dist/scriptFiles.d.ts +30 -0
  47. package/dist/structurePorts.d.ts +70 -0
  48. package/dist/write/writeExport.d.ts +67 -0
  49. package/package.json +53 -4
@@ -0,0 +1,567 @@
1
+ /**
2
+ * The prefix a `For Each` gives a relayed row signal, and the one it gives a relayed row value.
3
+ *
4
+ * These are the runtime's own names, registered in `foreach.tsx`'s `_managePortsForNode`: a
5
+ * template's signal output becomes `itemOutputSignal-<name>` on the repeater, a value output
6
+ * `itemOutput-<name>`. Shared because the plan gates on them and the emitter strips them, and a
7
+ * second copy of a prefix drifts.
8
+ */
9
+ export declare const ITEM_OUTPUT_SIGNAL = "itemOutputSignal-";
10
+ export declare const ITEM_OUTPUT_VALUE = "itemOutput-";
11
+ /**
12
+ * The export IR — the shared contract between the deterministic generators (EXP-002), AI logic
13
+ * translation (EXP-003) and the manifest consumers (EXP-004/005/007).
14
+ *
15
+ * Design rationale, invariants and the determinism rules (D1–D7) live in
16
+ * dev-docs/tasks/phase-18-code-export-v2/EXP-002-IR-DESIGN.md. The short version:
17
+ *
18
+ * - Plain serialisable data. No live models, no Maps, no methods. Arrays carry a defined order.
19
+ * - Identity is adopted, never minted: node ids from the substrate, the GraphSnapshot
20
+ * connectionKey format for connections, component paths in "Pages/Landing" form.
21
+ * - Parsing never drops and never fails on content. Unknown types, script parameters and
22
+ * statically-unknowable port sets become representable states, and later dispositions —
23
+ * not errors.
24
+ * - Nothing React-shaped appears here. The analysis/emit layer owns the React mapping.
25
+ */
26
+ export interface ExportIR {
27
+ project: ProjectIR;
28
+ /** Sorted by `path`, codepoint order (D1). */
29
+ components: ComponentIR[];
30
+ }
31
+ /**
32
+ * One stored parameter the export had to settle to read the graph the author saw (HLS-003).
33
+ *
34
+ * Structurally the migration's own `RunOnChangeWrite`, restated here rather than imported so the
35
+ * IR — the shared contract with EXP-003/005 — does not take a dependency on the editor's patch
36
+ * pass to describe its own shape. The producer imports the real one and assigns it.
37
+ */
38
+ export interface SettledParameterIR {
39
+ /** Component path, as the report prints it. */
40
+ component: string;
41
+ nodeId: string;
42
+ nodeType: string;
43
+ /** The governed input, full port name — `in-amount`, not `amount`. */
44
+ input: string;
45
+ /** Always `runOnChange-<input>`, and always settled to `false`. */
46
+ parameter: string;
47
+ }
48
+ export interface ProjectIR {
49
+ name: string;
50
+ catalogFormatVersion: string;
51
+ exporterVersion: string;
52
+ /**
53
+ * HLS-003 — what an editor load would have written that the files did not say, applied to this
54
+ * read so the export sees the canvas's graph rather than the file's.
55
+ *
56
+ * 🔴 **Empty is not the same as "the seam is closed."** It means this project does not exercise
57
+ * it: no node of the fifteen NDA-017 families with its control signal wired and a governed input
58
+ * left unstated. A project with no such node reads empty however open the seam is.
59
+ */
60
+ settledRunOnValueChange: SettledParameterIR[];
61
+ /** Source order preserved — these render as tokens.css in that order. */
62
+ designTokens: TokenIR[];
63
+ /** From nodegx.project.json metadata.dbCollections; typed API stubs are generated from these. */
64
+ collections: CollectionIR[];
65
+ /** One per Router node found in the project. */
66
+ routers: RouterIR[];
67
+ /**
68
+ * From nodegx.project.json metadata.cloudservices — the deployed backend this project's data,
69
+ * auth and cloud-function nodes already talk to. EXP-009 emits a real client against it.
70
+ * Absent when the project declares no backend, and the api modules stay stubs (AC7).
71
+ *
72
+ * Exactly these four fields are copied and nothing else: anything privileged someone pastes
73
+ * into project metadata (a master key, an admin token) must never reach the IR, because
74
+ * everything in the IR is presumed emittable into a browser bundle.
75
+ */
76
+ cloudservices?: CloudServicesIR;
77
+ /**
78
+ * Component paths under components/__cloud__ — cloud functions, executed by the backend's
79
+ * interpreter. The frontend export skips them entirely; they surface as one report note.
80
+ */
81
+ cloudComponents: string[];
82
+ /**
83
+ * Everything under `noodl_modules/` — custom node kits, icon sets, bundled fonts, ERG-002
84
+ * libraries (EXP-010). Directory order.
85
+ *
86
+ * 🔴 **Present even when empty, and every module is listed whatever became of it.** The defect
87
+ * this closed was an absence: the directory was never opened, so a kit node had no ports, no
88
+ * generator and no mention anywhere in the output. A module that failed to load is a row here
89
+ * with a `status` and a `message`, never a missing row.
90
+ */
91
+ modules: ModuleIR[];
92
+ /**
93
+ * P109-S17-EXPORTKITDEPS — the kit `dependencies` the deployed page loads, in its order, each once: project-relative
94
+ * (`noodl_modules/<dir>/<file>`) or an `http(s)` URL. Computed by the deploy injector itself
95
+ * (`@nodegx/module-inject` `toInjectModules`: its path rule, its module order, its `requires` refusals), never
96
+ * transcribed. Absent is none. These become classic `<script>`s before the app in the exported `index.html`.
97
+ */
98
+ moduleDependencies?: string[];
99
+ /**
100
+ * STY-004. The project's style dictionary — `nodegx.styles.json`, or the legacy `metadata.styles`
101
+ * plus `variants` in the project file. The three things a node can *point at* instead of holding:
102
+ * a colour style, a text style, a Look (today's `VariantModel`).
103
+ *
104
+ * 🔴 **The defect this closed was that the exporter never opened the file.** A variant and a text
105
+ * style on an ordinary leaf node both vanished from the export with zero mentions in a 1,059-line
106
+ * report, while the identical values written inline on the same kind of node exported perfectly —
107
+ * so the exporter handled values and silently dropped every reference (STY-001-FINDINGS §8.4b).
108
+ *
109
+ * Absent when the project declares no styles, which is all 48 fixtures in this package and all
110
+ * seven shipped templates.
111
+ */
112
+ styles?: StylesIR;
113
+ /**
114
+ * P109 ISL-005. The project's script files — `scripts/**.js`, code kept once for any Function to
115
+ * use (a Function lists the files under its `uses` parameter and reaches each as `Scripts.<name>`).
116
+ * Sorted by `path`, codepoint order (D1).
117
+ *
118
+ * Absent when the project has no `scripts/` folder, which is every fixture before ISL-005's.
119
+ */
120
+ scripts?: ScriptFileIR[];
121
+ }
122
+ /** One script file, as the runtime keys it (`noodl-runtime/src/scriptfiles.ts`). */
123
+ export interface ScriptFileIR {
124
+ /** Relative to `scripts/`, with `.js`: `engine/world.js`. */
125
+ path: string;
126
+ /** Verbatim. */
127
+ source: string;
128
+ }
129
+ /**
130
+ * A project's named styles. Field names are the **v2 file's** (`colors`, `textStyles`, `variants`);
131
+ * the legacy shape's `metadata.styles.text` is read into `textStyles` by the parser, exactly as
132
+ * `ProjectImporter.reconstructMetadata` maps it in the other direction
133
+ * (`noodl-editor/src/editor/src/io/ProjectImporter.ts:422-429`).
134
+ */
135
+ export interface StylesIR {
136
+ /** Style name → the colour it stands for. The runtime's `resolveColor` is `colors[v] ?? v`. */
137
+ colors: Record<string, string>;
138
+ /**
139
+ * Style name → the nine font parameters it sets (`fontFamily`, `fontSize`, `fontWeight`,
140
+ * `fontStyle`, `color`, `letterSpacing`, `lineHeight`, `textTransform`, `fontVariantNumeric` —
141
+ * the `textStyle` port's own `childPorts`, `node-shared-port-definitions.ts:2122-2133`).
142
+ * Values are as written: a `{value, unit}` dimension, a token reference or a literal.
143
+ */
144
+ textStyles: Record<string, Record<string, unknown>>;
145
+ /** Looks. Keyed in use by `typename` + `name` — the pair a node's `variant` field resolves against. */
146
+ variants: VariantIR[];
147
+ }
148
+ /** One Look, as `nodegx.styles.json` writes it. */
149
+ export interface VariantIR {
150
+ name: string;
151
+ /** The node type this Look applies to. A Look is per type — `Group`'s and `Text`'s never collide. */
152
+ typename: string;
153
+ /** The parameters it sets, beneath the node's own (react-component-node.ts:1817-1822). */
154
+ parameters: Record<string, unknown>;
155
+ /**
156
+ * Hover / pressed / disabled, and the transitions between them.
157
+ *
158
+ * 🔴 **Carried, deliberately unemitted.** Nothing in the corpus uses either field — 6 of 7
159
+ * templates hand-write those states as CSS text in a `CSS Definition` node instead
160
+ * (STY-001-FINDINGS §7) — and the Look design names states as the next thing after this and
161
+ * explicitly not part of it. Present here so the layer that emits them does not have to reopen
162
+ * the parser, and so the report can say a Look carried states that the export did not draw.
163
+ */
164
+ stateParameters: Record<string, Record<string, unknown>>;
165
+ stateTransitions: Record<string, Record<string, unknown>>;
166
+ }
167
+ /** Why a module's node definitions are, or are not, in {@link ModuleIR.nodes}. */
168
+ export type ModuleStatus =
169
+ /** Ran and registered at least one node. */
170
+ 'loaded'
171
+ /**
172
+ * Nothing to load and nothing wrong: an icon set, a bundled font, an ERG-002 library. Its files
173
+ * still ship. ⚠️ Distinct from every failure below — a font module reporting "threw" would send
174
+ * an author looking for a bug in a folder that contains no code.
175
+ */
176
+ | 'no-nodes-declared'
177
+ /** The folder has no readable `manifest.json`, so nothing knows what it is. Files still ship. */
178
+ | 'no-manifest'
179
+ /** A kit whose manifest names no `main` — its code arrives from a URL at runtime. */
180
+ | 'no-main'
181
+ /** The manifest names a `main` that could not be read. The one status meaning there is no code. */
182
+ | 'unreadable'
183
+ /** Ran and never called `Noodl.defineModule`. */
184
+ | 'no-define-module'
185
+ /** Called `defineModule` and named no nodes. */
186
+ | 'defines-no-nodes'
187
+ /** An ES-module build — a `<script>` tag cannot load it, in the editor or in the exported app. */
188
+ | 'es-module'
189
+ /** A CommonJS build — same. */
190
+ | 'commonjs'
191
+ /** Threw while loading. It registers nothing in the running app either. */
192
+ | 'threw';
193
+ /** One folder under `noodl_modules/`. */
194
+ export interface ModuleIR {
195
+ /** The folder name — `cashflow-kit`. The identity everything joins on. */
196
+ dirName: string;
197
+ /** The manifest's `name`, falling back to the folder name. What a surface displays. */
198
+ displayName: string;
199
+ /**
200
+ * Which of the four manifest shapes this is. Subtractive, because there is no `kind: 'node-kit'`
201
+ * marker to test — `projectmodules.ts` measured this across every project on this machine.
202
+ */
203
+ kind: 'kit' | 'iconset' | 'external-library' | 'asset';
204
+ status: ModuleStatus;
205
+ /** Why, in a sentence a person can act on. Absent only for a plain `loaded`-adjacent asset. */
206
+ message?: string;
207
+ /** The manifest's `main`, when it declares one. */
208
+ main?: string;
209
+ /** Node definitions this module registered. Empty for every status but `loaded`. */
210
+ nodes: KitNodeIR[];
211
+ /** `browser.stylesheets`, project-relative. These become `<link>`s in the exported page. */
212
+ stylesheets: string[];
213
+ /**
214
+ * An icon set's `iconClass` (`"lucide"`) — the CSS class its stylesheet defines glyph rules for.
215
+ *
216
+ * The join key between an emitted `<span class="lucide icon-alert-circle">` and the module whose
217
+ * stylesheet makes it render. Carried so the export can tell "this icon will work" from "this
218
+ * icon's set is not in the project", which are the same span with opposite outcomes.
219
+ */
220
+ iconClass?: string;
221
+ /**
222
+ * Every file under the module directory, project-relative, sorted. The copy list.
223
+ *
224
+ * 🔴 The whole directory rather than what the manifest names: a stylesheet's `url()` references
225
+ * are invisible to the manifest, and shipping the CSS without its font renders blank glyphs
226
+ * instead of an error.
227
+ */
228
+ assets: string[];
229
+ /** Manifest `runtimes`, defaulted to `['browser']` — the same default every other reader applies. */
230
+ runtimes: string[];
231
+ }
232
+ /**
233
+ * One custom node type a kit registers.
234
+ *
235
+ * Mirrors `KitNodeDefinition` in `parse/kitSource.ts`, which is where it is read from source. It
236
+ * is restated here because the IR is the contract other tasks build against and may not depend on
237
+ * the shape of the evaluator that happens to fill it.
238
+ */
239
+ export interface KitNodeIR {
240
+ /** The node type name the graph stores — `nodegx.cashflow.Pill`. */
241
+ type: string;
242
+ displayName?: string;
243
+ docs?: string;
244
+ allowChildren: boolean;
245
+ /** True when the definition has a `getReactComponent` — a visual node rather than a logic one. */
246
+ visual: boolean;
247
+ /**
248
+ * Every input, tagged with how the runtime delivers it. `via: 'prop'` is an `inputProps` entry
249
+ * that becomes a React prop; `via: 'node'` is an `inputs` entry whose `set` the node runs —
250
+ * the shape a kit's logic nodes use, and invisible if only `inputProps` is read.
251
+ */
252
+ inputs: Array<KitPortIR & {
253
+ via: 'prop' | 'node';
254
+ }>;
255
+ /**
256
+ * Every output, tagged with how the runtime wires it. `via: 'prop'` is an `outputProps` entry
257
+ * the component fires by calling a callback prop; `via: 'node'` is an `outputs` entry the node
258
+ * publishes itself. A `Money Pill`'s `dropped` exists only in the second, so reading one place
259
+ * loses it.
260
+ */
261
+ outputs: Array<KitPortIR & {
262
+ kind: 'value' | 'signal';
263
+ via: 'prop' | 'node';
264
+ }>;
265
+ }
266
+ export interface KitPortIR {
267
+ name: string;
268
+ /** Declared port type name ("string", "number", "color", "signal", …). */
269
+ type?: string;
270
+ /** The port type's `defaultUnit` ("px"). The runtime appends it to a default before it is a prop. */
271
+ defaultUnit?: string;
272
+ displayName?: string;
273
+ group?: string;
274
+ default?: unknown;
275
+ }
276
+ export interface CloudServicesIR {
277
+ /** "http://localhost:8581" — the backend's base URL. */
278
+ endpoint: string;
279
+ /** The Parse application id — the non-secret identifier every deployed app ships with. */
280
+ appId: string;
281
+ instanceId?: string;
282
+ /** "nodegx" — the discriminator EXP-009 §6 leaves room for BYOB backends on. */
283
+ type?: string;
284
+ }
285
+ export interface TokenIR {
286
+ /** CSS custom property name, exactly as authored ("--primary"). */
287
+ name: string;
288
+ value: string;
289
+ category?: string;
290
+ /** Authored description; becomes the comment above the token in tokens.css. */
291
+ description?: string;
292
+ }
293
+ export interface CollectionIR {
294
+ name: string;
295
+ /** EXP-020: `targetClass` where a Pointer or Relation column names the collection it points into. */
296
+ columns: Array<{
297
+ name: string;
298
+ type: string;
299
+ targetClass?: string;
300
+ }>;
301
+ }
302
+ export interface RouterIR {
303
+ /** The Router node's `name` parameter ("Main"). */
304
+ name: string;
305
+ /** Component id of the node's own component, and the node's id — provenance for the manifest. */
306
+ componentPath: string;
307
+ nodeId: string;
308
+ /** Legacy component path of the start page ("/Pages/Landing"). */
309
+ startPage?: string;
310
+ /** Legacy component paths, source order. */
311
+ routes: string[];
312
+ }
313
+ export interface ComponentIR {
314
+ id: string;
315
+ /** "Pages/Landing" — component.json `path` with the leading slash stripped. */
316
+ path: string;
317
+ /** From component.json `type`; anything not "page" is "component". */
318
+ role: 'page' | 'component';
319
+ /** Source order from nodes.json (D2). */
320
+ nodes: NodeIR[];
321
+ /** Source order from connections.json (D2). */
322
+ connections: ConnectionIR[];
323
+ /**
324
+ * nodes.json's own `visualRoots` — the ids the *runtime* renders the component from, in order.
325
+ * `componentModel.roots` is fed from this list and `componentinstance.ts:326` renders
326
+ * `roots[0]` alone, so it is the authoritative answer to "what draws"; the editor re-derives it
327
+ * on every save (`getVisualRootIds()`). Undefined for files that predate the field, where the
328
+ * planner falls back to its own parentless-node rule.
329
+ */
330
+ visualRoots?: string[];
331
+ intent: AuthoringIntent;
332
+ }
333
+ export interface NodeIR {
334
+ id: string;
335
+ /** Catalog typeName, or a "/"-prefixed legacy component path for component instances. */
336
+ type: string;
337
+ /**
338
+ * The catalog typeName this node resolved against, or null for component instances and types
339
+ * the catalog does not know (old projects). Ports beyond `declaredPorts` are looked up in the
340
+ * catalog through this reference rather than embedded per node.
341
+ */
342
+ catalogRef: string | null;
343
+ /**
344
+ * Present only when the serialized `label` key was present in nodes.json. Never read through a
345
+ * live model's defaulting getter (EXP-006's trap: that emits a title for every node).
346
+ */
347
+ authoredLabel?: string;
348
+ /** Sorted by name (D3). */
349
+ parameters: ParamIR[];
350
+ /** The node's own `dynamicports` declarations, as-written. Catalog ports are not duplicated here. */
351
+ declaredPorts: PortIR[];
352
+ /**
353
+ * 'complete' — catalog entry with dynamicPorts: null, or a component instance whose interface
354
+ * is its Component Inputs/Outputs.
355
+ * 'partial' — the catalog marks this type's port set as parameter-dependent (dynamicPorts
356
+ * non-null); declaredPorts + catalog cover what is statically knowable.
357
+ * 'unknown' — ports only execution reveals (Function-style nodes). EXP-003 back-fills
358
+ * `resolvedPorts` from traces.
359
+ */
360
+ portKnowledge: 'complete' | 'partial' | 'unknown';
361
+ /** Written by EXP-003 from trace data; absent until then. */
362
+ resolvedPorts?: PortIR[];
363
+ /**
364
+ * Verbatim author-written **JavaScript** for script-bearing nodes, from the first such
365
+ * parameter in name order. This is the EXP-003 LLM payload and the preserved-as-comment
366
+ * fallback when a translation stays unverified. Never trimmed, never reformatted.
367
+ *
368
+ * 🔴 "Script-bearing" is decided by the catalog's declared **language**, not by the presence of
369
+ * a code editor — `styleCss` is a codeeditor port on 29 node types and holds CSS, and Static
370
+ * Data's `json`/`csv` hold data. Selecting on the code editor alone put a Text node's CSS in
371
+ * this field, under this sentence. See `isJavaScriptCodeEditorType`.
372
+ */
373
+ sourceText?: string;
374
+ parent?: string;
375
+ /** Visual children, order = render order (D2). */
376
+ children?: string[];
377
+ /**
378
+ * STY-004. The Look this node wears — `nodes.json`'s own `variant` field, verbatim, resolved
379
+ * against {@link StylesIR.variants} by `typename` + `name`.
380
+ *
381
+ * 🔴 **A top-level field on the node, NOT an entry in `parameters`, and that is exactly why its
382
+ * loss was silent.** Everything unrecognised in `parameters` reaches `style.unhandled` and prints
383
+ * a `dropped, reported` note; a field the parser never copied reaches nothing, reports nothing,
384
+ * and left `variant` with 0 mentions in a 1,059-line export report.
385
+ */
386
+ variant?: string;
387
+ /**
388
+ * STY-004. What this node's Look and its text style supply, **already beneath its own
389
+ * `parameters`** — sorted by name (D3), absent when nothing is inherited.
390
+ *
391
+ * 🔴 **This is a second pile, not a merge into `parameters`, and the separation is the point.**
392
+ * `parameters` stays the file as written, so the report can say which values a person typed and
393
+ * which a Look lent them; a style layer that wants the effective set takes `inherited` first and
394
+ * lets `parameters` win, which is the runtime's own order.
395
+ *
396
+ * The order *within* this pile is already settled: the text style's nine font parameters sit
397
+ * lowest and the Look's own parameters override them, because in the runtime both a Look's and a
398
+ * node's parameters arrive as individual ports and every individual port beats the text-style
399
+ * bundle it came with (`components/visual/Text/Text.tsx:48-51` spreads `textStyle` then `style`;
400
+ * the port's own description says so out loud).
401
+ */
402
+ inheritedParameters?: ParamIR[];
403
+ }
404
+ export interface ParamIR {
405
+ name: string;
406
+ value: ParamValue;
407
+ }
408
+ export type ParamValue =
409
+ /** Strings (including "var(--token)" references, kept textual), numbers, booleans. */
410
+ {
411
+ kind: 'literal';
412
+ value: string | number | boolean;
413
+ }
414
+ /** {value: 340, unit: "px"} objects. */
415
+ | {
416
+ kind: 'dimension';
417
+ value: number;
418
+ unit: string;
419
+ }
420
+ /** An expression parameter — any input may carry one, evaluated live in the interpreter. */
421
+ | {
422
+ kind: 'expression';
423
+ source: string;
424
+ }
425
+ /** Code-editor-typed parameters (mapping scripts, function bodies). */
426
+ | {
427
+ kind: 'script';
428
+ source: string;
429
+ }
430
+ /** Structured editor values (query filters, sort specs, router page sets). */
431
+ | {
432
+ kind: 'json';
433
+ value: unknown;
434
+ };
435
+ export interface PortIR {
436
+ name: string;
437
+ plug: 'input' | 'output';
438
+ /**
439
+ * Never flattened: EXP-003's equivalence rules count and order signals, and the emit rule that
440
+ * value outputs land before the signal that follows them depends on knowing which is which.
441
+ */
442
+ kind: 'value' | 'signal';
443
+ /** Port type name when declared ("string", "boolean", "component", …). */
444
+ type?: string;
445
+ default?: unknown;
446
+ }
447
+ export interface ConnectionIR {
448
+ /**
449
+ * `${fromId}:${fromProperty}->${toId}:${toProperty}` — the GraphSnapshot.connectionKey format,
450
+ * adopted verbatim because EXP-006's wire labels are keyed by exactly this string.
451
+ */
452
+ key: string;
453
+ fromId: string;
454
+ fromProperty: string;
455
+ toId: string;
456
+ toProperty: string;
457
+ /** The source port's kind. 'value' when it cannot be determined statically (reported, not guessed). */
458
+ kind: 'value' | 'signal';
459
+ /**
460
+ * Author-written wire label. Irrecoverable if dropped — the wire itself vanishes in generated
461
+ * code, so this must land as a comment on the statement the wire becomes.
462
+ */
463
+ label?: string;
464
+ }
465
+ /** CAN-005's authoring-intent bundle, in serialisable form. */
466
+ export interface AuthoringIntent {
467
+ nodeComments: Array<{
468
+ nodeId: string;
469
+ text: string;
470
+ }>;
471
+ wireLabels: Array<{
472
+ connectionKey: string;
473
+ text: string;
474
+ }>;
475
+ /** Ordered, outermost first. */
476
+ regions: Array<{
477
+ text: string;
478
+ nodeIds: string[];
479
+ }>;
480
+ componentDescription?: string;
481
+ }
482
+ /**
483
+ * Dispositions are analysis output about the graph, not facts of it — they live in the manifest
484
+ * (ExportManifest.nodes), never on NodeIR.
485
+ */
486
+ export type Disposition = {
487
+ kind: 'static';
488
+ } | {
489
+ kind: 'collapsed';
490
+ into: string;
491
+ } | {
492
+ kind: 'deferred';
493
+ to: 'EXP-003';
494
+ reason: string;
495
+ } | {
496
+ kind: 'stubbed';
497
+ reason: string;
498
+ } | {
499
+ kind: 'unknown-type';
500
+ reason: string;
501
+ };
502
+ /**
503
+ * EXP-013 — one node the export left out, as a row rather than a sentence.
504
+ *
505
+ * `ComponentPlan.notes` is prose, and the report renders it. Two things the prose could not say
506
+ * were measured on `tests/fixtures/task-desk` (§50.2, then EXP-013): a refused node that only
507
+ * ever appears inside dropped-wire keys is never *named* as a node — `sweepUnreportedDeferrals`
508
+ * counts a mention inside `wire a:b->c:d` as the node having been reported — and a node refused
509
+ * only because the node that fires it was refused carries no pointer back to that node. This row
510
+ * is built from `dispositions` (which every gate writes) rather than from `notes` (which only
511
+ * some do), so a refused node is here whether or not any sentence mentions it.
512
+ */
513
+ export interface RefusedNode {
514
+ nodeId: string;
515
+ /** The catalog typeName, or the component path for an instance. */
516
+ type: string;
517
+ /** The picker's name for the type, when the catalog knows it; `type` otherwise. */
518
+ displayName: string;
519
+ /** The author's own label, only when nodes.json carried one. */
520
+ label?: string;
521
+ /** The exporter's reason, in its own words — the same text `notes` carries when it carries one. */
522
+ reason: string;
523
+ /**
524
+ * The refused node(s) whose refusal is the only reason this one is refused — the **roots** of the
525
+ * cascade, never an intermediate. Absent on a node the export has no rule for in its own right.
526
+ */
527
+ causedBy?: string[];
528
+ /**
529
+ * The node is a pathway rather than a feature: a backend verb, a navigation, an `On App Error`.
530
+ * Losing one of these to a cascade is what EXP-013's verdict is about.
531
+ */
532
+ pathway: boolean;
533
+ }
534
+ /** The durable sidecar emitted beside the code — see EXP-002-IR-DESIGN.md "The manifest". */
535
+ export interface ExportManifest {
536
+ exporterVersion: string;
537
+ catalogFormatVersion: string;
538
+ files: ManifestFile[];
539
+ nodes: ManifestNode[];
540
+ /** EXP-006's rule: nothing silently dropped. */
541
+ dropped: Array<{
542
+ what: string;
543
+ where: string;
544
+ reason: string;
545
+ }>;
546
+ }
547
+ export interface ManifestFile {
548
+ path: string;
549
+ kind: 'component' | 'page' | 'store' | 'logic' | 'api-stub' | 'styles' | 'scaffold';
550
+ sources: Array<{
551
+ componentPath: string;
552
+ nodeIds: string[];
553
+ }>;
554
+ /** SHA-256 of the body excluding the provenance header. */
555
+ contentHash: string;
556
+ }
557
+ export interface ManifestNode {
558
+ componentPath: string;
559
+ nodeId: string;
560
+ disposition: Disposition;
561
+ emittedAs?: Array<{
562
+ file: string;
563
+ symbol: string;
564
+ }>;
565
+ /** Written by EXP-003; EXP-005 must propagate it, never drop it at the framework boundary. */
566
+ verdict?: 'verified' | 'mismatched' | 'unverified';
567
+ }