@ifc-lite/viewer-core 0.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/server.js ADDED
@@ -0,0 +1,256 @@
1
+ /* This Source Code Form is subject to the terms of the Mozilla Public
2
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
3
+ * file, You can obtain one at https://mozilla.org/MPL/2.0/. */
4
+ /**
5
+ * Viewer HTTP server — serves the WebGL viewer and exposes a REST API
6
+ * for external tools to send live commands.
7
+ *
8
+ * This module is framework-agnostic; the CLI wraps it with arg parsing
9
+ * and stdin interaction.
10
+ */
11
+ import { createServer } from 'node:http';
12
+ import { createReadStream } from 'node:fs';
13
+ import { readFile, stat } from 'node:fs/promises';
14
+ import { dirname, resolve } from 'node:path';
15
+ import { getViewerHtml } from './viewer-html.js';
16
+ /** Valid command actions that the viewer understands */
17
+ export const VALID_ACTIONS = new Set([
18
+ 'colorize', 'isolate', 'xray', 'flyto', 'highlight',
19
+ 'colorizeEntities', 'isolateEntities', 'hideEntities', 'showEntities', 'resetColorEntities',
20
+ 'section', 'clearSection', 'colorByStorey', 'addGeometry',
21
+ 'showall', 'reset', 'picked', 'setView', 'removeCreated', 'camera',
22
+ ]);
23
+ /** MIME types for served files */
24
+ const MIME = {
25
+ '.html': 'text/html; charset=utf-8',
26
+ '.js': 'application/javascript; charset=utf-8',
27
+ '.wasm': 'application/wasm',
28
+ '.ifc': 'application/octet-stream',
29
+ '.json': 'application/json; charset=utf-8',
30
+ };
31
+ /** Resolve the path to @ifc-lite/wasm package */
32
+ async function resolveWasmDir() {
33
+ try {
34
+ // Use import.meta.resolve which understands ESM "exports" maps
35
+ const entryUrl = import.meta.resolve('@ifc-lite/wasm');
36
+ // entryUrl = file:///…/packages/wasm/pkg/ifc-lite.js → we need …/packages/wasm
37
+ const entryPath = entryUrl.replace(/^file:\/\//, '');
38
+ return resolve(dirname(entryPath), '..');
39
+ }
40
+ catch {
41
+ // Fallback: resolve from monorepo root
42
+ return resolve(dirname(import.meta.url.replace('file://', '')), '..', '..', '..', 'wasm');
43
+ }
44
+ }
45
+ /** Read full request body as string */
46
+ function readBody(req) {
47
+ return new Promise((resolve, reject) => {
48
+ const chunks = [];
49
+ req.on('data', (c) => chunks.push(c));
50
+ req.on('end', () => resolve(Buffer.concat(chunks).toString()));
51
+ req.on('error', reject);
52
+ });
53
+ }
54
+ export async function startViewerServer(opts) {
55
+ const { filePath, fileName, port: requestedPort, createHandler } = opts;
56
+ // Validate file exists and get size
57
+ let ifcSize = 0;
58
+ if (filePath) {
59
+ const ifcStat = await stat(filePath);
60
+ ifcSize = ifcStat.size;
61
+ }
62
+ const wasmDir = await resolveWasmDir();
63
+ // Read WASM assets
64
+ const wasmJs = await readFile(resolve(wasmDir, 'pkg', 'ifc-lite.js'));
65
+ const wasmBinary = await readFile(resolve(wasmDir, 'pkg', 'ifc-lite_bg.wasm'));
66
+ const wasmJsCached = wasmJs.toString().replace(/new URL\('ifc-lite_bg\.wasm', import\.meta\.url\)/g, "new URL('/wasm/ifc-lite_bg.wasm', location.origin)");
67
+ const viewerHtml = getViewerHtml(fileName);
68
+ // SSE clients
69
+ const sseClients = new Set();
70
+ function broadcast(data) {
71
+ const payload = `data: ${JSON.stringify(data)}\n\n`;
72
+ for (const res of sseClients) {
73
+ res.write(payload);
74
+ }
75
+ }
76
+ // Track IFC content created via /api/create for export
77
+ const createdSegments = [];
78
+ const server = createServer(async (req, res) => {
79
+ const url = new URL(req.url ?? '/', `http://${req.headers.host}`);
80
+ const path = url.pathname;
81
+ // CORS headers — restrict to localhost origins only
82
+ const origin = req.headers.origin ?? '';
83
+ if (/^https?:\/\/(localhost|127\.0\.0\.1)(:\d+)?$/.test(origin)) {
84
+ res.setHeader('Access-Control-Allow-Origin', origin);
85
+ }
86
+ res.setHeader('Access-Control-Allow-Methods', 'GET, POST, OPTIONS');
87
+ res.setHeader('Access-Control-Allow-Headers', 'Content-Type');
88
+ if (req.method === 'OPTIONS') {
89
+ res.writeHead(204);
90
+ res.end();
91
+ return;
92
+ }
93
+ // Routes
94
+ if (path === '/' && req.method === 'GET') {
95
+ res.writeHead(200, { 'Content-Type': MIME['.html'] });
96
+ res.end(viewerHtml);
97
+ return;
98
+ }
99
+ if (path === '/model.ifc' && req.method === 'GET') {
100
+ if (!filePath) {
101
+ res.writeHead(204);
102
+ res.end();
103
+ return;
104
+ }
105
+ res.writeHead(200, {
106
+ 'Content-Type': MIME['.ifc'],
107
+ 'Content-Length': ifcSize.toString(),
108
+ });
109
+ createReadStream(filePath).pipe(res);
110
+ return;
111
+ }
112
+ if (path === '/wasm/ifc-lite.js' && req.method === 'GET') {
113
+ res.writeHead(200, { 'Content-Type': MIME['.js'] });
114
+ res.end(wasmJsCached);
115
+ return;
116
+ }
117
+ if (path === '/wasm/ifc-lite_bg.wasm' && req.method === 'GET') {
118
+ res.writeHead(200, {
119
+ 'Content-Type': MIME['.wasm'],
120
+ 'Content-Length': wasmBinary.byteLength.toString(),
121
+ });
122
+ res.end(wasmBinary);
123
+ return;
124
+ }
125
+ // SSE endpoint
126
+ if (path === '/events' && req.method === 'GET') {
127
+ res.writeHead(200, {
128
+ 'Content-Type': 'text/event-stream',
129
+ 'Cache-Control': 'no-cache',
130
+ 'Connection': 'keep-alive',
131
+ });
132
+ res.write('data: {"action":"connected"}\n\n');
133
+ sseClients.add(res);
134
+ req.on('close', () => sseClients.delete(res));
135
+ return;
136
+ }
137
+ // Command API
138
+ if (path === '/api/command' && req.method === 'POST') {
139
+ try {
140
+ const body = await readBody(req);
141
+ const command = JSON.parse(body);
142
+ if (!command.action || !VALID_ACTIONS.has(command.action)) {
143
+ res.writeHead(400, { 'Content-Type': MIME['.json'] });
144
+ res.end(JSON.stringify({
145
+ ok: false,
146
+ error: `Unknown action: ${command.action ?? '(none)'}`,
147
+ validActions: [...VALID_ACTIONS],
148
+ }));
149
+ return;
150
+ }
151
+ broadcast(command);
152
+ res.writeHead(200, { 'Content-Type': MIME['.json'] });
153
+ res.end(JSON.stringify({ ok: true, action: command.action, clients: sseClients.size }));
154
+ }
155
+ catch (e) {
156
+ res.writeHead(400, { 'Content-Type': MIME['.json'] });
157
+ res.end(JSON.stringify({ ok: false, error: e.message }));
158
+ }
159
+ return;
160
+ }
161
+ // Create element API
162
+ if (path === '/api/create' && req.method === 'POST') {
163
+ if (!createHandler) {
164
+ res.writeHead(501, { 'Content-Type': MIME['.json'] });
165
+ res.end(JSON.stringify({ ok: false, error: 'Create handler not configured' }));
166
+ return;
167
+ }
168
+ try {
169
+ const body = await readBody(req);
170
+ const parsed = JSON.parse(body);
171
+ const elements = Array.isArray(parsed) ? parsed : [parsed];
172
+ if (elements.length === 0 || !elements[0].type) {
173
+ res.writeHead(400, { 'Content-Type': MIME['.json'] });
174
+ res.end(JSON.stringify({ ok: false, error: 'Missing "type" field' }));
175
+ return;
176
+ }
177
+ const result = await createHandler(elements);
178
+ broadcast({ action: 'addGeometry', ifcContent: result.content });
179
+ createdSegments.push(result.content);
180
+ res.writeHead(200, { 'Content-Type': MIME['.json'] });
181
+ res.end(JSON.stringify({
182
+ ok: true,
183
+ count: elements.length,
184
+ entities: result.entities,
185
+ ifcSize: result.stats.fileSize,
186
+ }));
187
+ }
188
+ catch (e) {
189
+ res.writeHead(400, { 'Content-Type': MIME['.json'] });
190
+ res.end(JSON.stringify({ ok: false, error: e.message }));
191
+ }
192
+ return;
193
+ }
194
+ // Clear created geometry
195
+ if (path === '/api/clear-created' && req.method === 'POST') {
196
+ const count = createdSegments.length;
197
+ createdSegments.length = 0;
198
+ broadcast({ action: 'removeCreated' });
199
+ res.writeHead(200, { 'Content-Type': MIME['.json'] });
200
+ res.end(JSON.stringify({ ok: true, cleared: count }));
201
+ return;
202
+ }
203
+ // Export created geometry
204
+ if (path === '/api/export' && req.method === 'GET') {
205
+ if (createdSegments.length === 0) {
206
+ res.writeHead(200, { 'Content-Type': MIME['.json'] });
207
+ res.end(JSON.stringify({ ok: false, error: 'No geometry has been created yet' }));
208
+ return;
209
+ }
210
+ const combined = createdSegments.join('\n');
211
+ res.writeHead(200, {
212
+ 'Content-Type': MIME['.ifc'],
213
+ 'Content-Disposition': `attachment; filename="created-${fileName}"`,
214
+ 'Content-Length': Buffer.byteLength(combined).toString(),
215
+ });
216
+ res.end(combined);
217
+ return;
218
+ }
219
+ // Status
220
+ if (path === '/api/status' && req.method === 'GET') {
221
+ res.writeHead(200, { 'Content-Type': MIME['.json'] });
222
+ res.end(JSON.stringify({
223
+ ok: true,
224
+ model: fileName,
225
+ clients: sseClients.size,
226
+ createdSegments: createdSegments.length,
227
+ }));
228
+ return;
229
+ }
230
+ // 404
231
+ res.writeHead(404, { 'Content-Type': 'text/plain' });
232
+ res.end('Not Found');
233
+ });
234
+ server.on('error', (err) => {
235
+ if (opts.onError) {
236
+ opts.onError(err);
237
+ }
238
+ });
239
+ return new Promise((promiseResolve) => {
240
+ server.listen(requestedPort, () => {
241
+ const addr = server.address();
242
+ const port = typeof addr === 'object' && addr ? addr.port : requestedPort;
243
+ const url = `http://localhost:${port}`;
244
+ if (opts.onReady) {
245
+ opts.onReady(port, url);
246
+ }
247
+ promiseResolve({
248
+ broadcast,
249
+ clientCount: () => sseClients.size,
250
+ createdSegments,
251
+ close: () => server.close(),
252
+ });
253
+ });
254
+ });
255
+ }
256
+ //# sourceMappingURL=server.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"server.js","sourceRoot":"","sources":["../src/server.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,19 @@
1
+ /**
2
+ * StreamingViewerAdapter — Forwards bim.viewer.* and bim.visibility.*
3
+ * calls to a running `ifc-lite view` server via its REST API.
4
+ *
5
+ * When eval/run/create commands are invoked with `--viewer PORT`, this
6
+ * adapter replaces the headless no-op viewer methods so that SDK calls
7
+ * like `bim.viewer.colorize(...)` actually update the 3D viewer in
8
+ * real time.
9
+ */
10
+ import type { ViewerBackendMethods, VisibilityBackendMethods } from '@ifc-lite/sdk';
11
+ /**
12
+ * Create a ViewerBackendMethods that streams commands to a running viewer.
13
+ */
14
+ export declare function createStreamingViewerAdapter(port: number): ViewerBackendMethods;
15
+ /**
16
+ * Create a VisibilityBackendMethods that streams commands to a running viewer.
17
+ */
18
+ export declare function createStreamingVisibilityAdapter(port: number): VisibilityBackendMethods;
19
+ //# sourceMappingURL=streaming-viewer.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"streaming-viewer.d.ts","sourceRoot":"","sources":["../src/streaming-viewer.ts"],"names":[],"mappings":"AAIA;;;;;;;;GAQG;AAEH,OAAO,KAAK,EACV,oBAAoB,EACpB,wBAAwB,EAEzB,MAAM,eAAe,CAAC;AAiBvB;;GAEG;AACH,wBAAgB,4BAA4B,CAAC,IAAI,EAAE,MAAM,GAAG,oBAAoB,CA4D/E;AAED;;GAEG;AACH,wBAAgB,gCAAgC,CAAC,IAAI,EAAE,MAAM,GAAG,wBAAwB,CA2BvF"}
@@ -0,0 +1,103 @@
1
+ /* This Source Code Form is subject to the terms of the Mozilla Public
2
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
3
+ * file, You can obtain one at https://mozilla.org/MPL/2.0/. */
4
+ /** Post a command to the running viewer server. Fire-and-forget. */
5
+ function sendCommand(port, cmd) {
6
+ fetch(`http://localhost:${port}/api/command`, {
7
+ method: 'POST',
8
+ headers: { 'Content-Type': 'application/json' },
9
+ body: JSON.stringify(cmd),
10
+ }).catch(() => {
11
+ /* silent — viewer may have closed */
12
+ });
13
+ }
14
+ function refsToIds(refs) {
15
+ return refs.map(r => r.expressId);
16
+ }
17
+ /**
18
+ * Create a ViewerBackendMethods that streams commands to a running viewer.
19
+ */
20
+ export function createStreamingViewerAdapter(port) {
21
+ return {
22
+ colorize(refs, color) {
23
+ sendCommand(port, {
24
+ action: 'colorizeEntities',
25
+ ids: refsToIds(refs),
26
+ color,
27
+ });
28
+ },
29
+ colorizeAll(batches) {
30
+ for (const batch of batches) {
31
+ sendCommand(port, {
32
+ action: 'colorizeEntities',
33
+ ids: refsToIds(batch.refs),
34
+ color: batch.color,
35
+ });
36
+ }
37
+ },
38
+ resetColors(refs) {
39
+ if (refs && refs.length > 0) {
40
+ sendCommand(port, {
41
+ action: 'resetColorEntities',
42
+ ids: refsToIds(refs),
43
+ });
44
+ }
45
+ else {
46
+ sendCommand(port, { action: 'showall' });
47
+ }
48
+ },
49
+ flyTo(refs) {
50
+ sendCommand(port, {
51
+ action: 'flyto',
52
+ ids: refsToIds(refs),
53
+ });
54
+ },
55
+ setSection(section) {
56
+ sendCommand(port, {
57
+ action: 'section',
58
+ section,
59
+ });
60
+ },
61
+ getSection() {
62
+ return null;
63
+ },
64
+ setCamera(state) {
65
+ sendCommand(port, {
66
+ action: 'camera',
67
+ state,
68
+ });
69
+ },
70
+ getCamera() {
71
+ return { mode: 'perspective' };
72
+ },
73
+ };
74
+ }
75
+ /**
76
+ * Create a VisibilityBackendMethods that streams commands to a running viewer.
77
+ */
78
+ export function createStreamingVisibilityAdapter(port) {
79
+ return {
80
+ hide(refs) {
81
+ sendCommand(port, {
82
+ action: 'hideEntities',
83
+ ids: refsToIds(refs),
84
+ });
85
+ },
86
+ show(refs) {
87
+ sendCommand(port, {
88
+ action: 'showEntities',
89
+ ids: refsToIds(refs),
90
+ });
91
+ },
92
+ isolate(refs) {
93
+ sendCommand(port, {
94
+ action: 'isolateEntities',
95
+ ids: refsToIds(refs),
96
+ });
97
+ },
98
+ reset() {
99
+ sendCommand(port, { action: 'showall' });
100
+ },
101
+ };
102
+ }
103
+ //# sourceMappingURL=streaming-viewer.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"streaming-viewer.js","sourceRoot":"","sources":["../src/streaming-viewer.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,19 @@
1
+ /**
2
+ * Self-contained WebGL 2 viewer HTML template for the CLI `view` command.
3
+ *
4
+ * Features:
5
+ * - Progressive geometry streaming via @ifc-lite/wasm
6
+ * - Edge-enhanced rendering (dFdx/dFdy normal discontinuity detection)
7
+ * - Ground grid with distance fade
8
+ * - Section plane clipping
9
+ * - Orbit camera with smooth inertia
10
+ * - Entity picking (GPU color-ID pass)
11
+ * - Live geometry addition (addGeometry command)
12
+ * - Full command API: colorize, isolate, xray, highlight, section, etc.
13
+ *
14
+ * Communication:
15
+ * CLI → Browser: Server-Sent Events on /events
16
+ * Browser → CLI: POST /api/command
17
+ */
18
+ export declare function getViewerHtml(modelName: string): string;
19
+ //# sourceMappingURL=viewer-html.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"viewer-html.d.ts","sourceRoot":"","sources":["../src/viewer-html.ts"],"names":[],"mappings":"AAIA;;;;;;;;;;;;;;;;GAgBG;AAEH,wBAAgB,aAAa,CAAC,SAAS,EAAE,MAAM,GAAG,MAAM,CA0xCvD"}