@ngis/plugin-ui 0.4.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +13 -0
- package/README.md +25 -0
- package/dist/index.cjs +71 -0
- package/dist/index.d.ts +114 -0
- package/dist/index.mjs +69 -0
- package/dist/src/api/analysis.d.ts +140 -0
- package/dist/src/api/resources.d.ts +530 -0
- package/dist/src/api/scenes.d.ts +251 -0
- package/dist/src/api/users.d.ts +22 -0
- package/dist/src/api/workflows.d.ts +191 -0
- package/dist/src/components/plugin-ui/AttributeTable.d.ts +26 -0
- package/dist/src/components/plugin-ui/BatchTable.d.ts +47 -0
- package/dist/src/components/plugin-ui/ChartCanvas.d.ts +14 -0
- package/dist/src/components/plugin-ui/ExtentPicker.d.ts +22 -0
- package/dist/src/components/plugin-ui/FieldSelect.d.ts +23 -0
- package/dist/src/components/plugin-ui/Flow.d.ts +256 -0
- package/dist/src/components/plugin-ui/ModelSelect.d.ts +36 -0
- package/dist/src/components/plugin-ui/ParamForm.d.ts +63 -0
- package/dist/src/components/plugin-ui/PickerField.d.ts +52 -0
- package/dist/src/components/plugin-ui/ResourceSelect.d.ts +37 -0
- package/dist/src/components/plugin-ui/ResultActions.d.ts +48 -0
- package/dist/src/components/plugin-ui/RunButton.d.ts +49 -0
- package/dist/src/components/plugin-ui/StatChart.d.ts +66 -0
- package/dist/src/components/plugin-ui/TaskChrome.d.ts +35 -0
- package/dist/src/components/plugin-ui/TaskList.d.ts +29 -0
- package/dist/src/components/plugin-ui/TaskStatus.d.ts +55 -0
- package/dist/src/components/plugin-ui/batch-model.d.ts +442 -0
- package/dist/src/components/plugin-ui/flow-inputs.d.ts +61 -0
- package/dist/src/components/plugin-ui/flow-model.d.ts +303 -0
- package/dist/src/components/plugin-ui/flow-ports.d.ts +155 -0
- package/dist/src/components/plugin-ui/param-form-data.d.ts +122 -0
- package/dist/src/components/plugin-ui/param-form-model.d.ts +187 -0
- package/dist/src/components/plugin-ui/param-widgets.d.ts +88 -0
- package/dist/src/components/plugin-ui/picker-data.d.ts +141 -0
- package/dist/src/components/plugin-ui/picker-model.d.ts +150 -0
- package/dist/src/components/plugin-ui/recipe-model.d.ts +320 -0
- package/dist/src/components/plugin-ui/result-data.d.ts +112 -0
- package/dist/src/components/plugin-ui/result-model.d.ts +96 -0
- package/dist/src/components/plugin-ui/run-data.d.ts +118 -0
- package/dist/src/components/plugin-ui/run-model.d.ts +179 -0
- package/dist/src/components/plugin-ui/stat-chart-model.d.ts +168 -0
- package/dist/src/components/plugin-ui/validation-messages.d.ts +18 -0
- package/dist/src/components/plugin-ui/w2-contracts.d.ts +151 -0
- package/dist/src/components/plugin-ui/w3-contracts.d.ts +328 -0
- package/dist/src/components/plugin-ui/w4-contracts.d.ts +510 -0
- package/dist/src/components/ui/button.d.ts +12 -0
- package/dist/src/components/ui/checkbox.d.ts +4 -0
- package/dist/src/components/ui/column-resize-handle.d.ts +23 -0
- package/dist/src/components/ui/dropdown-menu.d.ts +25 -0
- package/dist/src/components/ui/input.d.ts +3 -0
- package/dist/src/components/ui/label.d.ts +4 -0
- package/dist/src/components/ui/popover.d.ts +10 -0
- package/dist/src/components/ui/select.d.ts +15 -0
- package/dist/src/components/ui/switch.d.ts +6 -0
- package/dist/src/domain/map/types.d.ts +128 -0
- package/dist/src/features/gis/components/GisAttributeTable.d.ts +56 -0
- package/dist/src/features/gis/hooks/useDatasetFeatureWindow.d.ts +51 -0
- package/dist/src/lib/api-error.d.ts +81 -0
- package/dist/src/lib/api.d.ts +82 -0
- package/dist/src/lib/auth-session.d.ts +22 -0
- package/dist/src/lib/hooks/useColumnResize.d.ts +57 -0
- package/dist/src/lib/raster-color-layer/RasterColorLayer.d.ts +48 -0
- package/dist/src/lib/raster-color-layer/TileGrid.d.ts +31 -0
- package/dist/src/lib/raster-color-layer/colorRamp.d.ts +18 -0
- package/dist/src/lib/raster-color-layer/shaders/raster-color.frag.d.ts +2 -0
- package/dist/src/lib/raster-color-layer/shaders/raster-color.vert.d.ts +2 -0
- package/dist/src/lib/raster-color-layer/zoomInterpolate.d.ts +2 -0
- package/dist/src/lib/utils.d.ts +2 -0
- package/dist/src/sdk/facets/types.d.ts +370 -0
- package/dist/src/sdk/host-api.d.ts +256 -0
- package/dist/src/sdk/map-api.d.ts +288 -0
- package/dist/src/sdk/panels.d.ts +130 -0
- package/dist/src/types/analysis.d.ts +205 -0
- package/dist/src/types/common.d.ts +17 -0
- package/dist/src/types/extensions.d.ts +560 -0
- package/dist/src/types/gis.d.ts +242 -0
- package/dist/src/types/market-social.d.ts +378 -0
- package/dist/src/types/workflow.d.ts +83 -0
- package/package.json +59 -0
|
@@ -0,0 +1,303 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The flow engine's **React-free core** (Stage 14 Y5a — **F14.4**, **F14.5**,
|
|
3
|
+
* **F14.10**, **F14.1**).
|
|
4
|
+
*
|
|
5
|
+
* No React, no host, no clock, no API client, no DOM. That is not the house
|
|
6
|
+
* style repeated for a fourth time; it is the load-bearing claim of this wave.
|
|
7
|
+
* F14.10's semantics — a node is enabled iff every required in-port is
|
|
8
|
+
* satisfied, the reason is generated from the unsatisfied port's label, and
|
|
9
|
+
* there are exactly three disclosure states — are **entirely decidable from
|
|
10
|
+
* declarative data**. A core that needed React to decide them would make
|
|
11
|
+
* "every plugin-ui component stays host-renderable from declarative data" an
|
|
12
|
+
* assertion instead of a proof, and Stage 15's worker tier is the boundary that
|
|
13
|
+
* would eventually collect on the difference. So: `<Flow>` (Y5b) holds the
|
|
14
|
+
* React state and calls this module; this module decides everything.
|
|
15
|
+
*
|
|
16
|
+
* ## What this module decides, and the three questions behind it
|
|
17
|
+
*
|
|
18
|
+
* 1. **What is bound.** {@link resolveFlow} walks F14.4's four value forms
|
|
19
|
+
* (static literal, `{$context}`, `{$input}`, `"@ref"`) and three reference
|
|
20
|
+
* forms (`"@id"`, `"@id.member"`, `...ngisFlowSpread("@id")`) and answers one
|
|
21
|
+
* {@link NgisPortResolution} per in-port.
|
|
22
|
+
* 2. **What is enabled.** Satisfaction of the *required* in-ports, in
|
|
23
|
+
* declaration order, yields {@link NgisNodeEnablement} — the boolean, the
|
|
24
|
+
* disclosure state and the generated reason, as data.
|
|
25
|
+
* 3. **What is broken.** Anything F14.4 calls a *compile error* is a
|
|
26
|
+
* {@link NgisFlowDefect}: a developer-facing sentence, never a rendered
|
|
27
|
+
* string. See "Defects are data" below for why they are not thrown here.
|
|
28
|
+
*
|
|
29
|
+
* ## Why `{$context}` does not go through `compileWhenExpression`
|
|
30
|
+
*
|
|
31
|
+
* F14.10 says evaluator reuse is mandatory and names `compileWhenExpression`.
|
|
32
|
+
* It cannot apply to *value* resolution, and the reason is in the signature:
|
|
33
|
+
* `CompiledWhenExpression.evaluate(read)` returns a **`boolean`**
|
|
34
|
+
* (`src/sdk/context/when.ts:44`). There is no value channel to reuse. F14.4's
|
|
35
|
+
* own text is the resolution — form 2's payload is a *key*, not an expression
|
|
36
|
+
* ("the same `key := ident ("." ident)*` production `when` already lexes"), and
|
|
37
|
+
* a key is read, not evaluated. So this module adds **no grammar at all**: it
|
|
38
|
+
* hands the key to a reader. The clause's intent (there is no third grammar) is
|
|
39
|
+
* kept exactly; the named mechanism is simply the wrong shape for the job, and
|
|
40
|
+
* a `when`-shaped wrapper around a key read would have been the third grammar
|
|
41
|
+
* the clause forbids.
|
|
42
|
+
*
|
|
43
|
+
* The reader is a **function, not a store** (`(key) => unknown`), and that is
|
|
44
|
+
* what keeps this module pure. An unknown key is `undefined`, per F14.4.
|
|
45
|
+
*
|
|
46
|
+
* ## Defects are data, and the dev-mode throw is a separate call
|
|
47
|
+
*
|
|
48
|
+
* F14.4 calls an ambiguous `"@id"`, a non-`record` spread source and a
|
|
49
|
+
* duplicated spread key *compile errors*, and F14.5 calls a duplicate node id a
|
|
50
|
+
* development-mode error. All four are authoring mistakes with no honest
|
|
51
|
+
* end-user rendering: {@link NgisFlowReason} is a frozen two-arm union and
|
|
52
|
+
* neither arm can say "this reference is ambiguous". Throwing from inside
|
|
53
|
+
* resolution would also make the model partial, which a pure core that Y5b
|
|
54
|
+
* calls during render cannot afford. So resolution is **total** and collects
|
|
55
|
+
* {@link NgisFlowDefect}s; {@link assertFlowIsWellFormed} is the dev-gated
|
|
56
|
+
* throw, and its message is where F14.4's "lists the available members" lives.
|
|
57
|
+
*
|
|
58
|
+
* ## The two reconciliations worth reading before changing anything below
|
|
59
|
+
*
|
|
60
|
+
* **`"@run"` has two meanings in F14.4 and the destination decides which.** The
|
|
61
|
+
* general rule makes `"@id"` the node's *default output port* — for a
|
|
62
|
+
* `RunButton` that is `taskId: task`, so `"@run"` is a task handle. The
|
|
63
|
+
* "named outputs of a task node" bullet makes `"@run"` *the `NgisTaskOutput`
|
|
64
|
+
* with `primary === true`, else the first* — a file. Both are frozen text and
|
|
65
|
+
* they are not the same value. They are reconciled by the **destination port's
|
|
66
|
+
* kind**: a `task` in-port receives the handle, any other kind receives the
|
|
67
|
+
* primary output's `fileId` through {@link ngisPrimaryTaskOutput}. That is the
|
|
68
|
+
* only reading under which both sentences are true, and it is also what an
|
|
69
|
+
* author means by binding `"@run"` to an `AttributeTable`'s `resource` port.
|
|
70
|
+
*
|
|
71
|
+
* **Port ids are the member namespace — except where a reserved task member
|
|
72
|
+
* shadows one.** YD41 froze a port id as a *member name*, and `RunButton`'s out
|
|
73
|
+
* port is named `taskId`, which is also the first of F14.4's four reserved
|
|
74
|
+
* members. **Amended at the W3 merge (`V-226`):** the reserved member wins, so
|
|
75
|
+
* `"@run.taskId"` is the **id string**, not the handle. This paragraph
|
|
76
|
+
* previously said the opposite and it was the reading under which F14.22's
|
|
77
|
+
* `<TaskStatus taskId="@run.taskId">` received an object where both F14.4
|
|
78
|
+
* ("`taskId` **(string)**") and the seam (port kind `task` is "a task handle
|
|
79
|
+
* **id**") promise a string. The whole handle is still what a bare `"@run"`
|
|
80
|
+
* yields to a `task`-kind in-port, `.outputs.<name>` and named outputs are
|
|
81
|
+
* unchanged, and a colliding output stays reachable as `"@run.outputs.<name>"`.
|
|
82
|
+
*/
|
|
83
|
+
import type { NgisDataType, NgisTaskOutput } from "../../sdk/facets/types";
|
|
84
|
+
import { type NgisFlowNode, type NgisFlowRef, type NgisNodeEnablement, type NgisPortKind, type NgisPortResolution, type NgisPortSpec, type NgisResolvedValue, type NgisTaskRefValue, type NgisTypeVerdict } from "./w3-contracts";
|
|
85
|
+
/**
|
|
86
|
+
* F14.14's public rows for this session, and **exactly** those. Every one is
|
|
87
|
+
* *declared* in the kickoff seam `w3-contracts.ts` and *published* from here,
|
|
88
|
+
* so the barrels never import the seam and the seam stays a wave artifact
|
|
89
|
+
* (`packages/plugin-ui/src/index.ts`'s "W3 note"). A consumer that needs a seam
|
|
90
|
+
* name which is not a public row — `isNgisUnbound`, `NgisPortResolution`,
|
|
91
|
+
* `NGIS_PORT_ATTRIBUTES` — imports it from `./w3-contracts` by relative path,
|
|
92
|
+
* exactly as W2's consumers import `w2-contracts.ts`.
|
|
93
|
+
*/
|
|
94
|
+
export { ngisFlowSpread, ngisTypeVerdict } from "./w3-contracts";
|
|
95
|
+
export type { NgisDisclosureState, NgisFlowNode, NgisFlowReason, NgisFlowRef, NgisFlowSnapshot, NgisInputBinding, NgisNodeEnablement, NgisPortDirection, NgisPortKind, NgisPortSpec, NgisTypeVerdict, } from "./w3-contracts";
|
|
96
|
+
import type { NgisFlowSnapshot } from "./w3-contracts";
|
|
97
|
+
/**
|
|
98
|
+
* The scope's nodes, keyed. **Last-wins on `id`** (F14.5): the surviving node is
|
|
99
|
+
* the last declaration, held at the position of the *first*, so re-declaring a
|
|
100
|
+
* node does not reorder the scope and the generated-reason order stays stable
|
|
101
|
+
* across a re-render. A collision is recorded rather than swallowed —
|
|
102
|
+
* {@link resolveFlow} turns each into a `duplicate-node-id` defect and
|
|
103
|
+
* {@link assertFlowIsWellFormed} throws on it outside production.
|
|
104
|
+
*/
|
|
105
|
+
export interface NgisFlowRegistry {
|
|
106
|
+
readonly nodes: readonly NgisFlowNode[];
|
|
107
|
+
readonly byId: ReadonlyMap<string, NgisFlowNode>;
|
|
108
|
+
/** Ids declared more than once, in first-collision order. */
|
|
109
|
+
readonly duplicateIds: readonly string[];
|
|
110
|
+
}
|
|
111
|
+
export declare function createFlowRegistry(declared: readonly NgisFlowNode[]): NgisFlowRegistry;
|
|
112
|
+
export type NgisFlowDefectCode =
|
|
113
|
+
/** F14.5: two nodes claim one `id` in a scope. */
|
|
114
|
+
"duplicate-node-id"
|
|
115
|
+
/** A reference names a node the scope does not hold. */
|
|
116
|
+
| "unknown-node"
|
|
117
|
+
/** A reference names neither an out port, a reserved task member nor a task
|
|
118
|
+
* output name. `members` lists what exists. */
|
|
119
|
+
| "unknown-member"
|
|
120
|
+
/** F14.4: `"@id"` on a node with several out ports and no `"value"` port.
|
|
121
|
+
* `members` lists the available ones — never a guess. */
|
|
122
|
+
| "ambiguous-default-output"
|
|
123
|
+
/** F14.4: a spread whose source port is not kind `record`. */
|
|
124
|
+
| "spread-source-not-record"
|
|
125
|
+
/** F14.4: two spreads contributing the same record key. */
|
|
126
|
+
| "spread-key-conflict"
|
|
127
|
+
/** The verdict does not cover this: a `task` out bound to a `resource` in. */
|
|
128
|
+
| "port-kind-mismatch"
|
|
129
|
+
/** `ngisTypeVerdict === "fail"` (F14.1). Also an `invalid` resolution. */
|
|
130
|
+
| "type-mismatch"
|
|
131
|
+
/** A value arrived in a shape `NgisResolvedValue` cannot hold. */
|
|
132
|
+
| "unsupported-value"
|
|
133
|
+
/** F14.4 cuts ordering edges, so a data cycle is always an authoring bug. */
|
|
134
|
+
| "cycle";
|
|
135
|
+
/**
|
|
136
|
+
* One authoring mistake. `message` is a **developer-facing English sentence**,
|
|
137
|
+
* never a message key and never rendered to an end user — F13.11 keeps plugin
|
|
138
|
+
* strings literal and F14.10 requires user-facing reasons to be *generated*, so
|
|
139
|
+
* these two channels stay apart on purpose. The user-facing consequence of a
|
|
140
|
+
* defect is whatever {@link NgisNodeEnablement} says.
|
|
141
|
+
*/
|
|
142
|
+
export interface NgisFlowDefect {
|
|
143
|
+
readonly code: NgisFlowDefectCode;
|
|
144
|
+
readonly nodeId: string;
|
|
145
|
+
/** The in-port the defect was found resolving, or `null` for a scope-level one. */
|
|
146
|
+
readonly portId: string | null;
|
|
147
|
+
readonly message: string;
|
|
148
|
+
/** For `unknown-member` / `ambiguous-default-output`: what does exist. */
|
|
149
|
+
readonly members: readonly string[];
|
|
150
|
+
}
|
|
151
|
+
/**
|
|
152
|
+
* A task handle, structurally. Checked rather than assumed because the out-port
|
|
153
|
+
* value arrives from Y5b's React state, i.e. from a component, i.e. from data
|
|
154
|
+
* this module did not build.
|
|
155
|
+
*/
|
|
156
|
+
export declare function isNgisTaskRefValue(value: unknown): value is NgisTaskRefValue;
|
|
157
|
+
/**
|
|
158
|
+
* Whether an arbitrary value is one `NgisResolvedValue` can hold.
|
|
159
|
+
*
|
|
160
|
+
* The array arm needs both readings and they are distinguishable: F14.5's
|
|
161
|
+
* `resources` kind is `readonly string[]` and its `extent` kind is a
|
|
162
|
+
* four-number tuple, so an all-string array is the former and a four-number
|
|
163
|
+
* array the latter. Anything else — a mixed array, a five-number array, a
|
|
164
|
+
* nested record — is **not** representable, and `NgisContextValue` really can
|
|
165
|
+
* produce one (`readonly (string | number)[]`, `src/sdk/context/keys.ts:20`).
|
|
166
|
+
* That is why this is a guard and not a cast.
|
|
167
|
+
*/
|
|
168
|
+
export declare function isNgisResolvedValue(value: unknown): value is NgisResolvedValue;
|
|
169
|
+
/**
|
|
170
|
+
* F14.4's default output port: the one whose id is `"value"` if the node
|
|
171
|
+
* declares it, otherwise its **single** out port. `null` means the reference is
|
|
172
|
+
* ambiguous (or the node publishes nothing) and F14.4 forbids guessing.
|
|
173
|
+
*
|
|
174
|
+
* The `"value"` literal comes from `NGIS_W2_PORT_IDS`, not from a second copy —
|
|
175
|
+
* a W3 module re-declaring the reserved member vocabulary is the defect class
|
|
176
|
+
* `V-127` names, and the seam test guards it.
|
|
177
|
+
*/
|
|
178
|
+
export declare function defaultOutputPort(ports: readonly NgisPortSpec[]): NgisPortSpec | null;
|
|
179
|
+
export declare function portKindsCompatible(into: NgisPortKind, from: NgisPortKind): boolean;
|
|
180
|
+
/**
|
|
181
|
+
* The client-side binding check over two *declared* type sets (F14.1/F14.5).
|
|
182
|
+
*
|
|
183
|
+
* Empty or absent on either side means "any type" and therefore `"pass"` —
|
|
184
|
+
* F14.5's own rule, and the reason YD44's bidirectional wildcard matters.
|
|
185
|
+
*
|
|
186
|
+
* When both sides declare several types the verdict is the **most permissive**
|
|
187
|
+
* over the cross product, and that is a deliberate asymmetry with the server:
|
|
188
|
+
* a `"fail"` here must mean *no* value the source can hold is acceptable,
|
|
189
|
+
* because `host.models.validate` is the authority on the actual dataset type
|
|
190
|
+
* and blocking a binding that could legitimately resolve would reject real
|
|
191
|
+
* graphs offline. `"warn"` never blocks either way.
|
|
192
|
+
*/
|
|
193
|
+
export declare function bindingDataTypeVerdict(expected: readonly NgisDataType[] | undefined, actual: readonly NgisDataType[] | undefined): NgisTypeVerdict;
|
|
194
|
+
/**
|
|
195
|
+
* **F14.4's one `"@run"` rule, canonically (`V-127`, YD45).** The output with
|
|
196
|
+
* `primary === true`, else the first, else `null`.
|
|
197
|
+
*
|
|
198
|
+
* Generic over the row shape so the two host helpers delegate without a cast:
|
|
199
|
+
* `NgisResultOutput` is assignable to `NgisTaskOutput`, not the reverse.
|
|
200
|
+
*
|
|
201
|
+
* **The `fileId === null` case is decided here, explicitly, because the three
|
|
202
|
+
* pre-existing implementations disagreed about it.** This rule does **not**
|
|
203
|
+
* filter. F14.4 says "the `NgisTaskOutput` with `primary === true`; if none, the
|
|
204
|
+
* first" and says of the sibling `"@run.<name>"` rule that an absent name is
|
|
205
|
+
* unsatisfied "never silently the primary" — the whole section's principle is
|
|
206
|
+
* *never substitute*. Filtering first would silently return a **non-primary**
|
|
207
|
+
* output whenever the primary's file id has not been written yet, which is a
|
|
208
|
+
* real and common intermediate state (`fileId` fills in as a task integrates),
|
|
209
|
+
* so the substitution would be both invisible and transient. Selecting the
|
|
210
|
+
* primary and letting a `null` file id read as *unsatisfied, awaiting upstream*
|
|
211
|
+
* says the true thing: the right output is known and its file is not ready.
|
|
212
|
+
*
|
|
213
|
+
* Consequence, recorded rather than glossed: `first-party-plugins/`
|
|
214
|
+
* `terrain-quick-look/src/results.ts:62` filters *before* selecting, so making
|
|
215
|
+
* it delegate would change a shipping behaviour. It does not delegate and
|
|
216
|
+
* `V-127` stays open for that one call site — see the session note.
|
|
217
|
+
*/
|
|
218
|
+
export declare function ngisPrimaryTaskOutput<T extends NgisTaskOutput>(outputs: readonly T[]): T | null;
|
|
219
|
+
/**
|
|
220
|
+
* Split `"@dem"` / `"@run.taskId"` into a node id and a member — **the one
|
|
221
|
+
* implementation of the rule** (`V-262`, closed by F15.14's sibling work).
|
|
222
|
+
*
|
|
223
|
+
* A node id **may** contain dots (it is an author-supplied `id` prop), which
|
|
224
|
+
* makes `"@a.b"` ambiguous in exactly the way `X9-I-5` described for context
|
|
225
|
+
* keys. The tie is broken deterministically and in the author's favour: a body
|
|
226
|
+
* that names a node **exactly** is that node with no member, and only otherwise
|
|
227
|
+
* does the first `.` split. A node literally named `a.b` sitting beside a node
|
|
228
|
+
* `a` with a member `b` is therefore addressable, and the dotted node wins.
|
|
229
|
+
*
|
|
230
|
+
* `isKnownNode` rather than a registry, so `recipe-model.ts` — which holds a
|
|
231
|
+
* plain `Map` and used to mirror this by hand — delegates instead of copying.
|
|
232
|
+
* Callers that must reject an unknown source check `isKnownNode(sourceId)`
|
|
233
|
+
* themselves; this function reports what the reference *says*, and reporting
|
|
234
|
+
* "unknown node" with the candidate list is the caller's job.
|
|
235
|
+
*/
|
|
236
|
+
export declare function ngisSplitFlowRef(ref: NgisFlowRef, isKnownNode: (id: string) => boolean): {
|
|
237
|
+
sourceId: string;
|
|
238
|
+
member: string;
|
|
239
|
+
};
|
|
240
|
+
/** F14.4 form 2's reader. A function, not a store — that is the purity seam. */
|
|
241
|
+
export type NgisFlowContextReader = (key: string) => unknown;
|
|
242
|
+
/**
|
|
243
|
+
* Everything outside the graph that resolution reads. Y5b owns all three and
|
|
244
|
+
* this module owns none of them.
|
|
245
|
+
*/
|
|
246
|
+
export interface NgisFlowState {
|
|
247
|
+
/**
|
|
248
|
+
* Out-port values, keyed by {@link ngisPortRef}. An absent key is a port that
|
|
249
|
+
* **has not produced yet**, which is what separates F14.10's `pending` from
|
|
250
|
+
* its `blocked`; a present-but-unbound value (`null`, `""`, `[]`) is the user
|
|
251
|
+
* having cleared a control, and {@link isNgisUnbound} treats the two alike.
|
|
252
|
+
*
|
|
253
|
+
* **`null` is in the type because the doc above requires it, and only the
|
|
254
|
+
* merged tree showed that it was not** (W3 merge, `V-223`). Y5a's type read
|
|
255
|
+
* `NgisResolvedValue | undefined`, which cannot hold the `null` this very
|
|
256
|
+
* comment describes, while Y5b's `<Flow>` `publish` writes exactly that `null`
|
|
257
|
+
* when a control is cleared. Neither branch could fail on its own: Y5a never
|
|
258
|
+
* built a scope and Y5b resolved against a stub whose type admitted `null`.
|
|
259
|
+
* Widened here rather than narrowing `publish`, because collapsing "cleared"
|
|
260
|
+
* onto "never produced" is what turns `blocked` into `pending` and stops the
|
|
261
|
+
* graph dimming.
|
|
262
|
+
*/
|
|
263
|
+
readonly outputs: Readonly<Record<string, NgisResolvedValue | null | undefined>>;
|
|
264
|
+
/** F14.4 form 2. Absent ⇒ every `{$context}` reads `undefined`. */
|
|
265
|
+
readonly context?: NgisFlowContextReader;
|
|
266
|
+
/** F14.4 form 3. **Recipe-only, and enforcing that is Y6's** — this module
|
|
267
|
+
* resolves `{$input}` against whatever map it is handed. */
|
|
268
|
+
readonly promoted?: Readonly<Record<string, NgisResolvedValue>>;
|
|
269
|
+
}
|
|
270
|
+
/** What one pass over a scope answers. */
|
|
271
|
+
export interface NgisFlowResolution {
|
|
272
|
+
readonly registry: NgisFlowRegistry;
|
|
273
|
+
/** One entry per **in**-port, keyed by {@link ngisPortRef}. Out ports hold
|
|
274
|
+
* values rather than resolutions, and `NgisPortResolution` is defined by the
|
|
275
|
+
* seam as "what binding resolution answers for one in-port". */
|
|
276
|
+
readonly resolved: Readonly<Record<string, NgisPortResolution>>;
|
|
277
|
+
/** Registry order. */
|
|
278
|
+
readonly enablement: readonly NgisNodeEnablement[];
|
|
279
|
+
readonly enablementByNode: ReadonlyMap<string, NgisNodeEnablement>;
|
|
280
|
+
readonly defects: readonly NgisFlowDefect[];
|
|
281
|
+
}
|
|
282
|
+
/**
|
|
283
|
+
* Resolve a whole scope: every in-port, every node's enablement, every defect.
|
|
284
|
+
*
|
|
285
|
+
* **Total — it never throws.** A malformed graph produces defects and disabled
|
|
286
|
+
* nodes, and {@link assertFlowIsWellFormed} is the dev-mode escalation.
|
|
287
|
+
*/
|
|
288
|
+
export declare function resolveFlow(nodes: readonly NgisFlowNode[], state: NgisFlowState): NgisFlowResolution;
|
|
289
|
+
/**
|
|
290
|
+
* The serialization hook Y6 consumes (F14.6's input, not F14.6's schema).
|
|
291
|
+
*
|
|
292
|
+
* Deliberately the graph **as declared** plus the resolutions at snapshot time.
|
|
293
|
+
* It is not pre-shaped into recipe fields: F14.6 owns the recipe schema, and a
|
|
294
|
+
* snapshot that already spoke it would put half of Y6's mapping in W3, where no
|
|
295
|
+
* clause authorizes it. Everything Y6 needs to derive promoted inputs, node
|
|
296
|
+
* `params` and the `{graph,view}` envelope is present, and nothing more.
|
|
297
|
+
*
|
|
298
|
+
* The result is plain JSON — no `Map`, no `undefined`, no function — so it
|
|
299
|
+
* round-trips through `JSON.stringify`/`parse` unchanged. That is the property
|
|
300
|
+
* Y6 and Y7 actually depend on and it is asserted, not assumed.
|
|
301
|
+
*/
|
|
302
|
+
export declare function flowSnapshot(resolution: NgisFlowResolution): NgisFlowSnapshot;
|
|
303
|
+
export declare function assertFlowIsWellFormed(resolution: NgisFlowResolution): void;
|
|
@@ -0,0 +1,155 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* **F14.5's per-component port table, as data** (Stage 14 Y5b, W3).
|
|
3
|
+
*
|
|
4
|
+
* One builder per shipped component, pure and React-free, for the reason every
|
|
5
|
+
* other model/data split in this package exists: the table is frozen, Y2–Y4 may
|
|
6
|
+
* add a port but never remove or retype one, and a test that reads the table
|
|
7
|
+
* without a DOM is what makes that claim checkable. `Flow.tsx` calls these; no
|
|
8
|
+
* component hand-writes an `NgisPortSpec`.
|
|
9
|
+
*
|
|
10
|
+
* ## What a port id is, and what a node id is (YD41)
|
|
11
|
+
*
|
|
12
|
+
* A port id is a **member name, unique within its node** — which is why four
|
|
13
|
+
* components can all publish `value` and four can all read `taskId`. The
|
|
14
|
+
* component's `id` prop is the **node** id and is scope-unique. The reserved
|
|
15
|
+
* member vocabulary is `NGIS_W2_PORT_IDS` in `w2-contracts.ts`, used here and
|
|
16
|
+
* **not re-declared**: a second copy of `value`/`taskId`/`parent`/`resource` is
|
|
17
|
+
* the `V-127` defect class.
|
|
18
|
+
*
|
|
19
|
+
* ## Labels, and why the fallback is the node id
|
|
20
|
+
*
|
|
21
|
+
* `NgisPortSpec.label` is the **generated-reason text source** (F14.10) and is
|
|
22
|
+
* never a message key — a literal plugin string or the UI schema's `label`,
|
|
23
|
+
* whose provenance is `x-display-name` → `title` → `name`. Every component takes
|
|
24
|
+
* an optional `label` prop, so when an author omits it the port still needs
|
|
25
|
+
* something a sentence can be built from; the node id is the only other thing
|
|
26
|
+
* the author actually wrote (`<ResourceSelect id="dem"/>` → *"Choose dem
|
|
27
|
+
* first"*). Falling back to the port id would produce *"Choose value first"* for
|
|
28
|
+
* every picker in the flow, which names nothing.
|
|
29
|
+
*/
|
|
30
|
+
import type { NgisDataType } from "../../sdk/facets/types";
|
|
31
|
+
import type { NgisToolUiSchema } from "../../types/analysis";
|
|
32
|
+
import type { NgisPortSpec } from "./w3-contracts";
|
|
33
|
+
/**
|
|
34
|
+
* The `data-slot` each node reports.
|
|
35
|
+
*
|
|
36
|
+
* **CORRECTION.** `NgisFlowNode.slot` is documented as "from
|
|
37
|
+
* `NGIS_W2_DATA_SLOTS` or `NGIS_W3_DATA_SLOTS`", but **four of the eleven
|
|
38
|
+
* components render no component-specific `data-slot` at all**:
|
|
39
|
+
* `ResourceSelect`, `ModelSelect`, `FieldSelect` and `ExtentPicker` render only
|
|
40
|
+
* `PickerField`'s shared `ngis-picker-field` (`ExtentPicker` adds
|
|
41
|
+
* `ngis-extent-value` to an inner `<p>`), and `NGIS_W2_DATA_SLOTS` has no entry
|
|
42
|
+
* for any of them. So those four report the slot they really render. Minting
|
|
43
|
+
* four new slot names is *not* the fix: `verify-externals.mjs` fingerprints
|
|
44
|
+
* plugin-ui by these strings, W3's declared slots are `flow` and
|
|
45
|
+
* `flowInspector`, and the engine never branches on `slot` anyway — it is
|
|
46
|
+
* descriptive, and an honest description beats an invented table entry.
|
|
47
|
+
*/
|
|
48
|
+
export declare const NGIS_PICKER_SLOT = "ngis-picker-field";
|
|
49
|
+
export declare const NGIS_FLOW_SLOTS: {
|
|
50
|
+
readonly resourceSelect: "ngis-picker-field";
|
|
51
|
+
readonly modelSelect: "ngis-picker-field";
|
|
52
|
+
readonly fieldSelect: "ngis-picker-field";
|
|
53
|
+
readonly extentPicker: "ngis-picker-field";
|
|
54
|
+
readonly paramForm: "ngis-param-form";
|
|
55
|
+
readonly runButton: "ngis-run-button";
|
|
56
|
+
readonly taskStatus: "ngis-task-status";
|
|
57
|
+
readonly taskList: "ngis-task-list";
|
|
58
|
+
readonly resultActions: "ngis-result-actions";
|
|
59
|
+
readonly attributeTable: "ngis-attribute-table";
|
|
60
|
+
readonly statChart: "ngis-stat-chart";
|
|
61
|
+
};
|
|
62
|
+
export interface ResourcePortOptions {
|
|
63
|
+
readonly nodeId: string;
|
|
64
|
+
readonly label?: string;
|
|
65
|
+
readonly multiple?: boolean;
|
|
66
|
+
readonly types?: readonly NgisDataType[];
|
|
67
|
+
}
|
|
68
|
+
/** `value: resource`, or `resources` when `multiple` (F14.5). */
|
|
69
|
+
export declare function resourceSelectPorts(options: ResourcePortOptions): readonly NgisPortSpec[];
|
|
70
|
+
/** `value: value`, `valueType: "string"` — the tool id (F14.5). */
|
|
71
|
+
export declare function modelSelectPorts(options: {
|
|
72
|
+
readonly nodeId: string;
|
|
73
|
+
readonly label?: string;
|
|
74
|
+
}): readonly NgisPortSpec[];
|
|
75
|
+
/**
|
|
76
|
+
* `value: value` out, `parent: resource` in — and `parentPortId` names that
|
|
77
|
+
* **sibling in-port on the same node** (YD41), not a port on another node. The
|
|
78
|
+
* parent's value therefore arrives through one binding and one resolution path,
|
|
79
|
+
* which is what stops a `FieldSelect` having two notions of its parent.
|
|
80
|
+
*/
|
|
81
|
+
export declare function fieldSelectPorts(options: {
|
|
82
|
+
readonly nodeId: string;
|
|
83
|
+
readonly label?: string;
|
|
84
|
+
/** Whether the parent must be bound for this node to be enabled. */
|
|
85
|
+
readonly parentRequired?: boolean;
|
|
86
|
+
}): readonly NgisPortSpec[];
|
|
87
|
+
/** `value: extent` (F14.5). `NgisBounds` is `[west, south, east, north]`. */
|
|
88
|
+
export declare function extentPickerPorts(options: {
|
|
89
|
+
readonly nodeId: string;
|
|
90
|
+
readonly label?: string;
|
|
91
|
+
}): readonly NgisPortSpec[];
|
|
92
|
+
/**
|
|
93
|
+
* `value: record` out, plus **one `in` port per required UI-schema param not
|
|
94
|
+
* satisfied internally** (F14.5).
|
|
95
|
+
*
|
|
96
|
+
* "Not satisfied internally" is the `hide` list: F14.22 writes
|
|
97
|
+
* `hide={["ELEV"]}` precisely because that param is bound by a flow port
|
|
98
|
+
* instead of rendered as a field. A param the form *renders* is satisfied by the
|
|
99
|
+
* user inside the form, so it is not an in port — otherwise every generated form
|
|
100
|
+
* would report itself blocked on its own fields, and F14.9's client validation
|
|
101
|
+
* already owns that verdict.
|
|
102
|
+
*/
|
|
103
|
+
export declare function paramFormPorts(options: {
|
|
104
|
+
readonly nodeId: string;
|
|
105
|
+
readonly label?: string;
|
|
106
|
+
readonly schema: NgisToolUiSchema | null;
|
|
107
|
+
readonly hide?: readonly string[];
|
|
108
|
+
}): readonly NgisPortSpec[];
|
|
109
|
+
/**
|
|
110
|
+
* `taskId: task` out, plus **one `in` port per bound tool input** (F14.5).
|
|
111
|
+
*
|
|
112
|
+
* "Bound" is literal: the ports are the keys of the author's `inputs`, minus the
|
|
113
|
+
* spread directives. A spread contributes *values* at resolve time and its
|
|
114
|
+
* record's keys are not known until then, so it cannot contribute a declared
|
|
115
|
+
* port — and inventing one per required schema param whenever a spread is
|
|
116
|
+
* present would give the button in ports the author never wrote.
|
|
117
|
+
*
|
|
118
|
+
* Everything about a bound input other than its name comes from the tool's UI
|
|
119
|
+
* schema, which is F14.10's provenance chain (`NgisToolUiParam.label` =
|
|
120
|
+
* `x-display-name` → `title` → `name`) reaching the reason unchanged. A name the
|
|
121
|
+
* schema does not know still gets a port — **surfaced, never dropped**, the
|
|
122
|
+
* `ExtensionSettingsForm` rule — as an optional `value` port, so it cannot
|
|
123
|
+
* silently block a run.
|
|
124
|
+
*/
|
|
125
|
+
export declare function runButtonPorts(options: {
|
|
126
|
+
readonly nodeId: string;
|
|
127
|
+
readonly label?: string;
|
|
128
|
+
readonly schema: NgisToolUiSchema | null;
|
|
129
|
+
/** `inputs` keys with the spread directives already removed. */
|
|
130
|
+
readonly inputNames: readonly string[];
|
|
131
|
+
}): readonly NgisPortSpec[];
|
|
132
|
+
/** `taskId: task` in — `TaskStatus`, `TaskList` and `ResultActions` (F14.5). */
|
|
133
|
+
export declare function taskIdInPorts(options: {
|
|
134
|
+
readonly nodeId: string;
|
|
135
|
+
readonly label?: string;
|
|
136
|
+
}): readonly NgisPortSpec[];
|
|
137
|
+
/** `resource: resource` in (F14.5). The value is a **file** id. */
|
|
138
|
+
export declare function attributeTablePorts(options: {
|
|
139
|
+
readonly nodeId: string;
|
|
140
|
+
readonly label?: string;
|
|
141
|
+
}): readonly NgisPortSpec[];
|
|
142
|
+
/**
|
|
143
|
+
* `taskId: task` **or** `resource: resource` in (F14.5) — whichever the author
|
|
144
|
+
* declared, which for this component is "passed the prop at all".
|
|
145
|
+
*
|
|
146
|
+
* Declaring neither is legal and means the spec is the only gate: nothing on the
|
|
147
|
+
* 0.4 surface can derive an `NgisChartSpec` from a task or a resource, so the
|
|
148
|
+
* port is a satisfaction gate rather than a data source (`statChartGate`).
|
|
149
|
+
*/
|
|
150
|
+
export declare function statChartPorts(options: {
|
|
151
|
+
readonly nodeId: string;
|
|
152
|
+
readonly label?: string;
|
|
153
|
+
readonly hasTaskPort: boolean;
|
|
154
|
+
readonly hasResourcePort: boolean;
|
|
155
|
+
}): readonly NgisPortSpec[];
|
|
@@ -0,0 +1,122 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `<ParamForm>`'s host reads (Stage 14 Y3 — F14.2, F14.3, F14.9).
|
|
3
|
+
*
|
|
4
|
+
* Every function takes the 0.4 facade and returns plain data, for the same
|
|
5
|
+
* reason Y2's `picker-data.ts` does: the wave's real exit criterion is that the
|
|
6
|
+
* form works **through the facade `createGisPluginHostFactory` mints**, and that
|
|
7
|
+
* is only assertable offline if the host calls are functions a node test can
|
|
8
|
+
* call. A facade assembled anywhere else is an ungated 0.4 surface every unit
|
|
9
|
+
* test calls green.
|
|
10
|
+
*
|
|
11
|
+
* ## `ParamForm` reads `uiSchema`, never `schema`
|
|
12
|
+
*
|
|
13
|
+
* `host.models.schema()` is the raw, un-normalized passthrough of
|
|
14
|
+
* `GET /api/analysis/tools/{id}/schema` and F14.2 keeps it byte-verbatim — but
|
|
15
|
+
* **ParamForm must never call it**, because a client-side re-derivation of
|
|
16
|
+
* widgets from `x-*` keys is exactly the second normalizer this stage exists to
|
|
17
|
+
* kill. `legacy-normalizer.test.ts` proves the absence rather than asserting it
|
|
18
|
+
* in prose.
|
|
19
|
+
*
|
|
20
|
+
* ## `models.validate` is Y4a's leg, and it throws on this branch
|
|
21
|
+
*
|
|
22
|
+
* F14.9's `validate-server` phase calls `host.models.validate`. That leg is
|
|
23
|
+
* wired by Y4 in the same wave; until then the facet's guard throws
|
|
24
|
+
* `NgisFacetUnavailableError` — **deliberately, because a stub that resolved
|
|
25
|
+
* empty would be indistinguishable from a clean validation**. So
|
|
26
|
+
* {@link validateSubmission} tolerates the rejection as a *surfaced notice* and
|
|
27
|
+
* never as a crash, and never as a pass: `kind: "unavailable"` is its own arm,
|
|
28
|
+
* and the form says server validation did not run.
|
|
29
|
+
*/
|
|
30
|
+
import { type NgisHostAPI } from "../../sdk/host-api";
|
|
31
|
+
import { type NgisExecutionValidation, type NgisToolUiParam, type NgisToolUiSchema } from "../../types/analysis";
|
|
32
|
+
import { type NgisParamFormState } from "./param-form-model";
|
|
33
|
+
import { type NgisRunSubmission } from "./w2-contracts";
|
|
34
|
+
/**
|
|
35
|
+
* Why a union rather than `NgisToolUiSchema | null`: each arm is a state the
|
|
36
|
+
* author has to be able to tell apart, and collapsing "you were refused" into
|
|
37
|
+
* "this tool has no schema" is the silent-empty-state failure the stage's stub
|
|
38
|
+
* rule exists to prevent.
|
|
39
|
+
*/
|
|
40
|
+
export type NgisUiSchemaLoad =
|
|
41
|
+
/** The tool, or its schema, does not exist. */
|
|
42
|
+
{
|
|
43
|
+
readonly kind: "missing";
|
|
44
|
+
readonly toolId: string;
|
|
45
|
+
}
|
|
46
|
+
/**
|
|
47
|
+
* `ui_schema_version` is not the one this build understands. **A reader that
|
|
48
|
+
* does not know the version refuses; it never best-efforts a newer shape**
|
|
49
|
+
* (F14.2, and the same rule `recipeVersion` carries in F14.6).
|
|
50
|
+
*/
|
|
51
|
+
| {
|
|
52
|
+
readonly kind: "unsupported-version";
|
|
53
|
+
readonly version: number;
|
|
54
|
+
}
|
|
55
|
+
/** A scope refusal, a transport failure, or the W1 stub before Y3 wired it. */
|
|
56
|
+
| {
|
|
57
|
+
readonly kind: "refused";
|
|
58
|
+
readonly message: string;
|
|
59
|
+
} | {
|
|
60
|
+
readonly kind: "ready";
|
|
61
|
+
readonly schema: NgisToolUiSchema;
|
|
62
|
+
};
|
|
63
|
+
export declare function loadToolUiSchema(host: NgisHostAPI, toolId: string): Promise<NgisUiSchemaLoad>;
|
|
64
|
+
/**
|
|
65
|
+
* The param a `field-select` follows — QGIS's `parentLayerParameterName`, whose
|
|
66
|
+
* UI-schema spelling is the passthrough extension `x-parent-param`.
|
|
67
|
+
*
|
|
68
|
+
* F14.2 emits `field-select` for no builtin, so this is only ever reached by an
|
|
69
|
+
* author's `widgets` override or a recipe's promoted input. When the extension
|
|
70
|
+
* is absent, the parent is the schema's **first `role: "input"` param**, which
|
|
71
|
+
* is the only defensible guess: a form with one dataset input has exactly one
|
|
72
|
+
* candidate, and a form with several is told to name it. `null` when there is no
|
|
73
|
+
* input param at all — reported, never guessed at.
|
|
74
|
+
*/
|
|
75
|
+
export declare function parentParamNameFor(schema: NgisToolUiSchema, param: NgisToolUiParam): string | null;
|
|
76
|
+
/**
|
|
77
|
+
* The runtime option lists F14.9's *mutate* phase repopulates from — today only
|
|
78
|
+
* `field-select`'s. Keyed by param name, so `mutateParamForm` stays pure and the
|
|
79
|
+
* fetch stays here.
|
|
80
|
+
*
|
|
81
|
+
* A parent that cannot be resolved yields **no entry** rather than an empty
|
|
82
|
+
* list. An empty list would mean "this parent has no fields", which `mutate`
|
|
83
|
+
* would honour by clearing every unaltered value — so the difference between
|
|
84
|
+
* *unknown* and *empty* is a data-loss difference, not a cosmetic one.
|
|
85
|
+
*/
|
|
86
|
+
export declare function loadDependentOptionLists(host: NgisHostAPI, schema: NgisToolUiSchema, state: NgisParamFormState, widgetOf: (param: NgisToolUiParam) => NgisToolUiParam["widget"]): Promise<Readonly<Record<string, readonly string[]>>>;
|
|
87
|
+
export type NgisServerValidation = {
|
|
88
|
+
readonly kind: "ok";
|
|
89
|
+
readonly validation: NgisExecutionValidation;
|
|
90
|
+
}
|
|
91
|
+
/**
|
|
92
|
+
* `host.models.validate` is not implemented on this facade. **Not a pass.**
|
|
93
|
+
* The form renders the reason and the submit stays governed by the client
|
|
94
|
+
* rules alone — which is the honest position, because nothing was checked.
|
|
95
|
+
*/
|
|
96
|
+
| {
|
|
97
|
+
readonly kind: "unavailable";
|
|
98
|
+
readonly message: string;
|
|
99
|
+
}
|
|
100
|
+
/** The call was made and failed — a scope refusal, a 4xx, a transport error. */
|
|
101
|
+
| {
|
|
102
|
+
readonly kind: "failed";
|
|
103
|
+
readonly message: string;
|
|
104
|
+
};
|
|
105
|
+
export interface NgisValidateOptions {
|
|
106
|
+
readonly sceneId?: string;
|
|
107
|
+
readonly outputMode?: "ADD_LAYER" | "UPDATE_RESOURCE";
|
|
108
|
+
readonly targetResourceId?: string;
|
|
109
|
+
}
|
|
110
|
+
/**
|
|
111
|
+
* F14.9's `validate-server`. The request is built from the **same**
|
|
112
|
+
* `NgisRunSubmission` Y4 will submit, coerced through the **same**
|
|
113
|
+
* `toWireParams`, so a validate that passes cannot be followed by a run that
|
|
114
|
+
* fails on a differently-spelled `false`.
|
|
115
|
+
*
|
|
116
|
+
* `outputNames` ride in `params`: the analysis API takes an output name as an
|
|
117
|
+
* ordinary parameter key (`execution_validation.py` auto-fills a missing one),
|
|
118
|
+
* and `validate-execution` has no separate slot for them.
|
|
119
|
+
*/
|
|
120
|
+
export declare function validateSubmission(host: NgisHostAPI, submission: NgisRunSubmission, options?: NgisValidateOptions): Promise<NgisServerValidation>;
|
|
121
|
+
/** The whole of `validate-server`'s input, derived in one place. */
|
|
122
|
+
export declare function submissionOf(schema: NgisToolUiSchema, state: NgisParamFormState): NgisRunSubmission;
|