@ngis/plugin-ui 0.4.0 → 0.4.1
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/dist/index.cjs +1 -1
- package/dist/index.d.ts +2 -2
- package/dist/index.mjs +1 -1
- package/dist/src/api/analysis.d.ts +38 -1
- package/dist/src/api/crs.d.ts +110 -0
- package/dist/src/api/resources.d.ts +174 -10
- package/dist/src/api/scenes.d.ts +103 -4
- package/dist/src/api/workflows.d.ts +10 -1
- package/dist/src/components/plugin-ui/ChartCanvas.d.ts +3 -0
- package/dist/src/components/plugin-ui/ParamForm.d.ts +67 -5
- package/dist/src/components/plugin-ui/PickerField.d.ts +17 -0
- package/dist/src/components/plugin-ui/RunButton.d.ts +8 -0
- package/dist/src/components/plugin-ui/crs-issue-messages.d.ts +157 -0
- package/dist/src/components/plugin-ui/flow-ports.d.ts +17 -0
- package/dist/src/components/plugin-ui/param-widgets.d.ts +2 -2
- package/dist/src/components/plugin-ui/picker-model.d.ts +49 -0
- package/dist/src/components/plugin-ui/run-data.d.ts +32 -4
- package/dist/src/components/plugin-ui/w2-contracts.d.ts +61 -0
- package/dist/src/components/plugin-ui/w3-contracts.d.ts +16 -1
- package/dist/src/components/resources/CrsPicker/CrsChip.d.ts +60 -0
- package/dist/src/components/resources/CrsPicker/CrsParamControl.d.ts +10 -0
- package/dist/src/components/resources/CrsPicker/CrsPicker.d.ts +41 -0
- package/dist/src/components/resources/CrsPicker/crs-recent.d.ts +40 -0
- package/dist/src/components/resources/CrsPicker/index.d.ts +14 -0
- package/dist/src/components/resources/resource-library-crs.d.ts +117 -0
- package/dist/src/components/ui/dialog.d.ts +25 -0
- package/dist/src/domain/algorithms/flow-field-style.d.ts +68 -0
- package/dist/src/domain/map/types.d.ts +2 -0
- package/dist/src/domain/models/crs.d.ts +187 -0
- package/dist/src/domain/models/dataset-kind.d.ts +256 -0
- package/dist/src/domain/models/flow-field-style.d.ts +32 -0
- package/dist/src/features/gis/components/GisAttributeTable.d.ts +11 -3
- package/dist/src/features/gis/hooks/useDatasetFeatureWindow.d.ts +5 -9
- package/dist/src/features/gis/lib/attributeFilters.d.ts +24 -0
- package/dist/src/sdk/facets/types.d.ts +73 -2
- package/dist/src/sdk/map-api.d.ts +5 -2
- package/dist/src/types/analysis.d.ts +111 -7
- package/dist/src/types/extensions.d.ts +43 -0
- package/dist/src/types/gis.d.ts +1 -1
- package/package.json +6 -37
- package/LICENSE +0 -13
- package/README.md +0 -25
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import type { NgisDatasetKind } from "../domain/models/dataset-kind";
|
|
1
2
|
import type { WorkflowDagConfig } from "../types/workflow";
|
|
2
3
|
export type { WorkflowDagConfig, WorkflowDagEdge, WorkflowDagGraph, WorkflowDagNode, WorkflowDagScriptConfig, WorkflowDagScriptPort, WorkflowDagView, WorkflowDagViewEdge, WorkflowDagViewNode, WorkflowDagViewPosition, WorkflowDagViewport, } from "../types/workflow";
|
|
3
4
|
export type WorkflowDefinitionStatus = "DRAFT" | "PUBLISHED";
|
|
@@ -37,7 +38,15 @@ export interface WorkflowInstanceOutputDto {
|
|
|
37
38
|
nodeId: string;
|
|
38
39
|
fileId: string;
|
|
39
40
|
fileName?: string | null;
|
|
40
|
-
|
|
41
|
+
/**
|
|
42
|
+
* **Stage 29 (F29.1).** The registered kind id of the dataset this node
|
|
43
|
+
* produced — the same wire fact as `DatasetInfo.datasetType`, so it is typed
|
|
44
|
+
* from the one type rather than as a bare `string`. `null` means *no kind
|
|
45
|
+
* resolved*; `"GENERIC"` is gone. The union is **open** — a workflow may
|
|
46
|
+
* produce a kind registered after this client shipped, and the output row must
|
|
47
|
+
* still render.
|
|
48
|
+
*/
|
|
49
|
+
datasetType?: NgisDatasetKind | null;
|
|
41
50
|
primary?: boolean | null;
|
|
42
51
|
extracted?: boolean | null;
|
|
43
52
|
extractTarget?: WorkflowOutputTarget | null;
|
|
@@ -1,5 +1,8 @@
|
|
|
1
|
+
import * as echarts from "echarts/core";
|
|
1
2
|
import type { NgisChartSpec } from "../../sdk/host-api";
|
|
2
3
|
import { type NgisChartTheme } from "./stat-chart-model";
|
|
4
|
+
/** Shared, modular ECharts runtime for host-controlled chart canvases. */
|
|
5
|
+
export { echarts as ngisEcharts };
|
|
3
6
|
export interface ChartCanvasProps {
|
|
4
7
|
spec: NgisChartSpec;
|
|
5
8
|
theme: NgisChartTheme;
|
|
@@ -8,7 +8,24 @@ export interface NgisParamFormChange {
|
|
|
8
8
|
readonly submission: NgisRunSubmission | null;
|
|
9
9
|
/** Client rules (F14.9) refuse the submit. */
|
|
10
10
|
readonly blockedByClient: boolean;
|
|
11
|
-
/**
|
|
11
|
+
/**
|
|
12
|
+
* **Stage 25 (F25.6/F25.7), additive.** The server's verdict refuses the
|
|
13
|
+
* submit — `isSubmitBlocked` over the last answer it gave.
|
|
14
|
+
*
|
|
15
|
+
* It exists because a blocking **issue** can arrive with every F14.9 string
|
|
16
|
+
* array empty (the frozen `validate-execution-issues.json` is exactly that
|
|
17
|
+
* shape), so a caller gating on {@link blockingMessages}` .length` alone would
|
|
18
|
+
* leave Run live on a run the server had already refused. `validateBeforeSubmit`
|
|
19
|
+
* still refuses at press time either way; this is what lets a caller disable
|
|
20
|
+
* the control *with the reason*, which is F25.7 flow 1's second half.
|
|
21
|
+
*/
|
|
22
|
+
readonly blockedByServer: boolean;
|
|
23
|
+
/**
|
|
24
|
+
* The server's blocking messages, if it has answered — F14.9's string arrays
|
|
25
|
+
* **and**, since F25.6, the localised text of every blocking issue. An issue
|
|
26
|
+
* and a string are the same thing to a reader; keeping them in one list is
|
|
27
|
+
* what stops a caller having to know which channel a refusal came down.
|
|
28
|
+
*/
|
|
12
29
|
readonly blockingMessages: readonly string[];
|
|
13
30
|
/** `warnings` — never blocks (F14.9). */
|
|
14
31
|
readonly advisoryMessages: readonly string[];
|
|
@@ -19,15 +36,13 @@ export interface NgisParamFormChange {
|
|
|
19
36
|
*/
|
|
20
37
|
readonly validateBeforeSubmit: () => Promise<boolean>;
|
|
21
38
|
}
|
|
22
|
-
export interface
|
|
39
|
+
export interface ParamFormBaseProps {
|
|
23
40
|
/** The 0.4 facade — always the one `createGisPluginHostFactory` minted. */
|
|
24
41
|
host: NgisHostAPI;
|
|
25
42
|
/** The flow port id (F14.5, `record` out port). Also seeds the field DOM ids. */
|
|
26
43
|
id?: string;
|
|
27
44
|
/** Plugin-authored; rendered **literally** (F13.11). */
|
|
28
45
|
label?: string;
|
|
29
|
-
/** The tool whose UI schema this form is generated from. */
|
|
30
|
-
toolId: string;
|
|
31
46
|
/**
|
|
32
47
|
* Param names the form does not render — already bound by a flow port
|
|
33
48
|
* (F14.22's `hide={["ELEV"]}`). **An unknown name survives and is surfaced**,
|
|
@@ -60,4 +75,51 @@ export interface ParamFormProps {
|
|
|
60
75
|
/** Reports the exact layer chosen from a duplicate-named resource option. */
|
|
61
76
|
onSameNamedLayerSelect?: (layerId: string) => void;
|
|
62
77
|
}
|
|
63
|
-
|
|
78
|
+
/** Bind either an implementation id (host-owned code) or a manifest alias (a bundle). */
|
|
79
|
+
export type ParamFormProps = (ParamFormBaseProps & {
|
|
80
|
+
/** Legacy host-owned spelling. Third-party bundles should use `dependency`. */
|
|
81
|
+
toolId: string;
|
|
82
|
+
dependency?: never;
|
|
83
|
+
}) | (ParamFormBaseProps & {
|
|
84
|
+
/** Locally unique `manifest.toolDependencies[].alias`. */
|
|
85
|
+
dependency: string;
|
|
86
|
+
toolId?: never;
|
|
87
|
+
});
|
|
88
|
+
export declare function ParamForm(props: ParamFormProps): import("react/jsx-runtime").JSX.Element;
|
|
89
|
+
/**
|
|
90
|
+
* **F25.4's fixed "Platform" register, above the vendor parameters.**
|
|
91
|
+
*
|
|
92
|
+
* The problem this solves is not decoration: `ngis:outputCrs` appears on *every*
|
|
93
|
+
* tool form and belongs to none of them. Left inline among the tool's own
|
|
94
|
+
* parameters it reads as one of them — a buffer setting the tool author chose —
|
|
95
|
+
* and the user has no way to learn that the same control is on the next form
|
|
96
|
+
* too, in the same place, meaning the same thing.
|
|
97
|
+
*
|
|
98
|
+
* ## A register change, not a box
|
|
99
|
+
*
|
|
100
|
+
* The device is a **caption with a rule running off it**, the way a bench sheet
|
|
101
|
+
* separates the instrument's settings from the bench's. A card, a tinted panel
|
|
102
|
+
* or an accent bar would all read as *alert* — the visual grammar this app
|
|
103
|
+
* already uses for a warning — and would announce something on every form
|
|
104
|
+
* forever that is only ever ordinary.
|
|
105
|
+
*
|
|
106
|
+
* So the boundary is one hairline, and it closes below the group as well as
|
|
107
|
+
* opening above it: the vendor list resumes with **no caption at all**, because
|
|
108
|
+
* it is the default register and naming it would imply a symmetry that does not
|
|
109
|
+
* exist. The eyebrow is `text-muted-foreground`, i.e. **quieter than the field
|
|
110
|
+
* labels beneath it**, which inverts the usual section-header hierarchy on
|
|
111
|
+
* purpose. Persistent chrome earns less emphasis than the thing it frames, and
|
|
112
|
+
* the boldness this stage has to spend is already spent — on `CrsChip`'s unit
|
|
113
|
+
* sigil, where round means degrees and square means metres.
|
|
114
|
+
*
|
|
115
|
+
* **Exported for one reason only:** this repo has no DOM in tests
|
|
116
|
+
* (`S25-V127`), so a server render cannot drive `ParamForm` past its
|
|
117
|
+
* `loadToolUiSchema` effect into the field list — the group's markup is
|
|
118
|
+
* unreachable through the form. Exporting the section makes it assertable
|
|
119
|
+
* directly. It is **not** on `plugin-ui`'s barrel (`index.ts` names its exports
|
|
120
|
+
* one by one) and therefore not on any published surface.
|
|
121
|
+
*/
|
|
122
|
+
export declare function PlatformGroup({ title, children, }: {
|
|
123
|
+
title: string;
|
|
124
|
+
children: React.ReactNode;
|
|
125
|
+
}): import("react/jsx-runtime").JSX.Element;
|
|
@@ -22,6 +22,23 @@ export interface NgisPickerNotice {
|
|
|
22
22
|
/** Already-translated host text. */
|
|
23
23
|
text: string;
|
|
24
24
|
tone: NgisPickerNoticeTone;
|
|
25
|
+
/**
|
|
26
|
+
* **Stage 25 (F25.6), additive — no existing caller breaks.**
|
|
27
|
+
*
|
|
28
|
+
* The one-click remedy attached to a validation issue: F25.7's *"Use
|
|
29
|
+
* EPSG:32650"* and *"Set projection…"*. It is a field on the notice rather
|
|
30
|
+
* than a widening of `text`, and that choice is the contract:
|
|
31
|
+
*
|
|
32
|
+
* `text` stays a `string`, so this component still cannot invent copy the
|
|
33
|
+
* caller did not ask for. Widening `text` to `ReactNode` was **considered and
|
|
34
|
+
* rejected** at S25-02 — a smaller diff, but it reopens the rule in this
|
|
35
|
+
* file's own header that everything in `notices` is a host-translated string.
|
|
36
|
+
* A `{label, onClick}` pair keeps both halves owned by the caller.
|
|
37
|
+
*/
|
|
38
|
+
action?: {
|
|
39
|
+
label: string;
|
|
40
|
+
onClick: () => void;
|
|
41
|
+
};
|
|
25
42
|
}
|
|
26
43
|
export interface PickerFieldProps {
|
|
27
44
|
/** Control id; also `<label for>`. */
|
|
@@ -34,16 +34,24 @@ export type RunButtonProps = (RunButtonBaseProps & {
|
|
|
34
34
|
*/
|
|
35
35
|
submission: NgisRunSubmission | null;
|
|
36
36
|
toolId?: never;
|
|
37
|
+
dependency?: never;
|
|
37
38
|
inputs?: never;
|
|
38
39
|
}) | (RunButtonBaseProps & {
|
|
39
40
|
submission?: never;
|
|
40
41
|
/** The tool to run; its UI schema supplies each bound input's role,
|
|
41
42
|
* requiredness and label — F14.10's provenance chain, unaltered. */
|
|
42
43
|
toolId: string;
|
|
44
|
+
dependency?: never;
|
|
43
45
|
/**
|
|
44
46
|
* F14.4's bindings, keyed by tool parameter name, plus any number of
|
|
45
47
|
* `...ngisFlowSpread("@ref")` directives. Needs an enclosing `<Flow>`.
|
|
46
48
|
*/
|
|
47
49
|
inputs: Readonly<Record<string, NgisInputBinding>>;
|
|
50
|
+
}) | (RunButtonBaseProps & {
|
|
51
|
+
submission?: never;
|
|
52
|
+
/** Locally unique `manifest.toolDependencies[].alias`. */
|
|
53
|
+
dependency: string;
|
|
54
|
+
toolId?: never;
|
|
55
|
+
inputs: Readonly<Record<string, NgisInputBinding>>;
|
|
48
56
|
});
|
|
49
57
|
export declare function RunButton(props: RunButtonProps): import("react/jsx-runtime").JSX.Element;
|
|
@@ -0,0 +1,157 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Where a server verdict becomes a **field's** notice, and where a UI schema
|
|
3
|
+
* splits into the platform's parameters and the tool's (Stage 25, **F25.4 /
|
|
4
|
+
* F25.6**).
|
|
5
|
+
*
|
|
6
|
+
* `ParamForm.tsx` is the wiring and the chrome. Everything it *decides* about an
|
|
7
|
+
* issue or about a parameter's group lives here, pure and React-free, for the
|
|
8
|
+
* reason `w2-contracts.ts` and `param-form-model.ts` are: this repo has no DOM
|
|
9
|
+
* in tests (`S25-V127`), so a rule that lives inside a component render is a
|
|
10
|
+
* rule no test can reach. The three flows in design §5.3 are asserted against
|
|
11
|
+
* these functions **and** against the real form that calls them — which is only
|
|
12
|
+
* possible because the two are the same code.
|
|
13
|
+
*
|
|
14
|
+
* ## What this module deliberately does not do
|
|
15
|
+
*
|
|
16
|
+
* It never re-classifies. `severity` is the server's, decided from the
|
|
17
|
+
* requirement's *provenance* — only the 119 hand-authored seeds may block, and a
|
|
18
|
+
* classifier-derived requirement is advisory forever (F25.6, D25-5 as amended on
|
|
19
|
+
* the user's ruling). There is no branch on `code` here and no branch on
|
|
20
|
+
* `crsRequirementSource`, because a client that upgraded or downgraded one would
|
|
21
|
+
* reopen exactly what S25-01 measured: no classifier rule beat 0.800 precision,
|
|
22
|
+
* and a false refusal is strictly worse than a false permit (**G-U**).
|
|
23
|
+
*
|
|
24
|
+
* It also never *drops* one. An issue naming neither a `param` nor a `port` has
|
|
25
|
+
* no field to belong to and comes back from {@link unroutedIssues} for the
|
|
26
|
+
* form-level list. Silence would be the client deciding a refusal did not count.
|
|
27
|
+
*
|
|
28
|
+
* ## The name
|
|
29
|
+
*
|
|
30
|
+
* The W2 dispatch table carved this session a file called
|
|
31
|
+
* `plugin-ui/validation-messages.ts`. **Stage 25 has no such file and never had
|
|
32
|
+
* one** — the name was read off the cross-stage diff, where it is a file *Stage
|
|
33
|
+
* 24* creates (+44). Two stages independently creating the same path is a
|
|
34
|
+
* head-on collision at the `main` merge rather than the textual one D25-0 plans
|
|
35
|
+
* for, so this module carries a name only Stage 25 uses. Recorded as a
|
|
36
|
+
* correction of record against the dispatch table (`S25-V233`), not resolved
|
|
37
|
+
* silently.
|
|
38
|
+
*/
|
|
39
|
+
import { type NgisValidationIssue } from "../../domain/models/crs";
|
|
40
|
+
import type { NgisToolUiParam } from "../../types/analysis";
|
|
41
|
+
import type { NgisPickerNoticeTone } from "./PickerField";
|
|
42
|
+
/**
|
|
43
|
+
* Is this parameter the **platform's** rather than the tool's?
|
|
44
|
+
*
|
|
45
|
+
* F14.2 normalizes `role` to `"input" | "option" | "output"` — three *data*
|
|
46
|
+
* roles, and `ngis:outputCrs` is an `option` by that measure like any other
|
|
47
|
+
* non-port parameter. The platform/vendor distinction is a different axis
|
|
48
|
+
* entirely and rides on `x-role`, which reaches the client verbatim in
|
|
49
|
+
* `extensions` (F14.2's escape hatch). Reading it there rather than widening
|
|
50
|
+
* `NgisParamRole` is deliberate: the two axes are independent, and a fourth
|
|
51
|
+
* member of `role` would make `ngis:outputCrs` stop being an option, which it
|
|
52
|
+
* still is.
|
|
53
|
+
*/
|
|
54
|
+
export declare function isPlatformParam(param: NgisToolUiParam): boolean;
|
|
55
|
+
export interface NgisParamGroups {
|
|
56
|
+
/** `x-role: platform`, in schema order. Rendered **above** the vendor list. */
|
|
57
|
+
readonly platform: readonly NgisToolUiParam[];
|
|
58
|
+
/** Everything else, in schema order — the tool's own parameters, unchanged. */
|
|
59
|
+
readonly vendor: readonly NgisToolUiParam[];
|
|
60
|
+
}
|
|
61
|
+
/**
|
|
62
|
+
* The form's two groups, **in schema order within each** (F25.4).
|
|
63
|
+
*
|
|
64
|
+
* The order between them is fixed and is not a preference: the output CRS
|
|
65
|
+
* decides what the tool's numbers *mean* — at EPSG:4326 a buffer distance of 500
|
|
66
|
+
* is 500 degrees — so a user who reads the form top to bottom meets the unit
|
|
67
|
+
* before the number that carries it. Putting it after the vendor params would
|
|
68
|
+
* put the consequence after the cause.
|
|
69
|
+
*/
|
|
70
|
+
export declare function splitPlatformParams(params: readonly NgisToolUiParam[]): NgisParamGroups;
|
|
71
|
+
/**
|
|
72
|
+
* `hide` with every platform parameter removed.
|
|
73
|
+
*
|
|
74
|
+
* **F25.4: a platform parameter "is never hidden by an author override".** A
|
|
75
|
+
* plugin author writes `hide={[…]}` to suppress a param another node already
|
|
76
|
+
* binds (F14.22); `ngis:outputCrs` is bound by no node and belongs to no tool,
|
|
77
|
+
* so hiding it can only ever remove the user's one way to say what projection
|
|
78
|
+
* the run computes in. Filtered here rather than only at render, because `hide`
|
|
79
|
+
* also drives `toSubmission` — a param hidden there is absent from the payload,
|
|
80
|
+
* and the form would then submit a run with the CRS decision silently dropped.
|
|
81
|
+
*/
|
|
82
|
+
export declare function effectiveHide(hide: readonly string[], params: readonly NgisToolUiParam[]): readonly string[];
|
|
83
|
+
/**
|
|
84
|
+
* The issues that belong to one field.
|
|
85
|
+
*
|
|
86
|
+
* A field owns an issue when the issue names it as the **parameter that fixes
|
|
87
|
+
* it** (`param`) or as the **input port it is about** (`port`). Both are matched
|
|
88
|
+
* against the same name because a UI-schema input parameter *is* the port
|
|
89
|
+
* (F14.2): `port: "input"` and `param: "input"` address the same field, and a
|
|
90
|
+
* router that honoured only one of them would drop half the flows — R1's
|
|
91
|
+
* `CRS_UNKNOWN` and R5's `CRS_REPROJECT_NOTICE` are both per-port.
|
|
92
|
+
*/
|
|
93
|
+
export declare function issuesForField(issues: readonly NgisValidationIssue[], paramName: string): readonly NgisValidationIssue[];
|
|
94
|
+
/**
|
|
95
|
+
* The issues no field claims — form-level, and **never discarded**.
|
|
96
|
+
*
|
|
97
|
+
* An issue naming neither a `param` nor a `port` is about the run as a whole
|
|
98
|
+
* (R8's `CRS_UNSUPPORTED_BY_PROVIDER` is the shipped example: the provider
|
|
99
|
+
* cannot compute in that CRS, which is not one field's fault). It renders in the
|
|
100
|
+
* form's own notice list. The complement of {@link issuesForField} over every
|
|
101
|
+
* rendered field, so an issue naming a field the form does not render still
|
|
102
|
+
* surfaces rather than vanishing with it.
|
|
103
|
+
*/
|
|
104
|
+
export declare function unroutedIssues(issues: readonly NgisValidationIssue[], renderedNames: readonly string[]): readonly NgisValidationIssue[];
|
|
105
|
+
/**
|
|
106
|
+
* The notice tone for a severity — **the server's, rendered as given**.
|
|
107
|
+
*
|
|
108
|
+
* Two tones exist (`PickerField` has `info` and `warn`) and there are two
|
|
109
|
+
* severities, so this is a total map with nothing to decide. It is a named
|
|
110
|
+
* function anyway so that the mapping has one definition and the G-U rule has
|
|
111
|
+
* one place to be violated from, rather than an inline ternary at each of the
|
|
112
|
+
* three call sites.
|
|
113
|
+
*/
|
|
114
|
+
export declare function issueNoticeTone(severity: NgisValidationIssue["severity"]): NgisPickerNoticeTone;
|
|
115
|
+
/**
|
|
116
|
+
* Is any of these issues a refusal?
|
|
117
|
+
*
|
|
118
|
+
* The same rule `isSubmitBlocked` applies to a whole verdict, over an arbitrary
|
|
119
|
+
* subset — used to ask "does *this field* carry the reason Run is off?" without
|
|
120
|
+
* re-deciding what blocking means.
|
|
121
|
+
*/
|
|
122
|
+
export declare function hasBlockingIssue(issues: readonly NgisValidationIssue[]): boolean;
|
|
123
|
+
/**
|
|
124
|
+
* The **last hop** of a server-offered remedy: `fix.labelKey` + `fix.params` →
|
|
125
|
+
* the words on the button (Stage 25, `S25-V300`).
|
|
126
|
+
*
|
|
127
|
+
* S25-30b proved the `fix` reaches the *response boundary* on both the validate
|
|
128
|
+
* and the submit leg, and recorded that it had **not** been proved to the
|
|
129
|
+
* button. This is that hop, and it lived inside `ParamForm`'s render until now —
|
|
130
|
+
* where no gate could reach it, because this repo has no DOM in tests
|
|
131
|
+
* (`S25-V127`). Extracted for exactly that reason and for no other; the
|
|
132
|
+
* behaviour is unchanged.
|
|
133
|
+
*
|
|
134
|
+
* **A key the client does not carry renders as the key itself**, never as
|
|
135
|
+
* nothing and never as invented copy. That is the `ExtensionSettingsForm` rule
|
|
136
|
+
* (F14.9): a later stage may ship a `labelKey` this build has never heard of,
|
|
137
|
+
* and a button labelled `Crs.issues.fix.useUtm` is ugly but honest, while a
|
|
138
|
+
* button labelled nothing is a remedy the user cannot see and a sentence the
|
|
139
|
+
* client made up is worse than both.
|
|
140
|
+
*
|
|
141
|
+
* The translator is passed in rather than imported so this stays React-free and
|
|
142
|
+
* assertable — the `picker-model.ts` discipline. `next-intl`'s root translator
|
|
143
|
+
* satisfies it as-is.
|
|
144
|
+
*/
|
|
145
|
+
export interface NgisIssueTranslator {
|
|
146
|
+
has(key: string): boolean;
|
|
147
|
+
(key: string, params?: Readonly<Record<string, string | number>>): string;
|
|
148
|
+
}
|
|
149
|
+
export declare function issueFixLabel(fix: NonNullable<NgisValidationIssue["fix"]>, translate: NgisIssueTranslator): string;
|
|
150
|
+
/**
|
|
151
|
+
* The same hop for an issue's own sentence. Paired with {@link issueFixLabel}
|
|
152
|
+
* because the two must agree about what an unknown key does — one of them
|
|
153
|
+
* falling back to the key while the other fell back to `""` would put a notice
|
|
154
|
+
* on screen whose text and whose button disagreed about whether the server had
|
|
155
|
+
* said anything.
|
|
156
|
+
*/
|
|
157
|
+
export declare function issueText(issue: NgisValidationIssue, translate: NgisIssueTranslator): string;
|
|
@@ -48,6 +48,7 @@ import type { NgisPortSpec } from "./w3-contracts";
|
|
|
48
48
|
export declare const NGIS_PICKER_SLOT = "ngis-picker-field";
|
|
49
49
|
export declare const NGIS_FLOW_SLOTS: {
|
|
50
50
|
readonly resourceSelect: "ngis-picker-field";
|
|
51
|
+
readonly crsSelect: "crs-picker";
|
|
51
52
|
readonly modelSelect: "ngis-picker-field";
|
|
52
53
|
readonly fieldSelect: "ngis-picker-field";
|
|
53
54
|
readonly extentPicker: "ngis-picker-field";
|
|
@@ -89,6 +90,22 @@ export declare function extentPickerPorts(options: {
|
|
|
89
90
|
readonly nodeId: string;
|
|
90
91
|
readonly label?: string;
|
|
91
92
|
}): readonly NgisPortSpec[];
|
|
93
|
+
/**
|
|
94
|
+
* `value: crs` (Stage 25, **F25.9**).
|
|
95
|
+
*
|
|
96
|
+
* The value form is the ref's **key string** — `"EPSG:32650"` — and not the ref
|
|
97
|
+
* object, so what leaves this port is exactly what a `crs` widget writes into a
|
|
98
|
+
* params payload and exactly what reaches `POST /api/analysis/tasks` as
|
|
99
|
+
* `outputCrs`. One spelling, three layers.
|
|
100
|
+
*
|
|
101
|
+
* No `valueType`: that member is `kind: "value"`-only by F14.5, and a `crs` port
|
|
102
|
+
* is not a `value` port. Its shape is stated by its kind.
|
|
103
|
+
*/
|
|
104
|
+
export declare function crsSelectPorts(options: {
|
|
105
|
+
readonly nodeId: string;
|
|
106
|
+
readonly label?: string;
|
|
107
|
+
readonly required?: boolean;
|
|
108
|
+
}): readonly NgisPortSpec[];
|
|
92
109
|
/**
|
|
93
110
|
* `value: record` out, plus **one `in` port per required UI-schema param not
|
|
94
111
|
* satisfied internally** (F14.5).
|
|
@@ -10,13 +10,13 @@
|
|
|
10
10
|
* control that works, and F14.2 says an unknown widget is a render-time error
|
|
11
11
|
* surfaced in place.
|
|
12
12
|
*
|
|
13
|
-
* YB1 emits only seven of the
|
|
13
|
+
* YB1 emits only seven of the thirteen keys in v1 (`field-select`, `extent`,
|
|
14
14
|
* `textarea`, `multi-select` and `resource-multi` are never emitted — `V-45`
|
|
15
15
|
* and `src/types/analysis.ts`'s own note). The other five are reachable through
|
|
16
16
|
* an author's `widgets` override and a recipe's promoted inputs, and they are
|
|
17
17
|
* exercised by `src/sdk/__fixtures__/ui-schema/authored/w2-dependency-probe.json`.
|
|
18
18
|
*
|
|
19
|
-
* ##
|
|
19
|
+
* ## Five of the thirteen are pickers, not new controls
|
|
20
20
|
*
|
|
21
21
|
* `resource-select`, `resource-multi`, `field-select` and `extent` delegate to
|
|
22
22
|
* `ResourceSelect` / `FieldSelect` / `ExtentPicker`. Rebuilding those controls
|
|
@@ -13,6 +13,7 @@
|
|
|
13
13
|
* extracted from. `@ngis/plugin-ui` re-exports the components from here instead
|
|
14
14
|
* (F14.14 — "the components' runtime lives in the host bundle").
|
|
15
15
|
*/
|
|
16
|
+
import type { NgisCrsRef } from "../../domain/models/crs";
|
|
16
17
|
import { type NgisDataType, type NgisModelSearchResult, type NgisModelSummary, type NgisResourceField, type NgisResourceSummary, type NgisResourceType } from "../../sdk/facets/types";
|
|
17
18
|
import type { NgisBounds } from "../../sdk/map-api";
|
|
18
19
|
/** One row of any of the four pickers. Data only — no node, no callback. */
|
|
@@ -32,6 +33,26 @@ export interface NgisPickerOption {
|
|
|
32
33
|
hint: string | null;
|
|
33
34
|
/** Listed but not ready — greyed, still selectable, never hidden. */
|
|
34
35
|
muted: boolean;
|
|
36
|
+
/**
|
|
37
|
+
* **Stage 25 (F25.5 / F25.9, `S25-V216`).** The dataset's CRS behind this
|
|
38
|
+
* row, for the resource picker only. Absent on model and field options, which
|
|
39
|
+
* have no such thing — an optional member rather than a `null` one, because
|
|
40
|
+
* "a tool has no CRS" and "this dataset has no projection" are not the same
|
|
41
|
+
* sentence and a reader of this type should not have to guess which.
|
|
42
|
+
*/
|
|
43
|
+
crs?: NgisCrsRef | null;
|
|
44
|
+
/**
|
|
45
|
+
* **Stage 25 (`S25-V216` / `S25-V236`).** Whether this row is in the
|
|
46
|
+
* `NEEDS_CRS` state, i.e. whether binding it to a port produces a blocking
|
|
47
|
+
* `CRS_UNKNOWN`.
|
|
48
|
+
*
|
|
49
|
+
* It is a **separate field and not `crs === null`**, because the rule is not
|
|
50
|
+
* that: `needsProjection` reads the server's status *and* the ref, so a `DONE`
|
|
51
|
+
* row whose dataset read failed is also in this state. Deriving it at each
|
|
52
|
+
* call site is how the picker and the library would come to disagree about
|
|
53
|
+
* which rows carry the ⚠.
|
|
54
|
+
*/
|
|
55
|
+
needsCrs?: boolean;
|
|
35
56
|
/** A scene layer is currently hidden but remains a valid data input. */
|
|
36
57
|
hidden?: boolean;
|
|
37
58
|
/** Present only when another eligible layer has the same visible name. */
|
|
@@ -58,6 +79,34 @@ export declare function widenDataTypes(types?: readonly NgisDataType[]): NgisRes
|
|
|
58
79
|
* this is a choice rather than a limitation, and it is the contract's choice.
|
|
59
80
|
*/
|
|
60
81
|
export declare function resourceOptions(summaries: readonly NgisResourceSummary[]): NgisPickerOption[];
|
|
82
|
+
/**
|
|
83
|
+
* What `ResourceSelect` has to say about projection, as a decision rather than a
|
|
84
|
+
* render (Stage 25, **F25.5 / F25.7 flow 2 as the W3 kickoff amended it**).
|
|
85
|
+
*
|
|
86
|
+
* Three states, and the difference between the last two is the whole of
|
|
87
|
+
* `S25-V236`:
|
|
88
|
+
*
|
|
89
|
+
* - `"none"` — nothing listed is in `NEEDS_CRS`. Silence, not reassurance.
|
|
90
|
+
* - `"listed"` — rows exist in that state but the port is bound to none of them.
|
|
91
|
+
* The picker **says so and offers nothing**, because there is no dataset to
|
|
92
|
+
* attach an action to: "Set projection…" needs a subject.
|
|
93
|
+
* - `"bound"` — the port is bound to one. *This* is the run that will not start,
|
|
94
|
+
* and it is the only case where the affordance has a dataset to act on.
|
|
95
|
+
*
|
|
96
|
+
* The rule this encodes is the kickoff's ruling: the remedy is the **picker's**,
|
|
97
|
+
* because the picker is the one surface that knows which dataset a port carries.
|
|
98
|
+
* The server's `CRS_UNKNOWN` still arrives through `ParamForm`'s issue routing
|
|
99
|
+
* and is still what blocks Run — this function never decides that.
|
|
100
|
+
*/
|
|
101
|
+
export type NgisResourceCrsState = {
|
|
102
|
+
readonly kind: "none";
|
|
103
|
+
} | {
|
|
104
|
+
readonly kind: "listed";
|
|
105
|
+
} | {
|
|
106
|
+
readonly kind: "bound";
|
|
107
|
+
readonly option: NgisPickerOption;
|
|
108
|
+
};
|
|
109
|
+
export declare function resourceCrsState(options: readonly NgisPickerOption[], selected: readonly string[]): NgisResourceCrsState;
|
|
61
110
|
/**
|
|
62
111
|
* Project a file-typed resource input onto the scene layers a person actually
|
|
63
112
|
* recognises. The submitted value remains the resource's `effectiveFileId`;
|
|
@@ -31,17 +31,45 @@ import type { NgisExecutionValidation } from "../../types/analysis";
|
|
|
31
31
|
import { type NgisRunGate } from "./run-model";
|
|
32
32
|
import { type NgisRunSubmission } from "./w2-contracts";
|
|
33
33
|
/** Run-shaping options a submission does not carry (they are the caller's, not
|
|
34
|
-
* the generated form's). Mirrors `NgisTaskRunRequest
|
|
35
|
-
* only the validate leg takes. */
|
|
34
|
+
* the generated form's). Mirrors `NgisTaskRunRequest`. */
|
|
36
35
|
export interface NgisRunOptions {
|
|
37
36
|
sceneId?: string;
|
|
38
37
|
/** Lab-scene optimistic fence. Ordinary scene runs leave this absent. */
|
|
39
38
|
expectedSceneGeneration?: number;
|
|
40
39
|
outputMode?: "ADD_LAYER" | "UPDATE_RESOURCE";
|
|
41
40
|
targetResourceId?: string;
|
|
42
|
-
/**
|
|
43
|
-
|
|
41
|
+
/**
|
|
42
|
+
* **Stage 25 (F25.4), replacing `targetSrid?: number`** (the `S25-V124`
|
|
43
|
+
* ruling).
|
|
44
|
+
*
|
|
45
|
+
* An `NgisCrsRef` key. Two things changed at once and both matter:
|
|
46
|
+
*
|
|
47
|
+
* 1. **It is a string, not an integer.** An integer cannot name a custom CRS,
|
|
48
|
+
* and `formatCrs` exists because a bare number is not a CRS a user can
|
|
49
|
+
* read either.
|
|
50
|
+
* 2. **It goes on BOTH legs.** `targetSrid` reached `validate-execution` and
|
|
51
|
+
* stopped — so a run could validate against one projection decision and
|
|
52
|
+
* execute with none. {@link submitRun} now carries it too, which is what
|
|
53
|
+
* makes the validate verdict describe the run that actually happens.
|
|
54
|
+
*
|
|
55
|
+
* Absent is a real value: compute in the primary input's own CRS (R4).
|
|
56
|
+
*/
|
|
57
|
+
outputCrs?: string;
|
|
44
58
|
}
|
|
59
|
+
/**
|
|
60
|
+
* The output CRS a submission itself declares, if any.
|
|
61
|
+
*
|
|
62
|
+
* `ngis:outputCrs` is an ordinary parameter in the generated form (F25.4 puts it
|
|
63
|
+
* in the schema, F25.3 renders it with the `crs` widget), so a filled form
|
|
64
|
+
* already carries it in `submission.params`. Lifting it here is what makes the
|
|
65
|
+
* run body carry `outputCrs` **with no author involvement** — a plugin that
|
|
66
|
+
* never heard of Stage 25 still submits the user's choice.
|
|
67
|
+
*
|
|
68
|
+
* An explicit `options.outputCrs` wins, because a caller that passed one is
|
|
69
|
+
* making the decision on purpose. An empty string is absent, never `""` on the
|
|
70
|
+
* wire (F14.4's "empty is unbound").
|
|
71
|
+
*/
|
|
72
|
+
export declare function outputCrsOf(submission: NgisRunSubmission, options?: NgisRunOptions): string | undefined;
|
|
45
73
|
/**
|
|
46
74
|
* The `host.models.validate` request for one submission.
|
|
47
75
|
*
|
|
@@ -19,6 +19,7 @@
|
|
|
19
19
|
* Contract: F14.2 (widget vocabulary), F14.3 (validation DTOs), F14.5 (ports),
|
|
20
20
|
* F14.8 (presets), F14.9 (form lifecycle), F14.10 (no `disabled` prop).
|
|
21
21
|
*/
|
|
22
|
+
import type { NgisValidationIssue } from "../../domain/models/crs";
|
|
22
23
|
import type { NgisExecutionValidation, NgisUiSchemaValueType, NgisWidgetKey } from "../../types/analysis";
|
|
23
24
|
/**
|
|
24
25
|
* A single generated-form value. Narrower than `unknown` on purpose: F14.2's
|
|
@@ -115,6 +116,66 @@ export type NgisWidgetRegistry<TWidget> = {
|
|
|
115
116
|
* or `FAIL` port cannot run.
|
|
116
117
|
*/
|
|
117
118
|
export declare function blockingValidationMessages(validation: NgisExecutionValidation): readonly string[];
|
|
119
|
+
/**
|
|
120
|
+
* **Every typed issue the server sent, from both halves of one verdict (F25.6).**
|
|
121
|
+
*
|
|
122
|
+
* F25.6 put `issues[]` on `NgisExecutionValidation` **and** on
|
|
123
|
+
* `NgisBindingValidation`, exactly as F14.9 put its string arrays on both —
|
|
124
|
+
* an execution-level issue is usually about a *parameter*, a binding-level one is
|
|
125
|
+
* about a *port*. They are one channel read at two levels, never two channels,
|
|
126
|
+
* and this function is the single statement of that.
|
|
127
|
+
*
|
|
128
|
+
* It exists because they were being read at one level in the gate and at two in
|
|
129
|
+
* the form (`S29-V234`): `isSubmitBlocked` counted only the execution-level
|
|
130
|
+
* half while `ParamForm` merged both for display, so a `severity: "blocking"`
|
|
131
|
+
* issue arriving on the binding half printed the server's refusal and left Run
|
|
132
|
+
* live beside it. That is the shape G-U forbids — the client does not get to
|
|
133
|
+
* decline to act on a severity the server declared — and Stage 26's shipped
|
|
134
|
+
* `unknown_crs_issue` (`binding_validation.py:187`) writes to precisely that
|
|
135
|
+
* half. Order is execution-first so the compiler's fuller statement of a fact
|
|
136
|
+
* precedes the binding pass's barer one, matching the server's own `dedupe`.
|
|
137
|
+
*/
|
|
138
|
+
export declare function serverIssues(validation: NgisExecutionValidation): readonly NgisValidationIssue[];
|
|
139
|
+
/**
|
|
140
|
+
* **AMENDED additively by F25.6 (Stage 25).** F14.9's rule is unchanged and
|
|
141
|
+
* still counts: a `NOT_EXECUTABLE` verdict or any blocking string blocks the
|
|
142
|
+
* submit. What is added is a third input — a typed `issues[]` entry whose
|
|
143
|
+
* `severity` is `"blocking"`, on **either** half of the verdict.
|
|
144
|
+
*
|
|
145
|
+
* **`S29-V234` (Stage 29): "either half" was the contract and one half was the
|
|
146
|
+
* code.** This read `validation.issues` alone, while F25.6 declares `issues[]`
|
|
147
|
+
* on `NgisBindingValidation` too and the F14.9 rule it amends already reaches
|
|
148
|
+
* into the binding half for `errors`. It is {@link serverIssues} that is read
|
|
149
|
+
* now — not a widening of the rule, which was always stated over the channel
|
|
150
|
+
* rather than over one level of it, but the rule finally reading all of it.
|
|
151
|
+
*
|
|
152
|
+
* The client **never suppresses, downgrades or re-classifies a server issue**
|
|
153
|
+
* (G-U). There is deliberately no branch here on `code`, on provenance, or on
|
|
154
|
+
* whether the client thinks the situation warrants it: severity is decided
|
|
155
|
+
* server-side as a function of provenance (a `CLASSIFIER` requirement may only
|
|
156
|
+
* ever produce `"advisory"`), and a client that second-guessed it would be
|
|
157
|
+
* re-opening the very defect S25-01 measured — no classifier rule exceeded 0.800
|
|
158
|
+
* precision, and a false refusal is strictly worse than a false permit.
|
|
159
|
+
*
|
|
160
|
+
* **Stage 29 (F29.8), RULED SILENT by the user 2026-09-19 — this function gains
|
|
161
|
+
* NO second blocking source, and it has never had one.** A kind-`ADAPTABLE`
|
|
162
|
+
* binding applies its adapter with no confirmation step, so `KIND_ADAPTABLE` is
|
|
163
|
+
* an `advisory` the form renders and nothing more; the arms below are unchanged
|
|
164
|
+
* and there is deliberately no branch on `code` or on the `ADAPTABLE` verdict
|
|
165
|
+
* token. The reason is the same one G-U rests on, one level up: `"ADAPTABLE"` is
|
|
166
|
+
* the **same** token for Stage 26's CRS adaptation and Stage 29's kind
|
|
167
|
+
* adaptation, and today's CRS adaptation reprojects without asking — a gate on
|
|
168
|
+
* one of the two would make the token's meaning depend on which issue code
|
|
169
|
+
* happened to accompany it. **A session adding a confirm gate here is reopening
|
|
170
|
+
* a ruling, not fixing an omission**; `ParamForm.kindAdaptable.test.tsx` asserts
|
|
171
|
+
* this function's body against exactly that.
|
|
172
|
+
*
|
|
173
|
+
* What the ruling does **not** cover, stated because silence would look like
|
|
174
|
+
* coverage: it is about the `ADAPTABLE` verdict's consent model, not about data
|
|
175
|
+
* conversion generally. An adapter that mutated or replaced a user's
|
|
176
|
+
* authoritative data would be outside it and would need its own ruling
|
|
177
|
+
* (`S29-V215`).
|
|
178
|
+
*/
|
|
118
179
|
export declare function isSubmitBlocked(validation: NgisExecutionValidation): boolean;
|
|
119
180
|
/** Advisory only — never blocks (F14.9). */
|
|
120
181
|
export declare function advisoryValidationMessages(validation: NgisExecutionValidation): readonly string[];
|
|
@@ -41,7 +41,22 @@ import type { NgisDataType, NgisTaskOutput } from "../../sdk/facets/types";
|
|
|
41
41
|
import type { NgisUiSchemaValueType } from "../../types/analysis";
|
|
42
42
|
import type { NgisParamValue } from "./w2-contracts";
|
|
43
43
|
export type NgisPortDirection = "in" | "out";
|
|
44
|
-
export type NgisPortKind = "resource" | "resources" | "field" | "extent" | "task" | "value" | "record";
|
|
44
|
+
export type NgisPortKind = "resource" | "resources" | "field" | "extent" | "task" | "value" | "record" | "crs";
|
|
45
|
+
/**
|
|
46
|
+
* **Stage 25 (F25.9)** — `crs` is the eighth kind, and its value form is the
|
|
47
|
+
* ref's **key string** (`"EPSG:32650"`, `"custom:<ulid>"`), never an integer and
|
|
48
|
+
* never the ref object. That is the same value the `crs` widget puts in a params
|
|
49
|
+
* payload (F25.3) and the same one `ngis:outputCrs` carries to the submit body,
|
|
50
|
+
* so a port, a form field and a wire field all speak one spelling.
|
|
51
|
+
*
|
|
52
|
+
* It is a kind of its own rather than a `value` port with `valueType: "string"`
|
|
53
|
+
* for the reason every other kind here exists: the engine's generated reasons
|
|
54
|
+
* and the `data-port-*` attributes name what a port *is*, and "choose a CRS
|
|
55
|
+
* first" is not "choose a value first". Compatibility is a separate question,
|
|
56
|
+
* answered by {@link portKindsCompatible} — where `crs` is string-shaped and so
|
|
57
|
+
* interoperates with `resource`/`field`/`value`, because its wire form really is
|
|
58
|
+
* a bare string.
|
|
59
|
+
*/
|
|
45
60
|
/**
|
|
46
61
|
* F14.5's port spec, verbatim but for one clarification.
|
|
47
62
|
*
|