@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.
- package/README.md +59 -2
- package/dist/analyze/appState.d.ts +296 -0
- package/dist/analyze/plan.d.ts +3757 -0
- package/dist/analyze/querySettings.d.ts +75 -0
- package/dist/analyze/reach.d.ts +41 -0
- package/dist/analyze/scriptUses.d.ts +29 -0
- package/dist/catalog.d.ts +129 -0
- package/dist/cli/args.d.ts +90 -0
- package/dist/cli/exitCodes.d.ts +125 -0
- package/dist/cli/run.d.ts +30 -0
- package/dist/cli.inputs.json +174 -0
- package/dist/cli.mjs +53746 -0
- package/dist/cli.mjs.map +7 -0
- package/dist/emit/animateLib.d.ts +43 -0
- package/dist/emit/component.d.ts +144 -0
- package/dist/emit/dateLib.d.ts +45 -0
- package/dist/emit/emitApp.d.ts +38 -0
- package/dist/emit/idLib.d.ts +68 -0
- package/dist/emit/kits.d.ts +85 -0
- package/dist/emit/mediaLib.d.ts +75 -0
- package/dist/emit/naming.d.ts +76 -0
- package/dist/emit/parseLib.d.ts +106 -0
- package/dist/emit/preflight.d.ts +174 -0
- package/dist/emit/readme.d.ts +51 -0
- package/dist/emit/report.d.ts +309 -0
- package/dist/emit/scaffold.d.ts +67 -0
- package/dist/emit/statesLib.d.ts +52 -0
- package/dist/emit/style.d.ts +165 -0
- package/dist/emit/utilLib.d.ts +78 -0
- package/dist/errorMessage.d.ts +28 -0
- package/dist/index.cjs +52848 -0
- package/dist/index.cjs.map +7 -0
- package/dist/index.d.ts +37 -0
- package/dist/index.mjs +52769 -0
- package/dist/index.mjs.map +7 -0
- package/dist/ir/types.d.ts +567 -0
- package/dist/ledger.cjs +971 -0
- package/dist/ledger.cjs.map +7 -0
- package/dist/ledger.d.ts +75 -0
- package/dist/ledger.mjs +948 -0
- package/dist/ledger.mjs.map +7 -0
- package/dist/node-catalog.json +65333 -0
- package/dist/parse/kitSource.d.ts +135 -0
- package/dist/parse/parseModules.d.ts +42 -0
- package/dist/parse/parseProject.d.ts +13 -0
- package/dist/scriptFiles.d.ts +30 -0
- package/dist/structurePorts.d.ts +70 -0
- package/dist/write/writeExport.d.ts +67 -0
- 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
|
+
}
|