@ifc-lite/sandbox 1.14.2 → 1.14.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/bridge-schema.d.ts +39 -2
- package/dist/bridge-schema.d.ts.map +1 -1
- package/dist/bridge-schema.js +935 -181
- package/dist/bridge-schema.js.map +1 -1
- package/dist/bridge.d.ts +3 -1
- package/dist/bridge.d.ts.map +1 -1
- package/dist/bridge.js +9 -4
- package/dist/bridge.js.map +1 -1
- package/dist/index.d.ts +1 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js.map +1 -1
- package/dist/sandbox.d.ts +2 -0
- package/dist/sandbox.d.ts.map +1 -1
- package/dist/sandbox.js +16 -1
- package/dist/sandbox.js.map +1 -1
- package/dist/transpile.d.ts +2 -0
- package/dist/transpile.d.ts.map +1 -1
- package/dist/transpile.js +38 -3
- package/dist/transpile.js.map +1 -1
- package/dist/types.d.ts +2 -0
- package/dist/types.d.ts.map +1 -1
- package/dist/types.js +1 -0
- package/dist/types.js.map +1 -1
- package/package.json +2 -2
package/dist/bridge-schema.js
CHANGED
|
@@ -7,22 +7,42 @@ import { IfcCreator } from '@ifc-lite/sdk';
|
|
|
7
7
|
// ============================================================================
|
|
8
8
|
/** Simple registry for IfcCreator instances managed by the sandbox */
|
|
9
9
|
const creatorRegistry = (() => {
|
|
10
|
-
|
|
11
|
-
const
|
|
10
|
+
const nextHandleBySession = new Map();
|
|
11
|
+
const creatorsBySession = new Map();
|
|
12
|
+
function getSessionCreators(sessionId) {
|
|
13
|
+
let sessionCreators = creatorsBySession.get(sessionId);
|
|
14
|
+
if (!sessionCreators) {
|
|
15
|
+
sessionCreators = new Map();
|
|
16
|
+
creatorsBySession.set(sessionId, sessionCreators);
|
|
17
|
+
}
|
|
18
|
+
return sessionCreators;
|
|
19
|
+
}
|
|
12
20
|
return {
|
|
13
|
-
|
|
14
|
-
const handle =
|
|
15
|
-
|
|
21
|
+
registerForSession(sessionId, creator) {
|
|
22
|
+
const handle = nextHandleBySession.get(sessionId) ?? 1;
|
|
23
|
+
nextHandleBySession.set(sessionId, handle + 1);
|
|
24
|
+
getSessionCreators(sessionId).set(handle, creator);
|
|
16
25
|
return handle;
|
|
17
26
|
},
|
|
18
|
-
|
|
19
|
-
const creator =
|
|
27
|
+
getForSession(sessionId, handle) {
|
|
28
|
+
const creator = creatorsBySession.get(sessionId)?.get(handle);
|
|
20
29
|
if (!creator)
|
|
21
30
|
throw new Error(`Invalid creator handle: ${handle}`);
|
|
22
31
|
return creator;
|
|
23
32
|
},
|
|
24
|
-
|
|
25
|
-
|
|
33
|
+
removeForSession(sessionId, handle) {
|
|
34
|
+
const sessionCreators = creatorsBySession.get(sessionId);
|
|
35
|
+
if (!sessionCreators)
|
|
36
|
+
return;
|
|
37
|
+
sessionCreators.delete(handle);
|
|
38
|
+
if (sessionCreators.size === 0) {
|
|
39
|
+
creatorsBySession.delete(sessionId);
|
|
40
|
+
nextHandleBySession.delete(sessionId);
|
|
41
|
+
}
|
|
42
|
+
},
|
|
43
|
+
removeSession(sessionId) {
|
|
44
|
+
creatorsBySession.delete(sessionId);
|
|
45
|
+
nextHandleBySession.delete(sessionId);
|
|
26
46
|
},
|
|
27
47
|
};
|
|
28
48
|
})();
|
|
@@ -62,6 +82,487 @@ function toRef(raw) {
|
|
|
62
82
|
}
|
|
63
83
|
return null;
|
|
64
84
|
}
|
|
85
|
+
function mapNamedProperties(properties) {
|
|
86
|
+
return properties.map((property) => ({
|
|
87
|
+
name: property.name, Name: property.name,
|
|
88
|
+
value: property.value, Value: property.value,
|
|
89
|
+
NominalValue: property.value,
|
|
90
|
+
type: property.type, Type: property.type,
|
|
91
|
+
}));
|
|
92
|
+
}
|
|
93
|
+
/** Methods with non-standard signatures that need hand-written wiring */
|
|
94
|
+
const SPECIAL_METHODS = new Set([
|
|
95
|
+
'constructor', 'toIfc', 'setColor',
|
|
96
|
+
]);
|
|
97
|
+
/**
|
|
98
|
+
* Explicit allow-list of IfcCreator methods exposed in the sandbox.
|
|
99
|
+
* Only methods in this set (or in SPECIAL_METHODS/ELEMENT_METHODS/ZERO_ARG_METHODS)
|
|
100
|
+
* are wired. This prevents accidental exposure of private/internal helpers.
|
|
101
|
+
*/
|
|
102
|
+
const ALLOWED_METHODS = new Set([
|
|
103
|
+
// Spatial structure
|
|
104
|
+
'addIfcBuildingStorey',
|
|
105
|
+
// Building elements
|
|
106
|
+
'addIfcWall', 'addIfcSlab', 'addIfcColumn', 'addIfcBeam',
|
|
107
|
+
'addIfcStair', 'addIfcRoof', 'addIfcGableRoof', 'addIfcWallDoor', 'addIfcWallWindow', 'addIfcDoor', 'addIfcWindow',
|
|
108
|
+
'addIfcRamp', 'addIfcRailing', 'addIfcPlate', 'addIfcMember',
|
|
109
|
+
'addIfcFooting', 'addIfcPile', 'addIfcSpace', 'addIfcCurtainWall',
|
|
110
|
+
'addIfcFurnishingElement', 'addIfcBuildingElementProxy',
|
|
111
|
+
// Specialized profiles
|
|
112
|
+
'addIfcCircularColumn', 'addIfcIShapeBeam',
|
|
113
|
+
'addIfcLShapeMember', 'addIfcTShapeMember', 'addIfcUShapeMember',
|
|
114
|
+
'addIfcHollowCircularColumn', 'addIfcRectangleHollowBeam',
|
|
115
|
+
// Generic element creation
|
|
116
|
+
'addElement', 'addAxisElement', 'createProfile',
|
|
117
|
+
// Properties and materials
|
|
118
|
+
'addIfcPropertySet', 'addIfcElementQuantity', 'addIfcMaterial',
|
|
119
|
+
// Low-level geometry
|
|
120
|
+
'getWorldPlacementId',
|
|
121
|
+
]);
|
|
122
|
+
/** Methods that take (elementId, def) instead of (storeyId, params) */
|
|
123
|
+
const ELEMENT_METHODS = new Set([
|
|
124
|
+
'addIfcWallDoor', 'addIfcWallWindow',
|
|
125
|
+
'addIfcPropertySet', 'addIfcElementQuantity', 'addIfcMaterial',
|
|
126
|
+
]);
|
|
127
|
+
/** Methods that take a single params object after the handle */
|
|
128
|
+
const SINGLE_DUMP_METHODS = new Set([
|
|
129
|
+
'createProfile',
|
|
130
|
+
]);
|
|
131
|
+
/** Methods with zero args (just need the handle) */
|
|
132
|
+
const ZERO_ARG_METHODS = new Set([
|
|
133
|
+
'getWorldPlacementId',
|
|
134
|
+
]);
|
|
135
|
+
function classifyMethod(name, _fn) {
|
|
136
|
+
if (SPECIAL_METHODS.has(name))
|
|
137
|
+
return 'special';
|
|
138
|
+
if (ZERO_ARG_METHODS.has(name))
|
|
139
|
+
return 'no-args';
|
|
140
|
+
if (SINGLE_DUMP_METHODS.has(name))
|
|
141
|
+
return 'single-dump';
|
|
142
|
+
if (ELEMENT_METHODS.has(name))
|
|
143
|
+
return 'element-params';
|
|
144
|
+
return 'storey-params';
|
|
145
|
+
}
|
|
146
|
+
/** Humanize a method name for the doc string */
|
|
147
|
+
function methodDoc(name) {
|
|
148
|
+
// addIfcWall → 'Add an IfcWall'
|
|
149
|
+
// addElement → 'Add a generic element'
|
|
150
|
+
// createProfile → 'Create a profile from a ProfileDef'
|
|
151
|
+
if (name === 'addIfcBuildingStorey')
|
|
152
|
+
return 'Add a building storey. Returns storey expressId.';
|
|
153
|
+
if (name === 'addIfcGableRoof')
|
|
154
|
+
return 'Add a dual-pitch gable roof. `Slope` is in radians. Returns roof expressId.';
|
|
155
|
+
if (name === 'addIfcWallDoor')
|
|
156
|
+
return 'Add a door hosted in a wall opening. Position is wall-local [alongWall, 0, baseHeight]. Returns door expressId.';
|
|
157
|
+
if (name === 'addIfcWallWindow')
|
|
158
|
+
return 'Add a window hosted in a wall opening. Position is wall-local [alongWall, 0, sillHeight]. Returns window expressId.';
|
|
159
|
+
if (name === 'addElement')
|
|
160
|
+
return 'Create ANY IFC type with a profile at a placement. Returns expressId.';
|
|
161
|
+
if (name === 'addAxisElement')
|
|
162
|
+
return 'Create ANY IFC type extruded along a Start→End axis. Returns expressId.';
|
|
163
|
+
if (name === 'createProfile')
|
|
164
|
+
return 'Create a profile from a ProfileDef union. Returns profile ID.';
|
|
165
|
+
if (name === 'getWorldPlacementId')
|
|
166
|
+
return 'Get the world placement ID for use with addLocalPlacement.';
|
|
167
|
+
if (name.startsWith('addIfc')) {
|
|
168
|
+
const entity = name.slice(3); // remove 'add', keep 'IfcWall' etc.
|
|
169
|
+
return `Add ${entity}. Returns expressId.`;
|
|
170
|
+
}
|
|
171
|
+
if (name.startsWith('add')) {
|
|
172
|
+
const what = name.slice(3);
|
|
173
|
+
return `Add ${what}. Returns ID.`;
|
|
174
|
+
}
|
|
175
|
+
return `Call ${name} on the creator.`;
|
|
176
|
+
}
|
|
177
|
+
const CREATE_METHOD_SEMANTICS = {
|
|
178
|
+
project: {
|
|
179
|
+
taskTags: ['create', 'repair'],
|
|
180
|
+
useWhen: 'Start a new generated IFC model before creating storeys and elements.',
|
|
181
|
+
},
|
|
182
|
+
toIfc: {
|
|
183
|
+
taskTags: ['create', 'export'],
|
|
184
|
+
useWhen: 'Finalize the in-memory IFC model and produce STEP content for preview or download.',
|
|
185
|
+
},
|
|
186
|
+
setColor: {
|
|
187
|
+
taskTags: ['visualize', 'repair'],
|
|
188
|
+
useWhen: 'Assign a named RGB color to a created element using [r, g, b] values between 0 and 1.',
|
|
189
|
+
},
|
|
190
|
+
addIfcBuildingStorey: {
|
|
191
|
+
taskTags: ['create', 'repair'],
|
|
192
|
+
requiredKeys: ['Elevation'],
|
|
193
|
+
useWhen: 'Create a building storey container before adding level-based geometry.',
|
|
194
|
+
},
|
|
195
|
+
addIfcWall: {
|
|
196
|
+
taskTags: ['create', 'repair'],
|
|
197
|
+
placement: 'storey-relative',
|
|
198
|
+
requiredKeys: ['Start', 'End', 'Thickness', 'Height'],
|
|
199
|
+
positiveKeys: ['Thickness', 'Height'],
|
|
200
|
+
pointArity: { Start: 3, End: 3 },
|
|
201
|
+
axisPair: ['Start', 'End'],
|
|
202
|
+
useWhen: 'Create a wall from a start/end axis on the current storey.',
|
|
203
|
+
},
|
|
204
|
+
addIfcSlab: {
|
|
205
|
+
taskTags: ['create', 'repair'],
|
|
206
|
+
placement: 'storey-relative',
|
|
207
|
+
requiredKeys: ['Position', 'Thickness'],
|
|
208
|
+
anyOfKeys: [['Profile'], ['Width', 'Depth']],
|
|
209
|
+
positiveKeys: ['Thickness', 'Width', 'Depth'],
|
|
210
|
+
pointArity: { Position: 3 },
|
|
211
|
+
customValidationId: 'slab-shape',
|
|
212
|
+
useWhen: 'Create a slab from a rectangular footprint or a 2D point-array profile.',
|
|
213
|
+
},
|
|
214
|
+
addIfcColumn: {
|
|
215
|
+
taskTags: ['create', 'repair'],
|
|
216
|
+
placement: 'storey-relative',
|
|
217
|
+
requiredKeys: ['Position', 'Width', 'Depth', 'Height'],
|
|
218
|
+
positiveKeys: ['Width', 'Depth', 'Height'],
|
|
219
|
+
pointArity: { Position: 3 },
|
|
220
|
+
useWhen: 'Create a vertical column from a base position and dimensions.',
|
|
221
|
+
},
|
|
222
|
+
addIfcBeam: {
|
|
223
|
+
taskTags: ['create', 'repair'],
|
|
224
|
+
placement: 'storey-relative',
|
|
225
|
+
requiredKeys: ['Start', 'End', 'Width', 'Height'],
|
|
226
|
+
positiveKeys: ['Width', 'Height'],
|
|
227
|
+
pointArity: { Start: 3, End: 3 },
|
|
228
|
+
axisPair: ['Start', 'End'],
|
|
229
|
+
useWhen: 'Create a beam from an axis on the current storey.',
|
|
230
|
+
},
|
|
231
|
+
addIfcMember: {
|
|
232
|
+
taskTags: ['create', 'repair'],
|
|
233
|
+
placement: 'world',
|
|
234
|
+
requiredKeys: ['Start', 'End', 'Width', 'Height'],
|
|
235
|
+
positiveKeys: ['Width', 'Height'],
|
|
236
|
+
pointArity: { Start: 3, End: 3 },
|
|
237
|
+
axisPair: ['Start', 'End'],
|
|
238
|
+
useWhen: 'Create mullions, braces, or facade members with explicit world coordinates.',
|
|
239
|
+
cautions: [
|
|
240
|
+
'Inside storey loops, include the current storey elevation in Start/End Z for facade members.',
|
|
241
|
+
],
|
|
242
|
+
},
|
|
243
|
+
addIfcPlate: {
|
|
244
|
+
taskTags: ['create', 'repair'],
|
|
245
|
+
placement: 'world',
|
|
246
|
+
requiredKeys: ['Position', 'Width', 'Depth', 'Thickness'],
|
|
247
|
+
positiveKeys: ['Width', 'Depth', 'Thickness'],
|
|
248
|
+
pointArity: { Position: 3 },
|
|
249
|
+
forbiddenKeys: [
|
|
250
|
+
{ key: 'Height', message: '`bim.create.addIfcPlate(...)` uses `Depth` and `Thickness`, not `Height`.' },
|
|
251
|
+
{ key: 'Start', message: '`bim.create.addIfcPlate(...)` uses `Position`, not `Start`/`End`.' },
|
|
252
|
+
{ key: 'End', message: '`bim.create.addIfcPlate(...)` uses `Position`, not `Start`/`End`.' },
|
|
253
|
+
],
|
|
254
|
+
useWhen: 'Create thin world-placement panels or facade plates from a base point.',
|
|
255
|
+
cautions: [
|
|
256
|
+
'Facade plates repeated by storey usually need absolute Z = elevation + localOffset.',
|
|
257
|
+
],
|
|
258
|
+
},
|
|
259
|
+
addIfcCurtainWall: {
|
|
260
|
+
taskTags: ['create', 'repair'],
|
|
261
|
+
placement: 'world',
|
|
262
|
+
requiredKeys: ['Start', 'End', 'Height'],
|
|
263
|
+
positiveKeys: ['Height', 'Thickness'],
|
|
264
|
+
pointArity: { Start: 3, End: 3 },
|
|
265
|
+
axisPair: ['Start', 'End'],
|
|
266
|
+
useWhen: 'Create a world-placement curtain wall segment between two points.',
|
|
267
|
+
cautions: [
|
|
268
|
+
'Inside storey loops, include the current storey elevation in Start/End Z.',
|
|
269
|
+
],
|
|
270
|
+
},
|
|
271
|
+
addIfcRailing: {
|
|
272
|
+
taskTags: ['create', 'repair'],
|
|
273
|
+
placement: 'world',
|
|
274
|
+
requiredKeys: ['Start', 'End', 'Height'],
|
|
275
|
+
positiveKeys: ['Height', 'Width'],
|
|
276
|
+
pointArity: { Start: 3, End: 3 },
|
|
277
|
+
axisPair: ['Start', 'End'],
|
|
278
|
+
useWhen: 'Create a world-placement railing along an axis.',
|
|
279
|
+
},
|
|
280
|
+
addIfcStair: {
|
|
281
|
+
taskTags: ['create', 'repair'],
|
|
282
|
+
placement: 'storey-relative',
|
|
283
|
+
requiredKeys: ['Position', 'NumberOfRisers', 'RiserHeight', 'TreadLength', 'Width'],
|
|
284
|
+
positiveKeys: ['NumberOfRisers', 'RiserHeight', 'TreadLength', 'Width'],
|
|
285
|
+
pointArity: { Position: 3 },
|
|
286
|
+
useWhen: 'Create a stair from a base position and riser/tread definition.',
|
|
287
|
+
},
|
|
288
|
+
addIfcRoof: {
|
|
289
|
+
taskTags: ['create', 'repair'],
|
|
290
|
+
placement: 'storey-relative',
|
|
291
|
+
requiredKeys: ['Position', 'Width', 'Depth', 'Thickness'],
|
|
292
|
+
positiveKeys: ['Width', 'Depth', 'Thickness', 'Slope'],
|
|
293
|
+
pointArity: { Position: 3 },
|
|
294
|
+
forbiddenKeys: [
|
|
295
|
+
{ key: 'Profile', message: '`bim.create.addIfcRoof(...)` does not support `Profile`. Use `Position`, `Width`, `Depth`, `Thickness`, and optional `Slope`.' },
|
|
296
|
+
{ key: 'ExtrusionHeight', message: '`bim.create.addIfcRoof(...)` uses `Depth`, not `ExtrusionHeight`.' },
|
|
297
|
+
{ key: 'Height', message: '`bim.create.addIfcRoof(...)` uses `Thickness` and `Depth`, not `Height`.' },
|
|
298
|
+
{ key: 'Overhang', message: '`bim.create.addIfcRoof(...)` does not support `Overhang`. Use `addIfcGableRoof(...)` for a house-style roof with pitch and overhang.' },
|
|
299
|
+
],
|
|
300
|
+
customValidationId: 'roof-shape',
|
|
301
|
+
useWhen: 'Create flat or mono-pitch roof slabs only.',
|
|
302
|
+
cautions: [
|
|
303
|
+
'Slope is in radians.',
|
|
304
|
+
'Use addIfcGableRoof for house, pitched-roof, or gable-roof requests.',
|
|
305
|
+
],
|
|
306
|
+
},
|
|
307
|
+
addIfcGableRoof: {
|
|
308
|
+
taskTags: ['create', 'repair'],
|
|
309
|
+
placement: 'storey-relative',
|
|
310
|
+
requiredKeys: ['Position', 'Width', 'Depth', 'Thickness', 'Slope'],
|
|
311
|
+
positiveKeys: ['Width', 'Depth', 'Thickness', 'Slope'],
|
|
312
|
+
pointArity: { Position: 3 },
|
|
313
|
+
forbiddenKeys: [
|
|
314
|
+
{ key: 'Profile', message: '`bim.create.addIfcGableRoof(...)` does not support `Profile`. Use `Position`, `Width`, `Depth`, `Thickness`, `Slope`, and optional `Overhang`.' },
|
|
315
|
+
{ key: 'ExtrusionHeight', message: '`bim.create.addIfcGableRoof(...)` uses `Thickness`, not `ExtrusionHeight`.' },
|
|
316
|
+
{ key: 'Height', message: '`bim.create.addIfcGableRoof(...)` uses `Thickness` for roof thickness and derives ridge height from `Slope`.' },
|
|
317
|
+
],
|
|
318
|
+
customValidationId: 'roof-shape',
|
|
319
|
+
useWhen: 'Create standard dual-pitch house roofs.',
|
|
320
|
+
cautions: [
|
|
321
|
+
'Slope is in radians.',
|
|
322
|
+
],
|
|
323
|
+
},
|
|
324
|
+
addIfcWallDoor: {
|
|
325
|
+
taskTags: ['create', 'repair'],
|
|
326
|
+
placement: 'wall-local',
|
|
327
|
+
requiredKeys: ['Position', 'Width', 'Height'],
|
|
328
|
+
positiveKeys: ['Width', 'Height', 'Thickness'],
|
|
329
|
+
pointArity: { Position: 3 },
|
|
330
|
+
forbiddenKeys: [
|
|
331
|
+
{ key: 'Start', message: '`bim.create.addIfcWallDoor(...)` uses wall-local `Position`, not `Start`/`End`.' },
|
|
332
|
+
{ key: 'End', message: '`bim.create.addIfcWallDoor(...)` uses wall-local `Position`, not `Start`/`End`.' },
|
|
333
|
+
{ key: 'Rotation', message: '`bim.create.addIfcWallDoor(...)` auto-aligns to the host wall. Do not pass `Rotation`.' },
|
|
334
|
+
{ key: 'Direction', message: '`bim.create.addIfcWallDoor(...)` auto-aligns to the host wall. Do not pass `Direction`.' },
|
|
335
|
+
{ key: 'Axis', message: '`bim.create.addIfcWallDoor(...)` auto-aligns to the host wall. Do not pass `Axis`.' },
|
|
336
|
+
{ key: 'RefDirection', message: '`bim.create.addIfcWallDoor(...)` auto-aligns to the host wall. Do not pass `RefDirection`.' },
|
|
337
|
+
{ key: 'Placement', message: '`bim.create.addIfcWallDoor(...)` uses wall-local `Position`, not `Placement`.' },
|
|
338
|
+
],
|
|
339
|
+
useWhen: 'Create a wall-hosted door aligned to a host wall.',
|
|
340
|
+
},
|
|
341
|
+
addIfcWallWindow: {
|
|
342
|
+
taskTags: ['create', 'repair'],
|
|
343
|
+
placement: 'wall-local',
|
|
344
|
+
requiredKeys: ['Position', 'Width', 'Height'],
|
|
345
|
+
positiveKeys: ['Width', 'Height', 'Thickness'],
|
|
346
|
+
pointArity: { Position: 3 },
|
|
347
|
+
forbiddenKeys: [
|
|
348
|
+
{ key: 'Start', message: '`bim.create.addIfcWallWindow(...)` uses wall-local `Position`, not `Start`/`End`.' },
|
|
349
|
+
{ key: 'End', message: '`bim.create.addIfcWallWindow(...)` uses wall-local `Position`, not `Start`/`End`.' },
|
|
350
|
+
{ key: 'Rotation', message: '`bim.create.addIfcWallWindow(...)` auto-aligns to the host wall. Do not pass `Rotation`.' },
|
|
351
|
+
{ key: 'Direction', message: '`bim.create.addIfcWallWindow(...)` auto-aligns to the host wall. Do not pass `Direction`.' },
|
|
352
|
+
{ key: 'Axis', message: '`bim.create.addIfcWallWindow(...)` auto-aligns to the host wall. Do not pass `Axis`.' },
|
|
353
|
+
{ key: 'RefDirection', message: '`bim.create.addIfcWallWindow(...)` auto-aligns to the host wall. Do not pass `RefDirection`.' },
|
|
354
|
+
{ key: 'Placement', message: '`bim.create.addIfcWallWindow(...)` uses wall-local `Position`, not `Placement`.' },
|
|
355
|
+
],
|
|
356
|
+
useWhen: 'Create a wall-hosted window aligned to a host wall.',
|
|
357
|
+
},
|
|
358
|
+
addIfcDoor: {
|
|
359
|
+
taskTags: ['create', 'repair'],
|
|
360
|
+
placement: 'world',
|
|
361
|
+
requiredKeys: ['Position', 'Width', 'Height'],
|
|
362
|
+
positiveKeys: ['Width', 'Height', 'Thickness'],
|
|
363
|
+
pointArity: { Position: 3 },
|
|
364
|
+
forbiddenKeys: [
|
|
365
|
+
{ key: 'Start', message: '`bim.create.addIfcDoor(...)` uses `Position`, not `Start`/`End`.' },
|
|
366
|
+
{ key: 'End', message: '`bim.create.addIfcDoor(...)` uses `Position`, not `Start`/`End`.' },
|
|
367
|
+
{ key: 'Direction', message: '`bim.create.addIfcDoor(...)` does not support wall-axis rotation. It creates a world-aligned standalone door element.' },
|
|
368
|
+
{ key: 'Rotation', message: '`bim.create.addIfcDoor(...)` does not support rotation. For wall-hosted inserts, use `bim.create.addIfcWallDoor(...)` or wall `Openings`.' },
|
|
369
|
+
{ key: 'Axis', message: '`bim.create.addIfcDoor(...)` does not accept `Axis`. It is not a generic placement API.' },
|
|
370
|
+
{ key: 'RefDirection', message: '`bim.create.addIfcDoor(...)` does not accept `RefDirection`. It is not auto-aligned to wall direction.' },
|
|
371
|
+
{ key: 'Placement', message: '`bim.create.addIfcDoor(...)` uses `Position`, not `Placement`.' },
|
|
372
|
+
],
|
|
373
|
+
useWhen: 'Create a standalone world-aligned door element.',
|
|
374
|
+
cautions: [
|
|
375
|
+
'For wall-hosted inserts, use addIfcWallDoor or wall Openings instead.',
|
|
376
|
+
],
|
|
377
|
+
},
|
|
378
|
+
addIfcWindow: {
|
|
379
|
+
taskTags: ['create', 'repair'],
|
|
380
|
+
placement: 'world',
|
|
381
|
+
requiredKeys: ['Position', 'Width', 'Height'],
|
|
382
|
+
positiveKeys: ['Width', 'Height', 'Thickness'],
|
|
383
|
+
pointArity: { Position: 3 },
|
|
384
|
+
forbiddenKeys: [
|
|
385
|
+
{ key: 'Start', message: '`bim.create.addIfcWindow(...)` uses `Position`, not `Start`/`End`.' },
|
|
386
|
+
{ key: 'End', message: '`bim.create.addIfcWindow(...)` uses `Position`, not `Start`/`End`.' },
|
|
387
|
+
{ key: 'Direction', message: '`bim.create.addIfcWindow(...)` does not support wall-axis rotation. It creates a world-aligned standalone window element.' },
|
|
388
|
+
{ key: 'Rotation', message: '`bim.create.addIfcWindow(...)` does not support rotation. For wall-hosted inserts, use `bim.create.addIfcWallWindow(...)` or wall `Openings`.' },
|
|
389
|
+
{ key: 'Axis', message: '`bim.create.addIfcWindow(...)` does not accept `Axis`. It is not a generic placement API.' },
|
|
390
|
+
{ key: 'RefDirection', message: '`bim.create.addIfcWindow(...)` does not accept `RefDirection`. It is not auto-aligned to wall direction.' },
|
|
391
|
+
{ key: 'Placement', message: '`bim.create.addIfcWindow(...)` uses `Position`, not `Placement`.' },
|
|
392
|
+
],
|
|
393
|
+
useWhen: 'Create a standalone world-aligned window element.',
|
|
394
|
+
cautions: [
|
|
395
|
+
'For wall-hosted inserts, use addIfcWallWindow or wall Openings instead.',
|
|
396
|
+
],
|
|
397
|
+
},
|
|
398
|
+
addElement: {
|
|
399
|
+
taskTags: ['create', 'repair'],
|
|
400
|
+
placement: 'explicit-placement',
|
|
401
|
+
requiredKeys: ['IfcType', 'Placement', 'Profile', 'Depth'],
|
|
402
|
+
positiveKeys: ['Depth'],
|
|
403
|
+
customValidationId: 'generic-element',
|
|
404
|
+
useWhen: 'Create advanced IFC entities only when no dedicated helper exists.',
|
|
405
|
+
},
|
|
406
|
+
addAxisElement: {
|
|
407
|
+
taskTags: ['create', 'repair'],
|
|
408
|
+
placement: 'world',
|
|
409
|
+
requiredKeys: ['IfcType', 'Start', 'End', 'Profile'],
|
|
410
|
+
pointArity: { Start: 3, End: 3 },
|
|
411
|
+
axisPair: ['Start', 'End'],
|
|
412
|
+
customValidationId: 'axis-element',
|
|
413
|
+
useWhen: 'Create advanced axis-based IFC entities when no dedicated helper exists.',
|
|
414
|
+
},
|
|
415
|
+
};
|
|
416
|
+
/**
|
|
417
|
+
* Build all bim.create method schemas by discovering public methods
|
|
418
|
+
* on IfcCreator.prototype. New methods are automatically exposed.
|
|
419
|
+
*/
|
|
420
|
+
function buildCreateMethods() {
|
|
421
|
+
const methods = [];
|
|
422
|
+
// ── Special: project (creates IfcCreator, returns handle) ──
|
|
423
|
+
methods.push({
|
|
424
|
+
name: 'project',
|
|
425
|
+
doc: 'Create a new IFC project. Returns a creator handle (number).',
|
|
426
|
+
args: ['dump'],
|
|
427
|
+
paramNames: ['params'],
|
|
428
|
+
tsParamTypes: ['{ Name?: string; Description?: string; Schema?: string; LengthUnit?: string; Author?: string; Organization?: string }'],
|
|
429
|
+
tsReturn: 'number',
|
|
430
|
+
call: (_sdk, args, context) => {
|
|
431
|
+
const params = (args[0] ?? {});
|
|
432
|
+
const creator = new IfcCreator(params);
|
|
433
|
+
return creatorRegistry.registerForSession(context.sandboxSessionId, creator);
|
|
434
|
+
},
|
|
435
|
+
returns: 'value',
|
|
436
|
+
llmSemantics: CREATE_METHOD_SEMANTICS.project,
|
|
437
|
+
});
|
|
438
|
+
// ── Special: toIfc (finalizes + cleans up handle) ──
|
|
439
|
+
methods.push({
|
|
440
|
+
name: 'toIfc',
|
|
441
|
+
doc: 'Generate the IFC STEP file content. Returns { content, entities, stats }.',
|
|
442
|
+
args: ['number'],
|
|
443
|
+
paramNames: ['handle'],
|
|
444
|
+
tsReturn: '{ content: string; entities: Array<{ expressId: number; type: string; Name?: string }>; stats: { entityCount: number; fileSize: number } }',
|
|
445
|
+
call: (_sdk, args, context) => {
|
|
446
|
+
const handle = args[0];
|
|
447
|
+
try {
|
|
448
|
+
const creator = creatorRegistry.getForSession(context.sandboxSessionId, handle);
|
|
449
|
+
return creator.toIfc();
|
|
450
|
+
}
|
|
451
|
+
finally {
|
|
452
|
+
creatorRegistry.removeForSession(context.sandboxSessionId, handle);
|
|
453
|
+
}
|
|
454
|
+
},
|
|
455
|
+
returns: 'value',
|
|
456
|
+
llmSemantics: CREATE_METHOD_SEMANTICS.toIfc,
|
|
457
|
+
});
|
|
458
|
+
// ── Special: setColor (unique signature: handle, elementId, name, rgb) ──
|
|
459
|
+
methods.push({
|
|
460
|
+
name: 'setColor',
|
|
461
|
+
doc: 'Assign a named colour to an element. Call before toIfc().',
|
|
462
|
+
args: ['number', 'number', 'string', 'dump'],
|
|
463
|
+
paramNames: ['handle', 'elementId', 'name', 'rgb'],
|
|
464
|
+
tsReturn: 'void',
|
|
465
|
+
call: (_sdk, args, context) => {
|
|
466
|
+
const creator = creatorRegistry.getForSession(context.sandboxSessionId, args[0]);
|
|
467
|
+
creator.setColor(args[1], args[2], args[3]);
|
|
468
|
+
},
|
|
469
|
+
returns: 'void',
|
|
470
|
+
llmSemantics: CREATE_METHOD_SEMANTICS.setColor,
|
|
471
|
+
});
|
|
472
|
+
// ── Auto-discover all other public methods from IfcCreator.prototype ──
|
|
473
|
+
const proto = IfcCreator.prototype;
|
|
474
|
+
const methodNames = Object.getOwnPropertyNames(proto)
|
|
475
|
+
.filter(name => typeof proto[name] === 'function' && ALLOWED_METHODS.has(name))
|
|
476
|
+
.sort();
|
|
477
|
+
for (const name of methodNames) {
|
|
478
|
+
const pattern = classifyMethod(name, proto[name]);
|
|
479
|
+
if (pattern === 'special')
|
|
480
|
+
continue; // already handled above
|
|
481
|
+
switch (pattern) {
|
|
482
|
+
case 'storey-params':
|
|
483
|
+
// addIfcBuildingStorey takes (params) — 1 arg after handle
|
|
484
|
+
if (name === 'addIfcBuildingStorey') {
|
|
485
|
+
methods.push({
|
|
486
|
+
name,
|
|
487
|
+
doc: methodDoc(name),
|
|
488
|
+
args: ['number', 'dump'],
|
|
489
|
+
paramNames: ['handle', 'params'],
|
|
490
|
+
tsReturn: 'number',
|
|
491
|
+
call: (_sdk, args, context) => {
|
|
492
|
+
const creator = creatorRegistry.getForSession(context.sandboxSessionId, args[0]);
|
|
493
|
+
return creator[name](args[1]);
|
|
494
|
+
},
|
|
495
|
+
returns: 'value',
|
|
496
|
+
llmSemantics: CREATE_METHOD_SEMANTICS[name],
|
|
497
|
+
});
|
|
498
|
+
}
|
|
499
|
+
else {
|
|
500
|
+
// Standard: (storeyId, params) — 2 args after handle
|
|
501
|
+
methods.push({
|
|
502
|
+
name,
|
|
503
|
+
doc: methodDoc(name),
|
|
504
|
+
args: ['number', 'number', 'dump'],
|
|
505
|
+
paramNames: ['handle', 'storeyId', 'params'],
|
|
506
|
+
tsReturn: 'number',
|
|
507
|
+
call: (_sdk, args, context) => {
|
|
508
|
+
const creator = creatorRegistry.getForSession(context.sandboxSessionId, args[0]);
|
|
509
|
+
return creator[name](args[1], args[2]);
|
|
510
|
+
},
|
|
511
|
+
returns: 'value',
|
|
512
|
+
llmSemantics: CREATE_METHOD_SEMANTICS[name],
|
|
513
|
+
});
|
|
514
|
+
}
|
|
515
|
+
break;
|
|
516
|
+
case 'element-params':
|
|
517
|
+
// (elementId, def) — 2 args after handle, may return number or void
|
|
518
|
+
methods.push({
|
|
519
|
+
name,
|
|
520
|
+
doc: methodDoc(name),
|
|
521
|
+
args: ['number', 'number', 'dump'],
|
|
522
|
+
paramNames: ['handle', name === 'addIfcWallDoor' || name === 'addIfcWallWindow' ? 'wallId' : 'elementId', 'params'],
|
|
523
|
+
tsReturn: name === 'addIfcMaterial' ? 'void' : 'number',
|
|
524
|
+
call: (_sdk, args, context) => {
|
|
525
|
+
const creator = creatorRegistry.getForSession(context.sandboxSessionId, args[0]);
|
|
526
|
+
return creator[name](args[1], args[2]);
|
|
527
|
+
},
|
|
528
|
+
returns: name === 'addIfcMaterial' ? 'void' : 'value',
|
|
529
|
+
llmSemantics: CREATE_METHOD_SEMANTICS[name],
|
|
530
|
+
});
|
|
531
|
+
break;
|
|
532
|
+
case 'single-dump':
|
|
533
|
+
methods.push({
|
|
534
|
+
name,
|
|
535
|
+
doc: methodDoc(name),
|
|
536
|
+
args: ['number', 'dump'],
|
|
537
|
+
paramNames: ['handle', 'profile'],
|
|
538
|
+
tsReturn: 'number',
|
|
539
|
+
call: (_sdk, args, context) => {
|
|
540
|
+
const creator = creatorRegistry.getForSession(context.sandboxSessionId, args[0]);
|
|
541
|
+
return creator[name](args[1]);
|
|
542
|
+
},
|
|
543
|
+
returns: 'value',
|
|
544
|
+
llmSemantics: CREATE_METHOD_SEMANTICS[name],
|
|
545
|
+
});
|
|
546
|
+
break;
|
|
547
|
+
case 'no-args':
|
|
548
|
+
methods.push({
|
|
549
|
+
name,
|
|
550
|
+
doc: methodDoc(name),
|
|
551
|
+
args: ['number'],
|
|
552
|
+
paramNames: ['handle'],
|
|
553
|
+
tsReturn: 'number',
|
|
554
|
+
call: (_sdk, args, context) => {
|
|
555
|
+
const creator = creatorRegistry.getForSession(context.sandboxSessionId, args[0]);
|
|
556
|
+
return creator[name]();
|
|
557
|
+
},
|
|
558
|
+
returns: 'value',
|
|
559
|
+
llmSemantics: CREATE_METHOD_SEMANTICS[name],
|
|
560
|
+
});
|
|
561
|
+
break;
|
|
562
|
+
}
|
|
563
|
+
}
|
|
564
|
+
return methods;
|
|
565
|
+
}
|
|
65
566
|
// ============================================================================
|
|
66
567
|
// Schema Definitions
|
|
67
568
|
// ============================================================================
|
|
@@ -151,6 +652,26 @@ export const NAMESPACE_SCHEMAS = [
|
|
|
151
652
|
},
|
|
152
653
|
returns: 'value',
|
|
153
654
|
},
|
|
655
|
+
{
|
|
656
|
+
name: 'attributes',
|
|
657
|
+
doc: 'Get all named string/enum attributes for an entity',
|
|
658
|
+
args: ['dump'],
|
|
659
|
+
paramNames: ['entity'],
|
|
660
|
+
tsParamTypes: ['BimEntity'],
|
|
661
|
+
tsReturn: 'BimAttribute[]',
|
|
662
|
+
call: (sdk, args) => {
|
|
663
|
+
const ref = toRef(args[0]);
|
|
664
|
+
if (!ref)
|
|
665
|
+
return [];
|
|
666
|
+
return sdk.attributes(ref);
|
|
667
|
+
},
|
|
668
|
+
returns: 'value',
|
|
669
|
+
llmSemantics: {
|
|
670
|
+
taskTags: ['inspect', 'repair'],
|
|
671
|
+
inspectFirst: true,
|
|
672
|
+
useWhen: 'Inspect raw IFC occurrence attributes before guessing metadata names.',
|
|
673
|
+
},
|
|
674
|
+
},
|
|
154
675
|
{
|
|
155
676
|
name: 'properties',
|
|
156
677
|
doc: 'Get all IfcPropertySet data for an entity',
|
|
@@ -162,9 +683,22 @@ export const NAMESPACE_SCHEMAS = [
|
|
|
162
683
|
const ref = toRef(args[0]);
|
|
163
684
|
if (!ref)
|
|
164
685
|
return [];
|
|
165
|
-
return sdk.properties(ref)
|
|
686
|
+
return sdk.properties(ref).map(pset => {
|
|
687
|
+
const mappedProperties = mapNamedProperties(pset.properties);
|
|
688
|
+
return {
|
|
689
|
+
name: pset.name, Name: pset.name,
|
|
690
|
+
globalId: pset.globalId, GlobalId: pset.globalId,
|
|
691
|
+
properties: mappedProperties,
|
|
692
|
+
Properties: mappedProperties,
|
|
693
|
+
};
|
|
694
|
+
});
|
|
166
695
|
},
|
|
167
696
|
returns: 'value',
|
|
697
|
+
llmSemantics: {
|
|
698
|
+
taskTags: ['inspect', 'repair'],
|
|
699
|
+
inspectFirst: true,
|
|
700
|
+
useWhen: 'Inspect all property sets on an occurrence before guessing individual property names.',
|
|
701
|
+
},
|
|
168
702
|
},
|
|
169
703
|
{
|
|
170
704
|
name: 'quantities',
|
|
@@ -177,9 +711,314 @@ export const NAMESPACE_SCHEMAS = [
|
|
|
177
711
|
const ref = toRef(args[0]);
|
|
178
712
|
if (!ref)
|
|
179
713
|
return [];
|
|
180
|
-
return sdk.quantities(ref)
|
|
714
|
+
return sdk.quantities(ref).map(qset => {
|
|
715
|
+
const mappedQuantities = mapNamedProperties(qset.quantities);
|
|
716
|
+
return {
|
|
717
|
+
name: qset.name, Name: qset.name,
|
|
718
|
+
quantities: mappedQuantities,
|
|
719
|
+
Quantities: mappedQuantities,
|
|
720
|
+
};
|
|
721
|
+
});
|
|
181
722
|
},
|
|
182
723
|
returns: 'value',
|
|
724
|
+
llmSemantics: {
|
|
725
|
+
taskTags: ['inspect', 'repair'],
|
|
726
|
+
inspectFirst: true,
|
|
727
|
+
useWhen: 'Inspect all quantity sets on an occurrence.',
|
|
728
|
+
},
|
|
729
|
+
},
|
|
730
|
+
{
|
|
731
|
+
name: 'property',
|
|
732
|
+
doc: 'Get a single property value from an entity',
|
|
733
|
+
args: ['dump', 'string', 'string'],
|
|
734
|
+
paramNames: ['entity', 'psetName', 'propName'],
|
|
735
|
+
tsParamTypes: ['BimEntity'],
|
|
736
|
+
tsReturn: 'string | number | boolean | null',
|
|
737
|
+
call: (sdk, args) => {
|
|
738
|
+
const ref = toRef(args[0]);
|
|
739
|
+
if (!ref)
|
|
740
|
+
return null;
|
|
741
|
+
return sdk.property(ref, args[1], args[2]);
|
|
742
|
+
},
|
|
743
|
+
returns: 'value',
|
|
744
|
+
llmSemantics: {
|
|
745
|
+
taskTags: ['inspect', 'repair'],
|
|
746
|
+
inspectFirst: true,
|
|
747
|
+
useWhen: 'Read one known property when you already know the exact property-set and property names.',
|
|
748
|
+
},
|
|
749
|
+
},
|
|
750
|
+
{
|
|
751
|
+
name: 'classifications',
|
|
752
|
+
doc: 'Get classification references for an entity',
|
|
753
|
+
args: ['dump'],
|
|
754
|
+
paramNames: ['entity'],
|
|
755
|
+
tsParamTypes: ['BimEntity'],
|
|
756
|
+
tsReturn: 'BimClassification[]',
|
|
757
|
+
call: (sdk, args) => {
|
|
758
|
+
const ref = toRef(args[0]);
|
|
759
|
+
if (!ref)
|
|
760
|
+
return [];
|
|
761
|
+
return sdk.classifications(ref);
|
|
762
|
+
},
|
|
763
|
+
returns: 'value',
|
|
764
|
+
llmSemantics: {
|
|
765
|
+
taskTags: ['inspect', 'repair'],
|
|
766
|
+
inspectFirst: true,
|
|
767
|
+
useWhen: 'Read relationship-based classification references.',
|
|
768
|
+
cautions: ['Prefer this over guessing ad-hoc classification property names.'],
|
|
769
|
+
},
|
|
770
|
+
},
|
|
771
|
+
{
|
|
772
|
+
name: 'materials',
|
|
773
|
+
doc: 'Get material assignment for an entity',
|
|
774
|
+
args: ['dump'],
|
|
775
|
+
paramNames: ['entity'],
|
|
776
|
+
tsParamTypes: ['BimEntity'],
|
|
777
|
+
tsReturn: 'BimMaterial | null',
|
|
778
|
+
call: (sdk, args) => {
|
|
779
|
+
const ref = toRef(args[0]);
|
|
780
|
+
if (!ref)
|
|
781
|
+
return null;
|
|
782
|
+
return sdk.materials(ref);
|
|
783
|
+
},
|
|
784
|
+
returns: 'value',
|
|
785
|
+
llmSemantics: {
|
|
786
|
+
taskTags: ['inspect', 'repair'],
|
|
787
|
+
inspectFirst: true,
|
|
788
|
+
useWhen: 'Read assigned material data for an entity.',
|
|
789
|
+
cautions: ['Prefer this over querying Pset_MaterialCommon or Material.Name as ordinary property sets.'],
|
|
790
|
+
},
|
|
791
|
+
},
|
|
792
|
+
{
|
|
793
|
+
name: 'typeProperties',
|
|
794
|
+
doc: 'Get type-level property sets for an entity',
|
|
795
|
+
args: ['dump'],
|
|
796
|
+
paramNames: ['entity'],
|
|
797
|
+
tsParamTypes: ['BimEntity'],
|
|
798
|
+
tsReturn: 'BimTypeProperties | null',
|
|
799
|
+
call: (sdk, args) => {
|
|
800
|
+
const ref = toRef(args[0]);
|
|
801
|
+
if (!ref)
|
|
802
|
+
return null;
|
|
803
|
+
return sdk.typeProperties(ref);
|
|
804
|
+
},
|
|
805
|
+
returns: 'value',
|
|
806
|
+
llmSemantics: {
|
|
807
|
+
taskTags: ['inspect', 'repair'],
|
|
808
|
+
inspectFirst: true,
|
|
809
|
+
useWhen: 'Inspect type-level properties when occurrence-level property sets are missing expected data.',
|
|
810
|
+
},
|
|
811
|
+
},
|
|
812
|
+
{
|
|
813
|
+
name: 'documents',
|
|
814
|
+
doc: 'Get linked document references for an entity',
|
|
815
|
+
args: ['dump'],
|
|
816
|
+
paramNames: ['entity'],
|
|
817
|
+
tsParamTypes: ['BimEntity'],
|
|
818
|
+
tsReturn: 'BimDocument[]',
|
|
819
|
+
call: (sdk, args) => {
|
|
820
|
+
const ref = toRef(args[0]);
|
|
821
|
+
if (!ref)
|
|
822
|
+
return [];
|
|
823
|
+
return sdk.documents(ref);
|
|
824
|
+
},
|
|
825
|
+
returns: 'value',
|
|
826
|
+
llmSemantics: {
|
|
827
|
+
taskTags: ['inspect', 'repair'],
|
|
828
|
+
inspectFirst: true,
|
|
829
|
+
useWhen: 'Inspect linked document references and external documentation.',
|
|
830
|
+
},
|
|
831
|
+
},
|
|
832
|
+
{
|
|
833
|
+
name: 'relationships',
|
|
834
|
+
doc: 'Get structural relationship summary for an entity',
|
|
835
|
+
args: ['dump'],
|
|
836
|
+
paramNames: ['entity'],
|
|
837
|
+
tsParamTypes: ['BimEntity'],
|
|
838
|
+
tsReturn: 'BimRelationships',
|
|
839
|
+
call: (sdk, args) => {
|
|
840
|
+
const ref = toRef(args[0]);
|
|
841
|
+
if (!ref)
|
|
842
|
+
return { voids: [], fills: [], groups: [], connections: [] };
|
|
843
|
+
return sdk.relationships(ref);
|
|
844
|
+
},
|
|
845
|
+
returns: 'value',
|
|
846
|
+
llmSemantics: {
|
|
847
|
+
taskTags: ['inspect', 'repair'],
|
|
848
|
+
inspectFirst: true,
|
|
849
|
+
useWhen: 'Inspect structural and semantic relationships such as voids, fills, groups, and connections.',
|
|
850
|
+
},
|
|
851
|
+
},
|
|
852
|
+
{
|
|
853
|
+
name: 'quantity',
|
|
854
|
+
doc: 'Get a single quantity value from an entity',
|
|
855
|
+
args: ['dump', 'string', 'string'],
|
|
856
|
+
paramNames: ['entity', 'qsetName', 'quantityName'],
|
|
857
|
+
tsParamTypes: ['BimEntity'],
|
|
858
|
+
tsReturn: 'number | null',
|
|
859
|
+
call: (sdk, args) => {
|
|
860
|
+
const ref = toRef(args[0]);
|
|
861
|
+
if (!ref)
|
|
862
|
+
return null;
|
|
863
|
+
return sdk.quantity(ref, args[1], args[2]);
|
|
864
|
+
},
|
|
865
|
+
returns: 'value',
|
|
866
|
+
},
|
|
867
|
+
{
|
|
868
|
+
name: 'related',
|
|
869
|
+
doc: 'Get related entities by IFC relationship type',
|
|
870
|
+
args: ['dump', 'string', 'string'],
|
|
871
|
+
paramNames: ['entity', 'relType', 'direction'],
|
|
872
|
+
tsParamTypes: ['BimEntity', undefined, "'forward' | 'inverse'"],
|
|
873
|
+
tsReturn: 'BimEntity[]',
|
|
874
|
+
call: (sdk, args) => {
|
|
875
|
+
const ref = toRef(args[0]);
|
|
876
|
+
if (!ref)
|
|
877
|
+
return [];
|
|
878
|
+
return sdk.related(ref, args[1], args[2]).map(withAliases);
|
|
879
|
+
},
|
|
880
|
+
returns: 'value',
|
|
881
|
+
llmSemantics: {
|
|
882
|
+
taskTags: ['inspect', 'repair'],
|
|
883
|
+
inspectFirst: true,
|
|
884
|
+
useWhen: 'Traverse a known IFC relationship type in forward or inverse direction.',
|
|
885
|
+
},
|
|
886
|
+
},
|
|
887
|
+
{
|
|
888
|
+
name: 'containedIn',
|
|
889
|
+
doc: 'Get the spatial container of an entity',
|
|
890
|
+
args: ['dump'],
|
|
891
|
+
paramNames: ['entity'],
|
|
892
|
+
tsParamTypes: ['BimEntity'],
|
|
893
|
+
tsReturn: 'BimEntity | null',
|
|
894
|
+
call: (sdk, args) => {
|
|
895
|
+
const ref = toRef(args[0]);
|
|
896
|
+
if (!ref)
|
|
897
|
+
return null;
|
|
898
|
+
const entity = sdk.containedIn(ref);
|
|
899
|
+
return entity ? withAliases(entity) : null;
|
|
900
|
+
},
|
|
901
|
+
returns: 'value',
|
|
902
|
+
},
|
|
903
|
+
{
|
|
904
|
+
name: 'contains',
|
|
905
|
+
doc: 'Get entities contained in a spatial container',
|
|
906
|
+
args: ['dump'],
|
|
907
|
+
paramNames: ['entity'],
|
|
908
|
+
tsParamTypes: ['BimEntity'],
|
|
909
|
+
tsReturn: 'BimEntity[]',
|
|
910
|
+
call: (sdk, args) => {
|
|
911
|
+
const ref = toRef(args[0]);
|
|
912
|
+
if (!ref)
|
|
913
|
+
return [];
|
|
914
|
+
return sdk.contains(ref).map(withAliases);
|
|
915
|
+
},
|
|
916
|
+
returns: 'value',
|
|
917
|
+
},
|
|
918
|
+
{
|
|
919
|
+
name: 'decomposedBy',
|
|
920
|
+
doc: 'Get the parent aggregate of an entity',
|
|
921
|
+
args: ['dump'],
|
|
922
|
+
paramNames: ['entity'],
|
|
923
|
+
tsParamTypes: ['BimEntity'],
|
|
924
|
+
tsReturn: 'BimEntity | null',
|
|
925
|
+
call: (sdk, args) => {
|
|
926
|
+
const ref = toRef(args[0]);
|
|
927
|
+
if (!ref)
|
|
928
|
+
return null;
|
|
929
|
+
const entity = sdk.decomposedBy(ref);
|
|
930
|
+
return entity ? withAliases(entity) : null;
|
|
931
|
+
},
|
|
932
|
+
returns: 'value',
|
|
933
|
+
},
|
|
934
|
+
{
|
|
935
|
+
name: 'decomposes',
|
|
936
|
+
doc: 'Get aggregated children of an entity',
|
|
937
|
+
args: ['dump'],
|
|
938
|
+
paramNames: ['entity'],
|
|
939
|
+
tsParamTypes: ['BimEntity'],
|
|
940
|
+
tsReturn: 'BimEntity[]',
|
|
941
|
+
call: (sdk, args) => {
|
|
942
|
+
const ref = toRef(args[0]);
|
|
943
|
+
if (!ref)
|
|
944
|
+
return [];
|
|
945
|
+
return sdk.decomposes(ref).map(withAliases);
|
|
946
|
+
},
|
|
947
|
+
returns: 'value',
|
|
948
|
+
},
|
|
949
|
+
{
|
|
950
|
+
name: 'storey',
|
|
951
|
+
doc: 'Get the containing building storey of an entity',
|
|
952
|
+
args: ['dump'],
|
|
953
|
+
paramNames: ['entity'],
|
|
954
|
+
tsParamTypes: ['BimEntity'],
|
|
955
|
+
tsReturn: 'BimEntity | null',
|
|
956
|
+
call: (sdk, args) => {
|
|
957
|
+
const ref = toRef(args[0]);
|
|
958
|
+
if (!ref)
|
|
959
|
+
return null;
|
|
960
|
+
const entity = sdk.storey(ref);
|
|
961
|
+
return entity ? withAliases(entity) : null;
|
|
962
|
+
},
|
|
963
|
+
returns: 'value',
|
|
964
|
+
llmSemantics: {
|
|
965
|
+
taskTags: ['inspect', 'repair'],
|
|
966
|
+
inspectFirst: true,
|
|
967
|
+
useWhen: 'Resolve which building storey currently contains an entity.',
|
|
968
|
+
},
|
|
969
|
+
},
|
|
970
|
+
{
|
|
971
|
+
name: 'path',
|
|
972
|
+
doc: 'Get the spatial/aggregation path from project to entity',
|
|
973
|
+
args: ['dump'],
|
|
974
|
+
paramNames: ['entity'],
|
|
975
|
+
tsParamTypes: ['BimEntity'],
|
|
976
|
+
tsReturn: 'BimEntity[]',
|
|
977
|
+
call: (sdk, args) => {
|
|
978
|
+
const ref = toRef(args[0]);
|
|
979
|
+
if (!ref)
|
|
980
|
+
return [];
|
|
981
|
+
return sdk.path(ref).map(withAliases);
|
|
982
|
+
},
|
|
983
|
+
returns: 'value',
|
|
984
|
+
llmSemantics: {
|
|
985
|
+
taskTags: ['inspect', 'repair'],
|
|
986
|
+
inspectFirst: true,
|
|
987
|
+
useWhen: 'Inspect the full project-to-entity spatial path before generating hierarchy-aware edits.',
|
|
988
|
+
},
|
|
989
|
+
},
|
|
990
|
+
{
|
|
991
|
+
name: 'storeys',
|
|
992
|
+
doc: 'List all building storeys',
|
|
993
|
+
args: [],
|
|
994
|
+
tsReturn: 'BimEntity[]',
|
|
995
|
+
call: (sdk) => {
|
|
996
|
+
return sdk.storeys().map(withAliases);
|
|
997
|
+
},
|
|
998
|
+
returns: 'value',
|
|
999
|
+
llmSemantics: {
|
|
1000
|
+
taskTags: ['inspect', 'repair'],
|
|
1001
|
+
inspectFirst: true,
|
|
1002
|
+
useWhen: 'List all building storeys and use their actual names/elevations as generation targets.',
|
|
1003
|
+
},
|
|
1004
|
+
},
|
|
1005
|
+
{
|
|
1006
|
+
name: 'selection',
|
|
1007
|
+
doc: 'Get the current viewer selection as entities',
|
|
1008
|
+
args: [],
|
|
1009
|
+
tsReturn: 'BimEntity[]',
|
|
1010
|
+
call: (sdk) => {
|
|
1011
|
+
return sdk.viewer.getSelection()
|
|
1012
|
+
.map((ref) => sdk.entity(ref))
|
|
1013
|
+
.filter((entity) => Boolean(entity))
|
|
1014
|
+
.map(withAliases);
|
|
1015
|
+
},
|
|
1016
|
+
returns: 'value',
|
|
1017
|
+
llmSemantics: {
|
|
1018
|
+
taskTags: ['inspect', 'repair'],
|
|
1019
|
+
inspectFirst: true,
|
|
1020
|
+
useWhen: 'Inspect what the user currently selected in the viewer before proposing targeted edits or analysis.',
|
|
1021
|
+
},
|
|
183
1022
|
},
|
|
184
1023
|
],
|
|
185
1024
|
},
|
|
@@ -296,11 +1135,29 @@ export const NAMESPACE_SCHEMAS = [
|
|
|
296
1135
|
methods: [
|
|
297
1136
|
{
|
|
298
1137
|
name: 'setProperty',
|
|
299
|
-
doc: 'Set a
|
|
1138
|
+
doc: 'Set an IfcPropertySet or quantity value (not a root IFC attribute)',
|
|
300
1139
|
args: ['dump', 'string', 'string', 'dump'],
|
|
301
1140
|
paramNames: ['entity', 'psetName', 'propName', 'value'],
|
|
302
1141
|
call: (sdk, args) => {
|
|
303
|
-
|
|
1142
|
+
const ref = toRef(args[0]);
|
|
1143
|
+
if (!ref) {
|
|
1144
|
+
throw new Error('bim.mutate.setProperty: invalid entity reference');
|
|
1145
|
+
}
|
|
1146
|
+
sdk.mutate.setProperty(ref, args[1], args[2], args[3]);
|
|
1147
|
+
},
|
|
1148
|
+
returns: 'void',
|
|
1149
|
+
},
|
|
1150
|
+
{
|
|
1151
|
+
name: 'setAttribute',
|
|
1152
|
+
doc: 'Set a root IFC attribute such as Name, Description, ObjectType, or Tag',
|
|
1153
|
+
args: ['dump', 'string', 'string'],
|
|
1154
|
+
paramNames: ['entity', 'attrName', 'value'],
|
|
1155
|
+
call: (sdk, args) => {
|
|
1156
|
+
const ref = toRef(args[0]);
|
|
1157
|
+
if (!ref) {
|
|
1158
|
+
throw new Error('bim.mutate.setAttribute: invalid entity reference');
|
|
1159
|
+
}
|
|
1160
|
+
sdk.mutate.setAttribute(ref, args[1], args[2]);
|
|
304
1161
|
},
|
|
305
1162
|
returns: 'void',
|
|
306
1163
|
},
|
|
@@ -310,7 +1167,11 @@ export const NAMESPACE_SCHEMAS = [
|
|
|
310
1167
|
args: ['dump', 'string', 'string'],
|
|
311
1168
|
paramNames: ['entity', 'psetName', 'propName'],
|
|
312
1169
|
call: (sdk, args) => {
|
|
313
|
-
|
|
1170
|
+
const ref = toRef(args[0]);
|
|
1171
|
+
if (!ref) {
|
|
1172
|
+
throw new Error('bim.mutate.deleteProperty: invalid entity reference');
|
|
1173
|
+
}
|
|
1174
|
+
sdk.mutate.deleteProperty(ref, args[1], args[2]);
|
|
314
1175
|
},
|
|
315
1176
|
returns: 'void',
|
|
316
1177
|
},
|
|
@@ -356,190 +1217,68 @@ export const NAMESPACE_SCHEMAS = [
|
|
|
356
1217
|
],
|
|
357
1218
|
},
|
|
358
1219
|
// ── bim.create ─────────────────────────────────────────────
|
|
1220
|
+
//
|
|
1221
|
+
// Auto-discovered from IfcCreator.prototype at module load.
|
|
1222
|
+
// Adding a new public method to IfcCreator automatically exposes it
|
|
1223
|
+
// in the sandbox — no manual bridge wiring needed.
|
|
1224
|
+
//
|
|
359
1225
|
{
|
|
360
1226
|
name: 'create',
|
|
361
1227
|
doc: 'IFC creation from scratch',
|
|
362
1228
|
permission: 'export', // reuses export permission — creation produces files
|
|
1229
|
+
methods: buildCreateMethods(),
|
|
1230
|
+
},
|
|
1231
|
+
// ── bim.export ─────────────────────────────────────────────
|
|
1232
|
+
{
|
|
1233
|
+
name: 'files',
|
|
1234
|
+
doc: 'Uploaded file attachments',
|
|
1235
|
+
permission: 'files',
|
|
363
1236
|
methods: [
|
|
364
1237
|
{
|
|
365
|
-
name: '
|
|
366
|
-
doc: '
|
|
367
|
-
args: [
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
tsReturn: 'number',
|
|
371
|
-
call: (_sdk, args) => {
|
|
372
|
-
const params = (args[0] ?? {});
|
|
373
|
-
const creator = new IfcCreator(params);
|
|
374
|
-
return creatorRegistry.register(creator);
|
|
375
|
-
},
|
|
376
|
-
returns: 'value',
|
|
377
|
-
},
|
|
378
|
-
{
|
|
379
|
-
name: 'addIfcBuildingStorey',
|
|
380
|
-
doc: 'Add a building storey. Returns storey expressId.',
|
|
381
|
-
args: ['number', 'dump'],
|
|
382
|
-
paramNames: ['handle', 'params'],
|
|
383
|
-
tsParamTypes: [undefined, '{ Name?: string; Description?: string; Elevation: number }'],
|
|
384
|
-
tsReturn: 'number',
|
|
385
|
-
call: (_sdk, args) => {
|
|
386
|
-
const creator = creatorRegistry.get(args[0]);
|
|
387
|
-
return creator.addIfcBuildingStorey(args[1]);
|
|
388
|
-
},
|
|
389
|
-
returns: 'value',
|
|
390
|
-
},
|
|
391
|
-
{
|
|
392
|
-
name: 'addIfcWall',
|
|
393
|
-
doc: 'Add a wall to a storey. Returns wall expressId.',
|
|
394
|
-
args: ['number', 'number', 'dump'],
|
|
395
|
-
paramNames: ['handle', 'storeyId', 'params'],
|
|
396
|
-
tsParamTypes: [undefined, undefined, '{ Start: [number,number,number]; End: [number,number,number]; Thickness: number; Height: number; Name?: string; Openings?: Array<{ Width: number; Height: number; Position: [number,number,number]; Name?: string }> }'],
|
|
397
|
-
tsReturn: 'number',
|
|
398
|
-
call: (_sdk, args) => {
|
|
399
|
-
const creator = creatorRegistry.get(args[0]);
|
|
400
|
-
return creator.addIfcWall(args[1], args[2]);
|
|
401
|
-
},
|
|
402
|
-
returns: 'value',
|
|
403
|
-
},
|
|
404
|
-
{
|
|
405
|
-
name: 'addIfcSlab',
|
|
406
|
-
doc: 'Add a slab to a storey. Returns slab expressId.',
|
|
407
|
-
args: ['number', 'number', 'dump'],
|
|
408
|
-
paramNames: ['handle', 'storeyId', 'params'],
|
|
409
|
-
tsParamTypes: [undefined, undefined, '{ Position: [number,number,number]; Thickness: number; Width?: number; Depth?: number; Profile?: [number,number][]; Name?: string; Openings?: Array<{ Width: number; Height: number; Position: [number,number,number]; Name?: string }> }'],
|
|
410
|
-
tsReturn: 'number',
|
|
411
|
-
call: (_sdk, args) => {
|
|
412
|
-
const creator = creatorRegistry.get(args[0]);
|
|
413
|
-
return creator.addIfcSlab(args[1], args[2]);
|
|
414
|
-
},
|
|
415
|
-
returns: 'value',
|
|
416
|
-
},
|
|
417
|
-
{
|
|
418
|
-
name: 'addIfcColumn',
|
|
419
|
-
doc: 'Add a column to a storey. Returns column expressId.',
|
|
420
|
-
args: ['number', 'number', 'dump'],
|
|
421
|
-
paramNames: ['handle', 'storeyId', 'params'],
|
|
422
|
-
tsParamTypes: [undefined, undefined, '{ Position: [number,number,number]; Width: number; Depth: number; Height: number; Name?: string }'],
|
|
423
|
-
tsReturn: 'number',
|
|
424
|
-
call: (_sdk, args) => {
|
|
425
|
-
const creator = creatorRegistry.get(args[0]);
|
|
426
|
-
return creator.addIfcColumn(args[1], args[2]);
|
|
427
|
-
},
|
|
428
|
-
returns: 'value',
|
|
429
|
-
},
|
|
430
|
-
{
|
|
431
|
-
name: 'addIfcBeam',
|
|
432
|
-
doc: 'Add a beam to a storey. Returns beam expressId.',
|
|
433
|
-
args: ['number', 'number', 'dump'],
|
|
434
|
-
paramNames: ['handle', 'storeyId', 'params'],
|
|
435
|
-
tsParamTypes: [undefined, undefined, '{ Start: [number,number,number]; End: [number,number,number]; Width: number; Height: number; Name?: string }'],
|
|
436
|
-
tsReturn: 'number',
|
|
437
|
-
call: (_sdk, args) => {
|
|
438
|
-
const creator = creatorRegistry.get(args[0]);
|
|
439
|
-
return creator.addIfcBeam(args[1], args[2]);
|
|
440
|
-
},
|
|
441
|
-
returns: 'value',
|
|
442
|
-
},
|
|
443
|
-
{
|
|
444
|
-
name: 'addIfcStair',
|
|
445
|
-
doc: 'Add a stair to a storey. Returns stair expressId.',
|
|
446
|
-
args: ['number', 'number', 'dump'],
|
|
447
|
-
paramNames: ['handle', 'storeyId', 'params'],
|
|
448
|
-
tsParamTypes: [undefined, undefined, '{ Position: [number,number,number]; NumberOfRisers: number; RiserHeight: number; TreadLength: number; Width: number; Direction?: number; Name?: string }'],
|
|
449
|
-
tsReturn: 'number',
|
|
450
|
-
call: (_sdk, args) => {
|
|
451
|
-
const creator = creatorRegistry.get(args[0]);
|
|
452
|
-
return creator.addIfcStair(args[1], args[2]);
|
|
453
|
-
},
|
|
1238
|
+
name: 'list',
|
|
1239
|
+
doc: 'List uploaded file attachments available to scripts',
|
|
1240
|
+
args: [],
|
|
1241
|
+
tsReturn: 'BimFileAttachment[]',
|
|
1242
|
+
call: (sdk) => sdk.files.list(),
|
|
454
1243
|
returns: 'value',
|
|
455
1244
|
},
|
|
456
1245
|
{
|
|
457
|
-
name: '
|
|
458
|
-
doc: '
|
|
459
|
-
args: ['
|
|
460
|
-
paramNames: ['
|
|
461
|
-
|
|
462
|
-
|
|
463
|
-
call: (_sdk, args) => {
|
|
464
|
-
const creator = creatorRegistry.get(args[0]);
|
|
465
|
-
return creator.addIfcRoof(args[1], args[2]);
|
|
466
|
-
},
|
|
1246
|
+
name: 'text',
|
|
1247
|
+
doc: 'Get raw text content for an uploaded attachment by file name',
|
|
1248
|
+
args: ['string'],
|
|
1249
|
+
paramNames: ['name'],
|
|
1250
|
+
tsReturn: 'string | null',
|
|
1251
|
+
call: (sdk, args) => sdk.files.text(args[0]),
|
|
467
1252
|
returns: 'value',
|
|
468
|
-
|
|
469
|
-
|
|
470
|
-
|
|
471
|
-
doc: 'Assign a named colour to an element. Call before toIfc().',
|
|
472
|
-
args: ['number', 'number', 'string', 'dump'],
|
|
473
|
-
paramNames: ['handle', 'elementId', 'name', 'rgb'],
|
|
474
|
-
tsParamTypes: [undefined, undefined, undefined, '[number, number, number]'],
|
|
475
|
-
tsReturn: 'void',
|
|
476
|
-
call: (_sdk, args) => {
|
|
477
|
-
const creator = creatorRegistry.get(args[0]);
|
|
478
|
-
creator.setColor(args[1], args[2], args[3]);
|
|
479
|
-
},
|
|
480
|
-
returns: 'void',
|
|
481
|
-
},
|
|
482
|
-
{
|
|
483
|
-
name: 'addIfcMaterial',
|
|
484
|
-
doc: 'Assign an IFC material (simple or layered) to an element.',
|
|
485
|
-
args: ['number', 'number', 'dump'],
|
|
486
|
-
paramNames: ['handle', 'elementId', 'material'],
|
|
487
|
-
tsParamTypes: [undefined, undefined, '{ Name: string; Category?: string; Layers?: Array<{ Name: string; Thickness: number; Category?: string; IsVentilated?: boolean }> }'],
|
|
488
|
-
tsReturn: 'void',
|
|
489
|
-
call: (_sdk, args) => {
|
|
490
|
-
const creator = creatorRegistry.get(args[0]);
|
|
491
|
-
creator.addIfcMaterial(args[1], args[2]);
|
|
1253
|
+
llmSemantics: {
|
|
1254
|
+
taskTags: ['inspect', 'modify', 'repair', 'export'],
|
|
1255
|
+
useWhen: 'Read uploaded CSV, TSV, JSON, or text attachments without using fetch().',
|
|
492
1256
|
},
|
|
493
|
-
returns: 'void',
|
|
494
1257
|
},
|
|
495
1258
|
{
|
|
496
|
-
name: '
|
|
497
|
-
doc: '
|
|
498
|
-
args: ['
|
|
499
|
-
paramNames: ['
|
|
500
|
-
|
|
501
|
-
|
|
502
|
-
call: (_sdk, args) => {
|
|
503
|
-
const creator = creatorRegistry.get(args[0]);
|
|
504
|
-
return creator.addIfcPropertySet(args[1], args[2]);
|
|
505
|
-
},
|
|
1259
|
+
name: 'csv',
|
|
1260
|
+
doc: 'Get parsed CSV/TSV rows for an uploaded attachment by file name',
|
|
1261
|
+
args: ['string'],
|
|
1262
|
+
paramNames: ['name'],
|
|
1263
|
+
tsReturn: 'Record<string, string>[] | null',
|
|
1264
|
+
call: (sdk, args) => sdk.files.csv(args[0]),
|
|
506
1265
|
returns: 'value',
|
|
507
|
-
|
|
508
|
-
|
|
509
|
-
|
|
510
|
-
doc: 'Attach element quantities to an element. Returns qset expressId.',
|
|
511
|
-
args: ['number', 'number', 'dump'],
|
|
512
|
-
paramNames: ['handle', 'elementId', 'qset'],
|
|
513
|
-
tsParamTypes: [undefined, undefined, "{ Name: string; Quantities: Array<{ Name: string; Value: number; Kind: 'IfcQuantityLength' | 'IfcQuantityArea' | 'IfcQuantityVolume' | 'IfcQuantityCount' | 'IfcQuantityWeight' }> }"],
|
|
514
|
-
tsReturn: 'number',
|
|
515
|
-
call: (_sdk, args) => {
|
|
516
|
-
const creator = creatorRegistry.get(args[0]);
|
|
517
|
-
return creator.addIfcElementQuantity(args[1], args[2]);
|
|
1266
|
+
llmSemantics: {
|
|
1267
|
+
taskTags: ['inspect', 'modify', 'repair', 'export'],
|
|
1268
|
+
useWhen: 'Load uploaded CSV rows directly inside a script and join them against model entities.',
|
|
518
1269
|
},
|
|
519
|
-
returns: 'value',
|
|
520
1270
|
},
|
|
521
1271
|
{
|
|
522
|
-
name: '
|
|
523
|
-
doc: '
|
|
524
|
-
args: ['
|
|
525
|
-
paramNames: ['
|
|
526
|
-
tsReturn: '
|
|
527
|
-
call: (
|
|
528
|
-
const handle = args[0];
|
|
529
|
-
try {
|
|
530
|
-
const creator = creatorRegistry.get(handle);
|
|
531
|
-
return creator.toIfc();
|
|
532
|
-
}
|
|
533
|
-
finally {
|
|
534
|
-
// Always clean up the creator, even if toIfc() throws
|
|
535
|
-
creatorRegistry.remove(handle);
|
|
536
|
-
}
|
|
537
|
-
},
|
|
1272
|
+
name: 'csvColumns',
|
|
1273
|
+
doc: 'Get parsed CSV column names for an uploaded attachment by file name',
|
|
1274
|
+
args: ['string'],
|
|
1275
|
+
paramNames: ['name'],
|
|
1276
|
+
tsReturn: 'string[]',
|
|
1277
|
+
call: (sdk, args) => sdk.files.csvColumns(args[0]),
|
|
538
1278
|
returns: 'value',
|
|
539
1279
|
},
|
|
540
1280
|
],
|
|
541
1281
|
},
|
|
542
|
-
// ── bim.export ─────────────────────────────────────────────
|
|
543
1282
|
{
|
|
544
1283
|
name: 'export',
|
|
545
1284
|
doc: 'Data export',
|
|
@@ -569,6 +1308,18 @@ export const NAMESPACE_SCHEMAS = [
|
|
|
569
1308
|
},
|
|
570
1309
|
returns: 'value',
|
|
571
1310
|
},
|
|
1311
|
+
{
|
|
1312
|
+
name: 'ifc',
|
|
1313
|
+
doc: 'Export entities to IFC STEP text. Pass filename to auto-download a valid .ifc file',
|
|
1314
|
+
args: ['entityRefs', 'dump'],
|
|
1315
|
+
paramNames: ['entities', 'options'],
|
|
1316
|
+
tsParamTypes: [undefined, '{ schema?: "IFC2X3" | "IFC4" | "IFC4X3"; filename?: string; includeMutations?: boolean; visibleOnly?: boolean }'],
|
|
1317
|
+
tsReturn: 'string',
|
|
1318
|
+
call: (sdk, args) => {
|
|
1319
|
+
return sdk.export.ifc(args[0], args[1]);
|
|
1320
|
+
},
|
|
1321
|
+
returns: 'string',
|
|
1322
|
+
},
|
|
572
1323
|
{
|
|
573
1324
|
name: 'download',
|
|
574
1325
|
doc: 'Trigger a browser file download with the given content',
|
|
@@ -589,21 +1340,21 @@ export const NAMESPACE_SCHEMAS = [
|
|
|
589
1340
|
* Build all schema-defined namespaces on the `bim` handle.
|
|
590
1341
|
* Skips namespaces whose permission is disabled.
|
|
591
1342
|
*/
|
|
592
|
-
export function buildSchemaNamespaces(vm, bimHandle, sdk, permissions) {
|
|
1343
|
+
export function buildSchemaNamespaces(vm, bimHandle, sdk, permissions, context) {
|
|
593
1344
|
for (const schema of NAMESPACE_SCHEMAS) {
|
|
594
1345
|
if (!permissions[schema.permission])
|
|
595
1346
|
continue;
|
|
596
|
-
buildNamespace(vm, bimHandle, sdk, schema);
|
|
1347
|
+
buildNamespace(vm, bimHandle, sdk, schema, context);
|
|
597
1348
|
}
|
|
598
1349
|
}
|
|
599
|
-
function buildNamespace(vm, bimHandle, sdk, schema) {
|
|
1350
|
+
function buildNamespace(vm, bimHandle, sdk, schema, context) {
|
|
600
1351
|
const nsHandle = vm.newObject();
|
|
601
1352
|
for (const method of schema.methods) {
|
|
602
1353
|
const fn = vm.newFunction(method.name, (...handles) => {
|
|
603
1354
|
// Unmarshal arguments
|
|
604
1355
|
const nativeArgs = unmarshalArgs(vm, handles, method.args);
|
|
605
1356
|
// Call the SDK
|
|
606
|
-
const result = method.call(sdk, nativeArgs);
|
|
1357
|
+
const result = method.call(sdk, nativeArgs, context);
|
|
607
1358
|
// Marshal return value
|
|
608
1359
|
return marshalReturn(vm, result, method.returns);
|
|
609
1360
|
});
|
|
@@ -613,6 +1364,9 @@ function buildNamespace(vm, bimHandle, sdk, schema) {
|
|
|
613
1364
|
vm.setProp(bimHandle, schema.name, nsHandle);
|
|
614
1365
|
nsHandle.dispose();
|
|
615
1366
|
}
|
|
1367
|
+
export function disposeSchemaNamespaceSession(context) {
|
|
1368
|
+
creatorRegistry.removeSession(context.sandboxSessionId);
|
|
1369
|
+
}
|
|
616
1370
|
/** Unmarshal QuickJS handles to native JS values based on arg schema */
|
|
617
1371
|
function unmarshalArgs(vm, handles, argTypes) {
|
|
618
1372
|
const result = [];
|