@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.
Files changed (79) hide show
  1. package/LICENSE +13 -0
  2. package/README.md +25 -0
  3. package/dist/index.cjs +71 -0
  4. package/dist/index.d.ts +114 -0
  5. package/dist/index.mjs +69 -0
  6. package/dist/src/api/analysis.d.ts +140 -0
  7. package/dist/src/api/resources.d.ts +530 -0
  8. package/dist/src/api/scenes.d.ts +251 -0
  9. package/dist/src/api/users.d.ts +22 -0
  10. package/dist/src/api/workflows.d.ts +191 -0
  11. package/dist/src/components/plugin-ui/AttributeTable.d.ts +26 -0
  12. package/dist/src/components/plugin-ui/BatchTable.d.ts +47 -0
  13. package/dist/src/components/plugin-ui/ChartCanvas.d.ts +14 -0
  14. package/dist/src/components/plugin-ui/ExtentPicker.d.ts +22 -0
  15. package/dist/src/components/plugin-ui/FieldSelect.d.ts +23 -0
  16. package/dist/src/components/plugin-ui/Flow.d.ts +256 -0
  17. package/dist/src/components/plugin-ui/ModelSelect.d.ts +36 -0
  18. package/dist/src/components/plugin-ui/ParamForm.d.ts +63 -0
  19. package/dist/src/components/plugin-ui/PickerField.d.ts +52 -0
  20. package/dist/src/components/plugin-ui/ResourceSelect.d.ts +37 -0
  21. package/dist/src/components/plugin-ui/ResultActions.d.ts +48 -0
  22. package/dist/src/components/plugin-ui/RunButton.d.ts +49 -0
  23. package/dist/src/components/plugin-ui/StatChart.d.ts +66 -0
  24. package/dist/src/components/plugin-ui/TaskChrome.d.ts +35 -0
  25. package/dist/src/components/plugin-ui/TaskList.d.ts +29 -0
  26. package/dist/src/components/plugin-ui/TaskStatus.d.ts +55 -0
  27. package/dist/src/components/plugin-ui/batch-model.d.ts +442 -0
  28. package/dist/src/components/plugin-ui/flow-inputs.d.ts +61 -0
  29. package/dist/src/components/plugin-ui/flow-model.d.ts +303 -0
  30. package/dist/src/components/plugin-ui/flow-ports.d.ts +155 -0
  31. package/dist/src/components/plugin-ui/param-form-data.d.ts +122 -0
  32. package/dist/src/components/plugin-ui/param-form-model.d.ts +187 -0
  33. package/dist/src/components/plugin-ui/param-widgets.d.ts +88 -0
  34. package/dist/src/components/plugin-ui/picker-data.d.ts +141 -0
  35. package/dist/src/components/plugin-ui/picker-model.d.ts +150 -0
  36. package/dist/src/components/plugin-ui/recipe-model.d.ts +320 -0
  37. package/dist/src/components/plugin-ui/result-data.d.ts +112 -0
  38. package/dist/src/components/plugin-ui/result-model.d.ts +96 -0
  39. package/dist/src/components/plugin-ui/run-data.d.ts +118 -0
  40. package/dist/src/components/plugin-ui/run-model.d.ts +179 -0
  41. package/dist/src/components/plugin-ui/stat-chart-model.d.ts +168 -0
  42. package/dist/src/components/plugin-ui/validation-messages.d.ts +18 -0
  43. package/dist/src/components/plugin-ui/w2-contracts.d.ts +151 -0
  44. package/dist/src/components/plugin-ui/w3-contracts.d.ts +328 -0
  45. package/dist/src/components/plugin-ui/w4-contracts.d.ts +510 -0
  46. package/dist/src/components/ui/button.d.ts +12 -0
  47. package/dist/src/components/ui/checkbox.d.ts +4 -0
  48. package/dist/src/components/ui/column-resize-handle.d.ts +23 -0
  49. package/dist/src/components/ui/dropdown-menu.d.ts +25 -0
  50. package/dist/src/components/ui/input.d.ts +3 -0
  51. package/dist/src/components/ui/label.d.ts +4 -0
  52. package/dist/src/components/ui/popover.d.ts +10 -0
  53. package/dist/src/components/ui/select.d.ts +15 -0
  54. package/dist/src/components/ui/switch.d.ts +6 -0
  55. package/dist/src/domain/map/types.d.ts +128 -0
  56. package/dist/src/features/gis/components/GisAttributeTable.d.ts +56 -0
  57. package/dist/src/features/gis/hooks/useDatasetFeatureWindow.d.ts +51 -0
  58. package/dist/src/lib/api-error.d.ts +81 -0
  59. package/dist/src/lib/api.d.ts +82 -0
  60. package/dist/src/lib/auth-session.d.ts +22 -0
  61. package/dist/src/lib/hooks/useColumnResize.d.ts +57 -0
  62. package/dist/src/lib/raster-color-layer/RasterColorLayer.d.ts +48 -0
  63. package/dist/src/lib/raster-color-layer/TileGrid.d.ts +31 -0
  64. package/dist/src/lib/raster-color-layer/colorRamp.d.ts +18 -0
  65. package/dist/src/lib/raster-color-layer/shaders/raster-color.frag.d.ts +2 -0
  66. package/dist/src/lib/raster-color-layer/shaders/raster-color.vert.d.ts +2 -0
  67. package/dist/src/lib/raster-color-layer/zoomInterpolate.d.ts +2 -0
  68. package/dist/src/lib/utils.d.ts +2 -0
  69. package/dist/src/sdk/facets/types.d.ts +370 -0
  70. package/dist/src/sdk/host-api.d.ts +256 -0
  71. package/dist/src/sdk/map-api.d.ts +288 -0
  72. package/dist/src/sdk/panels.d.ts +130 -0
  73. package/dist/src/types/analysis.d.ts +205 -0
  74. package/dist/src/types/common.d.ts +17 -0
  75. package/dist/src/types/extensions.d.ts +560 -0
  76. package/dist/src/types/gis.d.ts +242 -0
  77. package/dist/src/types/market-social.d.ts +378 -0
  78. package/dist/src/types/workflow.d.ts +83 -0
  79. package/package.json +59 -0
