@snaptrude/plugin-core 0.9.17 → 0.9.18-networks-20261016.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (213) hide show
  1. package/AGENTS.md +231 -231
  2. package/CHANGELOG.md +408 -408
  3. package/CLAUDE.md +11 -11
  4. package/dist/api/analysis/daylight.d.ts +5 -5
  5. package/dist/api/analysis/solar.d.ts +2 -2
  6. package/dist/api/core/camera/index.d.ts +29 -0
  7. package/dist/api/core/camera/index.d.ts.map +1 -1
  8. package/dist/api/core/geom/create/index.d.ts +7 -1
  9. package/dist/api/core/geom/create/index.d.ts.map +1 -1
  10. package/dist/api/core/geom/update/contour.d.ts +8 -2
  11. package/dist/api/core/geom/update/contour.d.ts.map +1 -1
  12. package/dist/api/core/zoom/index.d.ts +13 -0
  13. package/dist/api/core/zoom/index.d.ts.map +1 -1
  14. package/dist/api/design/bim.d.ts +209 -0
  15. package/dist/api/design/bim.d.ts.map +1 -0
  16. package/dist/api/design/boolean/index.d.ts +2 -0
  17. package/dist/api/design/boolean/index.d.ts.map +1 -1
  18. package/dist/api/design/catalog.d.ts +320 -0
  19. package/dist/api/design/catalog.d.ts.map +1 -0
  20. package/dist/api/design/create/bulk-items.d.ts +65 -25
  21. package/dist/api/design/create/bulk-items.d.ts.map +1 -1
  22. package/dist/api/design/create/index.d.ts +272 -99
  23. package/dist/api/design/create/index.d.ts.map +1 -1
  24. package/dist/api/design/create/opening-fields.d.ts +13 -12
  25. package/dist/api/design/create/opening-fields.d.ts.map +1 -1
  26. package/dist/api/design/delete/index.d.ts +1 -0
  27. package/dist/api/design/delete/index.d.ts.map +1 -1
  28. package/dist/api/design/doors/index.d.ts +18 -24
  29. package/dist/api/design/doors/index.d.ts.map +1 -1
  30. package/dist/api/design/family.d.ts +797 -27
  31. package/dist/api/design/family.d.ts.map +1 -1
  32. package/dist/api/design/index.d.ts +16 -0
  33. package/dist/api/design/index.d.ts.map +1 -1
  34. package/dist/api/design/networks.d.ts +1763 -0
  35. package/dist/api/design/networks.d.ts.map +1 -0
  36. package/dist/api/design/query/index.d.ts +42 -32
  37. package/dist/api/design/query/index.d.ts.map +1 -1
  38. package/dist/api/design/selection/index.d.ts +10 -10
  39. package/dist/api/design/types/index.d.ts +40 -24
  40. package/dist/api/design/types/index.d.ts.map +1 -1
  41. package/dist/api/design/update/index.d.ts +43 -3
  42. package/dist/api/design/update/index.d.ts.map +1 -1
  43. package/dist/api/design/windows/index.d.ts +8 -8
  44. package/dist/api/design/windows/index.d.ts.map +1 -1
  45. package/dist/api/presentation/aiInspiration.d.ts +10 -10
  46. package/dist/api/presentation/annotate.d.ts +38 -22
  47. package/dist/api/presentation/annotate.d.ts.map +1 -1
  48. package/dist/api/presentation/drawingContract.d.ts +32 -0
  49. package/dist/api/presentation/drawingContract.d.ts.map +1 -0
  50. package/dist/api/presentation/drawings.d.ts +3523 -0
  51. package/dist/api/presentation/drawings.d.ts.map +1 -0
  52. package/dist/api/presentation/export.d.ts +23 -5
  53. package/dist/api/presentation/export.d.ts.map +1 -1
  54. package/dist/api/presentation/index.d.ts +32 -12
  55. package/dist/api/presentation/index.d.ts.map +1 -1
  56. package/dist/api/presentation/placedViews.d.ts +106 -6
  57. package/dist/api/presentation/placedViews.d.ts.map +1 -1
  58. package/dist/api/presentation/shapes.d.ts +2 -2
  59. package/dist/api/presentation/sheets.d.ts +175 -19
  60. package/dist/api/presentation/sheets.d.ts.map +1 -1
  61. package/dist/api/presentation/viewPresets.d.ts +222 -0
  62. package/dist/api/presentation/viewPresets.d.ts.map +1 -0
  63. package/dist/api/presentation/views.d.ts +103 -5
  64. package/dist/api/presentation/views.d.ts.map +1 -1
  65. package/dist/api/presentation/walkthroughs.d.ts +6 -6
  66. package/dist/errors/codes.d.ts +3 -1
  67. package/dist/errors/codes.d.ts.map +1 -1
  68. package/dist/index.cjs +3732 -2383
  69. package/dist/index.cjs.map +1 -1
  70. package/dist/index.js +3607 -2383
  71. package/dist/index.js.map +1 -1
  72. package/package.json +42 -42
  73. package/scripts/check-dist-fresh.mjs +22 -22
  74. package/scripts/generate-manifest.mjs +243 -238
  75. package/scripts/generate-manifest.test.mjs +303 -303
  76. package/src/api/analysis/daylight.ts +470 -470
  77. package/src/api/analysis/heatmaps.ts +683 -683
  78. package/src/api/analysis/illuminance.ts +155 -155
  79. package/src/api/analysis/index.ts +61 -61
  80. package/src/api/analysis/shadows.ts +183 -183
  81. package/src/api/analysis/solar.ts +237 -237
  82. package/src/api/analysis/sunlightHours.ts +211 -211
  83. package/src/api/analysis/sunpath.ts +83 -83
  84. package/src/api/analysis/weather.ts +179 -179
  85. package/src/api/core/buildings.ts +485 -485
  86. package/src/api/core/camera/index.ts +696 -667
  87. package/src/api/core/comment/index.ts +376 -376
  88. package/src/api/core/geom/create/index.ts +2139 -2133
  89. package/src/api/core/geom/delete/index.ts +84 -84
  90. package/src/api/core/geom/index.ts +35 -35
  91. package/src/api/core/geom/query/arc.ts +169 -169
  92. package/src/api/core/geom/query/brep.ts +621 -621
  93. package/src/api/core/geom/query/circle.ts +404 -404
  94. package/src/api/core/geom/query/contour.ts +540 -540
  95. package/src/api/core/geom/query/curve.ts +1102 -1102
  96. package/src/api/core/geom/query/edge.ts +130 -130
  97. package/src/api/core/geom/query/face.ts +508 -508
  98. package/src/api/core/geom/query/halfedge.ts +210 -210
  99. package/src/api/core/geom/query/index.ts +63 -63
  100. package/src/api/core/geom/query/profile.ts +473 -473
  101. package/src/api/core/geom/query/vertex.ts +205 -205
  102. package/src/api/core/geom/update/contour.ts +323 -317
  103. package/src/api/core/geom/update/curve.ts +209 -209
  104. package/src/api/core/geom/update/index.ts +29 -29
  105. package/src/api/core/geom/update/profile.ts +379 -379
  106. package/src/api/core/groups.ts +521 -521
  107. package/src/api/core/handles/index.ts +234 -234
  108. package/src/api/core/history/index.ts +84 -84
  109. package/src/api/core/index.ts +99 -99
  110. package/src/api/core/io/export/index.ts +126 -126
  111. package/src/api/core/io/import/index.ts +504 -504
  112. package/src/api/core/io/index.ts +42 -42
  113. package/src/api/core/io/job/index.ts +140 -140
  114. package/src/api/core/io/query/index.ts +71 -71
  115. package/src/api/core/io/terrain/index.ts +360 -360
  116. package/src/api/core/io/underlay/index.ts +705 -705
  117. package/src/api/core/layers.ts +540 -540
  118. package/src/api/core/math/index.ts +20 -20
  119. package/src/api/core/math/quat.ts +172 -172
  120. package/src/api/core/math/vec3.ts +179 -179
  121. package/src/api/core/mode/index.ts +108 -108
  122. package/src/api/core/project/index.ts +479 -479
  123. package/src/api/core/proposals/index.ts +570 -570
  124. package/src/api/core/storeys/index.ts +308 -308
  125. package/src/api/core/storeys/layers.ts +339 -339
  126. package/src/api/core/tags.ts +911 -911
  127. package/src/api/core/units/index.ts +164 -164
  128. package/src/api/core/user.ts +46 -46
  129. package/src/api/core/zoom/index.ts +64 -51
  130. package/src/api/design/bim.ts +121 -0
  131. package/src/api/design/boolean/index.ts +181 -179
  132. package/src/api/design/catalog.ts +203 -0
  133. package/src/api/design/create/bulk-items.ts +294 -254
  134. package/src/api/design/create/grids.ts +137 -137
  135. package/src/api/design/create/index.ts +2148 -1980
  136. package/src/api/design/create/opening-fields.ts +40 -37
  137. package/src/api/design/delete/index.ts +75 -74
  138. package/src/api/design/dimensions.ts +453 -453
  139. package/src/api/design/doors/index.ts +516 -500
  140. package/src/api/design/edit/index.ts +54 -54
  141. package/src/api/design/erase/index.ts +151 -151
  142. package/src/api/design/family.ts +1334 -704
  143. package/src/api/design/furniture/index.ts +319 -319
  144. package/src/api/design/grids.ts +237 -237
  145. package/src/api/design/index.ts +304 -274
  146. package/src/api/design/lock.ts +69 -69
  147. package/src/api/design/materials/index.ts +608 -608
  148. package/src/api/design/networks.ts +667 -0
  149. package/src/api/design/query/geometry/index.ts +319 -319
  150. package/src/api/design/query/grids.ts +197 -197
  151. package/src/api/design/query/index.ts +1236 -1226
  152. package/src/api/design/query/referenceLines.ts +52 -52
  153. package/src/api/design/query/spaces.ts +275 -275
  154. package/src/api/design/selection/index.ts +273 -273
  155. package/src/api/design/shared.ts +40 -40
  156. package/src/api/design/transform/index.ts +254 -254
  157. package/src/api/design/types/index.ts +163 -156
  158. package/src/api/design/update/index.ts +1024 -983
  159. package/src/api/design/visibility.ts +143 -143
  160. package/src/api/design/windows/index.ts +351 -333
  161. package/src/api/entity/buildableEnvelope.ts +322 -322
  162. package/src/api/entity/index.ts +30 -30
  163. package/src/api/entity/referenceLine.ts +235 -235
  164. package/src/api/entity/space.ts +612 -612
  165. package/src/api/entity/story.ts +517 -517
  166. package/src/api/index.ts +47 -47
  167. package/src/api/presentation/aiInspiration.ts +1116 -1116
  168. package/src/api/presentation/annotate.ts +417 -385
  169. package/src/api/presentation/camera.ts +289 -289
  170. package/src/api/presentation/diagrams.ts +194 -194
  171. package/src/api/presentation/drawingContract.ts +70 -0
  172. package/src/api/presentation/drawings.ts +1179 -0
  173. package/src/api/presentation/export.ts +134 -108
  174. package/src/api/presentation/import.ts +159 -159
  175. package/src/api/presentation/index.ts +160 -135
  176. package/src/api/presentation/placedViews.ts +1211 -1120
  177. package/src/api/presentation/sections.ts +420 -420
  178. package/src/api/presentation/shapes.ts +274 -274
  179. package/src/api/presentation/sheets.ts +746 -585
  180. package/src/api/presentation/slideshow.ts +134 -134
  181. package/src/api/presentation/tables.ts +84 -84
  182. package/src/api/presentation/viewPresets.ts +241 -0
  183. package/src/api/presentation/views.ts +713 -673
  184. package/src/api/presentation/walkthroughs.ts +1201 -1201
  185. package/src/api/program/adjacency.ts +164 -164
  186. package/src/api/program/areas.ts +408 -408
  187. package/src/api/program/classification.ts +138 -138
  188. package/src/api/program/cores.ts +83 -83
  189. package/src/api/program/departments.ts +517 -517
  190. package/src/api/program/index.ts +73 -73
  191. package/src/api/program/labels.ts +332 -332
  192. package/src/api/program/layout.ts +422 -422
  193. package/src/api/program/metadata.ts +364 -364
  194. package/src/api/program/metrics.ts +120 -120
  195. package/src/api/program/site.ts +748 -748
  196. package/src/api/program/spreadsheet.ts +2698 -2698
  197. package/src/api/workspace/index.ts +612 -612
  198. package/src/errors/codes.ts +143 -136
  199. package/src/errors/envelope.ts +75 -75
  200. package/src/errors/index.ts +21 -21
  201. package/src/errors/plugin-error.ts +134 -134
  202. package/src/handles.ts +297 -297
  203. package/src/host-utils.ts +67 -67
  204. package/src/index.ts +7 -7
  205. package/src/massParameters.ops.ts +246 -246
  206. package/src/massParameters.ts +427 -427
  207. package/src/types.ts +16 -16
  208. package/test/drawingContract.test.mjs +288 -0
  209. package/test/errors.test.mjs +185 -184
  210. package/test/massParameters.test.mjs +952 -952
  211. package/tsconfig.json +22 -22
  212. package/tsup.config.ts +12 -12
  213. package/api-manifest.json +0 -9711
