@snaptrude/plugin-client 0.0.0-dev-20260708130115 → 0.0.0-walkthroughs-20261005121215

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/CHANGELOG.md CHANGED
@@ -1,11 +1,210 @@
1
1
  # @snaptrude/plugin-client
2
2
 
3
- ## 0.0.0-dev-20260708130115
3
+ ## 0.9.16
4
4
 
5
5
  ### Patch Changes
6
6
 
7
+ - Updated dependencies [e8d4ec3]
8
+ - @snaptrude/plugin-core@0.9.16
9
+
10
+ ## 0.9.15
11
+
12
+ ### Patch Changes
13
+
14
+ - Updated dependencies [4bfe118]
15
+ - Updated dependencies [80c1a2b]
16
+ - Updated dependencies [0e3df96]
17
+ - Updated dependencies [d84ea44]
18
+ - @snaptrude/plugin-core@0.9.15
19
+
20
+ ## 0.9.14
21
+
22
+ ### Patch Changes
23
+
24
+ - Updated dependencies [22c919b]
25
+ - @snaptrude/plugin-core@0.9.14
26
+
27
+ ## 0.9.13
28
+
29
+ ### Patch Changes
30
+
31
+ - Updated dependencies [6c85137]
32
+ - @snaptrude/plugin-core@0.9.13
33
+
34
+ ## 0.9.12
35
+
36
+ ### Patch Changes
37
+
38
+ - Updated dependencies [6e4a055]
39
+ - Updated dependencies [8e37e1f]
40
+ - Updated dependencies [2a06dd9]
41
+ - @snaptrude/plugin-core@0.9.12
42
+
43
+ ## 0.9.11
44
+
45
+ ### Patch Changes
46
+
47
+ - e325bee: Add the structural-grids API surface: queries, creators, a sparse patch updater, structural line edits, and generic-family acceptance.
48
+ - **Query** — `design.query.listGrids(filter?)` (standard `PluginEntityFilter`; `"grid"` joins `PluginEntityType`, so `listByType("grid")` works) and the field-projected `design.query.grids.get(gridId, properties[])`: `angle` (degrees, CCW about +Y), `props`, `storeyVisibility`, `wings` (per-wing spacing/tagStart/span and per-line world-space `CurveHandle`, tag, visibility, effective + override props), `bounds`. Unknown ids throw `HANDLE_INVALID`.
49
+ - **Create** — `design.create.grid(linesX, linesY, spacingX, spacingY, options?)` (free 2-wing grid; optional `angle` in degrees and `center`, which defaults to the native viewport-centre behavior), `design.create.gridFromCurve(curve, options?)` (one wing/one line — line or arc), `design.create.gridsFromMasses(masses, spacingX, spacingY, options?)` (fitted, one group per footprint cluster, lenient skipping, ONE undo unit), and `design.create.columnsFromGrids(grids, width, depth)` (columns at every line intersection, ONE undo unit).
50
+ - **Update** — `design.update.grid(grid, patch)`: GROUP-LEVEL sparse patch — `props` (default styling), absolute `angle` in degrees, and `storeyVisibility`; each changed field commits its own undoable command through the panel's setters.
51
+ - **Grids family** — `design.grids.updateWings(grid, wings[])` (per-wing `spacing`/`count`/`spanMin`/`spanMax`; a `count` change re-indexes lines — re-query) and `design.grids.updateLines(grid, lines[])` (per-line `tag`/`distanceToPrevious`|`distanceToNext` (mutually exclusive)/`props`/`visible: false` (hide-only, product parity — show the GROUP to restore all lines); hiding every remaining visible line hides the GROUP instead — native last-line rule); plus `design.grids.addLine(grid, wing, position)` and `design.grids.removeLines(grid, lines, tagPolicy?)` (`"rewrite"` default | `"preserve"`; removing the group's last line throws `OPERATION_FAILED` — delete the group instead). Every update routes through the exact setters the native panel uses — one undoable command per changed field, identical to panel edits.
52
+ - **Generic families** — grid groups are accepted by `design.transform.move`/`rotate`, `design.visibility.hide`/`show`, `design.lock`/`unlock`, `design.delete.entities`, and `design.selection`; `transform.align` and `transform.mirror` throw `OPERATION_FAILED` for grids.
53
+
54
+ Grid lines have no stable identity: every line-level operation addresses them positionally as `{ wing, line }` index pairs into the `wings` arrays returned by `grids.get`; structural edits re-index lines, so re-query after mutating.
55
+
56
+ - Updated dependencies [7fd374e]
57
+ - Updated dependencies [e325bee]
58
+ - @snaptrude/plugin-core@0.9.11
59
+
60
+ ## 0.9.10
61
+
62
+ ### Patch Changes
63
+
64
+ - Version bump to track plugin-core 0.9.10 (`core.mode.set("program")` opens the Program tab; `workspace.openProgramMode` / `closeProgramMode`). No client code change.
65
+
66
+ ## 0.9.9
67
+
68
+ ### Patch Changes
69
+
70
+ - Version bump to track plugin-core 0.9.9 (program.metadata, program.labels, program.departments.setTargetCount, custom program sheets on program.spreadsheet, core.tags.listForComponents). No client code change — every namespace is served by the generic RPC proxy, so new plugin-core methods dispatch automatically once plugin-core is rebuilt.
71
+
72
+ ## 0.9.8
73
+
74
+ ### Patch Changes
75
+
76
+ - Updated dependencies [7fd374e]
77
+ - @snaptrude/plugin-core@0.9.8
78
+
79
+ ## 0.9.7
80
+
81
+ ### Patch Changes
82
+
83
+ - 6e60c61: Add bulk `design.create` creators — `wallRuns`, `floors`, `doors`, `windows`, `furnitureItems`: one host round-trip and one undo entry per batch.
84
+
85
+ Singular creators, fixes shipped alongside (behaviour changes for existing callers):
86
+ - `floor`, `slab`, `roof`, `ceiling`, `column`, `beam`: contour holes are now cut (holed contours previously failed or lost their holes).
87
+ - `floor`: the `position` offset is applied before the creation snapshot, so it survives redo and reload and drives storey assignment.
88
+ - `furniture`: `options.label` is now also the readable label (`design.query.getLabel`); `createNewSourceMesh` can no longer skip recording a source this call brought in (the host owns source persistence, recorded exactly once across concurrent calls); refuses with `PRECONDITION_FAILED` (`engineCode: "TOOL_ACTIVE"`) while the interactive furniture tool is active. The same refusal applies to `core.io.import.model` furniture placement.
89
+ - `door`, `window`: published as one save document instead of five; undo granularity unchanged.
90
+
91
+ - 2f10e6b: Add `design.dimensions` — dimension lines (Measuring Tape): `create`, `list`, `get`, `delete`, `hide`, `show`, plus the new `DimensionHandle`.
92
+ - Updated dependencies [6e60c61]
93
+ - Updated dependencies [2f10e6b]
94
+ - @snaptrude/plugin-core@0.9.7
95
+
96
+ ## 0.9.6
97
+
98
+ ### Patch Changes
99
+
100
+ - Updated dependencies
101
+ - @snaptrude/plugin-core@0.9.6
102
+
103
+ ## 0.9.4
104
+
105
+ ### Patch Changes
106
+
107
+ - 0008010: `brepFromLoft` auto-matches section edge counts (deterministic vertex insertion) and auto-aligns seams for straight-edge sections; `brepFromSweep` accepts closed (ring) paths and gains an optional taper scale law (`startScale`/`endScale` or per-point `scales`).
108
+ - f54ea17: `brepFromLoft` gains `options` (`compatibility: "strict" | "auto"`, `seamAlignment: "authored" | "auto"` — strict+authored restores pre-0.9.4 lofting); `brepFromSweep` options gain `transition` (`"miter"` default, `"round"` on open paths, `{ bevel }` chamfered corners on open and closed paths); new pure read `core.geom.query.brep.getEdgeCurve` returns exact line/arc/circle edge curve data.
109
+ - Updated dependencies [0008010]
110
+ - Updated dependencies [f54ea17]
111
+ - @snaptrude/plugin-core@0.9.4
112
+
113
+ ## 0.9.3
114
+
115
+ ### Patch Changes
116
+
117
+ - 3584bde: Expose configured BIM creation and Site Analysis sheet APIs: `design.create.opening` (catalog door/window with width/height/sillHeight), staircase `dimensions` overrides (strict-validated, quarter-turn rotation), and `program.site.getSiteAnalysis`/`updateSiteAnalysis` with merge/append semantics. `PluginSiteAnalysisUpdateOptions` accepts `null` as "no options".
118
+ - Updated dependencies [3584bde]
119
+ - @snaptrude/plugin-core@0.9.3
120
+
121
+ ## 0.9.2
122
+
123
+ ### Patch Changes
124
+
125
+ - 5b91c7b: OCC geometry batch: new `core.geom.create` brep constructors — `brepFromFillet`, `brepFromChamfer`, `brepFromOffset`, `brepFromShell`, `brepsFromSplit`, `brepFromSweep`, `brepFromRevolution`; `brepFromLoft` widened with optional `intermediateContours` (multi-section lofts); new `core.geom.query.brep.getDistance` (minimum distance between two solids with witness points).
126
+ - Updated dependencies [5b91c7b]
127
+ - @snaptrude/plugin-core@0.9.2
128
+
129
+ ## 0.9.0
130
+
131
+ ### Minor Changes
132
+
133
+ - Plugin API 0.9.0 — client types/passthrough for the 0.9 surface: the namespace reorganization (deprecate-in-place aliases — `entity.*` retired, `core.zoom` → `core.camera`, new `workspace.*` and `core.mode`), the analysis wave (`analysis.weather`/`solar`/`daylight` incl. async sDA polling), heatmaps v2 (named overlays, `renderField`, `renderSurfaceGrid`, scales, hover), the constructive-geometry brep family (`core.geom.create.brepFrom*` + `design.create.massFromBrep`), `presentation.shapes` and `presentation.placedViews`, geometry reads (`getTriangulatedMeshes`, `getEnclosure`), and the spreadsheet data lane (`datasets`, plugin-sourced bindings, `addImage`, ratified `addChart` enum). See `@snaptrude/plugin-core@0.9.0` and `upgrade-notes/0.8.0-to-0.9.0.md`.
134
+
135
+ ## 0.8.0
136
+
137
+ ### Minor Changes
138
+
139
+ - More additions to the APIs, infra changes.
140
+ - Plugin API 0.8.0 — the 0.8 API wave, handle lifecycle, and worker events.
141
+ - **Events** — `PluginWorker.subscribe("model:changed", handler)`: debounced model-change notifications delivered to the worker for user and plugin edits alike; subscriptions live for the plugin's lifetime and are cleared automatically on stop.
142
+ - **The 0.8 API wave** — client types/passthrough for export, element editing, camera, storey management, visibility, dimensions, locale/user reads, and the behavior corrections in the host (world-unit `program.site` footprints, real beam/column geometry, live-only `design.query.list*`); see `@snaptrude/plugin-core@0.8.0` and `upgrade-notes/0.7.1-to-0.8.0.md`.
143
+ - **Handle lifecycle (`core.handles.*`) + GC-backed `Handle` objects** — `Handle` is now a class wrapping the wire id (public type names unchanged): `handle.id`, `toString()` stays the bare id, `equals()` compares by id, and `toJSON()` emits the `{__h: id}` wire tag — `JSON.stringify` output containing handles changes shape. New namespace: `core.handles.release(handles[])` (batched, arena-agnostic, silent no-op on unknown/released ids), `core.handles.releaseAll(kind?)`, mint-recording scopes `beginScope()` / `endScope(scope, retain?)` (token-addressed, retain promotes to the enclosing scope), and `core.handles.stats()`. The client runs a FinalizationRegistry-backed auto-release queue, so unreferenced handles are reclaimed host-side without explicit code. `core.geom.delete.profile/contour` are retained as deprecated aliases of `core.handles.release`. Entity-style kinds (component/material/underlay/terrain/importJob) remain branded strings.
144
+
145
+ ### Patch Changes
146
+
147
+ - Updated dependencies
148
+ - @snaptrude/plugin-core@0.8.0
149
+
150
+ ## 0.7.1
151
+
152
+ ### Patch Changes
153
+
154
+ - 8f22cb7: Expand `presentation.aiInspiration` to the full AI Inspiration surface (18 methods) and make it a required member of `PluginPresentationApi` now that the host implementation lands.
155
+ - New methods: `getModelCapabilities`, `getSelection`, `getPresetCatalog`, `getJob`, `cancelJob`, `listJobs`, `getWorkflowForShape`, `getSelectedBranchRerunPlan`, `rerunSelectedBranch`, `extractRecipeFromShape`, `listRecipes`, `saveRecipe`, `deleteRecipe`, `runRecipe` (plus job/workflow/recipe Zod schemas).
156
+ - `generate`/`refine` (and `rerunSelectedBranch`/`runRecipe`) accept `runMode: "blocking" | "job"`; `PluginAIInspirationRunResult` is accordingly loosened to `{ jobId?, output?, outputs?, metadata? }` — job-mode calls resolve with a `jobId` instead of outputs.
157
+
7
158
  - Updated dependencies [8f22cb7]
8
- - @snaptrude/plugin-core@0.0.0-dev-20260708130115
159
+ - @snaptrude/plugin-core@0.7.1
160
+
161
+ ## 0.7.0
162
+
163
+ ### Minor Changes
164
+
165
+ - e1915e4: Plugin API 0.7.0 — new families and methods (supersedes the cancelled `design.update.setLabel` 0.6.1 patch, now folded in).
166
+ - **`core.proposals.*`** — design options as infrastructure: `list` / `get` / `getActive` / `listForComponent` / `isActive` (reads) plus `create` / `rename` / `setActive` / `delete` (non-undoable writes).
167
+ - **`program.layout.*`** — the space-solver promoted to a real async-job family: `arrange` / `pack` now start a backend job and return immediately, with `getState` (poll) and `cancel`.
168
+ - **Door & window catalogs + placement** — `design.doors.*` / `design.windows.*` gain `listCatalogGroups` / `listCatalog` / `getCatalogItem` / `exists` (shared catalog DTOs), and `design.create.door` / `design.create.window` place a catalog item into a host wall.
169
+ - **`design.create.smartLayout`** — clone an in-scene template cluster into one or more target spaces in a single undo batch.
170
+ - **Furniture categories** — `design.furniture.listCategories` plus a `category` filter on `listCatalog`, and `thumbnailUrl` on catalog items.
171
+ - **`design.transform.align`** — snap components' bounding-box edges (left/right/center/top/bottom/middle), optionally to a reference.
172
+ - **Per-face materials** — `design.materials.applyToFaces` / `resetFaces` / `getByFace` / `listByFace` (BREP faces by durable index, visual-only).
173
+ - **Staircase params** — `design.query.getStaircaseParams` (read) and `design.update.staircase` (sparse parametric write).
174
+ - **Footprint-area lock** — `design.lockArea` / `unlockArea` / `isAreaLocked` / `listAreaLocked` for Room/Department spaces.
175
+ - **`entity.story.duplicate`** — duplicate a storey (or a subset) up/down, auto-creating the target storey.
176
+ - **`presentation.import.svg`** — import a guaranteed-vector SVG onto the Present sheet.
177
+ - **View display settings** — `presentation.views.getSettings` / `updateSettings` (background, color mode, axis, edges, labels).
178
+ - **`design.update.setLabel`** — set any component's panel Label (write pair of `design.query.getLabel`); undoable, lock-aware, validated.
179
+ - **`program.areas`** — area members now report their `areaClass` (`NET` / `GROSS` / `EXCLUDED`), with net/gross documented as the plain-English synonyms for carpet/built-up.
180
+ - **Typed plugin API errors** — every host failure now crosses the wire as a structured envelope instead of a flattened `Execution error: <msg>` string: a stable `code` from the shared plugin-core error registry, the offending handle, an authored `hint`, and `retryAfterMs` / `didYouMean` enrichment where applicable. Zod validation failures forward the first 10 issues, each projected to `{path, code, message}` (full count in `details.issueCount`); internal faults are sanitized and host-logged under an `errorId`. Every handler across the API surface now throws typed codes, each doc page gained an Errors section, and a drift test pins thrown code literals to the registry.
181
+
182
+ ### Patch Changes
183
+
184
+ - Updated dependencies [e1915e4]
185
+ - @snaptrude/plugin-core@0.7.0
186
+
187
+ ## 0.6.0
188
+
189
+ ### Minor Changes
190
+
191
+ - a68e866: New top-level `analysis` namespace:
192
+ `analysis.sunpath` (enable/disable/isActive), `analysis.shadows`
193
+ (enable/disable/isEnabled/setDateTime/getDateTime — ISO date-time strings),
194
+ and the async heatmap job families `analysis.sunlightHours` and
195
+ `analysis.illuminance` (compute/get/cancel/reset; illuminance is Pro-gated).
196
+ Also extends `program.site` with `getLocation()` and `getNorthAngle()` geo
197
+ reads. The react host has landed, so all new members are required `abstract`
198
+ and in the discovery manifest; the plugin-client mounts the `analysis` RPC
199
+ namespace.
200
+
201
+ ### Patch Changes
202
+
203
+ - Updated dependencies [934405e]
204
+ - Updated dependencies [a68e866]
205
+ - Updated dependencies
206
+ - Updated dependencies
207
+ - @snaptrude/plugin-core@0.6.0
9
208
 
10
209
  ## 0.5.0
11
210
 
@@ -1,4 +1,4 @@
1
- import { PluginApi, PluginCoreApi, PluginDesignApi, PluginEntityApi, PluginProgramApi, PluginPresentationApi } from "@snaptrude/plugin-core";
1
+ import { PluginApi, PluginCoreApi, PluginDesignApi, PluginEntityApi, PluginProgramApi, PluginPresentationApi, PluginAnalysisApi, PluginWorkspaceApi } from "@snaptrude/plugin-core";
2
2
  export declare class ClientPluginApi extends PluginApi {
3
3
  private static instance;
4
4
  /**
@@ -12,6 +12,8 @@ export declare class ClientPluginApi extends PluginApi {
12
12
  entity: PluginEntityApi;
13
13
  program: PluginProgramApi;
14
14
  presentation: PluginPresentationApi;
15
+ analysis: PluginAnalysisApi;
16
+ workspace: PluginWorkspaceApi;
15
17
  private constructor();
16
18
  static getInstance(): ClientPluginApi;
17
19
  }
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/api/index.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,SAAS,EACT,aAAa,EACb,eAAe,EACf,eAAe,EACf,gBAAgB,EAChB,qBAAqB,EACtB,MAAM,wBAAwB,CAAA;AAG/B,qBAAa,eAAgB,SAAQ,SAAS;IAC5C,OAAO,CAAC,MAAM,CAAC,QAAQ,CAAiB;IAExC;;;;;OAKG;IACI,IAAI,EAAE,aAAa,CAAA;IACnB,MAAM,EAAE,eAAe,CAAA;IACvB,MAAM,EAAE,eAAe,CAAA;IACvB,OAAO,EAAE,gBAAgB,CAAA;IACzB,YAAY,EAAE,qBAAqB,CAAA;IAE1C,OAAO;IAUP,MAAM,CAAC,WAAW,IAAI,eAAe;CAMtC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/api/index.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,SAAS,EACT,aAAa,EACb,eAAe,EACf,eAAe,EACf,gBAAgB,EAChB,qBAAqB,EACrB,iBAAiB,EACjB,kBAAkB,EACnB,MAAM,wBAAwB,CAAA;AAG/B,qBAAa,eAAgB,SAAQ,SAAS;IAC5C,OAAO,CAAC,MAAM,CAAC,QAAQ,CAAiB;IAExC;;;;;OAKG;IACI,IAAI,EAAE,aAAa,CAAA;IACnB,MAAM,EAAE,eAAe,CAAA;IACvB,MAAM,EAAE,eAAe,CAAA;IACvB,OAAO,EAAE,gBAAgB,CAAA;IACzB,YAAY,EAAE,qBAAqB,CAAA;IACnC,QAAQ,EAAE,iBAAiB,CAAA;IAC3B,SAAS,EAAE,kBAAkB,CAAA;IAEpC,OAAO;IAYP,MAAM,CAAC,WAAW,IAAI,eAAe;CAMtC"}
@@ -0,0 +1,28 @@
1
+ /**
2
+ * Snaptrude plugin events.
3
+ *
4
+ * Plugins can react to changes in the host app. Events are delivered through
5
+ * the host event bus — a separate channel from the request/response
6
+ * `snaptrude.*` API — and carry a small structured-clone-safe payload, never
7
+ * live model data.
8
+ *
9
+ * Subscribe from a {@link PluginWorker} subclass with `this.subscribe(...)`.
10
+ *
11
+ * NOTE: events are a subscription surface, not `call()` RPC methods, so they do
12
+ * NOT appear in the discovery manifest (which enumerates callable methods
13
+ * only). This module is the typed, discoverable declaration of the surface.
14
+ */
15
+ /** Every event a plugin can subscribe to. */
16
+ export declare const PLUGIN_EVENTS: readonly ["selection:changed", "tool:activated", "project:saved", "view:changed", "model:changed"];
17
+ /** Union of subscribable event names. */
18
+ export type PluginEventName = (typeof PLUGIN_EVENTS)[number];
19
+ /**
20
+ * Payload for `model:changed` — fired (debounced) whenever the project model is
21
+ * mutated, by a user edit OR a plugin edit. Deliberately minimal: it signals
22
+ * *that* the model changed, never *what* changed. Re-query the API for details.
23
+ */
24
+ export interface ModelChangedEvent {
25
+ /** Origin of the change. Currently always `"command"` (the edit chokepoint). */
26
+ source: string;
27
+ }
28
+ //# sourceMappingURL=events.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"events.d.ts","sourceRoot":"","sources":["../src/events.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;GAaG;AAEH,6CAA6C;AAC7C,eAAO,MAAM,aAAa,oGAMhB,CAAA;AAEV,yCAAyC;AACzC,MAAM,MAAM,eAAe,GAAG,CAAC,OAAO,aAAa,CAAC,CAAC,MAAM,CAAC,CAAA;AAE5D;;;;GAIG;AACH,MAAM,WAAW,iBAAiB;IAChC,gFAAgF;IAChF,MAAM,EAAE,MAAM,CAAA;CACf"}
@@ -0,0 +1,27 @@
1
+ import { Handle } from "@snaptrude/plugin-core";
2
+ /**
3
+ * Return THE `Handle` instance for this id — the existing live wrapper when
4
+ * present, else a fresh one wired for auto-release.
5
+ */
6
+ export declare function intern(id: string): Handle<string>;
7
+ /** TEST SEAM ONLY: replace the release transport (pass `undefined` to restore). */
8
+ export declare function __setReleaseTransport(transport?: (batch: string[]) => Promise<void>): void;
9
+ /** Exported for tests and for an eager flush before a plugin self-completes. */
10
+ export declare function flushReleaseQueue(): Promise<void>;
11
+ /**
12
+ * Recursively re-wrap a host result: `{ __h: "<id>" }` tags (produced by
13
+ * `Handle.toJSON` through the host's JSON-round-trip serialization) become
14
+ * interned `Handle` instances. Arrays and plain records are walked; all other
15
+ * values pass through untouched.
16
+ */
17
+ export declare function rewrapResult(value: unknown): unknown;
18
+ /**
19
+ * Recursively unwrap outgoing args: `Handle` instances become their wire id
20
+ * strings. Uses a memo Map (not a bail-out set) so shared/diamond references
21
+ * get the SAME converted subtree — a bail-out would leak un-unwrapped Handle
22
+ * instances through the second reference. Cycles are handled by memoizing the
23
+ * output object before recursing. Non-plain objects pass through untouched
24
+ * (structured clone imposes plain-data args anyway).
25
+ */
26
+ export declare function unwrapArgs(value: unknown, memo?: Map<object, unknown>): unknown;
27
+ //# sourceMappingURL=handle-runtime.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"handle-runtime.d.ts","sourceRoot":"","sources":["../src/handle-runtime.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAE,MAAM,wBAAwB,CAAA;AAqC/C;;;GAGG;AACH,wBAAgB,MAAM,CAAC,EAAE,EAAE,MAAM,GAAG,MAAM,CAAC,MAAM,CAAC,CAwBjD;AAqBD,mFAAmF;AACnF,wBAAgB,qBAAqB,CACnC,SAAS,CAAC,EAAE,CAAC,KAAK,EAAE,MAAM,EAAE,KAAK,OAAO,CAAC,IAAI,CAAC,GAC7C,IAAI,CASN;AAED,gFAAgF;AAChF,wBAAsB,iBAAiB,IAAI,OAAO,CAAC,IAAI,CAAC,CAgCvD;AAED;;;;;GAKG;AACH,wBAAgB,YAAY,CAAC,KAAK,EAAE,OAAO,GAAG,OAAO,CAYpD;AAED;;;;;;;GAOG;AACH,wBAAgB,UAAU,CAAC,KAAK,EAAE,OAAO,EAAE,IAAI,uBAA6B,GAAG,OAAO,CAqBrF"}
@@ -7,5 +7,7 @@ export interface HostApiWrapped {
7
7
  call<M extends PluginApiMethod>(payload: PluginApiCallPayload<M>): Promise<PluginApiCallResult<M>>;
8
8
  }
9
9
  export declare function createHostApi(endpoint?: Comlink.Endpoint): HostApi;
10
+ /** TEST SEAM ONLY: replace the Comlink host instance (pass `undefined` to reset). */
11
+ export declare function __setHostApiInstance(instance?: HostApi): void;
10
12
  export declare function getHostApi(): HostApiWrapped;
11
13
  //# sourceMappingURL=host-api.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"host-api.d.ts","sourceRoot":"","sources":["../src/host-api.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,OAAO,MAAM,SAAS,CAAA;AAClC,OAAO,KAAK,EACV,eAAe,EACf,oBAAoB,EACpB,0BAA0B,EAC1B,mBAAmB,EACpB,MAAM,wBAAwB,CAAA;AAE/B,MAAM,WAAW,OAAO;IACtB,IAAI,CAAC,CAAC,SAAS,eAAe,EAC5B,OAAO,EAAE,oBAAoB,CAAC,CAAC,CAAC,GAC/B,OAAO,CAAC,0BAA0B,CAAC,CAAC,CAAC,CAAC,CAAA;CAC1C;AAED,MAAM,WAAW,cAAc;IAC7B,IAAI,CAAC,CAAC,SAAS,eAAe,EAC5B,OAAO,EAAE,oBAAoB,CAAC,CAAC,CAAC,GAC/B,OAAO,CAAC,mBAAmB,CAAC,CAAC,CAAC,CAAC,CAAA;CACnC;AAED,wBAAgB,aAAa,CAAC,QAAQ,CAAC,EAAE,OAAO,CAAC,QAAQ,GAAG,OAAO,CAIlE;AAID,wBAAgB,UAAU,IAAI,cAAc,CAkB3C"}
1
+ {"version":3,"file":"host-api.d.ts","sourceRoot":"","sources":["../src/host-api.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,OAAO,MAAM,SAAS,CAAA;AAClC,OAAO,KAAK,EACV,eAAe,EACf,oBAAoB,EACpB,0BAA0B,EAC1B,mBAAmB,EACpB,MAAM,wBAAwB,CAAA;AAI/B,MAAM,WAAW,OAAO;IACtB,IAAI,CAAC,CAAC,SAAS,eAAe,EAC5B,OAAO,EAAE,oBAAoB,CAAC,CAAC,CAAC,GAC/B,OAAO,CAAC,0BAA0B,CAAC,CAAC,CAAC,CAAC,CAAA;CAC1C;AAED,MAAM,WAAW,cAAc;IAC7B,IAAI,CAAC,CAAC,SAAS,eAAe,EAC5B,OAAO,EAAE,oBAAoB,CAAC,CAAC,CAAC,GAC/B,OAAO,CAAC,mBAAmB,CAAC,CAAC,CAAC,CAAC,CAAA;CACnC;AAED,wBAAgB,aAAa,CAAC,QAAQ,CAAC,EAAE,OAAO,CAAC,QAAQ,GAAG,OAAO,CAIlE;AAID,qFAAqF;AACrF,wBAAgB,oBAAoB,CAAC,QAAQ,CAAC,EAAE,OAAO,GAAG,IAAI,CAE7D;AAED,wBAAgB,UAAU,IAAI,cAAc,CA0C3C"}
package/dist/index.cjs CHANGED
@@ -30,25 +30,163 @@ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: tru
30
30
  // src/index.ts
31
31
  var index_exports = {};
32
32
  __export(index_exports, {
33
+ CODE_META: () => import_plugin_core4.CODE_META,
33
34
  ClientPluginApi: () => ClientPluginApi,
35
+ PLUGIN_ERROR_CODES: () => import_plugin_core4.PLUGIN_ERROR_CODES,
36
+ PLUGIN_EVENTS: () => PLUGIN_EVENTS,
37
+ PluginError: () => import_plugin_core4.PluginError,
38
+ PluginExecutionError: () => import_plugin_core4.PluginExecutionError,
39
+ PluginHandleError: () => import_plugin_core4.PluginHandleError,
40
+ PluginInternalError: () => import_plugin_core4.PluginInternalError,
41
+ PluginLifecycleError: () => import_plugin_core4.PluginLifecycleError,
42
+ PluginNotFoundError: () => import_plugin_core4.PluginNotFoundError,
43
+ PluginPermissionError: () => import_plugin_core4.PluginPermissionError,
44
+ PluginQuotaError: () => import_plugin_core4.PluginQuotaError,
45
+ PluginTimeoutError: () => import_plugin_core4.PluginTimeoutError,
46
+ PluginTransportError: () => import_plugin_core4.PluginTransportError,
47
+ PluginValidationError: () => import_plugin_core4.PluginValidationError,
34
48
  PluginWorker: () => PluginWorker,
49
+ __setHostApiInstance: () => __setHostApiInstance,
50
+ __setReleaseTransport: () => __setReleaseTransport,
35
51
  createHostApi: () => createHostApi,
52
+ flushReleaseQueue: () => flushReleaseQueue,
53
+ fromEnvelope: () => import_plugin_core4.fromEnvelope,
36
54
  getHostApi: () => getHostApi,
37
- snaptrude: () => snaptrude
55
+ intern: () => intern,
56
+ isErrorEnvelope: () => import_plugin_core4.isErrorEnvelope,
57
+ isPluginErrorCode: () => import_plugin_core4.isPluginErrorCode,
58
+ rewrapResult: () => rewrapResult,
59
+ snaptrude: () => snaptrude,
60
+ unwrapArgs: () => unwrapArgs
38
61
  });
39
62
  module.exports = __toCommonJS(index_exports);
40
63
 
41
64
  // src/api/index.ts
42
- var import_plugin_core = require("@snaptrude/plugin-core");
65
+ var import_plugin_core3 = require("@snaptrude/plugin-core");
43
66
 
44
67
  // src/host-api.ts
45
68
  var Comlink = __toESM(require("comlink"), 1);
69
+ var import_plugin_core2 = require("@snaptrude/plugin-core");
70
+
71
+ // src/handle-runtime.ts
72
+ var import_plugin_core = require("@snaptrude/plugin-core");
73
+ var interned = /* @__PURE__ */ new Map();
74
+ var RELEASE_FLUSH_SIZE = 64;
75
+ var RELEASE_FLUSH_MS = 250;
76
+ var MAX_FLUSH_FAILURES = 5;
77
+ var releaseQueue = /* @__PURE__ */ new Set();
78
+ var flushTimer = null;
79
+ var consecutiveFlushFailures = 0;
80
+ var finalizer = new FinalizationRegistry((id) => {
81
+ if (interned.get(id)?.deref()) return;
82
+ interned.delete(id);
83
+ enqueueRelease(id);
84
+ });
85
+ function intern(id) {
86
+ releaseQueue.delete(id);
87
+ const existing = interned.get(id)?.deref();
88
+ if (existing) return existing;
89
+ const handle = new import_plugin_core.Handle(id);
90
+ handle[Symbol.asyncDispose] = async () => {
91
+ if (interned.get(id)?.deref() === handle) interned.delete(id);
92
+ enqueueRelease(id);
93
+ };
94
+ interned.set(id, new WeakRef(handle));
95
+ finalizer.register(handle, id);
96
+ return handle;
97
+ }
98
+ function enqueueRelease(id) {
99
+ releaseQueue.add(id);
100
+ if (releaseQueue.size >= RELEASE_FLUSH_SIZE) {
101
+ void flushReleaseQueue();
102
+ return;
103
+ }
104
+ flushTimer ?? (flushTimer = setTimeout(() => {
105
+ void flushReleaseQueue();
106
+ }, RELEASE_FLUSH_MS));
107
+ }
108
+ var releaseTransport = async (batch) => {
109
+ await getHostApi().call({
110
+ method: "core.handles.release",
111
+ args: [batch]
112
+ });
113
+ };
114
+ function __setReleaseTransport(transport) {
115
+ releaseTransport = transport ?? (async (batch) => {
116
+ await getHostApi().call({
117
+ method: "core.handles.release",
118
+ args: [batch]
119
+ });
120
+ });
121
+ }
122
+ async function flushReleaseQueue() {
123
+ if (flushTimer) {
124
+ clearTimeout(flushTimer);
125
+ flushTimer = null;
126
+ }
127
+ if (releaseQueue.size === 0) return;
128
+ const batch = [...releaseQueue];
129
+ releaseQueue.clear();
130
+ try {
131
+ await releaseTransport(batch);
132
+ consecutiveFlushFailures = 0;
133
+ } catch (err) {
134
+ consecutiveFlushFailures += 1;
135
+ if (consecutiveFlushFailures >= MAX_FLUSH_FAILURES) {
136
+ console.warn(
137
+ `[snaptrude] dropping ${batch.length} handle release(s) after ${consecutiveFlushFailures} failed flushes`,
138
+ err
139
+ );
140
+ consecutiveFlushFailures = 0;
141
+ return;
142
+ }
143
+ for (const id of batch) {
144
+ if (!interned.get(id)?.deref()) releaseQueue.add(id);
145
+ }
146
+ }
147
+ }
148
+ function rewrapResult(value) {
149
+ if (Array.isArray(value)) return value.map(rewrapResult);
150
+ if (value && typeof value === "object") {
151
+ const record = value;
152
+ const keys = Object.keys(record);
153
+ if (keys.length === 1 && keys[0] === "__h" && typeof record.__h === "string") {
154
+ return intern(record.__h);
155
+ }
156
+ for (const key of keys) record[key] = rewrapResult(record[key]);
157
+ return record;
158
+ }
159
+ return value;
160
+ }
161
+ function unwrapArgs(value, memo = /* @__PURE__ */ new Map()) {
162
+ if (value instanceof import_plugin_core.Handle) return value.id;
163
+ if (value === null || typeof value !== "object") return value;
164
+ const hit = memo.get(value);
165
+ if (hit !== void 0) return hit;
166
+ if (Array.isArray(value)) {
167
+ const out2 = [];
168
+ memo.set(value, out2);
169
+ for (const item of value) out2.push(unwrapArgs(item, memo));
170
+ return out2;
171
+ }
172
+ const proto = Object.getPrototypeOf(value);
173
+ if (proto !== Object.prototype && proto !== null) return value;
174
+ const out = {};
175
+ memo.set(value, out);
176
+ for (const [key, item] of Object.entries(value)) out[key] = unwrapArgs(item, memo);
177
+ return out;
178
+ }
179
+
180
+ // src/host-api.ts
46
181
  function createHostApi(endpoint) {
47
182
  return Comlink.wrap(
48
183
  endpoint ?? globalThis
49
184
  );
50
185
  }
51
186
  var _instance = null;
187
+ function __setHostApiInstance(instance) {
188
+ _instance = instance ?? null;
189
+ }
52
190
  function getHostApi() {
53
191
  if (!_instance) {
54
192
  _instance = createHostApi();
@@ -58,16 +196,35 @@ function getHostApi() {
58
196
  if (!_instance) {
59
197
  throw new Error("Host API not initialized");
60
198
  }
61
- return _instance.call(payload).then((result) => {
62
- if (result.success) {
63
- return result.data;
64
- } else {
65
- throw new Error(result.error);
66
- }
67
- });
199
+ let result;
200
+ try {
201
+ result = await _instance.call(payload);
202
+ } catch (transportErr) {
203
+ if (import_plugin_core2.PluginError.is(transportErr)) throw transportErr;
204
+ throw rehydrate(
205
+ (0, import_plugin_core2.makeClientEnvelope)(
206
+ "TRANSPORT_LOST",
207
+ transportErr instanceof Error ? transportErr.message : String(transportErr),
208
+ { methodPath: payload.method }
209
+ )
210
+ );
211
+ }
212
+ if (result.success) {
213
+ return rewrapResult(result.data);
214
+ }
215
+ throw rehydrate(
216
+ result.errorInfo ?? (0, import_plugin_core2.makeClientEnvelope)("UNKNOWN", result.error ?? "Unknown host error", {
217
+ methodPath: payload.method
218
+ })
219
+ );
68
220
  }
69
221
  };
70
222
  }
223
+ function rehydrate(envelope) {
224
+ const error = (0, import_plugin_core2.fromEnvelope)(envelope);
225
+ Error.captureStackTrace?.(error, rehydrate);
226
+ return error;
227
+ }
71
228
 
72
229
  // src/rpc-proxy.ts
73
230
  function createRpcNamespace(basePath) {
@@ -79,7 +236,8 @@ function createRpcNamespace(basePath) {
79
236
  apply(_target, _thisArg, argArray) {
80
237
  const payload = {
81
238
  method: path,
82
- args: argArray
239
+ // Handle instances anywhere in the tuple become their wire id strings.
240
+ args: argArray.map((arg) => unwrapArgs(arg))
83
241
  };
84
242
  return getHostApi().call(payload);
85
243
  }
@@ -90,7 +248,7 @@ var NOOP = () => {
90
248
  };
91
249
 
92
250
  // src/api/index.ts
93
- var ClientPluginApi = class _ClientPluginApi extends import_plugin_core.PluginApi {
251
+ var ClientPluginApi = class _ClientPluginApi extends import_plugin_core3.PluginApi {
94
252
  constructor() {
95
253
  super();
96
254
  this.core = createRpcNamespace("core");
@@ -98,6 +256,8 @@ var ClientPluginApi = class _ClientPluginApi extends import_plugin_core.PluginAp
98
256
  this.entity = createRpcNamespace("entity");
99
257
  this.program = createRpcNamespace("program");
100
258
  this.presentation = createRpcNamespace("presentation");
259
+ this.analysis = createRpcNamespace("analysis");
260
+ this.workspace = createRpcNamespace("workspace");
101
261
  }
102
262
  static getInstance() {
103
263
  if (!_ClientPluginApi.instance) {
@@ -119,6 +279,17 @@ var PluginWorker = class {
119
279
  ;
120
280
  this.hostAPI.ui.sendToUI({ action, payload });
121
281
  }
282
+ /**
283
+ * Show a transient notification toast in the Snaptrude editor.
284
+ *
285
+ * @param message - Text to display.
286
+ * @param type - Severity; `"error"`/`"warning"` linger a little longer than
287
+ * `"info"`. Defaults to `"info"`.
288
+ */
289
+ notify(message, type = "info") {
290
+ ;
291
+ this.hostAPI.ui.showNotification(message, type);
292
+ }
122
293
  /**
123
294
  * Signal the host that this plugin has finished its work and should be
124
295
  * stopped. Use this in headless (UI-less) plugins that run a task and
@@ -128,6 +299,35 @@ var PluginWorker = class {
128
299
  ;
129
300
  this.hostAPI.lifecycle.complete();
130
301
  }
302
+ /**
303
+ * Subscribe to a host event (e.g. `"model:changed"`, fired debounced on any
304
+ * user- or plugin-initiated model edit). The callback runs in this worker
305
+ * each time the event fires; its argument is a small structured-clone-safe
306
+ * payload (see the event's payload type, e.g. `ModelChangedEvent`).
307
+ *
308
+ * Fire-and-forget: subscribe once (typically in `init()`). Subscriptions live
309
+ * for the plugin's lifetime and are cleared automatically when the plugin is
310
+ * stopped — there is no `unsubscribe` yet.
311
+ *
312
+ * @example
313
+ * ```ts
314
+ * import type { ModelChangedEvent } from "@snaptrude/plugin-client";
315
+ *
316
+ * async init() {
317
+ * this.subscribe("model:changed", (e) => {
318
+ * const { source } = e as ModelChangedEvent;
319
+ * console.log("model changed via", source);
320
+ * });
321
+ * }
322
+ * ```
323
+ */
324
+ subscribe(event, callback) {
325
+ ;
326
+ this.hostAPI.events.on(
327
+ event,
328
+ Comlink2.proxy(callback)
329
+ );
330
+ }
131
331
  async init() {
132
332
  console.log(this.pluginId, "init() called");
133
333
  console.log(this.pluginId, "Initialization complete");
@@ -165,14 +365,47 @@ var PluginWorker = class {
165
365
  }
166
366
  };
167
367
 
368
+ // src/events.ts
369
+ var PLUGIN_EVENTS = [
370
+ "selection:changed",
371
+ "tool:activated",
372
+ "project:saved",
373
+ "view:changed",
374
+ "model:changed"
375
+ ];
376
+
168
377
  // src/index.ts
378
+ var import_plugin_core4 = require("@snaptrude/plugin-core");
169
379
  var snaptrude = ClientPluginApi.getInstance();
170
380
  // Annotate the CommonJS export names for ESM import in node:
171
381
  0 && (module.exports = {
382
+ CODE_META,
172
383
  ClientPluginApi,
384
+ PLUGIN_ERROR_CODES,
385
+ PLUGIN_EVENTS,
386
+ PluginError,
387
+ PluginExecutionError,
388
+ PluginHandleError,
389
+ PluginInternalError,
390
+ PluginLifecycleError,
391
+ PluginNotFoundError,
392
+ PluginPermissionError,
393
+ PluginQuotaError,
394
+ PluginTimeoutError,
395
+ PluginTransportError,
396
+ PluginValidationError,
173
397
  PluginWorker,
398
+ __setHostApiInstance,
399
+ __setReleaseTransport,
174
400
  createHostApi,
401
+ flushReleaseQueue,
402
+ fromEnvelope,
175
403
  getHostApi,
176
- snaptrude
404
+ intern,
405
+ isErrorEnvelope,
406
+ isPluginErrorCode,
407
+ rewrapResult,
408
+ snaptrude,
409
+ unwrapArgs
177
410
  });
178
411
  //# sourceMappingURL=index.cjs.map