@@ -0,0 +1,320 @@
1
+ /**
2
+ * A flow graph, serialized — **recipe JSON, the `{graph,view}` envelope, and the
3
+ * publish payloads** (Stage 14 Y6, W4 — F14.6).
4
+ *
5
+ * Pure: no React, no host, no `src/lib/api.ts`, no DOM. The seam this stage has
6
+ * now used four times, and here it is load-bearing rather than stylistic — *"a
7
+ * recipe is data and not code"* is a claim a React-free serializer proves and a
8
+ * component-entangled one only asserts. The DTO types below are imported with
9
+ * `import type`, which is erased, so this module has no runtime dependency on
10
+ * `src/api/**` while still keeping **one** source of truth per DTO. Everything
11
+ * that touches the wire is `recipe-data.ts`.
12
+ *
13
+ * ## The one thing to understand before reading further
14
+ *
15
+ * A `<Flow>` scope holds **more nodes than a recipe has**. Eleven components
16
+ * declare ports; only one of them runs a tool with inputs the server can bind —
17
+ * `RunButton`, whose out port is the sole `task`-kind port in F14.5's table. A
18
+ * `ResourceSelect` is an *interactive control*, not a computation: it has no
19
+ * graph node, so a reference to it can never be an edge. That single fact is
20
+ * where four of this session's corrections come from, and
21
+ * {@link recipeToolNodes} is where it is decided once.
22
+ *
23
+ * ## Where each of F14.6's four value forms ends up
24
+ *
25
+ * | flow binding | recipe | `{graph,view}` |
26
+ * |---|---|---|
27
+ * | `"@otherToolNode"` | kept verbatim (form 4) | `edges[]` |
28
+ * | `"@picker"` on a file port | `{ $input }` (form 3) | `inputs.<key>: ""` + an input slot |
29
+ * | `"@picker"` on an option port | the resolved value (form 1) | `params.<key>` |
30
+ * | unbound file port | `{ $input }` (form 3) | `inputs.<key>: ""` + an input slot |
31
+ * | anything else that resolved | the resolved value (form 1) | `params.<key>` |
32
+ *
33
+ * Contract: F14.1 (the Pascal boundary), F14.4 (value forms, `fieldMap` never),
34
+ * F14.5 (ports), F14.6 (recipe JSON, the mapping, the publish sequence), F14.19
35
+ * (review posture), F14.21 (`recipeColumns`, which stays in the seam).
36
+ * `w4-contracts.ts` is the kickoff seam and is read by relative path only —
37
+ * public recipe names come through this file, never through the seam, exactly as
38
+ * `flow-model.ts` publishes W3's.
39
+ */
40
+ import type { CreateWorkflowToolPayload, InvokeWorkflowToolPayload } from "../../api/workflows";
41
+ import type { NgisDataType } from "../../sdk/facets/types";
42
+ import type { CreateReferenceVersionRequest } from "../../types/extensions";
43
+ import type { WorkflowDagConfig } from "../../types/workflow";
44
+ import { type NgisFlowNode, type NgisFlowSnapshot } from "./w3-contracts";
45
+ import { type NgisRecipe, type NgisRecipeOutput, type NgisToolInputSlot, type NgisToolOutputSlot } from "./w4-contracts";
46
+ export { NGIS_RECIPE_VERSION } from "./w4-contracts";
47
+ export type { NgisRecipe, NgisRecipeInput, NgisRecipeNode, NgisRecipeOutput, NgisToolInputSlot, NgisToolOutputSlot, } from "./w4-contracts";
48
+ export { recipeColumns } from "./w4-contracts";
49
+ /**
50
+ * The `view` key the recipe JSON is stored under.
51
+ *
52
+ * **Not a preference — the only place it can go.** `normalizeNode` rebuilds
53
+ * every graph node from scratch and keeps exactly fifteen keys
54
+ * (`NGIS_GRAPH_NODE_KEYS`), and the lossless presentation capture that would
55
+ * otherwise stash the original runs **only for a non-envelope payload**
56
+ * (`WorkflowGraphNormalizer.java:70-77`). Y6 sends an envelope, so anything a
57
+ * recipe knows that the graph does not — `recipeVersion`, promoted-input ids,
58
+ * labels, declared `dataTypes` — is dropped on save with no error unless it is
59
+ * also in `view`, which the backend stores verbatim and never reads.
60
+ *
61
+ * `view` already carries `nodes`/`edges`/`viewport` for the Stage-11 canvas, so
62
+ * this is a sibling key rather than the whole object.
63
+ */
64
+ export declare const NGIS_RECIPE_VIEW_KEY = "ngisRecipe";
65
+ /**
66
+ * What a snapshot cannot tell you, and therefore what the author supplies.
67
+ *
68
+ * `outputs` is here rather than derived because a recipe output is a *decision*:
69
+ * F14.5 has no OUTPUT component, so no node in the scope corresponds to one, and
70
+ * its `outputMode` chooses what happens to a file after the run. {@link
71
+ * defaultRecipeOutputs} is the sane default the wizard starts from.
72
+ */
73
+ export interface NgisRecipeMeta {
74
+ /** Stable across saves (F14.6). The wizard keeps the previous one. */
75
+ readonly id: string;
76
+ readonly name: string;
77
+ readonly description?: string | null;
78
+ /** Defaults to {@link defaultRecipeOutputs}. Must be non-empty to publish. */
79
+ readonly outputs?: readonly NgisRecipeOutput[];
80
+ /**
81
+ * Node ids whose `toolId` names a **published workflow tool** rather than an
82
+ * analysis tool — F14.6's `kind: "recipe"`, i.e. a nested recipe.
83
+ *
84
+ * **CORRECTION — F14.6's `nodes[].kind` has no channel in the snapshot, and
85
+ * the kickoff's CORRECTION 2 widening does not supply one.**
86
+ * `NgisFlowNodeConfig` carries `toolId` and `toolVersion` and nothing else, so
87
+ * `tool` and `recipe` are indistinguishable from a graph alone: the only thing
88
+ * that separates them is the registry row's `provider`, which lives on
89
+ * `NgisModelSummary` and therefore behind a host read. Emitting the wrong one
90
+ * matters — it decides whether the node carries `modelToolId` or
91
+ * `workflowToolId`, and dispatch is unordered first-match over those very
92
+ * fields (CORRECTION 5). So it is an explicit input here, resolved through the
93
+ * facade by `resolveRecipeNodeKinds` in `recipe-data.ts`, and defaults to
94
+ * **none** — a plain `tool` node — because that is the answer for every
95
+ * non-nested recipe and a wrong `workflowToolId` is the more expensive
96
+ * mistake.
97
+ */
98
+ readonly nestedNodeIds?: readonly string[];
99
+ }
100
+ /**
101
+ * The nodes of a `<Flow>` scope that become graph nodes.
102
+ *
103
+ * **A node runs a tool iff it declares a `task` out port and carries a
104
+ * `config`.** Both halves are needed and neither is a component-identity check —
105
+ * the engine's own rule is that the registry knows nothing about a node beyond
106
+ * its `slot` and its ports, so the discriminator is drawn from F14.5's port
107
+ * table instead of from a slot allowlist:
108
+ *
109
+ * - `<ParamForm>` also carries a `config` (it loads a UI schema, and YD49
110
+ * populates the pair from it), but its out port is kind `record`. It renders a
111
+ * form; it does not run anything, and a recipe with a node per form would
112
+ * publish the same tool twice.
113
+ * - A `<ResourceSelect>` / `<FieldSelect>` / `<ExtentPicker>` carries no config
114
+ * at all.
115
+ *
116
+ * **This is the filter F14.6 needs and does not state, and without it publish
117
+ * fails in two different ways.** `promotableInputs` promotes any unbound
118
+ * `resource`-kind in-port, and two components other than `RunButton` declare
119
+ * one: `FieldSelect`'s `parent` and `ParamForm`'s hidden required inputs. Those
120
+ * would mint `ToolInputSlot`s whose `bindNodeId` names a node the graph does not
121
+ * contain, and `validateSlots` refuses with
122
+ * `"input slot <key> binds to unknown node <id>"`
123
+ * (`WorkflowToolSchemaService.java:187-192`) — for `ParamForm` — while
124
+ * `FieldSelect`'s would publish a slot that silently does nothing.
125
+ */
126
+ export declare function recipeToolNodes(snapshot: NgisFlowSnapshot): readonly NgisFlowNode[];
127
+ /**
128
+ * Every tool node no other tool node consumes — the terminal producers, which
129
+ * are exactly what `inferDraft` would have called output slots
130
+ * (`WorkflowToolSchemaService.java:60-68`).
131
+ *
132
+ * `ADD_LAYER` for all of them, because it is the only mode with no second
133
+ * required field: `UPDATE_RESOURCE` needs a `targetResourceId` **at definition
134
+ * save** (`WorkflowTypeCheckService.java:181-185`), so defaulting to it would
135
+ * produce a graph that cannot be saved.
136
+ */
137
+ export declare function defaultRecipeOutputs(snapshot: NgisFlowSnapshot): readonly NgisRecipeOutput[];
138
+ /**
139
+ * `NgisFlowSnapshot` → `NgisRecipe`.
140
+ *
141
+ * Two passes over each tool node, and the split is what keeps F14.4's
142
+ * precedence rule from being implemented twice:
143
+ *
144
+ * 1. **declared in-ports** decide promotion and edges — the only two things a
145
+ * port's *binding* determines rather than its *value*;
146
+ * 2. **`snapshot.resolved`** bakes everything else, spread-contributed sibling
147
+ * keys included. The engine already wrote that map with "later explicit keys
148
+ * win over spread keys" applied (`flow-model.ts`, `V-224`), so reading it is
149
+ * how this serializer avoids a second flattening rule that could disagree
150
+ * with the run path. A spread that resolved and a spread that did not are
151
+ * distinguishable there, which is the whole reason the directive entries
152
+ * exist.
153
+ *
154
+ * **`V-224` is the reason the second pass reads resolutions rather than
155
+ * bindings.** `THRESHOLD` is never a declared port on a `RunButton` — F14.5
156
+ * gives it "one in port per bound tool *input*" and an option is not an input —
157
+ * so a serializer that walked `node.inputs` alone would emit no `params` at all
158
+ * and the published recipe would run on the schema's `default_value`. That is
159
+ * the wrong run, not a type error, and it is exactly what the fixture's
160
+ * `"THRESHOLD": "2.5"` pins.
161
+ */
162
+ export declare function serializeRecipe(snapshot: NgisFlowSnapshot, meta: NgisRecipeMeta): NgisRecipe;
163
+ /**
164
+ * `NgisRecipe` → the bare `{graph, view}` envelope
165
+ * `POST /api/workflows/definitions` takes as its **body**.
166
+ *
167
+ * `name` and `sceneId` are query parameters, not body fields (F14.6's own
168
+ * CORRECTION), so they are not here.
169
+ *
170
+ * Three properties of the emitted graph that are contract rather than style:
171
+ *
172
+ * 1. **Exactly one discriminator per node.** Dispatch is
173
+ * `executors.stream().filter(supports).findFirst()` with no `@Order`
174
+ * (`WorkflowTaskRunner.java:61-65`) and `checkAmbiguity` guards only the
175
+ * `workflowToolId` combinations, so `modelToolId` **+** `type: "OUTPUT"` is
176
+ * unguarded and would dispatch by classpath-scan order. A TASK node carries
177
+ * `modelToolId` and no `workflowToolId`; a WORKFLOW_TOOL node the reverse; an
178
+ * OUTPUT node neither.
179
+ * 2. **No `fieldMap`, ever** (F14.4). Despite the name it is a flat *string* —
180
+ * a server-side path+pipeline expression over a task's output JSON — and the
181
+ * flow layer has no model of that shape.
182
+ * 3. **The recipe rides in `view`.** See {@link NGIS_RECIPE_VIEW_KEY}.
183
+ */
184
+ export declare function recipeEnvelope(recipe: NgisRecipe): WorkflowDagConfig;
185
+ /**
186
+ * What `view` held. A reader that does not know the version **refuses** — it
187
+ * never best-efforts, exactly as `ui_schema_version` does not (F14.2, F14.6).
188
+ */
189
+ export type NgisRecipeRead =
190
+ /** No recipe in this definition — a hand-built or canvas-authored DAG. */
191
+ {
192
+ readonly kind: "absent";
193
+ } | {
194
+ readonly kind: "unsupported-version";
195
+ readonly version: number;
196
+ }
197
+ /** Present but not the frozen shape. Reported, never silently repaired. */
198
+ | {
199
+ readonly kind: "malformed";
200
+ readonly reason: string;
201
+ } | {
202
+ readonly kind: "ready";
203
+ readonly recipe: NgisRecipe;
204
+ };
205
+ /**
206
+ * Read a recipe back out of a definition's `{graph, view}`.
207
+ *
208
+ * The backend stores `view` **verbatim and never reads it**, so this is a pure
209
+ * round-trip of what {@link recipeEnvelope} wrote — which is what makes the
210
+ * envelope leg of the publish chain assertable offline without a database.
211
+ */
212
+ export declare function readRecipe(config: unknown): NgisRecipeRead;
213
+ /**
214
+ * Per-output publish enrichment.
215
+ *
216
+ * **CORRECTION — the kickoff's CORRECTION 8 cannot be satisfied from the recipe
217
+ * JSON, because F14.6's `outputs[]` has no type field.** The correction is right
218
+ * that an unenriched output slot serves `data_type: "file"` and makes the
219
+ * generated dialog show a file picker where the author declared a raster; but the
220
+ * frozen `NgisRecipeOutput` is `{id, label, from, outputMode, targetResourceId?}`
221
+ * and the seam is read-only, so there is nowhere in a recipe to record the answer.
222
+ * It is therefore a **publish-time** input, offered by the wizard, keyed by
223
+ * recipe-output id and defaulting to the `File` wildcard — which is honest
224
+ * (`File` really is what an unenriched slot means) rather than a guess.
225
+ */
226
+ export interface NgisRecipePublishOptions {
227
+ readonly definitionId: string;
228
+ readonly name: string;
229
+ readonly description?: string | null;
230
+ /** Recipe-output id → canonical data type (F14.1). Absent ⇒ `File`. */
231
+ readonly outputDataTypes?: Readonly<Record<string, NgisDataType>>;
232
+ readonly summary?: string;
233
+ readonly useCases?: readonly string[];
234
+ readonly synonyms?: readonly string[];
235
+ readonly domain?: readonly string[];
236
+ readonly operationType?: readonly string[];
237
+ }
238
+ /**
239
+ * The explicit `inputSlots` / `outputSlots` F14.6 publishes instead of relying on
240
+ * `getDraft`.
241
+ *
242
+ * **Serialized by name, never positionally.** `ToolInputSlot` is
243
+ * `(slotKey, name, description, dataType, bindNodeId, bindInputKey, optional)`
244
+ * and `ToolOutputSlot` is `(slotKey, name, description, bindNodeId, dataType)` —
245
+ * `dataType` and `bindNodeId` are *transposed* between them, and `ToolOutputSlot`
246
+ * keeps a 4-arg secondary constructor so a 4-tuple compiles and yields
247
+ * `dataType: null`.
248
+ *
249
+ * `bindInputKey` is the field F14.6 omits and the one publish actually dies on:
250
+ * the record is `@JsonIgnoreProperties(ignoreUnknown = true)`, so the six-field
251
+ * shape posts with **no error** and fails at
252
+ * `WorkflowToolSchemaService.java:193-196` with `"input slot <key> is missing
253
+ * bindInputKey"` — and at invoke it is the key the caller's file id is injected
254
+ * under (`WorkflowToolService.java:421-423`), so a wrong value would put a real
255
+ * file under the wrong parameter.
256
+ *
257
+ * An output slot's `slotKey` is the **recipe output's id**, not the producing
258
+ * node's id as `inferDraft` mints it. `compileToolSchema` keys the schema's
259
+ * `properties` by `slotKey` and `mapOutputs` keys a nested invocation's result by
260
+ * it too, so two outputs off one producer would collide under `inferDraft`'s
261
+ * convention; `bindNodeId` still names the producer, which is what
262
+ * `validateSlots` and `outputNodeIds` read.
263
+ */
264
+ export declare function recipeSlots(recipe: NgisRecipe, options?: Pick<NgisRecipePublishOptions, "outputDataTypes">): {
265
+ readonly inputSlots: readonly NgisToolInputSlot[];
266
+ readonly outputSlots: readonly NgisToolOutputSlot[];
267
+ };
268
+ /**
269
+ * Inputs whose declared types were **widened to the `File` wildcard** at the slot
270
+ * boundary.
271
+ *
272
+ * A slot carries one `dataType` and a port may declare several, so
273
+ * `ngisSlotDataType` widens a multi-type or untyped declaration to `File` — the
274
+ * only spelling that is compatible on both sides of
275
+ * `WorkflowTypeCheckService.compatible` and therefore the only one that preserves
276
+ * `types={["table","raster"]}`. The widening is lossy, so the wizard says so
277
+ * rather than applying it silently.
278
+ */
279
+ export declare function recipeSlotWidenings(recipe: NgisRecipe): readonly {
280
+ readonly inputId: string;
281
+ readonly dataTypes: readonly NgisDataType[];
282
+ }[];
283
+ /** `POST /api/workflows/tools` — `PublishToolRequest`, in full. */
284
+ export declare function recipePublishPayload(recipe: NgisRecipe, options: NgisRecipePublishOptions): CreateWorkflowToolPayload;
285
+ /**
286
+ * `POST /api/workflows/tools/{toolId}/invoke` — the headless run.
287
+ *
288
+ * `inputs` is `slotKey → fileId` and a published workflow tool's whole invocable
289
+ * surface: `compileToolSchema` emits `x-role` of only `"input"` and `"output"`
290
+ * and **never `"option"`**, so there are no knobs to pass. Every option the
291
+ * author chose is already baked into the published snapshot's node `params`.
292
+ *
293
+ * `LIBRARY` is legal here and **illegal at definition save** — the two
294
+ * `outputMode` enums are different sets (CORRECTION 4) — which is why this
295
+ * function's mode is widened beyond `NgisRecipeOutput`'s.
296
+ */
297
+ export declare function recipeInvokePayload(options: {
298
+ readonly inputs: Readonly<Record<string, string>>;
299
+ readonly sceneId?: string | null;
300
+ readonly outputMode?: "ADD_LAYER" | "UPDATE_RESOURCE" | "LIBRARY";
301
+ readonly targetResourceId?: string | null;
302
+ }): InvokeWorkflowToolPayload;
303
+ /**
304
+ * `POST /api/extensions/{extId}/versions/reference` — the `tool`-kind listing.
305
+ *
306
+ * `provider` is `"workflow"` because a published recipe lands in the shared
307
+ * `ngis_model_tool` id space with `provider = "workflow"` and
308
+ * `modelbox_id = "workflow-tools"`, which is also why `GetToolSchema(toolId)` and
309
+ * YB1's ui-schema endpoint resolve it exactly like a builtin.
310
+ *
311
+ * `toolVersion` is an `Integer` here while `ngis-workflow`'s node pin is a
312
+ * `long`, and this endpoint answers **`200`, not `201`**.
313
+ */
314
+ export declare function recipeReferenceRequest(options: {
315
+ readonly version: string;
316
+ readonly minNgisVersion: string;
317
+ readonly toolId: string;
318
+ readonly toolVersion: number;
319
+ readonly changelog?: string | null;
320
+ }): CreateReferenceVersionRequest;
@@ -0,0 +1,112 @@
1
+ /**
2
+ * The host reads behind `<ResultActions>` and `<AttributeTable>` (Stage 14 Y4b).
3
+ *
4
+ * Same discipline as Y2's `picker-data.ts`: every function takes the 0.4 facade
5
+ * and returns plain data, so the wave's real exit criterion — "these work
6
+ * **through `createGisPluginHostFactory`**" — is asserted offline. A node test
7
+ * builds the real, scope-enforced facade and calls these, so a scope refusal or
8
+ * a facet stub surfaces here exactly as it would in a browser. A facade
9
+ * assembled anywhere else is an ungated 0.4 surface every unit test calls green.
10
+ *
11
+ * **The scopes each read needs, on record**, because a refusal has to be
12
+ * legible: `tasks.get`/`tasks.outputs`/`tasks.watch` are `tasks:read`,
13
+ * `resources.list` is `resources:read`, `viz.addResultLayer` is `viz:write`, and
14
+ * `viz.openTable` / `viz.chart` are **ungated** (they mutate nothing — F14.3's
15
+ * amendment to F13.5, already on `NGIS_UNGATED_MEMBERS`).
16
+ */
17
+ import type { NgisTaskState } from "../../sdk/facets/types";
18
+ import type { NgisChartSpec, NgisHostAPI } from "../../sdk/host-api";
19
+ import { joinOutputs, projectResult, type NgisResultOutput, type NgisResultView } from "./result-model";
20
+ /**
21
+ * The scene's `fileId → resourceId` map.
22
+ *
23
+ * The join exists because the two vocabularies really are different: a task
24
+ * output is addressed by **file** id (F14.4, `output-files[].file_id`) while
25
+ * `host.viz.openTable` takes a **scene-resource** id, and `viz.ts` rejects a
26
+ * file id passed in its place rather than guessing. `NgisResourceSummary.fileId`
27
+ * is the only bridge, and it is a `resources:read` call.
28
+ */
29
+ export declare function loadResourceIdsByFileId(host: NgisHostAPI): Promise<ReadonlyMap<string, string>>;
30
+ /**
31
+ * One coherent read of "where is this run, and what can be done with it".
32
+ *
33
+ * The output fallback is the interesting half. `NgisTaskState.outputs` is
34
+ * populated from the tracked task's `outputFiles`, which the DIRECT runtime
35
+ * fills after the run succeeds — but a component that attached to an
36
+ * already-finished task (or a WORKFLOW run whose instance was polled before its
37
+ * outputs were fetched) can legitimately see `SUCCESS` with `outputs: []`. So a
38
+ * terminal-success state with no outputs asks `host.tasks.outputs` once. It is
39
+ * **not** treated as "this run produced nothing": that conflation is what would
40
+ * make a real result look like an empty one, which is the failure mode F13.5's
41
+ * refusal rule exists to prevent.
42
+ */
43
+ export declare function readResultView(host: NgisHostAPI, taskId: string | null, state?: NgisTaskState | null): Promise<NgisResultView>;
44
+ /**
45
+ * The post-hoc path (`viz:write`). Registers a scene resource for the output's
46
+ * file id when absent, then adds a layer — which is precisely what the
47
+ * submit-time path would have done, and the reason `ResultActions` only offers
48
+ * this in the `detached` and `integration-failed` phases.
49
+ */
50
+ export declare function addResultOutputLayer(host: NgisHostAPI, output: NgisResultOutput, name?: string): Promise<{
51
+ resourceId: string;
52
+ layerId: string;
53
+ }>;
54
+ /**
55
+ * Ungated. Rejects with a named message when the resource has no layer in the
56
+ * scene — the attribute panel targets the *selected layer's* dataset, so the
57
+ * resource must be rendered first. Surfaced, never swallowed.
58
+ */
59
+ export declare function openResultTable(host: NgisHostAPI, output: NgisResultOutput): Promise<void>;
60
+ /** Ungated (F14.3). The host renders the declarative spec; the plugin ships no
61
+ * chart code, which is what keeps this survivable into Stage 15's worker tier. */
62
+ export declare function renderResultChart(host: NgisHostAPI, spec: NgisChartSpec): Promise<void>;
63
+ /**
64
+ * Public default for ResultActions' field chart. The data read stays behind the
65
+ * resource facet, so a plugin gets the same scope enforcement and lab-scene
66
+ * isolation as every other smart component instead of importing an app API.
67
+ */
68
+ export declare function buildResultFieldChart(host: NgisHostAPI, output: {
69
+ fileId: string;
70
+ name: string | null;
71
+ }, field: string): Promise<NgisChartSpec>;
72
+ /**
73
+ * What `<AttributeTable>` can learn about the file id on its `resource` in-port.
74
+ *
75
+ * Four arms rather than "a name or nothing", for the same reason
76
+ * `NgisFieldLoad` has four: each is a state an author has to be able to tell
77
+ * apart, and collapsing "not in this scene" into "no name" would hide a resource
78
+ * that was removed.
79
+ */
80
+ export type NgisTableTarget =
81
+ /** The in-port is unsatisfied. */
82
+ {
83
+ readonly kind: "no-resource";
84
+ }
85
+ /** The file id matches no resource in the open scene. */
86
+ | {
87
+ readonly kind: "unresolved";
88
+ readonly fileId: string;
89
+ }
90
+ /** Resolved, but attributes are vector-only. */
91
+ | {
92
+ readonly kind: "not-vector";
93
+ readonly type: string;
94
+ } | {
95
+ readonly kind: "ready";
96
+ readonly fileId: string;
97
+ readonly resourceId: string;
98
+ readonly name: string;
99
+ };
100
+ /**
101
+ * `resources:read`. Resolves the parent exactly the way `FieldSelect` does —
102
+ * through `resources.list()` keyed on `fileId` — because `resources.get` takes a
103
+ * scene-resource id and the port carries a file id (F14.5).
104
+ *
105
+ * The `not-vector` arm matters: `GisAttributeTable` queries
106
+ * `datasets/{fileId}/attributes/*`, which only a vector dataset answers. Handing
107
+ * it a raster would render an error inside the wrapped grid instead of a reason
108
+ * beside the control.
109
+ */
110
+ export declare function loadTableTarget(host: NgisHostAPI, fileId: string | null): Promise<NgisTableTarget>;
111
+ /** Re-exported so a caller can build a view from outputs it already holds. */
112
+ export { joinOutputs, projectResult };
@@ -0,0 +1,96 @@
1
+ /**
2
+ * `<ResultActions>`'s whole decision, as one pure projection (Stage 14 Y4b,
3
+ * **F14.5** / **F14.10**).
4
+ *
5
+ * ## The distinction this module exists to keep visible: complete ≠ integrated
6
+ *
7
+ * `NgisTaskStatus` is the *run*. `NgisTaskStage` is the *lifecycle including
8
+ * scene integration*. `status: "SUCCESS"` with `stage:
9
+ * "waiting_scene_integration"` is a real and common state, and it is why
10
+ * `analysis-task-runtime.ts` runs an integration poll **alongside** the SSE
11
+ * stream rather than trusting the stream's terminal write. `integration_status`
12
+ * is a separate axis, so a result surface that collapsed the two would offer a
13
+ * post-hoc "add to map" the instant a task went `SUCCESS` — racing the
14
+ * submit-time integration that is already on its way and producing a duplicate
15
+ * layer.
16
+ *
17
+ * That is the whole reason {@link NgisResultPhase} has six terminal-ish arms
18
+ * instead of `succeeded | failed`:
19
+ *
20
+ * | phase | `status` / `stage` | what it means for the actions |
21
+ * |---|---|---|
22
+ * | `no-task` | the `taskId` in-port is unsatisfied | nothing to act on; a reason, not a dead control (F14.10) |
23
+ * | `running` | `submitting` / `QUEUED` / `RUNNING` | the run is still going |
24
+ * | `integrating` | `SUCCESS` + `waiting_output_datasets` \| `waiting_scene_integration` \| `refreshing_scene` | **complete, not integrated.** No post-hoc add — submit-time integration owns this |
25
+ * | `integrated` | `SUCCESS` + `success`, every output resolved to a scene resource | the scene has it; open the table, chart it |
26
+ * | `detached` | `SUCCESS` + `success`, ≥1 output is not a scene resource | this is what `host.viz.addResultLayer` is *for* |
27
+ * | `integration-failed` | `SUCCESS` + `failed` | the computation happened, the lifecycle did not — post-hoc add is the recovery |
28
+ * | `failed` / `cancelled` | `FAILED` / `CANCELLED` | there is no output |
29
+ *
30
+ * **Submit-time integration is the preferred path** and is reached by passing
31
+ * `sceneId`/`outputMode` to `tasks.run` (Y4a's `RunButton`). `addResultLayer` is
32
+ * the post-hoc path, and `detached` / `integration-failed` are the only two
33
+ * phases where offering it is honest.
34
+ *
35
+ * ## No `disabled`, anywhere
36
+ *
37
+ * F14.10: enablement is derived, and a nothing-to-show state renders a *reason*
38
+ * rather than a dead control. So this module returns which actions are
39
+ * *available* plus the phase that explains the rest, and `ResultActions` renders
40
+ * the reason through `PickerField`'s notice slot. Nothing here produces text —
41
+ * the component translates the phase under `GIS.pluginUi.run.result.*`.
42
+ */
43
+ import type { NgisTaskOutput, NgisTaskState } from "../../sdk/facets/types";
44
+ export type NgisResultPhase = "no-task" | "running" | "integrating" | "integrated" | "detached" | "integration-failed" | "failed" | "cancelled";
45
+ /**
46
+ * One task output, joined to the scene. `resourceId` is the join `host.viz`
47
+ * needs and `NgisTaskOutput` does not carry: outputs are addressed by **file**
48
+ * id (F14.4), while `viz.openTable` takes a **scene-resource** id, and the join
49
+ * is `NgisResourceSummary.fileId`.
50
+ */
51
+ export interface NgisResultOutput {
52
+ readonly fileId: string;
53
+ /** The declared output parameter's name (DIRECT) or the producing node id
54
+ * (WORKFLOW). `null` when the wire carried none. */
55
+ readonly name: string | null;
56
+ readonly primary: boolean;
57
+ /** `null` when this output is not (yet) a resource in the open scene. */
58
+ readonly resourceId: string | null;
59
+ }
60
+ export interface NgisResultView {
61
+ readonly phase: NgisResultPhase;
62
+ /** File-id-bearing outputs only. An output row with no `file_id` cannot be
63
+ * added, opened or charted, so carrying it would only produce dead controls. */
64
+ readonly outputs: readonly NgisResultOutput[];
65
+ /** Post-hoc `host.viz.addResultLayer` would do something. */
66
+ readonly canAddLayer: boolean;
67
+ /** At least one output is a scene resource, so `host.viz.openTable` resolves. */
68
+ readonly canOpenTable: boolean;
69
+ /** The run produced something, so charting a spec over it is meaningful. */
70
+ readonly canChart: boolean;
71
+ /** The run's or the integration's own message, verbatim. Never invented. */
72
+ readonly error: string | null;
73
+ }
74
+ export declare function joinOutputs(outputs: readonly NgisTaskOutput[], resourceIdByFileId: ReadonlyMap<string, string>): readonly NgisResultOutput[];
75
+ /**
76
+ * The projection. `resourceIdByFileId` is the scene's `fileId → resourceId` map
77
+ * (`host.resources.list()`); an empty map is a scene with no resources, not a
78
+ * missing read, and it correctly yields `detached` for a successful run — which
79
+ * is exactly the state post-hoc add exists for.
80
+ */
81
+ export declare function projectResult(state: NgisTaskState | null, resourceIdByFileId: ReadonlyMap<string, string>): NgisResultView;
82
+ /**
83
+ * The one output a bare `"@run"` reference resolves to: `primary === true`, else
84
+ * the first (F14.4, and `executors/base.py` guarantees at most one `primary`).
85
+ * `ResultActions` acts on this by default so the common single-output case needs
86
+ * no picking.
87
+ *
88
+ * **Delegates to the canonical rule (`V-127`, YD45)**, generically, because
89
+ * `NgisResultOutput` is assignable to `NgisTaskOutput` and not the reverse.
90
+ * Behaviour is unchanged: every caller reaches this through {@link joinOutputs},
91
+ * which has already dropped the file-id-less rows, so the canonical rule's
92
+ * decision *not* to filter is a no-op on this path.
93
+ */
94
+ export declare function primaryOutput(outputs: readonly NgisResultOutput[]): NgisResultOutput | null;
95
+ /** A stable, human-ish name for an output row. Never a key — see F13.11. */
96
+ export declare function outputLabel(output: NgisResultOutput): string;
@@ -0,0 +1,118 @@
1
+ /**
2
+ * The host calls behind `RunButton` / `TaskStatus` / `TaskList` (Stage 14 Y4a).
3
+ *
4
+ * Every function takes the 0.4 facade and returns plain data, for the same reason
5
+ * `picker-data.ts` does: the wave's real exit criterion is "these components work
6
+ * **through `createGisPluginHostFactory`**", and that is only assertable offline
7
+ * if the host reads live above the components rather than inside a `useEffect`.
8
+ * A scope refusal (`NgisScopeError`) or a facet stub therefore surfaces here
9
+ * exactly as it would in a browser, and nothing below swallows one.
10
+ *
11
+ * ## The DIRECT/WORKFLOW branch is not here, and must never be
12
+ *
13
+ * F13.2 puts the fork inside `host.tasks` — `run()` branches on the resolved
14
+ * model's `provider` and `watch()` attaches to the store-backed SSE pipeline for
15
+ * DIRECT or starts a poll timer for WORKFLOW. This module calls one API and does
16
+ * not know which branch it got. The one place the difference is *visible* is
17
+ * {@link hasLogStream}, which reads the `kind` the facet already reported rather
18
+ * than re-deciding it.
19
+ *
20
+ * ## The order of operations, which is F14.9 and not a preference
21
+ *
22
+ * `validate` **always runs once before submit**. {@link runSubmission} is the
23
+ * only sanctioned path for that reason: it validates, refuses on a blocking
24
+ * verdict, and submits only afterwards. A caller that has already validated on
25
+ * change still goes through it — a debounced validation can be stale by the time
26
+ * the button is pressed, and the final call is the one that counts.
27
+ */
28
+ import type { NgisHostAPI } from "../../sdk/host-api";
29
+ import type { NgisModelValidateRequest, NgisTaskHandle, NgisTaskState, NgisTaskWatcher } from "../../sdk/facets/types";
30
+ import type { NgisExecutionValidation } from "../../types/analysis";
31
+ import { type NgisRunGate } from "./run-model";
32
+ import { type NgisRunSubmission } from "./w2-contracts";
33
+ /** Run-shaping options a submission does not carry (they are the caller's, not
34
+ * the generated form's). Mirrors `NgisTaskRunRequest` plus `targetSrid`, which
35
+ * only the validate leg takes. */
36
+ export interface NgisRunOptions {
37
+ sceneId?: string;
38
+ /** Lab-scene optimistic fence. Ordinary scene runs leave this absent. */
39
+ expectedSceneGeneration?: number;
40
+ outputMode?: "ADD_LAYER" | "UPDATE_RESOURCE";
41
+ targetResourceId?: string;
42
+ /** `validate-execution` only — `POST /api/analysis/tasks` has no such field. */
43
+ targetSrid?: number;
44
+ }
45
+ /**
46
+ * The `host.models.validate` request for one submission.
47
+ *
48
+ * Note what is **absent**: `owner_id`. F14.3 is explicit that it comes from the
49
+ * authenticated caller and is never read from a body, which is why
50
+ * `NgisModelValidateRequest` has no such member to fill in.
51
+ */
52
+ export declare function validateRequestOf(submission: NgisRunSubmission, options?: NgisRunOptions): NgisModelValidateRequest;
53
+ /** F14.9's server-validation leg. `models:read` — it runs nothing. */
54
+ export declare function validateRun(host: NgisHostAPI, submission: NgisRunSubmission, options?: NgisRunOptions): Promise<NgisExecutionValidation>;
55
+ /**
56
+ * `host.tasks.run`, with the bindings folded into `params`.
57
+ *
58
+ * `models:run` — running a model is the privileged act (F13.5). The handle's
59
+ * `kind` tells the caller which branch the facade took, and that is the only
60
+ * place the fork is observable from out here.
61
+ */
62
+ export declare function submitRun(host: NgisHostAPI, submission: NgisRunSubmission, options?: NgisRunOptions): Promise<NgisTaskHandle>;
63
+ /** What one press of a run action produced. Exactly one of the three arms. */
64
+ export type NgisRunOutcome =
65
+ /** Validation answered, and it blocks. Nothing was submitted. */
66
+ {
67
+ readonly kind: "blocked";
68
+ readonly validation: NgisExecutionValidation;
69
+ readonly gate: NgisRunGate;
70
+ } | {
71
+ readonly kind: "submitted";
72
+ readonly validation: NgisExecutionValidation;
73
+ readonly gate: NgisRunGate;
74
+ readonly handle: NgisTaskHandle;
75
+ }
76
+ /** The validate or the run call itself rejected — a scope refusal, a transport
77
+ * failure, a facet stub. Surfaced, never folded into `blocked`: "the server
78
+ * says no" and "we could not ask" are different things to tell an author. */
79
+ | {
80
+ readonly kind: "error";
81
+ readonly error: unknown;
82
+ readonly phase: "validate" | "submit";
83
+ };
84
+ /**
85
+ * **F14.9's "always once before submit", implemented once.**
86
+ *
87
+ * The whole sequence lives in one function so the guarantee is a property of the
88
+ * code rather than of a component remembering to call two things in order. A
89
+ * `RunButton` that validated in an effect and submitted in a click handler would
90
+ * satisfy the letter of the clause only as long as nobody reordered the handler.
91
+ */
92
+ export declare function runSubmission(host: NgisHostAPI, submission: NgisRunSubmission, options?: NgisRunOptions): Promise<NgisRunOutcome>;
93
+ /**
94
+ * **F14.10's single sanctioned publication out of a flow scope.**
95
+ *
96
+ * Flow state is component-local; every `host.context.set` notifies the
97
+ * dependency index and re-evaluates every compiled expression whose read-set
98
+ * intersects, so publishing per-port values would put per-keystroke traffic
99
+ * through the global index. Exactly one value leaves: the task id, and only so a
100
+ * plugin can gate a status-bar badge on it.
101
+ *
102
+ * **The key is bare.** `host.context.set` auto-namespaces (F14.11), and passing
103
+ * a prefixed key is rejected rather than re-prefixed — a component that wrote
104
+ * `plugin:<id>:taskId` itself would double-prefix or throw
105
+ * `NgisContextKeyError`, depending on which spelling the store is on. Writing
106
+ * `"taskId"` is correct under both.
107
+ */
108
+ export declare function publishTaskId(host: NgisHostAPI, taskId: string): Promise<void>;
109
+ /**
110
+ * `host.tasks.watch`, unwrapped.
111
+ *
112
+ * Thin on purpose — the branch, the equality guard and the "no workflow SSE"
113
+ * decision all live in the facet, and a wrapper that added its own would be a
114
+ * second notion of when a task changed. `tasks:read`.
115
+ */
116
+ export declare function watchTask(host: NgisHostAPI, taskId: string, listener: (state: NgisTaskState) => void): NgisTaskWatcher;
117
+ /** `tasks:cancel`. Resolves; the state change arrives through the watcher. */
118
+ export declare function cancelTask(host: NgisHostAPI, taskId: string): Promise<void>;