@@ -1,303 +1,303 @@
1
- // Unit tests for the discovery-manifest generator (pure codegen logic).
2
- // Run: `node --test scripts/generate-manifest.test.mjs` (from packages/plugin-core)
3
- import { test } from "node:test"
4
- import assert from "node:assert/strict"
5
- import { buildManifest } from "./generate-manifest.mjs"
6
-
7
- const manifest = buildManifest()
8
- const byPath = new Map(manifest.map((m) => [m.path, m]))
9
-
10
- test("every entry has a dotted path and a namespace", () => {
11
- for (const m of manifest) {
12
- assert.match(m.path, /^[a-z][a-zA-Z0-9.]*$/, `bad path: ${m.path}`)
13
- assert.equal(typeof m.namespace, "string")
14
- assert.ok(
15
- Array.isArray(m.examplePrompts),
16
- `${m.path} examplePrompts not array`,
17
- )
18
- }
19
- })
20
-
21
- test("output is sorted and unique by path", () => {
22
- const paths = manifest.map((m) => m.path)
23
- assert.deepEqual(
24
- paths,
25
- [...paths].sort((a, b) => a.localeCompare(b)),
26
- "not sorted",
27
- )
28
- assert.equal(new Set(paths).size, paths.length, "duplicate paths")
29
- })
30
-
31
- test("all 4 Phase-1 slice methods are present with intent + prompts", () => {
32
- const slice = [
33
- "program.departments.list",
34
- "program.departments.get",
35
- "program.metrics.get",
36
- "presentation.views.list",
37
- ]
38
- for (const p of slice) {
39
- const m = byPath.get(p)
40
- assert.ok(m, `missing slice method: ${p}`)
41
- assert.ok(m.summary.length > 0, `${p} has no intent summary`)
42
- assert.ok(m.examplePrompts.length >= 3, `${p} has < 3 example prompts`)
43
- assert.ok(
44
- m.resultType && m.resultType !== "void",
45
- `${p} has no result type`,
46
- )
47
- }
48
- })
49
-
50
- test("departments.get carries its args type; list takes no args", () => {
51
- assert.equal(byPath.get("program.departments.get").argsType, "string")
52
- assert.equal(byPath.get("program.departments.list").argsType, null)
53
- })
54
-
55
- test("return-type wrapper PluginApiReturn<...> is unwrapped", () => {
56
- for (const m of manifest) {
57
- assert.ok(
58
- !/^PluginApiReturn</.test(m.resultType ?? ""),
59
- `${m.path} not unwrapped`,
60
- )
61
- }
62
- })
63
-
64
- test("author-ahead (optional) members are kept OUT of the manifest", () => {
65
- // Optional sub-namespaces (`aiInspiration?`) and optional leaf methods
66
- // (`departments.delete?`, `cores.create?`) are authored ahead of the host and
67
- // must not appear in the discovery manifest until promoted to required
68
- // `abstract` members. (See the discipline note in generate-manifest.mjs.)
69
- const authoredAhead = [
70
- "presentation.aiInspiration.listModels",
71
- "program.departments.delete",
72
- "program.cores.create",
73
- ]
74
- for (const p of authoredAhead) {
75
- assert.ok(
76
- !byPath.has(p),
77
- `optional author-ahead member leaked into manifest: ${p}`,
78
- )
79
- }
80
- })
81
-
82
- test("promoted methods are present in the manifest", () => {
83
- // Methods whose react host bodies have landed and been verified, flipped from
84
- // optional author-ahead to required `abstract`.
85
- const promoted = [
86
- "program.departments.create",
87
- "program.departments.setTargetArea",
88
- "program.site.get",
89
- "program.site.getContext",
90
- "presentation.import.image",
91
- "presentation.import.pdf",
92
- "presentation.sheets.list",
93
- "presentation.sheets.place",
94
- "presentation.diagrams.place",
95
- // 2026-07-02 promotions — react hosts landed and verified:
96
- "program.spreadsheet.render",
97
- "program.adjacency.getMatrix",
98
- "program.adjacency.compute",
99
- "program.spreadsheet.getTakeoff",
100
- "program.spreadsheet.runScript",
101
- "presentation.views.get",
102
- "presentation.views.getActive",
103
- "presentation.views.capture",
104
- "presentation.views.setActive",
105
- "presentation.views.create",
106
- "core.storeys.layers.setActive",
107
- "core.storeys.layers.update",
108
- "program.areas.update",
109
- // 2026-07-06 promotions — analysis namespace + program.site geo reads,
110
- // react hosts landed:
111
- "analysis.sunpath.enable",
112
- "analysis.sunpath.disable",
113
- "analysis.sunpath.isActive",
114
- "analysis.shadows.enable",
115
- "analysis.shadows.disable",
116
- "analysis.shadows.isEnabled",
117
- "analysis.shadows.setDateTime",
118
- "analysis.shadows.getDateTime",
119
- "analysis.sunlightHours.compute",
120
- "analysis.sunlightHours.get",
121
- "analysis.sunlightHours.cancel",
122
- "analysis.sunlightHours.reset",
123
- "analysis.illuminance.compute",
124
- "analysis.illuminance.get",
125
- "analysis.illuminance.cancel",
126
- "analysis.illuminance.reset",
127
- "program.site.getLocation",
128
- "program.site.getNorthAngle",
129
- // 2026-07-07 promotion — analysis.heatmaps, react host landed:
130
- "analysis.heatmaps.renderSpaces",
131
- "analysis.heatmaps.renderGrid",
132
- "analysis.heatmaps.reset",
133
- "analysis.heatmaps.isActive",
134
- // 2026-07-09 promotion — program.layout became an async-job family (was
135
- // author-ahead optional `arrange`/`pack`; now required + getState/cancel):
136
- "program.layout.arrange",
137
- "program.layout.pack",
138
- "program.layout.getState",
139
- "program.layout.cancel",
140
- ]
141
- for (const p of promoted) {
142
- assert.ok(
143
- byPath.has(p),
144
- `expected promoted method missing from manifest: ${p}`,
145
- )
146
- }
147
- })
148
-
149
- test("v2 family reads (all 5 families) are present with intent + prompts", () => {
150
- // All 22 reads of the five landed families. Every sub-api mount is now required
151
- // `abstract` (the react hosts have landed), so each read is in the discovery
152
- // manifest with a summary, >= 3 example prompts, and a result type.
153
- const reads = [
154
- "core.groups.list",
155
- "core.groups.get",
156
- "core.groups.getForComponent",
157
- "core.groups.listMembers",
158
- "core.groups.isLocked",
159
- "core.groups.getBounds",
160
- "core.buildings.list",
161
- "core.buildings.get",
162
- "core.buildings.listStoreys",
163
- "core.buildings.getActive",
164
- "core.storeys.layers.list",
165
- "core.storeys.layers.get",
166
- "core.storeys.layers.getActive",
167
- // core.layers — the Layers panel (0.9.14)
168
- "core.layers.list",
169
- "core.layers.get",
170
- "core.layers.getActive",
171
- "core.tags.listCategories",
172
- "core.tags.getCategory",
173
- "core.tags.list",
174
- "core.tags.get",
175
- "core.tags.getTagsForComponent",
176
- "core.tags.listComponents",
177
- "program.areas.get",
178
- "program.areas.list",
179
- "program.areas.listMembers",
180
- ]
181
- for (const p of reads) {
182
- const m = byPath.get(p)
183
- assert.ok(m, `missing v2 family read: ${p}`)
184
- assert.ok(m.summary.length > 0, `${p} has no intent summary`)
185
- assert.ok(m.examplePrompts.length >= 3, `${p} has < 3 example prompts`)
186
- assert.ok(
187
- m.resultType && m.resultType !== "void",
188
- `${p} has no result type`,
189
- )
190
- }
191
- })
192
-
193
- test("v2 family writes are IN the discovery manifest (hosts landed, promoted)", () => {
194
- // 2026-07-02: every v2 family write's react host body has landed, so the
195
- // contract members were promoted from optional `foo?: (args) => ...`
196
- // fn-properties to required `abstract` methods — they now appear in the
197
- // discovery build.
198
- const promotedWrites = [
199
- "core.groups.create",
200
- "core.groups.update",
201
- "core.groups.delete",
202
- "core.buildings.setActive",
203
- "core.buildings.create",
204
- "core.buildings.update",
205
- "core.buildings.copy",
206
- "core.buildings.delete",
207
- "core.storeys.layers.setActive",
208
- "core.storeys.layers.update",
209
- // core.layers — the Layers panel (0.9.14)
210
- "core.layers.create",
211
- "core.layers.rename",
212
- "core.layers.update",
213
- "core.layers.setActive",
214
- "core.layers.delete",
215
- "core.layers.assign",
216
- "core.tags.createCategory",
217
- "core.tags.updateCategory",
218
- "core.tags.deleteCategory",
219
- "core.tags.create",
220
- "core.tags.update",
221
- "core.tags.delete",
222
- "core.tags.assign",
223
- "core.tags.unassign",
224
- "program.areas.update",
225
- ]
226
- for (const p of promotedWrites) {
227
- assert.ok(
228
- byPath.has(p),
229
- `promoted v2 family write missing from manifest: ${p}`,
230
- )
231
- }
232
- })
233
-
234
- test("0.7.0 batch — new families & methods present with intent + prompts", () => {
235
- // The 0.7.0 wave: core.proposals, the program.layout async-job family,
236
- // door/window catalogs + placement, furniture categories, smart layout,
237
- // align, per-face materials, staircase read/write, footprint-area lock,
238
- // storey duplicate, SVG import, and view display settings. Every entry is a
239
- // required `abstract` member (react hosts landed), so each must be in the
240
- // discovery manifest with a summary, >= 3 example prompts, and a result type.
241
- const batch = [
242
- // core.proposals — design-option infrastructure
243
- "core.proposals.list",
244
- "core.proposals.get",
245
- "core.proposals.getActive",
246
- "core.proposals.listForComponent",
247
- "core.proposals.isActive",
248
- "core.proposals.create",
249
- "core.proposals.rename",
250
- "core.proposals.setActive",
251
- "core.proposals.delete",
252
- // program.layout — async space-solver job family
253
- "program.layout.arrange",
254
- "program.layout.pack",
255
- "program.layout.getState",
256
- "program.layout.cancel",
257
- // door / window catalogs (shared DTOs) + placement creators
258
- "design.doors.listCatalogGroups",
259
- "design.doors.listCatalog",
260
- "design.doors.getCatalogItem",
261
- "design.doors.exists",
262
- "design.windows.listCatalogGroups",
263
- "design.windows.listCatalog",
264
- "design.windows.getCatalogItem",
265
- "design.windows.exists",
266
- "design.create.door",
267
- "design.create.window",
268
- // furniture categories + smart layout
269
- "design.furniture.listCategories",
270
- "design.create.smartLayout",
271
- // align
272
- "design.transform.align",
273
- // per-face materials
274
- "design.materials.applyToFaces",
275
- "design.materials.resetFaces",
276
- "design.materials.getByFace",
277
- "design.materials.listByFace",
278
- // staircase read/write
279
- "design.query.getStaircaseParams",
280
- "design.update.staircase",
281
- // footprint-area lock
282
- "design.lockArea",
283
- "design.unlockArea",
284
- "design.isAreaLocked",
285
- "design.listAreaLocked",
286
- // storey duplicate
287
- "entity.story.duplicate",
288
- // present-canvas SVG import + view display settings
289
- "presentation.import.svg",
290
- "presentation.views.getSettings",
291
- "presentation.views.updateSettings",
292
- ]
293
- for (const p of batch) {
294
- const m = byPath.get(p)
295
- assert.ok(m, `missing 0.7.0 batch method: ${p}`)
296
- assert.ok(m.summary.length > 0, `${p} has no intent summary`)
297
- assert.ok(m.examplePrompts.length >= 3, `${p} has < 3 example prompts`)
298
- assert.ok(
299
- m.resultType && m.resultType !== "void",
300
- `${p} has no result type`,
301
- )
302
- }
303
- })
1
+ // Unit tests for the discovery-manifest generator (pure codegen logic).
2
+ // Run: `node --test scripts/generate-manifest.test.mjs` (from packages/plugin-core)
3
+ import { test } from "node:test"
4
+ import assert from "node:assert/strict"
5
+ import { buildManifest } from "./generate-manifest.mjs"
6
+
7
+ const manifest = buildManifest()
8
+ const byPath = new Map(manifest.map((m) => [m.path, m]))
9
+
10
+ test("every entry has a dotted path and a namespace", () => {
11
+ for (const m of manifest) {
12
+ assert.match(m.path, /^[a-z][a-zA-Z0-9.]*$/, `bad path: ${m.path}`)
13
+ assert.equal(typeof m.namespace, "string")
14
+ assert.ok(
15
+ Array.isArray(m.examplePrompts),
16
+ `${m.path} examplePrompts not array`,
17
+ )
18
+ }
19
+ })
20
+
21
+ test("output is sorted and unique by path", () => {
22
+ const paths = manifest.map((m) => m.path)
23
+ assert.deepEqual(
24
+ paths,
25
+ [...paths].sort((a, b) => a.localeCompare(b)),
26
+ "not sorted",
27
+ )
28
+ assert.equal(new Set(paths).size, paths.length, "duplicate paths")
29
+ })
30
+
31
+ test("all 4 Phase-1 slice methods are present with intent + prompts", () => {
32
+ const slice = [
33
+ "program.departments.list",
34
+ "program.departments.get",
35
+ "program.metrics.get",
36
+ "presentation.views.list",
37
+ ]
38
+ for (const p of slice) {
39
+ const m = byPath.get(p)
40
+ assert.ok(m, `missing slice method: ${p}`)
41
+ assert.ok(m.summary.length > 0, `${p} has no intent summary`)
42
+ assert.ok(m.examplePrompts.length >= 3, `${p} has < 3 example prompts`)
43
+ assert.ok(
44
+ m.resultType && m.resultType !== "void",
45
+ `${p} has no result type`,
46
+ )
47
+ }
48
+ })
49
+
50
+ test("departments.get carries its args type; list takes no args", () => {
51
+ assert.equal(byPath.get("program.departments.get").argsType, "string")
52
+ assert.equal(byPath.get("program.departments.list").argsType, null)
53
+ })
54
+
55
+ test("return-type wrapper PluginApiReturn<...> is unwrapped", () => {
56
+ for (const m of manifest) {
57
+ assert.ok(
58
+ !/^PluginApiReturn</.test(m.resultType ?? ""),
59
+ `${m.path} not unwrapped`,
60
+ )
61
+ }
62
+ })
63
+
64
+ test("author-ahead (optional) members are kept OUT of the manifest", () => {
65
+ // Optional sub-namespaces (`aiInspiration?`) and optional leaf methods
66
+ // (`departments.delete?`, `cores.create?`) are authored ahead of the host and
67
+ // must not appear in the discovery manifest until promoted to required
68
+ // `abstract` members. (See the discipline note in generate-manifest.mjs.)
69
+ const authoredAhead = [
70
+ "presentation.aiInspiration.listModels",
71
+ "program.departments.delete",
72
+ "program.cores.create",
73
+ ]
74
+ for (const p of authoredAhead) {
75
+ assert.ok(
76
+ !byPath.has(p),
77
+ `optional author-ahead member leaked into manifest: ${p}`,
78
+ )
79
+ }
80
+ })
81
+
82
+ test("promoted methods are present in the manifest", () => {
83
+ // Methods whose react host bodies have landed and been verified, flipped from
84
+ // optional author-ahead to required `abstract`.
85
+ const promoted = [
86
+ "program.departments.create",
87
+ "program.departments.setTargetArea",
88
+ "program.site.get",
89
+ "program.site.getContext",
90
+ "presentation.import.image",
91
+ "presentation.import.pdf",
92
+ "presentation.sheets.list",
93
+ "presentation.sheets.place",
94
+ "presentation.diagrams.place",
95
+ // 2026-07-02 promotions — react hosts landed and verified:
96
+ "program.spreadsheet.render",
97
+ "program.adjacency.getMatrix",
98
+ "program.adjacency.compute",
99
+ "program.spreadsheet.getTakeoff",
100
+ "program.spreadsheet.runScript",
101
+ "presentation.views.get",
102
+ "presentation.views.getActive",
103
+ "presentation.views.capture",
104
+ "presentation.views.setActive",
105
+ "presentation.views.create",
106
+ "core.storeys.layers.setActive",
107
+ "core.storeys.layers.update",
108
+ "program.areas.update",
109
+ // 2026-07-06 promotions — analysis namespace + program.site geo reads,
110
+ // react hosts landed:
111
+ "analysis.sunpath.enable",
112
+ "analysis.sunpath.disable",
113
+ "analysis.sunpath.isActive",
114
+ "analysis.shadows.enable",
115
+ "analysis.shadows.disable",
116
+ "analysis.shadows.isEnabled",
117
+ "analysis.shadows.setDateTime",
118
+ "analysis.shadows.getDateTime",
119
+ "analysis.sunlightHours.compute",
120
+ "analysis.sunlightHours.get",
121
+ "analysis.sunlightHours.cancel",
122
+ "analysis.sunlightHours.reset",
123
+ "analysis.illuminance.compute",
124
+ "analysis.illuminance.get",
125
+ "analysis.illuminance.cancel",
126
+ "analysis.illuminance.reset",
127
+ "program.site.getLocation",
128
+ "program.site.getNorthAngle",
129
+ // 2026-07-07 promotion — analysis.heatmaps, react host landed:
130
+ "analysis.heatmaps.renderSpaces",
131
+ "analysis.heatmaps.renderGrid",
132
+ "analysis.heatmaps.reset",
133
+ "analysis.heatmaps.isActive",
134
+ // 2026-07-09 promotion — program.layout became an async-job family (was
135
+ // author-ahead optional `arrange`/`pack`; now required + getState/cancel):
136
+ "program.layout.arrange",
137
+ "program.layout.pack",
138
+ "program.layout.getState",
139
+ "program.layout.cancel",
140
+ ]
141
+ for (const p of promoted) {
142
+ assert.ok(
143
+ byPath.has(p),
144
+ `expected promoted method missing from manifest: ${p}`,
145
+ )
146
+ }
147
+ })
148
+
149
+ test("v2 family reads (all 5 families) are present with intent + prompts", () => {
150
+ // All 22 reads of the five landed families. Every sub-api mount is now required
151
+ // `abstract` (the react hosts have landed), so each read is in the discovery
152
+ // manifest with a summary, >= 3 example prompts, and a result type.
153
+ const reads = [
154
+ "core.groups.list",
155
+ "core.groups.get",
156
+ "core.groups.getForComponent",
157
+ "core.groups.listMembers",
158
+ "core.groups.isLocked",
159
+ "core.groups.getBounds",
160
+ "core.buildings.list",
161
+ "core.buildings.get",
162
+ "core.buildings.listStoreys",
163
+ "core.buildings.getActive",
164
+ "core.storeys.layers.list",
165
+ "core.storeys.layers.get",
166
+ "core.storeys.layers.getActive",
167
+ // core.layers — the Layers panel (0.9.14)
168
+ "core.layers.list",
169
+ "core.layers.get",
170
+ "core.layers.getActive",
171
+ "core.tags.listCategories",
172
+ "core.tags.getCategory",
173
+ "core.tags.list",
174
+ "core.tags.get",
175
+ "core.tags.getTagsForComponent",
176
+ "core.tags.listComponents",
177
+ "program.areas.get",
178
+ "program.areas.list",
179
+ "program.areas.listMembers",
180
+ ]
181
+ for (const p of reads) {
182
+ const m = byPath.get(p)
183
+ assert.ok(m, `missing v2 family read: ${p}`)
184
+ assert.ok(m.summary.length > 0, `${p} has no intent summary`)
185
+ assert.ok(m.examplePrompts.length >= 3, `${p} has < 3 example prompts`)
186
+ assert.ok(
187
+ m.resultType && m.resultType !== "void",
188
+ `${p} has no result type`,
189
+ )
190
+ }
191
+ })
192
+
193
+ test("v2 family writes are IN the discovery manifest (hosts landed, promoted)", () => {
194
+ // 2026-07-02: every v2 family write's react host body has landed, so the
195
+ // contract members were promoted from optional `foo?: (args) => ...`
196
+ // fn-properties to required `abstract` methods — they now appear in the
197
+ // discovery build.
198
+ const promotedWrites = [
199
+ "core.groups.create",
200
+ "core.groups.update",
201
+ "core.groups.delete",
202
+ "core.buildings.setActive",
203
+ "core.buildings.create",
204
+ "core.buildings.update",
205
+ "core.buildings.copy",
206
+ "core.buildings.delete",
207
+ "core.storeys.layers.setActive",
208
+ "core.storeys.layers.update",
209
+ // core.layers — the Layers panel (0.9.14)
210
+ "core.layers.create",
211
+ "core.layers.rename",
212
+ "core.layers.update",
213
+ "core.layers.setActive",
214
+ "core.layers.delete",
215
+ "core.layers.assign",
216
+ "core.tags.createCategory",
217
+ "core.tags.updateCategory",
218
+ "core.tags.deleteCategory",
219
+ "core.tags.create",
220
+ "core.tags.update",
221
+ "core.tags.delete",
222
+ "core.tags.assign",
223
+ "core.tags.unassign",
224
+ "program.areas.update",
225
+ ]
226
+ for (const p of promotedWrites) {
227
+ assert.ok(
228
+ byPath.has(p),
229
+ `promoted v2 family write missing from manifest: ${p}`,
230
+ )
231
+ }
232
+ })
233
+
234
+ test("0.7.0 batch — new families & methods present with intent + prompts", () => {
235
+ // The 0.7.0 wave: core.proposals, the program.layout async-job family,
236
+ // door/window catalogs + placement, furniture categories, smart layout,
237
+ // align, per-face materials, staircase read/write, footprint-area lock,
238
+ // storey duplicate, SVG import, and view display settings. Every entry is a
239
+ // required `abstract` member (react hosts landed), so each must be in the
240
+ // discovery manifest with a summary, >= 3 example prompts, and a result type.
241
+ const batch = [
242
+ // core.proposals — design-option infrastructure
243
+ "core.proposals.list",
244
+ "core.proposals.get",
245
+ "core.proposals.getActive",
246
+ "core.proposals.listForComponent",
247
+ "core.proposals.isActive",
248
+ "core.proposals.create",
249
+ "core.proposals.rename",
250
+ "core.proposals.setActive",
251
+ "core.proposals.delete",
252
+ // program.layout — async space-solver job family
253
+ "program.layout.arrange",
254
+ "program.layout.pack",
255
+ "program.layout.getState",
256
+ "program.layout.cancel",
257
+ // door / window catalogs (shared DTOs) + placement creators
258
+ "design.doors.listCatalogGroups",
259
+ "design.doors.listCatalog",
260
+ "design.doors.getCatalogItem",
261
+ "design.doors.exists",
262
+ "design.windows.listCatalogGroups",
263
+ "design.windows.listCatalog",
264
+ "design.windows.getCatalogItem",
265
+ "design.windows.exists",
266
+ "design.create.door",
267
+ "design.create.window",
268
+ // furniture categories + smart layout
269
+ "design.furniture.listCategories",
270
+ "design.create.smartLayout",
271
+ // align
272
+ "design.transform.align",
273
+ // per-face materials
274
+ "design.materials.applyToFaces",
275
+ "design.materials.resetFaces",
276
+ "design.materials.getByFace",
277
+ "design.materials.listByFace",
278
+ // staircase read/write
279
+ "design.query.getStaircaseParams",
280
+ "design.update.staircase",
281
+ // footprint-area lock
282
+ "design.lockArea",
283
+ "design.unlockArea",
284
+ "design.isAreaLocked",
285
+ "design.listAreaLocked",
286
+ // storey duplicate
287
+ "entity.story.duplicate",
288
+ // present-canvas SVG import + view display settings
289
+ "presentation.import.svg",
290
+ "presentation.views.getSettings",
291
+ "presentation.views.updateSettings",
292
+ ]
293
+ for (const p of batch) {
294
+ const m = byPath.get(p)
295
+ assert.ok(m, `missing 0.7.0 batch method: ${p}`)
296
+ assert.ok(m.summary.length > 0, `${p} has no intent summary`)
297
+ assert.ok(m.examplePrompts.length >= 3, `${p} has < 3 example prompts`)
298
+ assert.ok(
299
+ m.resultType && m.resultType !== "void",
300
+ `${p} has no result type`,
301
+ )
302
+ }
303
+ })