@ngis/plugin-sdk 0.4.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +13 -0
- package/README.md +31 -0
- package/dist/index.cjs +76 -0
- package/dist/index.d.ts +77 -0
- package/dist/index.mjs +102 -0
- package/dist/src/api/analysis.d.ts +140 -0
- package/dist/src/api/resources.d.ts +530 -0
- package/dist/src/api/scenes.d.ts +251 -0
- package/dist/src/api/users.d.ts +22 -0
- package/dist/src/domain/algorithms/deck-layer-style.d.ts +34 -0
- package/dist/src/domain/algorithms/maplibre-style.d.ts +63 -0
- package/dist/src/domain/algorithms/raster-color-ramps.d.ts +37 -0
- package/dist/src/domain/algorithms/scene-layer-type.d.ts +25 -0
- package/dist/src/domain/algorithms/symbol-compile.d.ts +100 -0
- package/dist/src/domain/algorithms/symbol-params.d.ts +90 -0
- package/dist/src/domain/map/bounds.d.ts +8 -0
- package/dist/src/domain/map/constants.d.ts +40 -0
- package/dist/src/domain/map/layer-builders.d.ts +32 -0
- package/dist/src/domain/map/rendered-layers.d.ts +30 -0
- package/dist/src/domain/map/style-ready.d.ts +24 -0
- package/dist/src/domain/map/tile-url.d.ts +20 -0
- package/dist/src/domain/map/types.d.ts +128 -0
- package/dist/src/domain/models/deck-layer-style.d.ts +211 -0
- package/dist/src/domain/models/scene-layer-type.d.ts +23 -0
- package/dist/src/domain/models/symbol.d.ts +424 -0
- package/dist/src/lib/api.d.ts +82 -0
- package/dist/src/lib/auth-session.d.ts +22 -0
- package/dist/src/lib/raster-color-layer/RasterColorLayer.d.ts +48 -0
- package/dist/src/lib/raster-color-layer/TileGrid.d.ts +31 -0
- package/dist/src/lib/raster-color-layer/colorRamp.d.ts +18 -0
- package/dist/src/lib/raster-color-layer/shaders/raster-color.frag.d.ts +2 -0
- package/dist/src/lib/raster-color-layer/shaders/raster-color.vert.d.ts +2 -0
- package/dist/src/lib/raster-color-layer/zoomInterpolate.d.ts +2 -0
- package/dist/src/sdk/commands.d.ts +113 -0
- package/dist/src/sdk/context/keys.d.ts +148 -0
- package/dist/src/sdk/context/publishers.d.ts +198 -0
- package/dist/src/sdk/context/store.d.ts +78 -0
- package/dist/src/sdk/context/when.d.ts +69 -0
- package/dist/src/sdk/contributions/dispatch.d.ts +78 -0
- package/dist/src/sdk/contributions/panel-id.d.ts +25 -0
- package/dist/src/sdk/contributions/registry.d.ts +199 -0
- package/dist/src/sdk/contributions/symbol-renderers.d.ts +107 -0
- package/dist/src/sdk/contributions/types.d.ts +288 -0
- package/dist/src/sdk/contributions/views-bridge.d.ts +92 -0
- package/dist/src/sdk/define-plugin.d.ts +17 -0
- package/dist/src/sdk/facets/storage.d.ts +152 -0
- package/dist/src/sdk/facets/types.d.ts +370 -0
- package/dist/src/sdk/host-api.d.ts +256 -0
- package/dist/src/sdk/map-api.d.ts +288 -0
- package/dist/src/sdk/panels.d.ts +130 -0
- package/dist/src/sdk/plugin.d.ts +145 -0
- package/dist/src/sdk/rail-tools.d.ts +58 -0
- package/dist/src/sdk/scopes.d.ts +158 -0
- package/dist/src/systems/renderer/DeckSceneRenderer.d.ts +26 -0
- package/dist/src/systems/renderer/FlowFieldLayer.d.ts +68 -0
- package/dist/src/systems/renderer/LayerRendererRegistry.d.ts +132 -0
- package/dist/src/systems/renderer/authenticated-deck-load.d.ts +4 -0
- package/dist/src/systems/renderer/deck/deck-layer-builders.d.ts +29 -0
- package/dist/src/systems/renderer/deck/deck-layer-helpers.d.ts +21 -0
- package/dist/src/systems/renderer/deck/mvt-point-aggregation-layer.d.ts +107 -0
- package/dist/src/systems/renderer/flow-field/flow-field-resources.d.ts +84 -0
- package/dist/src/systems/renderer/flow-field/flow-field-shaders.d.ts +4 -0
- package/dist/src/systems/renderer/symbol/SpriteRegistry.d.ts +34 -0
- package/dist/src/systems/renderer/symbol/entries.d.ts +67 -0
- package/dist/src/types/analysis.d.ts +205 -0
- package/dist/src/types/common.d.ts +17 -0
- package/dist/src/types/extensions.d.ts +560 -0
- package/dist/src/types/gis.d.ts +242 -0
- package/dist/src/types/market-social.d.ts +378 -0
- package/package.json +47 -0
|
@@ -0,0 +1,242 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Shared UI types for the GIS workspace (`type=GIS` scratch scene).
|
|
3
|
+
* Kept separate from `analysis.ts` / `map-styling.ts` so the GIS surface can
|
|
4
|
+
* evolve without coupling to the other workspaces.
|
|
5
|
+
*/
|
|
6
|
+
/** Left dock tab: the layer stack vs. the scene's data (resources). */
|
|
7
|
+
export type GisLeftTab = "layers" | "data";
|
|
8
|
+
/**
|
|
9
|
+
* Lifecycle state of the scratch workspace:
|
|
10
|
+
* - `opening` — calling `GET /gis/workspace`
|
|
11
|
+
* - `prompt` — prior session has content; awaiting Restore vs. Start empty
|
|
12
|
+
* - `ready` — workspace scene loaded and rendered
|
|
13
|
+
* - `error` — open/load failed
|
|
14
|
+
*/
|
|
15
|
+
export type GisWorkspaceStatus = "opening" | "prompt" | "ready" | "error";
|
|
16
|
+
/** Camera state persisted to / restored from `scene.configJson`. */
|
|
17
|
+
export interface GisViewport {
|
|
18
|
+
center: [number, number];
|
|
19
|
+
zoom: number;
|
|
20
|
+
pitch?: number;
|
|
21
|
+
bearing?: number;
|
|
22
|
+
}
|
|
23
|
+
/**
|
|
24
|
+
* Protomaps hosted-style theme that backs the basemap switcher. Mapped straight
|
|
25
|
+
* to the `SceneMapCanvas` `basemap` prop (the Protomaps v5 theme segment).
|
|
26
|
+
*/
|
|
27
|
+
export type GisBasemap = "light" | "dark" | "white" | "grayscale" | "black";
|
|
28
|
+
/** Basemap options shown in the switcher, in display order. */
|
|
29
|
+
export declare const GIS_BASEMAPS: readonly GisBasemap[];
|
|
30
|
+
export declare const GIS_DEFAULT_BASEMAP: GisBasemap;
|
|
31
|
+
/**
|
|
32
|
+
* Terra Draw modes the rail drives directly (a subset of the library's modes).
|
|
33
|
+
* Each maps 1:1 to `TerraDraw.setMode(mode)`.
|
|
34
|
+
*/
|
|
35
|
+
export type GisDrawMode = "point" | "linestring" | "polygon" | "rectangle" | "circle" | "text" | "select";
|
|
36
|
+
/**
|
|
37
|
+
* The measurement flavours the rail can start. Distinct from `GisDrawMode`:
|
|
38
|
+
* measuring is an ephemeral overlay (its own isolated Terra Draw instance via
|
|
39
|
+
* `MaplibreMeasureControl`), never a drawn feature.
|
|
40
|
+
*/
|
|
41
|
+
export type GisMeasureKind = "distance" | "area";
|
|
42
|
+
/**
|
|
43
|
+
* What the cursor snaps to while drawing lines/polygons or measuring:
|
|
44
|
+
* `node` = existing vertices only, `edge` = nearest point on a segment only,
|
|
45
|
+
* `both` = vertices win over edges when both are in range.
|
|
46
|
+
*/
|
|
47
|
+
export type GisSnapTarget = "node" | "edge" | "both";
|
|
48
|
+
/**
|
|
49
|
+
* Shared snapping configuration applied to BOTH the draw and measure Terra Draw
|
|
50
|
+
* controls (via their `modeOptions` → `snapping.toCustom`). One config drives
|
|
51
|
+
* both tool sets, matching the reason measuring was migrated onto the library
|
|
52
|
+
* control. Note: Terra Draw only supports snapping on line/polygon modes, so
|
|
53
|
+
* rectangle/circle/point/text draw tools are unaffected.
|
|
54
|
+
*/
|
|
55
|
+
export interface GisSnapConfig {
|
|
56
|
+
/** Master on/off. When false, `toCustom` returns undefined (no snap). */
|
|
57
|
+
enabled: boolean;
|
|
58
|
+
/** Snap to vertices, edges, or both (node-priority). */
|
|
59
|
+
target: GisSnapTarget;
|
|
60
|
+
/** Snap radius in screen pixels. */
|
|
61
|
+
tolerancePx: number;
|
|
62
|
+
/**
|
|
63
|
+
* Scene-layer ids to snap to. `null` means "all currently visible scene
|
|
64
|
+
* layers"; a list narrows snapping to just those layers. Runtime-only (layer
|
|
65
|
+
* ids are scene-specific, so this is never persisted).
|
|
66
|
+
*/
|
|
67
|
+
layerIds: string[] | null;
|
|
68
|
+
/**
|
|
69
|
+
* Also snap across controls — drawing snaps to measured geometry and vice
|
|
70
|
+
* versa (each control reads the other's Terra Draw snapshot).
|
|
71
|
+
*/
|
|
72
|
+
includeDrawnFeatures: boolean;
|
|
73
|
+
}
|
|
74
|
+
/** Sensible starting snapping config: off, both targets, 10px, all layers. */
|
|
75
|
+
export declare const GIS_DEFAULT_SNAP_CONFIG: GisSnapConfig;
|
|
76
|
+
/**
|
|
77
|
+
* Stable ids for every catalog entry. Persisted inside the saved layout — treat
|
|
78
|
+
* this as an append-only enum (never renumber or repurpose an existing id).
|
|
79
|
+
*/
|
|
80
|
+
export type GisToolId = "draw-point" | "draw-line" | "draw-polygon" | "draw-rectangle" | "draw-circle" | "draw-text" | "select" | "delete-selection" | "delete-all" | "undo" | "redo" | "export" | "attribute-table" | "basemap" | "zoom-in" | "zoom-out" | "home" | "measure-distance" | "measure-area" | "snapping" | "code" | "workflow" | "tools" | "style" | "ai" | "plugins" | "measure" | "identify" | "select-by-attribute";
|
|
81
|
+
/**
|
|
82
|
+
* Items persisted in the customizable rail. `GisToolId` remains the frozen
|
|
83
|
+
* command catalog; host-only composite controls can ride on the rail without
|
|
84
|
+
* becoming invocable map tools.
|
|
85
|
+
*/
|
|
86
|
+
export type GisRailItemId = GisToolId | "multi-view" | "flow-field";
|
|
87
|
+
/** How a tool behaves when invoked from the rail. */
|
|
88
|
+
export type GisToolKind = "mode" | "measure" | "action" | "dropdown";
|
|
89
|
+
/** One-shot actions the rail fires on the map via the imperative handle
|
|
90
|
+
* (plus `code-panel`, which toggles the code dock instead of the map). */
|
|
91
|
+
export type GisToolAction = "delete-selection" | "delete-all" | "undo" | "redo" | "export" | "attribute-table" | "code-panel" | "workflow-panel" | "tools-panel" | "style-panel" | "ai-panel" | "zoom-in" | "zoom-out" | "home";
|
|
92
|
+
/** Catalog grouping used by the customize dialog. */
|
|
93
|
+
export type GisToolCategory = "draw" | "measure" | "edit" | "history" | "data" | "navigation" | "analysis";
|
|
94
|
+
/** Minimal slice of `WorkspacePanelRegistry` a rail tool's `invoke` needs. */
|
|
95
|
+
export interface GisRailPanelControl {
|
|
96
|
+
open: (slot: "right" | "bottom", id: string) => void;
|
|
97
|
+
toggle: (slot: "right" | "bottom", id: string) => void;
|
|
98
|
+
setActive: (slot: "right" | "bottom", id: string | null) => void;
|
|
99
|
+
getActive: (slot: "right" | "bottom") => string | null;
|
|
100
|
+
}
|
|
101
|
+
/**
|
|
102
|
+
* Minimal slice of the `NgisMapAPI` facade (`sdk/map-api.ts`) a rail tool's
|
|
103
|
+
* `invoke` needs — draw / measure / camera commands plus the panel registry.
|
|
104
|
+
* The concrete facade has a far larger surface; this is just what the rail
|
|
105
|
+
* exercises, so the real `NgisMapAPI` satisfies it structurally.
|
|
106
|
+
*/
|
|
107
|
+
export interface GisRailApi {
|
|
108
|
+
draw: {
|
|
109
|
+
setMode: (mode: GisDrawMode) => void;
|
|
110
|
+
cancelMode: () => void;
|
|
111
|
+
clear: () => void;
|
|
112
|
+
deleteSelection: () => void;
|
|
113
|
+
undo: () => void;
|
|
114
|
+
redo: () => void;
|
|
115
|
+
export: () => void;
|
|
116
|
+
getActiveMode: () => string | null;
|
|
117
|
+
};
|
|
118
|
+
measure: {
|
|
119
|
+
start: (kind: GisMeasureKind) => void;
|
|
120
|
+
clear: () => void;
|
|
121
|
+
};
|
|
122
|
+
camera: {
|
|
123
|
+
zoomIn: () => void;
|
|
124
|
+
zoomOut: () => void;
|
|
125
|
+
zoomHome: () => void;
|
|
126
|
+
/** Structural mirror of `NgisCameraFacet.getView` (additive widening,
|
|
127
|
+
* session P5 — the dynamic rail-tool path reads the current center).
|
|
128
|
+
* `null` until the facade's raw map is bound. */
|
|
129
|
+
getView: () => {
|
|
130
|
+
center: [number, number];
|
|
131
|
+
zoom: number;
|
|
132
|
+
pitch?: number;
|
|
133
|
+
bearing?: number;
|
|
134
|
+
} | null;
|
|
135
|
+
};
|
|
136
|
+
panels: GisRailPanelControl;
|
|
137
|
+
}
|
|
138
|
+
/** Minimal slice of `useGisRailStore` a rail tool's `invoke` needs. */
|
|
139
|
+
export interface GisRailStoreHandle {
|
|
140
|
+
getState: () => {
|
|
141
|
+
activeToolId: GisToolId | null;
|
|
142
|
+
actions: {
|
|
143
|
+
setActiveMode: (mode: GisDrawMode | null, toolId: GisToolId | null) => void;
|
|
144
|
+
setActiveMeasure: (kind: GisMeasureKind | null, toolId: GisToolId | null) => void;
|
|
145
|
+
clearActive: () => void;
|
|
146
|
+
};
|
|
147
|
+
};
|
|
148
|
+
}
|
|
149
|
+
/**
|
|
150
|
+
* ctx passed to `GisToolDef.invoke`. v1 (C8) exposes the `NgisMapAPI` facade
|
|
151
|
+
* (replacing C7's raw `{ mapHandle }` ref — the facade subsumes it and adds the
|
|
152
|
+
* panel registry the old `onToggleAttributeTable`/`onToggleWorkflow` callbacks
|
|
153
|
+
* used to cover) and the rail store (for the active-tool lit-state bookkeeping
|
|
154
|
+
* a toggle needs). Both fields are structural, so future widening stays additive.
|
|
155
|
+
*/
|
|
156
|
+
export interface GisRailInvokeContext {
|
|
157
|
+
api: GisRailApi;
|
|
158
|
+
railStore: GisRailStoreHandle;
|
|
159
|
+
}
|
|
160
|
+
/** A single catalog entry. `icon` is a lucide name resolved in-component. */
|
|
161
|
+
export interface GisToolDef {
|
|
162
|
+
id: GisToolId;
|
|
163
|
+
icon: string;
|
|
164
|
+
/** i18n key suffix under `GIS.rail.tools.*`. */
|
|
165
|
+
labelKey: string;
|
|
166
|
+
category: GisToolCategory;
|
|
167
|
+
kind: GisToolKind;
|
|
168
|
+
/** For `kind: "mode"` — the Terra Draw mode to activate. */
|
|
169
|
+
drawMode?: GisDrawMode;
|
|
170
|
+
/** For `kind: "measure"` — the measurement flavour to start. */
|
|
171
|
+
measureKind?: GisMeasureKind;
|
|
172
|
+
/** For `kind: "action"` — the imperative handle method to call. */
|
|
173
|
+
action?: GisToolAction;
|
|
174
|
+
/** Whether the tool unfolds the contextual readout strip while active. */
|
|
175
|
+
hasContextStrip?: boolean;
|
|
176
|
+
/** Disabled "coming soon" catalog entry — rendered greyed, not invocable. */
|
|
177
|
+
disabled?: boolean;
|
|
178
|
+
/**
|
|
179
|
+
* Hide this tool whenever its backing panel is not registered. The tool id
|
|
180
|
+
* remains in persisted layouts so disabling a plugin is reversible: when
|
|
181
|
+
* the panel registers again, its button returns to the user's saved place.
|
|
182
|
+
*/
|
|
183
|
+
requiredPanel?: {
|
|
184
|
+
slot: "right" | "bottom";
|
|
185
|
+
id: string;
|
|
186
|
+
};
|
|
187
|
+
/** Invoked by `GisCommandRail`'s dispatcher in place of the old hardcoded
|
|
188
|
+
* switch. Absent for `kind: "dropdown"` entries (`basemap`/`snapping`),
|
|
189
|
+
* which render their own inline UI and never go through the dispatcher. */
|
|
190
|
+
invoke?: (ctx: GisRailInvokeContext) => void;
|
|
191
|
+
}
|
|
192
|
+
export interface GisHostRailItemDef {
|
|
193
|
+
id: Exclude<GisRailItemId, GisToolId>;
|
|
194
|
+
icon: string;
|
|
195
|
+
/** i18n key suffix under `GIS.rail.tools.*`. */
|
|
196
|
+
labelKey: string;
|
|
197
|
+
category: GisToolCategory;
|
|
198
|
+
kind: "host";
|
|
199
|
+
}
|
|
200
|
+
export type GisRailItemDef = GisToolDef | GisHostRailItemDef;
|
|
201
|
+
/** One user-named cluster of tools on the rail. */
|
|
202
|
+
export interface GisRailGroup {
|
|
203
|
+
id: string;
|
|
204
|
+
/** Custom name; when null, render the default i18n label for `nameKey`. */
|
|
205
|
+
name: string | null;
|
|
206
|
+
/** Default-group i18n key under `GIS.rail.groups.*` (seeded groups only). */
|
|
207
|
+
nameKey?: string;
|
|
208
|
+
/** Ordered, visible tools in this group. */
|
|
209
|
+
toolIds: GisRailItemId[];
|
|
210
|
+
}
|
|
211
|
+
/**
|
|
212
|
+
* Per-user persisted rail layout. A tool is "pinned to the bar" exactly when it
|
|
213
|
+
* lives in a group; every other catalog tool stays reachable from the "More
|
|
214
|
+
* tools" overflow menu. Deleting a group simply returns its tools to overflow —
|
|
215
|
+
* nothing is ever lost.
|
|
216
|
+
*/
|
|
217
|
+
export interface GisRailLayout {
|
|
218
|
+
version: number;
|
|
219
|
+
groups: GisRailGroup[];
|
|
220
|
+
}
|
|
221
|
+
/** Live measurement readout shown in the rail's contextual strip. */
|
|
222
|
+
export interface GisMeasureSummary {
|
|
223
|
+
/** Vertex count of the measured path / polygon ring. */
|
|
224
|
+
points: number;
|
|
225
|
+
/** Total path length (distance) or polygon perimeter (area), in meters. */
|
|
226
|
+
lengthMeters: number;
|
|
227
|
+
/** Polygon area in square meters (0 for a distance measurement). */
|
|
228
|
+
areaSqMeters: number;
|
|
229
|
+
}
|
|
230
|
+
/** Live geometry readout shown in the rail's contextual strip. */
|
|
231
|
+
export interface GisDrawSummary {
|
|
232
|
+
/** Number of user-drawn features currently on the map. */
|
|
233
|
+
features: number;
|
|
234
|
+
/** Total vertex count across those features. */
|
|
235
|
+
vertices: number;
|
|
236
|
+
/** Total line length in meters (linestrings). */
|
|
237
|
+
lengthMeters: number;
|
|
238
|
+
/** Total polygon area in square meters. */
|
|
239
|
+
areaSqMeters: number;
|
|
240
|
+
/** Scene features intersected by the drawn geometry (draw-to-query). */
|
|
241
|
+
matched: number;
|
|
242
|
+
}
|
|
@@ -0,0 +1,378 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* DTOs for the Stage 15 **community market** — ratings, comments, abuse reports,
|
|
3
|
+
* install counts, the recipe flywheel counter, extension packs, and the
|
|
4
|
+
* verified-publisher flag.
|
|
5
|
+
*
|
|
6
|
+
* **Authored at the W1 kickoff and read-only to the wave** (the YD48 rule: a
|
|
7
|
+
* signature is not a seam — shared shapes are implemented once, by the kickoff,
|
|
8
|
+
* and no session re-declares one). ZB1 owns the ratings/comments/reports/
|
|
9
|
+
* install-count/stats wire; ZB2 owns packs; ZB3 owns the verified-publisher
|
|
10
|
+
* flag. Z4/Z5/Z6 consume these types and the byte-identical fixtures beside
|
|
11
|
+
* them (`src/types/__fixtures__/market-social/`) — never invented shapes.
|
|
12
|
+
*
|
|
13
|
+
* Field names are the **wire** names; nothing is renamed to taste. Java
|
|
14
|
+
* `Instant` serializes as an ISO-8601 `string`. The paging envelope is the one
|
|
15
|
+
* that already exists — {@link PageResult} — never a second one.
|
|
16
|
+
*
|
|
17
|
+
* Contract: `docs/open_ecosystem_stage/README.md` → **F15.5** (ratings/comments/
|
|
18
|
+
* reports), **F15.6** (install counts + flywheel), **F15.7** (packs), **F15.11**
|
|
19
|
+
* (verified publisher), **F15.18** (this module and its fixture set).
|
|
20
|
+
*
|
|
21
|
+
* Every surface here is **G-B**: authenticated, LAN-only, no anonymous row. Every
|
|
22
|
+
* record carries the authenticated user id.
|
|
23
|
+
*/
|
|
24
|
+
import type { PageResult } from "./common";
|
|
25
|
+
/**
|
|
26
|
+
* Integer 1–5. Modelled as a union rather than `number` so a client cannot post
|
|
27
|
+
* a 0 or a 6 past the type checker — the server rejects them either way, but the
|
|
28
|
+
* refusal should not need a round trip to discover.
|
|
29
|
+
*/
|
|
30
|
+
export type RatingValue = 1 | 2 | 3 | 4 | 5;
|
|
31
|
+
/** Server-side cap on {@link RatingDto.text}, measured on the serialized UTF-8. */
|
|
32
|
+
export declare const RATING_TEXT_MAX_LENGTH = 2000;
|
|
33
|
+
/**
|
|
34
|
+
* One user's rating of one extension.
|
|
35
|
+
*
|
|
36
|
+
* **Installers only at creation** — the server requires an install row for
|
|
37
|
+
* (user, extension) at write time. It is *not* required afterward: a rating
|
|
38
|
+
* **survives uninstall**, because the recorded experience was real (F15.5).
|
|
39
|
+
* One per (user, extension), a UNIQUE constraint rather than a convention, and
|
|
40
|
+
* **editable in place** — an opinion is not an immutable artifact, so there is
|
|
41
|
+
* no version here.
|
|
42
|
+
*/
|
|
43
|
+
export interface RatingDto {
|
|
44
|
+
extId: string;
|
|
45
|
+
/** The rating's author. Always present — there is no anonymous surface. */
|
|
46
|
+
userId: string;
|
|
47
|
+
value: RatingValue;
|
|
48
|
+
/** `null` when the rater left a score and no words. */
|
|
49
|
+
text: string | null;
|
|
50
|
+
createdAt: string;
|
|
51
|
+
/** Equal to {@link createdAt} until the first in-place edit. */
|
|
52
|
+
updatedAt: string;
|
|
53
|
+
}
|
|
54
|
+
/** `POST`/`PUT /api/extensions/{extId}/rating` — the caller is the authenticated user. */
|
|
55
|
+
export interface RatingUpsertRequest {
|
|
56
|
+
value: RatingValue;
|
|
57
|
+
/** Omit or `null` to score without text; over {@link RATING_TEXT_MAX_LENGTH} is a `400`. */
|
|
58
|
+
text?: string | null;
|
|
59
|
+
}
|
|
60
|
+
/**
|
|
61
|
+
* Aggregate shown on the market card and detail page.
|
|
62
|
+
*
|
|
63
|
+
* **Shown from the first rating** — no display threshold. A LAN community is too
|
|
64
|
+
* small for one to buy anything, and every surface is authenticated anyway
|
|
65
|
+
* (F15.5). `mean` is rounded to one decimal server-side; `count` 0 means nothing
|
|
66
|
+
* has been rated and `mean` is `null`, never `0` (a 0 would render as a real
|
|
67
|
+
* score).
|
|
68
|
+
*/
|
|
69
|
+
export interface RatingAggregateDto {
|
|
70
|
+
extId: string;
|
|
71
|
+
count: number;
|
|
72
|
+
mean: number | null;
|
|
73
|
+
}
|
|
74
|
+
/**
|
|
75
|
+
* **Flat v1 — no threads.** There is deliberately no `parentId`: adding one
|
|
76
|
+
* later is additive, while shipping a tree that nothing renders is not.
|
|
77
|
+
*
|
|
78
|
+
* Author may edit and delete their own; **ADMIN may hard-delete any**, which
|
|
79
|
+
* writes an audit event recording actor and target. Moderation is
|
|
80
|
+
* report-driven, not pre-moderated — a comment posts immediately under G-B.
|
|
81
|
+
*/
|
|
82
|
+
export interface CommentDto {
|
|
83
|
+
id: string;
|
|
84
|
+
extId: string;
|
|
85
|
+
authorId: string;
|
|
86
|
+
body: string;
|
|
87
|
+
createdAt: string;
|
|
88
|
+
updatedAt: string;
|
|
89
|
+
}
|
|
90
|
+
/** `POST /api/extensions/{extId}/comments` and the author's in-place edit. */
|
|
91
|
+
export interface CommentUpsertRequest {
|
|
92
|
+
body: string;
|
|
93
|
+
}
|
|
94
|
+
/** What a report points at. An extension, or one social row on it. */
|
|
95
|
+
export type ReportTargetType = "EXTENSION" | "COMMENT" | "RATING";
|
|
96
|
+
/**
|
|
97
|
+
* `OPEN → RESOLVED | DISMISSED`. Transitions are **ADMIN-only**, and the two
|
|
98
|
+
* terminal states are terminal: a fresh concern about the same target is a new
|
|
99
|
+
* report, never a reopen.
|
|
100
|
+
*/
|
|
101
|
+
export type ReportStatus = "OPEN" | "RESOLVED" | "DISMISSED";
|
|
102
|
+
/**
|
|
103
|
+
* One abuse report.
|
|
104
|
+
*
|
|
105
|
+
* **One OPEN report per (reporter, target)** — a duplicate submission returns
|
|
106
|
+
* the existing row rather than minting queue noise, so a client may treat
|
|
107
|
+
* submit as idempotent while the first report is still open.
|
|
108
|
+
*/
|
|
109
|
+
export interface ReportDto {
|
|
110
|
+
id: string;
|
|
111
|
+
targetType: ReportTargetType;
|
|
112
|
+
targetId: string;
|
|
113
|
+
/** The extension the target belongs to — present even when the target is a comment
|
|
114
|
+
* or rating, so the ADMIN queue can group without a second lookup. */
|
|
115
|
+
extId: string;
|
|
116
|
+
reporterId: string;
|
|
117
|
+
/** Required and non-blank at submission. */
|
|
118
|
+
reason: string;
|
|
119
|
+
status: ReportStatus;
|
|
120
|
+
/** ADMIN's optional note, written at the transition. `null` while `OPEN`. */
|
|
121
|
+
resolutionNote: string | null;
|
|
122
|
+
/** The ADMIN who moved it out of `OPEN`. `null` while `OPEN`. */
|
|
123
|
+
resolvedBy: string | null;
|
|
124
|
+
resolvedAt: string | null;
|
|
125
|
+
createdAt: string;
|
|
126
|
+
}
|
|
127
|
+
/** `POST /api/extensions/{extId}/reports`. */
|
|
128
|
+
export interface ReportCreateRequest {
|
|
129
|
+
targetType: ReportTargetType;
|
|
130
|
+
targetId: string;
|
|
131
|
+
reason: string;
|
|
132
|
+
}
|
|
133
|
+
/** `POST /api/extensions/reports/{id}/resolve` \| `/dismiss` — ADMIN only. */
|
|
134
|
+
export interface ReportTransitionRequest {
|
|
135
|
+
resolutionNote?: string | null;
|
|
136
|
+
}
|
|
137
|
+
/**
|
|
138
|
+
* The ADMIN report queue — **paged from day one**, the lesson of the review
|
|
139
|
+
* queue that shipped as a plain unpaged array (Track B substrate, item 4).
|
|
140
|
+
*/
|
|
141
|
+
export type ReportQueuePage = PageResult<ReportDto>;
|
|
142
|
+
/**
|
|
143
|
+
* `GET /api/extensions/stats/recipes` — **the exit criterion's "community-recipe
|
|
144
|
+
* counting exists"**, open to any authenticated caller (it exposes no ownership
|
|
145
|
+
* detail, only cohort totals).
|
|
146
|
+
*
|
|
147
|
+
* Counts **PUBLISHED recipe-bearing versions**: a `tool`-kind reference version
|
|
148
|
+
* whose referenced tool has `provider = "workflow"`. Both granularities ship
|
|
149
|
+
* because they answer different questions — `*Versions` measures activity,
|
|
150
|
+
* `*Extensions` measures breadth.
|
|
151
|
+
*
|
|
152
|
+
* **Cohort is the owner's role at query time**, which is documented rather than
|
|
153
|
+
* denormalized: an owner promoted to ADMIN reclassifies their history. Accepted
|
|
154
|
+
* at LAN scale. The flywheel *signal* (community > 1× first-party) is measured
|
|
155
|
+
* at +6 months; this stage owes only the counting.
|
|
156
|
+
*/
|
|
157
|
+
export interface RecipeStatsDto {
|
|
158
|
+
adminVersions: number;
|
|
159
|
+
communityVersions: number;
|
|
160
|
+
adminExtensions: number;
|
|
161
|
+
communityExtensions: number;
|
|
162
|
+
/** When the counts were taken, ISO-8601. */
|
|
163
|
+
asOf: string;
|
|
164
|
+
}
|
|
165
|
+
/**
|
|
166
|
+
* The additive fields Stage 15 lands on the **existing** `ExtensionSummaryDto` /
|
|
167
|
+
* `ExtensionDetailDto`. Declared as a separate interface here so ZB1's additive
|
|
168
|
+
* change and Z4's consumption agree before either is written; the shipped
|
|
169
|
+
* `ExtensionSummary` in `./extensions` gains these members, it is not replaced.
|
|
170
|
+
*
|
|
171
|
+
* `installCount` is `COUNT(ngis_extension_install WHERE enabled = true)` — a
|
|
172
|
+
* disabled install is not adoption, and uninstall already deletes the row.
|
|
173
|
+
* Shown from **1**, no threshold.
|
|
174
|
+
*/
|
|
175
|
+
export interface ExtensionSocialFields {
|
|
176
|
+
installCount: number;
|
|
177
|
+
/** `null` until the first rating — never a synthetic 0-star aggregate. */
|
|
178
|
+
rating: RatingAggregateDto | null;
|
|
179
|
+
/** F15.11 — an operator attestation about the *publisher*, carried on the extension
|
|
180
|
+
* because that is where the market renders it. Confers zero capability. */
|
|
181
|
+
publisherVerified: boolean;
|
|
182
|
+
}
|
|
183
|
+
/**
|
|
184
|
+
* What the badge attests on a LAN deployment: **the operator knows who this
|
|
185
|
+
* publisher is.** Identity attestation by the deployment's operator, nothing
|
|
186
|
+
* more — not code review, not an endorsement, and it grants no extra capability.
|
|
187
|
+
*
|
|
188
|
+
* ADMIN-granted and revocable; there is **no self-serve path this stage**
|
|
189
|
+
* (DNS-TXT-style self-verification is a G-C-era design, deliberately out).
|
|
190
|
+
* Stored in `ngis-extension` riding migration **V5** — never in `ngis-auth`: no
|
|
191
|
+
* cross-service migration is declared for this stage, and this is a market
|
|
192
|
+
* concept, not an account one.
|
|
193
|
+
*/
|
|
194
|
+
export interface VerifiedPublisherDto {
|
|
195
|
+
userId: string;
|
|
196
|
+
verified: boolean;
|
|
197
|
+
/** The ADMIN who last granted or revoked. */
|
|
198
|
+
grantedBy: string;
|
|
199
|
+
grantedAt: string;
|
|
200
|
+
/** Free-text operator note recording *how* identity was established. */
|
|
201
|
+
note: string | null;
|
|
202
|
+
}
|
|
203
|
+
/** `PUT /api/extensions/publishers/{userId}/verified` — ADMIN only. */
|
|
204
|
+
export interface VerifiedPublisherRequest {
|
|
205
|
+
verified: boolean;
|
|
206
|
+
note?: string | null;
|
|
207
|
+
}
|
|
208
|
+
/**
|
|
209
|
+
* Packs are **new tables** (migration V6), never a fourth `ExtensionKind`.
|
|
210
|
+
* A kind would ripple through every wire enum, the frozen
|
|
211
|
+
* `PackageManifestValidator`, the G-D `kind === "ui-plugin"` filters and every
|
|
212
|
+
* client narrowing on the three-kind union — for a thing that is a *grouping*,
|
|
213
|
+
* not an artifact.
|
|
214
|
+
*
|
|
215
|
+
* The ACL vocabulary is the extension one **minus `GROUP`** (F15.16: no
|
|
216
|
+
* group-membership service exists platform-wide to resolve against).
|
|
217
|
+
*/
|
|
218
|
+
export type PackVisibility = "PRIVATE" | "PUBLIC";
|
|
219
|
+
/** One curated member. Order is the pack author's; it is presentation, not dependency. */
|
|
220
|
+
export interface PackMemberDto {
|
|
221
|
+
extId: string;
|
|
222
|
+
/** 0-based position in the curated list. */
|
|
223
|
+
position: number;
|
|
224
|
+
/** Denormalized for rendering the pack without N detail fetches. */
|
|
225
|
+
displayName: string;
|
|
226
|
+
kind: string;
|
|
227
|
+
}
|
|
228
|
+
/**
|
|
229
|
+
* A pack **curates identity, not versions**: it pins no member version. Each
|
|
230
|
+
* member installs under the user's normal version policy (`LATEST` by default,
|
|
231
|
+
* pin-able per install afterward) exactly as a direct install would — versions
|
|
232
|
+
* stay the registry's business.
|
|
233
|
+
*/
|
|
234
|
+
export interface PackDto {
|
|
235
|
+
packId: string;
|
|
236
|
+
ownerId: string;
|
|
237
|
+
displayName: string;
|
|
238
|
+
summary: string | null;
|
|
239
|
+
visibility: PackVisibility;
|
|
240
|
+
members: PackMemberDto[];
|
|
241
|
+
createdAt: string;
|
|
242
|
+
updatedAt: string;
|
|
243
|
+
}
|
|
244
|
+
/** Per-member result of a pack install. */
|
|
245
|
+
export type PackInstallStatus = "INSTALLED" | "ALREADY_INSTALLED" | "FAILED";
|
|
246
|
+
/**
|
|
247
|
+
* One member's outcome.
|
|
248
|
+
*
|
|
249
|
+
* **A member the caller cannot see FAILs with a reason — a pack is never an ACL
|
|
250
|
+
* bypass.** That is the standing install-is-adoption-never-bypass rule extended
|
|
251
|
+
* to packs, and it is why this is a per-member report rather than a single
|
|
252
|
+
* status code.
|
|
253
|
+
*/
|
|
254
|
+
export interface PackInstallMemberResultDto {
|
|
255
|
+
extId: string;
|
|
256
|
+
status: PackInstallStatus;
|
|
257
|
+
/** Present iff `status === "FAILED"`. */
|
|
258
|
+
reason: string | null;
|
|
259
|
+
}
|
|
260
|
+
/**
|
|
261
|
+
* **No rollback.** Members that installed stay installed when a sibling fails;
|
|
262
|
+
* existing installs are left untouched (`ALREADY_INSTALLED`). Partial success is
|
|
263
|
+
* the designed outcome, not an error path — the UI reports per member.
|
|
264
|
+
*/
|
|
265
|
+
export interface PackInstallResultDto {
|
|
266
|
+
packId: string;
|
|
267
|
+
results: PackInstallMemberResultDto[];
|
|
268
|
+
}
|
|
269
|
+
/**
|
|
270
|
+
* `POST /api/extensions/scene/{sceneId}/template/install` (F15.8) — adopting a
|
|
271
|
+
* scene template's composition into the caller's own library.
|
|
272
|
+
*
|
|
273
|
+
* **The same per-member report as a pack, deliberately.** The Java record reuses
|
|
274
|
+
* `PackInstallMemberResultDto` verbatim (`SceneTemplateInstallResultDto`), because
|
|
275
|
+
* the act is identical — adopt a curated set, per member, never bypassing an ACL —
|
|
276
|
+
* and only the curation channel differs: a pack is a curated `extId` list, a scene
|
|
277
|
+
* template is a scene's E6 composition. A member the caller cannot see FAILs.
|
|
278
|
+
*
|
|
279
|
+
* Authored at the **W4 kickoff**, not by a session: `V15-095` asked for this pair
|
|
280
|
+
* at the W1 close and `V15-287` recorded at the W3 close that neither the Z5
|
|
281
|
+
* kickoff nor the orchestrator had made it, so the leg had shipped in W1 and gone
|
|
282
|
+
* two full waves with no frontend type, no fixture and no client. There is still
|
|
283
|
+
* **no UI caller** — that is a market-detail surface and no W4 session owns one.
|
|
284
|
+
*/
|
|
285
|
+
export interface SceneTemplateInstallResultDto {
|
|
286
|
+
sceneId: string;
|
|
287
|
+
results: PackInstallMemberResultDto[];
|
|
288
|
+
}
|
|
289
|
+
/** `POST`/`PUT /api/extensions/packs` — members given as an ordered `extId` list. */
|
|
290
|
+
export interface PackUpsertRequest {
|
|
291
|
+
displayName: string;
|
|
292
|
+
summary?: string | null;
|
|
293
|
+
visibility: PackVisibility;
|
|
294
|
+
extIds: string[];
|
|
295
|
+
}
|
|
296
|
+
/**
|
|
297
|
+
* Which of F15.10's three static checks produced a finding.
|
|
298
|
+
*
|
|
299
|
+
* The list is closed on purpose: F15.10 names exactly three checks, and a
|
|
300
|
+
* reviewer UI that renders per-check sections needs to know the sections before
|
|
301
|
+
* a scan runs. A fourth check is a contract change, not a new string.
|
|
302
|
+
*/
|
|
303
|
+
export type ScanCheckId = "imports" | "secrets" | "permissions";
|
|
304
|
+
/**
|
|
305
|
+
* **There is no blocking severity, and that is the contract, not an oversight.**
|
|
306
|
+
* F15.10 makes all three checks advisory — E4 human review stays the
|
|
307
|
+
* decision-maker, because a heuristic that blocks an upload gives a false
|
|
308
|
+
* positive no appeal path. The existing hard refusals (malformed manifest,
|
|
309
|
+
* oversized/malformed bundle) are unchanged and live nowhere near this type.
|
|
310
|
+
*/
|
|
311
|
+
export type ScanSeverity = "INFO" | "WARN";
|
|
312
|
+
/**
|
|
313
|
+
* One scanner finding.
|
|
314
|
+
*
|
|
315
|
+
* `code` is the stable machine identity a UI keys its copy off; `message` is the
|
|
316
|
+
* server's English fallback for a code the client does not know. Render `code`
|
|
317
|
+
* when you have a translation, `message` when you do not — never concatenate.
|
|
318
|
+
*/
|
|
319
|
+
export interface ScanFindingDto {
|
|
320
|
+
check: ScanCheckId;
|
|
321
|
+
severity: ScanSeverity;
|
|
322
|
+
/**
|
|
323
|
+
* Stable, screaming-snake. The set ZB3 mints at W2:
|
|
324
|
+
* `IMPORT_NOT_ALLOWLISTED` · `EVAL_CALL` · `NEW_FUNCTION_CALL` ·
|
|
325
|
+
* `SECRET_PATTERN` · `SECRET_ENTROPY`. A client must tolerate an unknown code
|
|
326
|
+
* (render `message`) rather than narrow on this being exhaustive.
|
|
327
|
+
*/
|
|
328
|
+
code: string;
|
|
329
|
+
message: string;
|
|
330
|
+
/** 1-based line in `bundle.mjs` when the finding is positional; `null` otherwise. */
|
|
331
|
+
line: number | null;
|
|
332
|
+
/**
|
|
333
|
+
* The offending specifier or a **redacted** excerpt — never the matched secret
|
|
334
|
+
* itself, and truncated server-side. A scan result is readable by every ADMIN
|
|
335
|
+
* reviewer, so it must not become a second place credentials live.
|
|
336
|
+
*/
|
|
337
|
+
evidence: string | null;
|
|
338
|
+
}
|
|
339
|
+
/**
|
|
340
|
+
* Scope delta against the previous PUBLISHED version of the same extension —
|
|
341
|
+
* the reviewer blocker F15.10 exists to remove (today a scope-widening bump has
|
|
342
|
+
* to be caught by diffing two JSON bodies by hand).
|
|
343
|
+
*
|
|
344
|
+
* Computed server-side against **PUBLISHED** only: a DRAFT or REJECTED
|
|
345
|
+
* predecessor is not what users are running, so diffing against it would show a
|
|
346
|
+
* widening that never reached anyone.
|
|
347
|
+
*/
|
|
348
|
+
export interface ScanPermissionDiffDto {
|
|
349
|
+
/** `null` when no PUBLISHED predecessor exists — a first publish adds everything. */
|
|
350
|
+
previousVersion: string | null;
|
|
351
|
+
/** Scopes present now and not before. **The reviewer's actual signal.** */
|
|
352
|
+
added: string[];
|
|
353
|
+
removed: string[];
|
|
354
|
+
unchanged: string[];
|
|
355
|
+
}
|
|
356
|
+
/**
|
|
357
|
+
* The persisted scan payload: a JSONB column on `ngis_extension_version`,
|
|
358
|
+
* surfaced on {@link import("./extensions").ExtensionReviewQueueEntryDto}.
|
|
359
|
+
*
|
|
360
|
+
* **`completed: false` is not "clean".** The scanner runs inside the upload
|
|
361
|
+
* request and must never fail an upload it could not analyse, so a scanner error
|
|
362
|
+
* persists as `completed: false` with empty findings. A UI that renders that as
|
|
363
|
+
* a green check is asserting a result nobody obtained — show *not scanned*.
|
|
364
|
+
*/
|
|
365
|
+
export interface VersionScanResultDto {
|
|
366
|
+
/**
|
|
367
|
+
* Payload schema version. Present because these bytes are stored, not just
|
|
368
|
+
* transported: a row written by today's build is read by every later build,
|
|
369
|
+
* and a stored blob with no version is unmigratable.
|
|
370
|
+
*/
|
|
371
|
+
schema: 1;
|
|
372
|
+
scannedAt: string;
|
|
373
|
+
completed: boolean;
|
|
374
|
+
/** Empty means the checks ran and found nothing — iff `completed` is `true`. */
|
|
375
|
+
findings: ScanFindingDto[];
|
|
376
|
+
/** `null` when the permission check itself did not run. */
|
|
377
|
+
permissionDiff: ScanPermissionDiffDto | null;
|
|
378
|
+
}
|
package/package.json
ADDED
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@ngis/plugin-sdk",
|
|
3
|
+
"version": "0.4.0",
|
|
4
|
+
"description": "Types-first SDK for authoring NeoGIS map plugins.",
|
|
5
|
+
"license": "Apache-2.0",
|
|
6
|
+
"private": false,
|
|
7
|
+
"type": "module",
|
|
8
|
+
"sideEffects": false,
|
|
9
|
+
"main": "./dist/index.cjs",
|
|
10
|
+
"module": "./dist/index.mjs",
|
|
11
|
+
"types": "./dist/index.d.ts",
|
|
12
|
+
"exports": {
|
|
13
|
+
".": {
|
|
14
|
+
"types": "./dist/index.d.ts",
|
|
15
|
+
"import": "./dist/index.mjs",
|
|
16
|
+
"require": "./dist/index.cjs"
|
|
17
|
+
}
|
|
18
|
+
},
|
|
19
|
+
"files": [
|
|
20
|
+
"dist"
|
|
21
|
+
],
|
|
22
|
+
"engines": {
|
|
23
|
+
"node": ">=20"
|
|
24
|
+
},
|
|
25
|
+
"repository": {
|
|
26
|
+
"type": "git",
|
|
27
|
+
"url": "git+https://github.com/GeoScenarioScripter/NeoGIS-Frontend.git",
|
|
28
|
+
"directory": "packages/plugin-sdk"
|
|
29
|
+
},
|
|
30
|
+
"homepage": "https://github.com/GeoScenarioScripter/NeoGIS-Frontend#readme",
|
|
31
|
+
"bugs": {
|
|
32
|
+
"url": "https://github.com/GeoScenarioScripter/NeoGIS-Frontend/issues"
|
|
33
|
+
},
|
|
34
|
+
"keywords": [
|
|
35
|
+
"neogis",
|
|
36
|
+
"gis",
|
|
37
|
+
"plugin",
|
|
38
|
+
"sdk"
|
|
39
|
+
],
|
|
40
|
+
"publishConfig": {
|
|
41
|
+
"access": "public"
|
|
42
|
+
},
|
|
43
|
+
"scripts": {
|
|
44
|
+
"build": "node scripts/build.mjs",
|
|
45
|
+
"prepack": "npm run build"
|
|
46
|
+
}
|
|
47
|
+
}
|