@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,187 @@
1
+ /**
2
+ * `<ParamForm>`'s pure half (Stage 14 Y3 — **F14.9**, F14.8, F14.2).
3
+ *
4
+ * Nothing here imports React, a store or an API client. That is the same rule
5
+ * Y2's `picker-model.ts` keeps, and it matters more here: F14.9 freezes a
6
+ * **four-phase lifecycle** whose hardest requirement — *a repopulate never
7
+ * overwrites a field whose `altered` is `true`* — is a rule about state
8
+ * transitions, and a rule spread across `useEffect` bodies cannot be asserted
9
+ * without a DOM. So every phase is a total function from the UI schema plus the
10
+ * current state to the next state, and `ParamForm.tsx` only renders the result.
11
+ *
12
+ * The four phase names are `w2-contracts.ts`'s `NgisParamFormPhase`, verbatim,
13
+ * for the reason the kickoff froze them: they are ArcGIS's (design-v2 App. E),
14
+ * not invented here, and a fifth phase would be a contract change.
15
+ *
16
+ * ## What each phase is responsible for
17
+ *
18
+ * | phase | function | rule it carries |
19
+ * |---|---|---|
20
+ * | `initialize` | {@link initializeParamForm} | seed from `default_value`; `altered: false`, `hasBeenValidated: false` |
21
+ * | `mutate` | {@link mutateParamForm} | `depends_on` visibility, then repopulation — **never clobbering `altered`** |
22
+ * | `validate-client` | {@link validateParamFormClient} | `required`, `value_type`, `choices`, `minimum`/`maximum` — errors block submit |
23
+ * | `validate-server` | `param-form-data.ts` | `host.models.validate`, debounced 400 ms |
24
+ *
25
+ * `mutate` runs **before** validation, always. Validating first would report a
26
+ * `required` error on a field the very next repopulate is about to hide.
27
+ *
28
+ * ## Hidden is not disabled, and a hidden field does not submit
29
+ *
30
+ * F14.10 forbids a `disabled` prop anywhere in plugin-ui, and F14.9's
31
+ * `depends_on` is *visibility*. A field the schema says is not applicable is
32
+ * therefore absent from client validation and absent from the submission — not
33
+ * present-and-empty. `NgisFieldState.visible` is the flag; nothing here ever
34
+ * produces a `disabled`.
35
+ */
36
+ import type { NgisToolUiParam, NgisToolUiSchema } from "../../types/analysis";
37
+ import { type NgisFieldState, type NgisParamValue, type NgisParamValues, type NgisRunSubmission } from "./w2-contracts";
38
+ /**
39
+ * A schema-shaped defect the form found in its own inputs — an unresolvable
40
+ * `depends_on`, a dependency cycle, an override naming a param that does not
41
+ * exist. **Surfaced, never masked** (the `ExtensionSettingsForm` rule): the
42
+ * form still renders, and the reason is rendered beside it.
43
+ */
44
+ export interface NgisParamFormDefect {
45
+ readonly kind: "unknown-dependency" | "dependency-cycle" | "unknown-hide" | "unknown-widget-override";
46
+ /** The param the defect is attached to, or the offending override key. */
47
+ readonly subject: string;
48
+ /** The other end of the relation, when there is one. */
49
+ readonly detail: string | null;
50
+ }
51
+ export interface NgisParamFormState {
52
+ /** Keyed by `NgisToolUiParam.name`. Every param of the schema is present. */
53
+ readonly fields: Readonly<Record<string, NgisFieldState>>;
54
+ /** The last phase that ran. Diagnostic; the form never branches on it. */
55
+ readonly phase: "initialize" | "mutate" | "validate-client";
56
+ /**
57
+ * Values kept because their field is `altered` even though the current option
58
+ * list no longer offers them (F14.9). Keyed by param name. Rendered as a
59
+ * notice; never a reason to clear the value.
60
+ */
61
+ readonly stale: Readonly<Record<string, string>>;
62
+ readonly defects: readonly NgisParamFormDefect[];
63
+ }
64
+ /** An author's per-param overrides. Unknown keys **survive and are surfaced**. */
65
+ export interface NgisParamFormOverrides {
66
+ /** Param names the author renders elsewhere (already bound by a flow port). */
67
+ readonly hide?: readonly string[];
68
+ /** Param name → widget key. Selects `textarea`/`extent`/`field-select`. */
69
+ readonly widgets?: Readonly<Record<string, NgisToolUiParam["widget"]>>;
70
+ }
71
+ export interface NgisParamFormInit extends NgisParamFormOverrides {
72
+ /**
73
+ * Wire-shaped seed values (F14.8's preset `params`, or a saved recipe's
74
+ * bindings). Applied over `default_value`, and coerced through
75
+ * `fromWireValue` so a form value and the value the server will see cannot
76
+ * disagree.
77
+ */
78
+ readonly seed?: Readonly<Record<string, string>>;
79
+ /** Option lists a widget resolves at runtime — `field-select`'s field names. */
80
+ readonly optionLists?: Readonly<Record<string, readonly string[]>>;
81
+ }
82
+ /**
83
+ * The spelling a `depends_on.equals[]` entry is compared against, and the one
84
+ * `toWireParams` will send. **A boolean parent is compared as its wire
85
+ * spelling** (`"true"`), which is why the authored probe pins
86
+ * `REFINE_PASSES.depends_on[0].equals === ["true"]` rather than a JSON `true`.
87
+ */
88
+ export declare function wireSpelling(value: NgisParamValue): string;
89
+ /**
90
+ * **initialize** (F14.9). Every field seeded from `default_value` — or from a
91
+ * preset/recipe seed — with `altered: false` and `hasBeenValidated: false`.
92
+ *
93
+ * `altered` stays `false` even for a seeded field, because F14.9's text is
94
+ * explicit and `altered` means *the user edited this*. The consequence is real
95
+ * and deliberate: a preset value in a field whose option list repopulates can
96
+ * be dropped by `mutate`, exactly as a `default_value` can. A launch that must
97
+ * not lose a preset value hides that param instead (`hide`), which is what
98
+ * F14.22's `hide={["ELEV"]}` is for.
99
+ *
100
+ * `mutate` is run immediately, so the state this returns already has correct
101
+ * visibility — a form is never rendered with a field the schema says is not
102
+ * applicable.
103
+ */
104
+ export declare function initializeParamForm(schema: NgisToolUiSchema, init?: NgisParamFormInit): NgisParamFormState;
105
+ /**
106
+ * **mutate** (F14.9). Runs after every interaction and **before** validation.
107
+ *
108
+ * Two steps, in this order:
109
+ *
110
+ * 1. **Visibility** from `depends_on` — see {@link resolveVisibility}.
111
+ * 2. **Repopulation.** A value the current option list no longer offers is
112
+ * dropped **only when the field is not `altered`**; an altered value is kept
113
+ * and reported in `stale` so the form can say so. That is F14.9's one hard
114
+ * rule, and it is the same decision Y2's `reconcileFieldValue` makes for
115
+ * `FieldSelect` — stated once, here, for every widget with a list.
116
+ *
117
+ * A multi-valued field (`multiple`) is repopulated **per member**: the members
118
+ * the new list still offers survive, and the ones it does not are dropped from
119
+ * an unaltered field or kept-and-reported on an altered one. Dropping the whole
120
+ * selection because one member went away would be a clobber by another name.
121
+ */
122
+ export declare function mutateParamForm(schema: NgisToolUiSchema, state: NgisParamFormState, optionLists?: Readonly<Record<string, readonly string[]>>): NgisParamFormState;
123
+ /** Record the user's edit. Sets `altered`, which `mutate` must then respect. */
124
+ export declare function setParamValue(schema: NgisToolUiSchema, state: NgisParamFormState, name: string, value: NgisParamValue, optionLists?: Readonly<Record<string, readonly string[]>>): NgisParamFormState;
125
+ /**
126
+ * The client-side rules, and **only** the rules F14.9 names: `required`,
127
+ * `value_type`, `choices` membership, `minimum`/`maximum`. Every one of them is
128
+ * read off the UI schema; none is inferred, and none duplicates a server rule
129
+ * the server is authoritative for (that is `validate-server`).
130
+ *
131
+ * A message is a **key under `GIS.pluginUi.form`** plus its placeholders,
132
+ * resolved by the component — not a sentence. Keeping the copy out of here is
133
+ * what lets this run in a node test with no `next-intl` provider.
134
+ */
135
+ export interface NgisParamFormError {
136
+ readonly param: string;
137
+ readonly rule: "required" | "value-type" | "choices" | "minimum" | "maximum";
138
+ readonly bound: number | null;
139
+ }
140
+ /** Hidden fields are skipped: either `depends_on` or the caller's `hide` override. */
141
+ export declare function validateParamFormClient(schema: NgisToolUiSchema, state: NgisParamFormState, hidden?: readonly string[]): readonly NgisParamFormError[];
142
+ /** Fold client errors back onto the fields, marking them `hasBeenValidated`. */
143
+ export declare function applyClientErrors(state: NgisParamFormState, errors: readonly NgisParamFormError[], render: (error: NgisParamFormError) => string): NgisParamFormState;
144
+ export declare function valuesOf(state: NgisParamFormState): NgisParamValues;
145
+ /**
146
+ * The Y3 → Y4 data contract (`NgisRunSubmission`), split by **role** exactly as
147
+ * `validate-execution` takes it: `params` is option-role only, `bindings` is
148
+ * input-role resource ports, `outputNames` is the `output-name` widgets.
149
+ * Merging them here would force Y4 to re-split with a second copy of the role
150
+ * rules — the class of duplication this stage exists to remove.
151
+ *
152
+ * A hidden or empty field contributes nothing. `multiple` inputs are split on
153
+ * `,`, which is the spelling F14.2 fixes for a comma-joined file-id list.
154
+ */
155
+ export declare function toSubmission(schema: NgisToolUiSchema, state: NgisParamFormState, hidden?: readonly string[]): NgisRunSubmission;
156
+ /**
157
+ * The params the visible fields would submit, in the wire shape
158
+ * `validate-execution` and the task API both take. Y3's pre-flight and Y4's
159
+ * submit go through `toWireParams` — this only chooses *which* keys.
160
+ */
161
+ export declare function visibleParamNames(schema: NgisToolUiSchema, state: NgisParamFormState): readonly string[];
162
+ /**
163
+ * F14.8's launch resolution. **A `$context:` reference resolving to `null`,
164
+ * `undefined` or `""` refuses the launch, naming the key** — it is never
165
+ * submitted as an empty string, because an empty string is a value the server
166
+ * would accept and act on.
167
+ *
168
+ * `$$` escapes a literal `$` (`parsePresetValue`, frozen at the kickoff so the
169
+ * rail launcher and a recipe reader parse alike).
170
+ *
171
+ * Keys are visited in **sorted order** so that a stanza with two unresolvable
172
+ * references always names the same one. A refusal message that changed between
173
+ * runs would be untriageable.
174
+ */
175
+ export type NgisPresetResolution = {
176
+ readonly kind: "resolved";
177
+ readonly params: Readonly<Record<string, string>>;
178
+ } | {
179
+ readonly kind: "refused";
180
+ /** The context key that resolved to nothing. */
181
+ readonly key: string;
182
+ /** The preset param that referenced it. */
183
+ readonly param: string;
184
+ };
185
+ /** What a context read can return — F13.2's `NgisContextValue` shape, widened. */
186
+ export type NgisPresetContextValue = string | number | boolean | null | readonly (string | number)[] | undefined;
187
+ export declare function resolvePresetParams(params: Readonly<Record<string, string>>, read: (key: string) => NgisPresetContextValue): NgisPresetResolution;
@@ -0,0 +1,88 @@
1
+ /**
2
+ * The widget registry (Stage 14 Y3 — **F14.2 / F14.9 / YD35**).
3
+ *
4
+ * **Total over `NgisWidgetKey`, with no fallback widget.** The type is
5
+ * `NgisWidgetRegistry<NgisParamWidget>` from `w2-contracts.ts`, i.e.
6
+ * `{ readonly [K in NgisWidgetKey]: T }` — so a key F14.2 grows and this file
7
+ * does not cover is a **compile** error, not a runtime `undefined` that renders
8
+ * nothing. That is the whole reason the registry is not a `Partial` plus a
9
+ * default text input: a silently-degraded control is indistinguishable from a
10
+ * control that works, and F14.2 says an unknown widget is a render-time error
11
+ * surfaced in place.
12
+ *
13
+ * YB1 emits only seven of the twelve keys in v1 (`field-select`, `extent`,
14
+ * `textarea`, `multi-select` and `resource-multi` are never emitted — `V-45`
15
+ * and `src/types/analysis.ts`'s own note). The other five are reachable through
16
+ * an author's `widgets` override and a recipe's promoted inputs, and they are
17
+ * exercised by `src/sdk/__fixtures__/ui-schema/authored/w2-dependency-probe.json`.
18
+ *
19
+ * ## Four of the twelve are Y2's pickers, not new controls
20
+ *
21
+ * `resource-select`, `resource-multi`, `field-select` and `extent` delegate to
22
+ * `ResourceSelect` / `FieldSelect` / `ExtentPicker`. Rebuilding those controls
23
+ * would give the form a second, subtly different notion of type widening, of
24
+ * field repopulation and of the two extent sources. They are rendered with
25
+ * **no `label`**, because the form's own field chrome carries the label — the
26
+ * picker's `PickerField` then contributes only its notice slot, which is where
27
+ * F14.1's widening and F14.9's kept-but-absent field surface.
28
+ *
29
+ * ## No `disabled`, anywhere
30
+ *
31
+ * F14.10: enablement is derived from port satisfaction and nothing else, and no
32
+ * plugin-ui component accepts a `disabled` prop. A widget with nothing to offer
33
+ * renders the reason instead of a dead control.
34
+ */
35
+ import type { ReactNode } from "react";
36
+ import type { NgisHostAPI } from "../../sdk/host-api";
37
+ import type { NgisBounds } from "../../sdk/map-api";
38
+ import type { NgisToolUiParam, NgisToolUiSchema } from "../../types/analysis";
39
+ import type { NgisFieldState, NgisParamValue, NgisParamValues, NgisWidgetRegistry } from "./w2-contracts";
40
+ /** Already-translated host copy a widget may render. Keys are `GIS.pluginUi.form`. */
41
+ export interface NgisParamWidgetLabels {
42
+ /** A `select` / `multi-select` whose schema carries no `choices`. */
43
+ noChoices: string;
44
+ /** Suffix hint on an `output-name` field. Receives `output_extension`. */
45
+ outputExtension: (extension: string) => string;
46
+ /** Clear control on a single-value list. */
47
+ clear: string;
48
+ /** Placeholder on a closed list. */
49
+ placeholder: string;
50
+ }
51
+ export interface NgisParamWidgetProps {
52
+ /** The 0.4 facade — always the one `createGisPluginHostFactory` minted. */
53
+ host: NgisHostAPI;
54
+ /** The whole schema: `field-select` needs to resolve its parent param. */
55
+ schema: NgisToolUiSchema;
56
+ param: NgisToolUiParam;
57
+ field: NgisFieldState;
58
+ /**
59
+ * Every field's current value. Only `field-select` reads it — it follows a
60
+ * *sibling* param's value (QGIS's `parentLayerParameterName`) — but it is on
61
+ * all twelve props rather than smuggled in through a module-level slot,
62
+ * because a widget is rendered by React later than the form's render pass and
63
+ * anything ambient would be stale by then.
64
+ */
65
+ values: NgisParamValues;
66
+ /** `<label for>` target. Seeded from the form's `id` plus the param name. */
67
+ controlId: string;
68
+ /** Class for portalled `<Select>` content — Radix lands on `document.body`. */
69
+ contentClassName?: string;
70
+ /** Exact layer selected from a duplicate-named resource choice, if any. */
71
+ onSameNamedLayerSelect?: (layerId: string) => void;
72
+ /** Records the edit. Sets `altered`, which F14.9's *mutate* must respect. */
73
+ onChange: (value: NgisParamValue) => void;
74
+ labels: NgisParamWidgetLabels;
75
+ }
76
+ /**
77
+ * A widget is a React component, so it may hold hooks — `ResourceSelect` and
78
+ * `FieldSelect` do. The registry stores components and `ParamForm` renders them
79
+ * as `<Widget …/>`; nothing calls one as a plain function.
80
+ */
81
+ export type NgisParamWidget = (props: NgisParamWidgetProps) => ReactNode;
82
+ /** `[west, south, east, north]` round-tripped through the comma-joined wire form. */
83
+ export declare function parseBounds(value: NgisParamValue): NgisBounds | null;
84
+ /**
85
+ * **Total over `NgisWidgetKey`. There is no fallback entry, and adding one would
86
+ * be a contract change** (F14.2, YD35).
87
+ */
88
+ export declare const NGIS_PARAM_WIDGETS: NgisWidgetRegistry<NgisParamWidget>;
@@ -0,0 +1,141 @@
1
+ /**
2
+ * The host reads behind the four W1 pickers (Stage 14 Y2).
3
+ *
4
+ * Every function here takes the 0.4 facade and returns plain data. Keeping them
5
+ * out of the components is what lets the wave's real exit criterion — "the
6
+ * pickers work **through `createGisPluginHostFactory`**" — be asserted offline:
7
+ * a node test builds the real, scope-enforced facade and calls these, so a
8
+ * scope refusal or a facet stub surfaces here exactly as it would in the
9
+ * browser. A facade assembled anywhere else is an ungated 0.4 surface every
10
+ * unit test calls green, and this stage has already hit that once.
11
+ *
12
+ * **`ModelSelect` reads `host.models.list`, not `host.models.search`.** F14.3
13
+ * adds the `search` leg and F14.24 ships it as a stub that *throws* until Y4
14
+ * implements it in W2 — deliberately, so a missing implementation cannot read
15
+ * as an empty result. `list` is the F13.2 surface that exists today, and its
16
+ * `query` filters client-side on name/description. The server-side
17
+ * `inputTypes`/`outputTypes` facets `search` will bring are therefore not
18
+ * expressible here; that is a W2 upgrade, not a W1 gap to paper over.
19
+ */
20
+ import type { NgisHostAPI } from "../../sdk/host-api";
21
+ import type { NgisDataType, NgisModelSearchRequest, NgisResourceField, NgisResourceType } from "../../sdk/facets/types";
22
+ import type { NgisBounds } from "../../sdk/map-api";
23
+ import { type NgisFieldReconcileResult, type NgisPickerOption, type NgisTypeWidening } from "./picker-model";
24
+ export interface NgisResourceLoad {
25
+ options: readonly NgisPickerOption[];
26
+ /** F14.1 — rendered beneath the control, never swallowed. */
27
+ widenings: readonly NgisTypeWidening[];
28
+ /** Cached metadata could not be refreshed; retry is meaningful. */
29
+ unavailableCount: number;
30
+ /** The scene references a Dataset that no longer exists. */
31
+ missingCount: number;
32
+ }
33
+ export declare function loadResourceOptions(host: NgisHostAPI, types?: readonly NgisDataType[], options?: {
34
+ refresh?: boolean;
35
+ }): Promise<NgisResourceLoad>;
36
+ export interface NgisModelFilter {
37
+ provider?: string;
38
+ category?: string;
39
+ modelboxId?: string;
40
+ query?: string;
41
+ }
42
+ export declare function loadModelOptions(host: NgisHostAPI, filter?: NgisModelFilter): Promise<readonly NgisPickerOption[]>;
43
+ /**
44
+ * `host.models.search` — the F14.3 leg, wired in W2 and reached from
45
+ * `ModelSelect` from W4 (**YD54**, the upgrade YD46 deferred out of W3).
46
+ *
47
+ * **It is a second leg, not a replacement, and the reason is a capability `search`
48
+ * does not have.** `GET /api/analysis/tools/search` declares no `category` and no
49
+ * `modelbox_id` filter — only `modelbox_path` — so `NgisModelSearchRequest`
50
+ * (`facets/types.ts:241-250`) has no member for either, while
51
+ * `NgisModelsFacet.list`'s filter has both and `ModelSelect` takes both as props.
52
+ * Routing everything through `search` would therefore have silently dropped two
53
+ * filters, which is worse than a picker that cannot filter by data type. The
54
+ * component keeps `list` for the catalog-narrowing path and reaches this one for
55
+ * the facet path, and the two arms are **mutually exclusive by type** so no author
56
+ * can write the combination that would have to drop something.
57
+ *
58
+ * One more asymmetry, recorded because it is invisible: `search` is **capped**.
59
+ * `limit` defaults to 10 server-side and the server maximum is 50
60
+ * (`facets/types.ts:243-244`), so a search hit list is a ranked top-N and never the
61
+ * catalog. `list` is unpaged. A caller that wants "every tool" wants `list`.
62
+ */
63
+ export declare function loadModelSearchOptions(host: NgisHostAPI, request: NgisModelSearchRequest): Promise<readonly NgisPickerOption[]>;
64
+ /**
65
+ * Why this is a union rather than an option list plus a boolean: each arm is a
66
+ * state the author has to be able to tell apart, and collapsing "no parent
67
+ * chosen" into "no fields" is the exact silent-empty-state failure the stage's
68
+ * stub rule exists to prevent.
69
+ */
70
+ export type NgisFieldLoad =
71
+ /** No parent value bound yet. */
72
+ {
73
+ kind: "no-parent";
74
+ }
75
+ /** The parent's file id matches no resource in this scene. */
76
+ | {
77
+ kind: "unresolved";
78
+ fileId: string;
79
+ }
80
+ /** Resolved, but fields are vector-only (`NgisResourceDetail.fields`). */
81
+ | {
82
+ kind: "not-vector";
83
+ type: NgisResourceType;
84
+ } | {
85
+ kind: "ready";
86
+ options: readonly NgisPickerOption[];
87
+ };
88
+ /**
89
+ * `FieldSelect` follows a **resource port**, whose value is a *file* id
90
+ * (F14.5), while `host.resources.get` takes a *scene-resource* id. The join is
91
+ * `NgisResourceSummary.fileId`, so this resolves the parent through
92
+ * `resources.list()` first. Reported as `unresolved` rather than guessed: a
93
+ * file id that is not in the scene is a real state (the resource was removed),
94
+ * and treating it as "no fields" would hide it.
95
+ */
96
+ export declare function loadFieldOptions(host: NgisHostAPI, parentFileId: string | null): Promise<NgisFieldLoad>;
97
+ /** The live state `FieldSelect` carries across a repopulate (F14.9). */
98
+ export interface NgisFieldSelection {
99
+ value: string | null;
100
+ /** `true` once the user has picked. Never reset by a repopulate. */
101
+ altered: boolean;
102
+ }
103
+ /** The fields a load actually produced; `[]` for every not-ready arm. */
104
+ export declare function fieldsOf(load: NgisFieldLoad): readonly NgisResourceField[];
105
+ /**
106
+ * **The whole of `FieldSelect`'s parent-change decision, in one function.**
107
+ *
108
+ * F14.9 asks for two things that pull against each other — repopulate on parent
109
+ * change, and never clobber a value the user altered — so the decision lives
110
+ * here rather than inside a `useEffect`: a rule spread across an effect body
111
+ * cannot be asserted without a DOM, and a rule that cannot be asserted is a rule
112
+ * that regresses. `FieldSelect` calls exactly this and renders the result, so
113
+ * the test and the component agree by construction rather than by resemblance.
114
+ *
115
+ * `load === null` means the new parent's fields are still in flight and
116
+ * **nothing is decided yet**: the value is passed through untouched. Reconciling
117
+ * against an empty list mid-load would clear an unaltered selection and then
118
+ * restore nothing, which reads as a flicker and is indistinguishable from a
119
+ * parent that genuinely has no fields.
120
+ */
121
+ export declare function reconcileFieldLoad(load: NgisFieldLoad | null, selection: NgisFieldSelection): NgisFieldReconcileResult;
122
+ export type NgisExtentMode = "draw" | "viewport";
123
+ /**
124
+ * Both modes ride the **ungated** 0.3 surface (F14.18), so neither needs a
125
+ * scope and neither can be refused by consent.
126
+ *
127
+ * `draw` is `host.api.draw.snapshot()`. It returned `[]` in every shipping host
128
+ * until this session widened `GisMapHandle` with `getDrawSnapshot()` — the
129
+ * producer (`DrawController`) and the SDK-side delegation (`map-api.ts`) both
130
+ * already existed; only the composed handle never surfaced it.
131
+ *
132
+ * `viewport` is `host.api.getMap()?.getBounds()`. **Not `camera.getView()`** —
133
+ * that returns `{ center, zoom, pitch, bearing }` and no extent can be derived
134
+ * from it without the viewport size the facade does not expose.
135
+ *
136
+ * `null` means "no extent available right now" and the caller must say which of
137
+ * the two reasons applies; it is never rendered as an empty bbox.
138
+ */
139
+ export declare function readExtent(host: NgisHostAPI, mode: NgisExtentMode): NgisBounds | null;
140
+ /** Whether a raw map is bound at all — the two `null`s of {@link readExtent}. */
141
+ export declare function hasBoundMap(host: NgisHostAPI): boolean;
@@ -0,0 +1,150 @@
1
+ /**
2
+ * The pure projections behind the four W1 pickers (Stage 14 Y2 — F14.1, F14.18).
3
+ *
4
+ * Nothing here imports React, a store or an API client: every function is a
5
+ * total mapping from data the host already has to data a picker renders. That
6
+ * is what keeps the components **host-renderable from declarative data** — the
7
+ * property Stage 15's worker tier depends on — and it is what makes the four
8
+ * behaviours that actually matter (type widening, field repopulation, the two
9
+ * extent sources) testable offline without a DOM.
10
+ *
11
+ * `src/components/` rather than `src/sdk/`: these read the SDK's frozen types
12
+ * but they are host UI, and `src/sdk/` is the module set `@ngis/plugin-sdk` is
13
+ * extracted from. `@ngis/plugin-ui` re-exports the components from here instead
14
+ * (F14.14 — "the components' runtime lives in the host bundle").
15
+ */
16
+ import { type NgisDataType, type NgisModelSearchResult, type NgisModelSummary, type NgisResourceField, type NgisResourceSummary, type NgisResourceType } from "../../sdk/facets/types";
17
+ import type { NgisBounds } from "../../sdk/map-api";
18
+ /** One row of any of the four pickers. Data only — no node, no callback. */
19
+ export interface NgisPickerOption {
20
+ /**
21
+ * Stable UI identity. Resource-layer rows use a layer id here while keeping
22
+ * `value` as the file id a tool parameter requires.
23
+ */
24
+ id?: string;
25
+ /** Concrete scene-layer identity, absent for the legacy resource fallback. */
26
+ sceneLayerId?: string;
27
+ /** The port value this row binds. */
28
+ value: string;
29
+ /** Display text. Never a message key: resource/tool/field names are data. */
30
+ label: string;
31
+ /** Secondary line (type, status, provider). `null` when there is nothing to add. */
32
+ hint: string | null;
33
+ /** Listed but not ready — greyed, still selectable, never hidden. */
34
+ muted: boolean;
35
+ /** A scene layer is currently hidden but remains a valid data input. */
36
+ hidden?: boolean;
37
+ /** Present only when another eligible layer has the same visible name. */
38
+ sameName?: boolean;
39
+ /** Group path, or a stable layer id when the layer has no group. */
40
+ disambiguation?: string;
41
+ }
42
+ /** One requested {@link NgisDataType} and the resource types it actually lists. */
43
+ export interface NgisTypeWidening {
44
+ requested: NgisDataType;
45
+ listed: readonly NgisResourceType[];
46
+ }
47
+ export interface NgisResourceTypeFilter {
48
+ /** What `host.resources.list({ types })` is asked for; `null` means no filter. */
49
+ resourceTypes: readonly NgisResourceType[] | null;
50
+ /** Empty when every requested type resolves to itself. */
51
+ widenings: readonly NgisTypeWidening[];
52
+ }
53
+ export declare function widenDataTypes(types?: readonly NgisDataType[]): NgisResourceTypeFilter;
54
+ /**
55
+ * **The value is the file id, not the scene-resource id** (F14.2's
56
+ * `resource-select`, F14.5's `resource` port kind): every dataset API and every
57
+ * tool binding takes `effectiveFileId`. `NgisResourceSummary` carries both, so
58
+ * this is a choice rather than a limitation, and it is the contract's choice.
59
+ */
60
+ export declare function resourceOptions(summaries: readonly NgisResourceSummary[]): NgisPickerOption[];
61
+ /**
62
+ * Project a file-typed resource input onto the scene layers a person actually
63
+ * recognises. The submitted value remains the resource's `effectiveFileId`;
64
+ * `id` is the layer identity used only by the picker UI. This intentionally
65
+ * keeps every layer when several render the same file.
66
+ *
67
+ * A source that predates the optional `summary.layers` projection falls back
68
+ * to the old resource row. A source that supplies `layers: []` has stated that
69
+ * the resource has no scene layer, so it is not selectable here.
70
+ */
71
+ export declare function resourceLayerOptions(summaries: readonly NgisResourceSummary[]): NgisPickerOption[];
72
+ /** A disabled tool stays listed and greyed — never hidden (the XD12 posture). */
73
+ export declare function modelOptions(models: readonly NgisModelSummary[]): NgisPickerOption[];
74
+ /**
75
+ * `host.models.search`'s hits, as options (Stage 14 Y7 — **YD54**, the `ModelSelect`
76
+ * upgrade YD46 deferred out of W3).
77
+ *
78
+ * **Two things `search` cannot tell us that `list` can, stated where they are
79
+ * lost.** `NgisModelSearchResult` (`src/sdk/facets/types.ts:253-277`) is an
80
+ * allowlist projection of `ToolSearchResult` and carries **no `enabled` flag** and
81
+ * **no `category`** — so on this path:
82
+ *
83
+ * - **`muted` is always `false`.** XD12's posture ("a disabled tool is listed
84
+ * and greyed, never hidden") cannot be honoured, because the search projection
85
+ * does not report enablement. Recovering it would mean one `models.get` per
86
+ * hit, which is the N+1 XD7 already refused for `resources.types`. Recorded as
87
+ * `V-287` rather than papered over with a guess.
88
+ * - **`hint` falls back to `modelboxPath`**, which `list` does not carry at all,
89
+ * so the two legs' hints differ in kind. That is honest: a search hit really is
90
+ * located by its modelbox path, and printing a `category` the projection never
91
+ * received would be an invention.
92
+ *
93
+ * Order is the server's — `search` ranks by `score` and the ranking *is* the
94
+ * answer, so re-sorting here would discard the only thing the facet added over
95
+ * `list`.
96
+ */
97
+ export declare function modelSearchOptions(results: readonly NgisModelSearchResult[]): NgisPickerOption[];
98
+ export declare function fieldOptions(fields: readonly NgisResourceField[]): NgisPickerOption[];
99
+ export interface NgisFieldReconcileInput {
100
+ /** The repopulated field list for the *new* parent. */
101
+ fields: readonly NgisResourceField[];
102
+ value: string | null;
103
+ /** `true` once the user has picked. F14.9's `altered` flag. */
104
+ altered: boolean;
105
+ }
106
+ export interface NgisFieldReconcileResult {
107
+ value: string | null;
108
+ /**
109
+ * The altered value that survived a repopulate without being present in the
110
+ * new list. Non-`null` means "render the mismatch"; it is never a reason to
111
+ * clear the value.
112
+ */
113
+ missing: string | null;
114
+ }
115
+ /**
116
+ * QGIS's `parentLayerParameterName` behaviour, plus F14.9's one hard rule: **a
117
+ * repopulate never overwrites a field whose `altered` is `true`.**
118
+ *
119
+ * So an altered value that the new parent does not carry is *kept and
120
+ * surfaced*, not silently cleared — the `ExtensionSettingsForm` rule ("refusals
121
+ * and approximations are surfaced, not masked"). Clearing it would look to the
122
+ * author like the host discarding their input; blocking on it is Y3's
123
+ * validation phase and Y5's enablement, not this component's call.
124
+ *
125
+ * A value the user never touched carries no such claim, so a repopulate clears
126
+ * it when the new parent cannot honour it.
127
+ */
128
+ export declare function reconcileFieldValue(input: NgisFieldReconcileInput): NgisFieldReconcileResult;
129
+ /**
130
+ * `NgisBounds` = `[west, south, east, north]` over every position in a raw
131
+ * Terra Draw snapshot, or `null` when the snapshot holds no usable coordinate.
132
+ *
133
+ * The snapshot is typed `unknown[]` all the way from
134
+ * `host.api.draw.snapshot()`, so this walks the coordinate tree structurally
135
+ * rather than trusting a GeoJSON type — a draw session can hold any geometry
136
+ * kind and F14 does not restrict which.
137
+ */
138
+ export declare function boundsOfDrawSnapshot(features: readonly unknown[]): NgisBounds | null;
139
+ /**
140
+ * A MapLibre `LngLatBounds` → `NgisBounds`.
141
+ *
142
+ * **CORRECTION carried from F14.18:** `camera.getView()` returns
143
+ * `{ center, zoom, pitch, bearing }`, **not** an extent, so viewport mode
144
+ * cannot be built from it. It needs the raw map's `getBounds()`, which the
145
+ * ungated `getMap()` already provides. Duck-typed so the projection stays
146
+ * testable without a GL context.
147
+ */
148
+ export declare function boundsFromLngLatBounds(value: unknown): NgisBounds | null;
149
+ /** Six decimals ≈ 0.1 m — enough to read, short enough to fit the control. */
150
+ export declare function formatBounds(bounds: NgisBounds): string;