@lib3mf-rs/lib3mf-wasm 0.1.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/README.md ADDED
@@ -0,0 +1,11 @@
1
+ # lib3mf-wasm
2
+
3
+ WebAssembly bindings for `lib3mf-rs`.
4
+
5
+ Allows running 3MF validation, parsing, and analysis directly in the browser.
6
+
7
+ ## Building
8
+
9
+ ```bash
10
+ wasm-pack build --target web
11
+ ```
@@ -0,0 +1,189 @@
1
+ /* tslint:disable */
2
+ /* eslint-disable */
3
+
4
+ /**
5
+ * WebAssembly wrapper around the core 3MF Model.
6
+ *
7
+ * This struct provides JavaScript-accessible methods for working with 3MF files
8
+ * in the browser. It wraps [`lib3mf_core::Model`] and exposes a subset of its
9
+ * functionality through WASM bindings.
10
+ *
11
+ * # Primary API Surface
12
+ *
13
+ * The main way to use this from JavaScript:
14
+ *
15
+ * 1. Parse a 3MF file from bytes using [`WasmModel::from_bytes()`]
16
+ * 2. Query model properties: [`unit()`](WasmModel::unit), [`object_count()`](WasmModel::object_count)
17
+ *
18
+ * # JavaScript Usage
19
+ *
20
+ * ```javascript
21
+ * const model = WasmModel.from_bytes(fileBytes);
22
+ * console.log(`Unit: ${model.unit()}`);
23
+ * console.log(`Objects: ${model.object_count()}`);
24
+ * ```
25
+ *
26
+ * # Full Workflow Example
27
+ *
28
+ * ```javascript
29
+ * import init, { WasmModel, set_panic_hook } from './lib3mf_wasm.js';
30
+ *
31
+ * await init();
32
+ * set_panic_hook();
33
+ *
34
+ * // Load from file input
35
+ * const file = document.getElementById('file-input').files[0];
36
+ * const buffer = await file.arrayBuffer();
37
+ * const model = WasmModel.from_bytes(new Uint8Array(buffer));
38
+ *
39
+ * // Display basic info
40
+ * document.getElementById('unit').textContent = model.unit();
41
+ * document.getElementById('count').textContent = model.object_count();
42
+ * ```
43
+ */
44
+ export class WasmModel {
45
+ free(): void;
46
+ [Symbol.dispose](): void;
47
+ /**
48
+ * Parse a 3MF file from a byte array (e.g. from a file upload).
49
+ *
50
+ * This is the primary way to load 3MF files in the browser. It handles the full
51
+ * parsing pipeline:
52
+ *
53
+ * 1. Interprets bytes as a ZIP archive
54
+ * 2. Parses OPC relationships to locate the model XML
55
+ * 3. Parses the XML into the in-memory model structure
56
+ * 4. Returns a [`WasmModel`] ready for inspection
57
+ *
58
+ * # Arguments
59
+ *
60
+ * * `data` - The bytes of the .3mf file (ZIP archive). Typically obtained from a
61
+ * browser file input via `FileReader` or `File.arrayBuffer()`.
62
+ *
63
+ * # Returns
64
+ *
65
+ * Returns a [`WasmModel`] on success, or throws a JavaScript error on failure.
66
+ *
67
+ * # Errors
68
+ *
69
+ * This function can fail in several ways:
70
+ *
71
+ * - **Invalid ZIP**: The bytes don't represent a valid ZIP archive
72
+ * - **Missing model part**: The archive lacks a valid OPC relationship to a 3D model
73
+ * - **Malformed XML**: The model XML is invalid or doesn't conform to the 3MF schema
74
+ * - **Parser errors**: Semantic errors in the 3MF structure (invalid references, etc.)
75
+ *
76
+ * Errors are returned as JavaScript exceptions with descriptive messages.
77
+ *
78
+ * # Example (Rust)
79
+ *
80
+ * ```ignore
81
+ * use lib3mf_wasm::WasmModel;
82
+ *
83
+ * let file_bytes = std::fs::read("model.3mf")?;
84
+ * let model = WasmModel::from_bytes(&file_bytes)?;
85
+ * ```
86
+ *
87
+ * # JavaScript Usage
88
+ *
89
+ * ```javascript
90
+ * try {
91
+ * const buffer = await file.arrayBuffer();
92
+ * const model = WasmModel.from_bytes(new Uint8Array(buffer));
93
+ * console.log("Parsed successfully");
94
+ * } catch (error) {
95
+ * console.error(`Parse failed: ${error}`);
96
+ * }
97
+ * ```
98
+ */
99
+ static from_bytes(data: Uint8Array): WasmModel;
100
+ /**
101
+ * Create a new empty 3MF Model.
102
+ *
103
+ * This creates a model with default values (millimeters unit, no objects, empty build).
104
+ * In most cases, you should use [`WasmModel::from_bytes()`] instead to parse an existing
105
+ * 3MF file.
106
+ *
107
+ * # JavaScript Usage
108
+ *
109
+ * ```javascript
110
+ * const model = new WasmModel();
111
+ * // Model is empty - unit is Millimeter by default
112
+ * ```
113
+ *
114
+ * # Note
115
+ *
116
+ * This constructor has limited utility since the WASM bindings don't currently expose
117
+ * model-building APIs. It's primarily for internal use and testing.
118
+ */
119
+ constructor();
120
+ /**
121
+ * Get the total number of objects in the model resources.
122
+ *
123
+ * This counts all objects in the model's resource collection, not just
124
+ * objects referenced by build items.
125
+ *
126
+ * # JavaScript Usage
127
+ *
128
+ * ```javascript
129
+ * const count = model.object_count();
130
+ * console.log(`Model contains ${count} objects`);
131
+ * ```
132
+ *
133
+ * # Note
134
+ *
135
+ * Build items may reference the same object multiple times (instances),
136
+ * so the number of build items may differ from the object count.
137
+ */
138
+ object_count(): number;
139
+ /**
140
+ * Get the unit of measurement used in the model.
141
+ *
142
+ * Returns the unit as a string for display in JavaScript.
143
+ *
144
+ * # Possible Return Values
145
+ *
146
+ * - `"Millimeter"` (default, most common)
147
+ * - `"Centimeter"`
148
+ * - `"Inch"`
149
+ * - `"Foot"`
150
+ * - `"Meter"`
151
+ * - `"MicroMeter"`
152
+ *
153
+ * # JavaScript Usage
154
+ *
155
+ * ```javascript
156
+ * const unit = model.unit();
157
+ * console.log(`Model uses ${unit} units`);
158
+ * ```
159
+ */
160
+ unit(): string;
161
+ }
162
+
163
+ /**
164
+ * Set up better panic messages for debugging in browser console.
165
+ *
166
+ * This function configures the panic hook to provide detailed error messages
167
+ * when the WASM module panics. Without this, panics will show generic
168
+ * "unreachable executed" messages that are difficult to debug.
169
+ *
170
+ * # When to Call
171
+ *
172
+ * Call this once during initialization, before any other API calls:
173
+ *
174
+ * ```javascript
175
+ * import init, { set_panic_hook } from './lib3mf_wasm.js';
176
+ *
177
+ * await init();
178
+ * set_panic_hook(); // Call once at startup
179
+ *
180
+ * // Now make other API calls...
181
+ * ```
182
+ *
183
+ * # Performance
184
+ *
185
+ * This adds a small amount of overhead to panics, but has no impact on normal
186
+ * execution. It's recommended for development builds but can be omitted in
187
+ * production if you want minimal bundle size.
188
+ */
189
+ export function set_panic_hook(): void;
package/lib3mf_wasm.js ADDED
@@ -0,0 +1,9 @@
1
+ /* @ts-self-types="./lib3mf_wasm.d.ts" */
2
+
3
+ import * as wasm from "./lib3mf_wasm_bg.wasm";
4
+ import { __wbg_set_wasm } from "./lib3mf_wasm_bg.js";
5
+ __wbg_set_wasm(wasm);
6
+ wasm.__wbindgen_start();
7
+ export {
8
+ WasmModel, set_panic_hook
9
+ } from "./lib3mf_wasm_bg.js";
@@ -0,0 +1,382 @@
1
+ /**
2
+ * WebAssembly wrapper around the core 3MF Model.
3
+ *
4
+ * This struct provides JavaScript-accessible methods for working with 3MF files
5
+ * in the browser. It wraps [`lib3mf_core::Model`] and exposes a subset of its
6
+ * functionality through WASM bindings.
7
+ *
8
+ * # Primary API Surface
9
+ *
10
+ * The main way to use this from JavaScript:
11
+ *
12
+ * 1. Parse a 3MF file from bytes using [`WasmModel::from_bytes()`]
13
+ * 2. Query model properties: [`unit()`](WasmModel::unit), [`object_count()`](WasmModel::object_count)
14
+ *
15
+ * # JavaScript Usage
16
+ *
17
+ * ```javascript
18
+ * const model = WasmModel.from_bytes(fileBytes);
19
+ * console.log(`Unit: ${model.unit()}`);
20
+ * console.log(`Objects: ${model.object_count()}`);
21
+ * ```
22
+ *
23
+ * # Full Workflow Example
24
+ *
25
+ * ```javascript
26
+ * import init, { WasmModel, set_panic_hook } from './lib3mf_wasm.js';
27
+ *
28
+ * await init();
29
+ * set_panic_hook();
30
+ *
31
+ * // Load from file input
32
+ * const file = document.getElementById('file-input').files[0];
33
+ * const buffer = await file.arrayBuffer();
34
+ * const model = WasmModel.from_bytes(new Uint8Array(buffer));
35
+ *
36
+ * // Display basic info
37
+ * document.getElementById('unit').textContent = model.unit();
38
+ * document.getElementById('count').textContent = model.object_count();
39
+ * ```
40
+ */
41
+ export class WasmModel {
42
+ static __wrap(ptr) {
43
+ ptr = ptr >>> 0;
44
+ const obj = Object.create(WasmModel.prototype);
45
+ obj.__wbg_ptr = ptr;
46
+ WasmModelFinalization.register(obj, obj.__wbg_ptr, obj);
47
+ return obj;
48
+ }
49
+ __destroy_into_raw() {
50
+ const ptr = this.__wbg_ptr;
51
+ this.__wbg_ptr = 0;
52
+ WasmModelFinalization.unregister(this);
53
+ return ptr;
54
+ }
55
+ free() {
56
+ const ptr = this.__destroy_into_raw();
57
+ wasm.__wbg_wasmmodel_free(ptr, 0);
58
+ }
59
+ /**
60
+ * Parse a 3MF file from a byte array (e.g. from a file upload).
61
+ *
62
+ * This is the primary way to load 3MF files in the browser. It handles the full
63
+ * parsing pipeline:
64
+ *
65
+ * 1. Interprets bytes as a ZIP archive
66
+ * 2. Parses OPC relationships to locate the model XML
67
+ * 3. Parses the XML into the in-memory model structure
68
+ * 4. Returns a [`WasmModel`] ready for inspection
69
+ *
70
+ * # Arguments
71
+ *
72
+ * * `data` - The bytes of the .3mf file (ZIP archive). Typically obtained from a
73
+ * browser file input via `FileReader` or `File.arrayBuffer()`.
74
+ *
75
+ * # Returns
76
+ *
77
+ * Returns a [`WasmModel`] on success, or throws a JavaScript error on failure.
78
+ *
79
+ * # Errors
80
+ *
81
+ * This function can fail in several ways:
82
+ *
83
+ * - **Invalid ZIP**: The bytes don't represent a valid ZIP archive
84
+ * - **Missing model part**: The archive lacks a valid OPC relationship to a 3D model
85
+ * - **Malformed XML**: The model XML is invalid or doesn't conform to the 3MF schema
86
+ * - **Parser errors**: Semantic errors in the 3MF structure (invalid references, etc.)
87
+ *
88
+ * Errors are returned as JavaScript exceptions with descriptive messages.
89
+ *
90
+ * # Example (Rust)
91
+ *
92
+ * ```ignore
93
+ * use lib3mf_wasm::WasmModel;
94
+ *
95
+ * let file_bytes = std::fs::read("model.3mf")?;
96
+ * let model = WasmModel::from_bytes(&file_bytes)?;
97
+ * ```
98
+ *
99
+ * # JavaScript Usage
100
+ *
101
+ * ```javascript
102
+ * try {
103
+ * const buffer = await file.arrayBuffer();
104
+ * const model = WasmModel.from_bytes(new Uint8Array(buffer));
105
+ * console.log("Parsed successfully");
106
+ * } catch (error) {
107
+ * console.error(`Parse failed: ${error}`);
108
+ * }
109
+ * ```
110
+ * @param {Uint8Array} data
111
+ * @returns {WasmModel}
112
+ */
113
+ static from_bytes(data) {
114
+ const ptr0 = passArray8ToWasm0(data, wasm.__wbindgen_malloc);
115
+ const len0 = WASM_VECTOR_LEN;
116
+ const ret = wasm.wasmmodel_from_bytes(ptr0, len0);
117
+ if (ret[2]) {
118
+ throw takeFromExternrefTable0(ret[1]);
119
+ }
120
+ return WasmModel.__wrap(ret[0]);
121
+ }
122
+ /**
123
+ * Create a new empty 3MF Model.
124
+ *
125
+ * This creates a model with default values (millimeters unit, no objects, empty build).
126
+ * In most cases, you should use [`WasmModel::from_bytes()`] instead to parse an existing
127
+ * 3MF file.
128
+ *
129
+ * # JavaScript Usage
130
+ *
131
+ * ```javascript
132
+ * const model = new WasmModel();
133
+ * // Model is empty - unit is Millimeter by default
134
+ * ```
135
+ *
136
+ * # Note
137
+ *
138
+ * This constructor has limited utility since the WASM bindings don't currently expose
139
+ * model-building APIs. It's primarily for internal use and testing.
140
+ */
141
+ constructor() {
142
+ const ret = wasm.wasmmodel_new();
143
+ this.__wbg_ptr = ret >>> 0;
144
+ WasmModelFinalization.register(this, this.__wbg_ptr, this);
145
+ return this;
146
+ }
147
+ /**
148
+ * Get the total number of objects in the model resources.
149
+ *
150
+ * This counts all objects in the model's resource collection, not just
151
+ * objects referenced by build items.
152
+ *
153
+ * # JavaScript Usage
154
+ *
155
+ * ```javascript
156
+ * const count = model.object_count();
157
+ * console.log(`Model contains ${count} objects`);
158
+ * ```
159
+ *
160
+ * # Note
161
+ *
162
+ * Build items may reference the same object multiple times (instances),
163
+ * so the number of build items may differ from the object count.
164
+ * @returns {number}
165
+ */
166
+ object_count() {
167
+ const ret = wasm.wasmmodel_object_count(this.__wbg_ptr);
168
+ return ret >>> 0;
169
+ }
170
+ /**
171
+ * Get the unit of measurement used in the model.
172
+ *
173
+ * Returns the unit as a string for display in JavaScript.
174
+ *
175
+ * # Possible Return Values
176
+ *
177
+ * - `"Millimeter"` (default, most common)
178
+ * - `"Centimeter"`
179
+ * - `"Inch"`
180
+ * - `"Foot"`
181
+ * - `"Meter"`
182
+ * - `"MicroMeter"`
183
+ *
184
+ * # JavaScript Usage
185
+ *
186
+ * ```javascript
187
+ * const unit = model.unit();
188
+ * console.log(`Model uses ${unit} units`);
189
+ * ```
190
+ * @returns {string}
191
+ */
192
+ unit() {
193
+ let deferred1_0;
194
+ let deferred1_1;
195
+ try {
196
+ const ret = wasm.wasmmodel_unit(this.__wbg_ptr);
197
+ deferred1_0 = ret[0];
198
+ deferred1_1 = ret[1];
199
+ return getStringFromWasm0(ret[0], ret[1]);
200
+ } finally {
201
+ wasm.__wbindgen_free(deferred1_0, deferred1_1, 1);
202
+ }
203
+ }
204
+ }
205
+ if (Symbol.dispose) WasmModel.prototype[Symbol.dispose] = WasmModel.prototype.free;
206
+
207
+ /**
208
+ * Set up better panic messages for debugging in browser console.
209
+ *
210
+ * This function configures the panic hook to provide detailed error messages
211
+ * when the WASM module panics. Without this, panics will show generic
212
+ * "unreachable executed" messages that are difficult to debug.
213
+ *
214
+ * # When to Call
215
+ *
216
+ * Call this once during initialization, before any other API calls:
217
+ *
218
+ * ```javascript
219
+ * import init, { set_panic_hook } from './lib3mf_wasm.js';
220
+ *
221
+ * await init();
222
+ * set_panic_hook(); // Call once at startup
223
+ *
224
+ * // Now make other API calls...
225
+ * ```
226
+ *
227
+ * # Performance
228
+ *
229
+ * This adds a small amount of overhead to panics, but has no impact on normal
230
+ * execution. It's recommended for development builds but can be omitted in
231
+ * production if you want minimal bundle size.
232
+ */
233
+ export function set_panic_hook() {
234
+ wasm.set_panic_hook();
235
+ }
236
+ export function __wbg_Error_8c4e43fe74559d73(arg0, arg1) {
237
+ const ret = Error(getStringFromWasm0(arg0, arg1));
238
+ return ret;
239
+ }
240
+ export function __wbg___wbindgen_throw_be289d5034ed271b(arg0, arg1) {
241
+ throw new Error(getStringFromWasm0(arg0, arg1));
242
+ }
243
+ export function __wbg_error_7534b8e9a36f1ab4(arg0, arg1) {
244
+ let deferred0_0;
245
+ let deferred0_1;
246
+ try {
247
+ deferred0_0 = arg0;
248
+ deferred0_1 = arg1;
249
+ console.error(getStringFromWasm0(arg0, arg1));
250
+ } finally {
251
+ wasm.__wbindgen_free(deferred0_0, deferred0_1, 1);
252
+ }
253
+ }
254
+ export function __wbg_new_8a6f238a6ece86ea() {
255
+ const ret = new Error();
256
+ return ret;
257
+ }
258
+ export function __wbg_stack_0ed75d68575b0f3c(arg0, arg1) {
259
+ const ret = arg1.stack;
260
+ const ptr1 = passStringToWasm0(ret, wasm.__wbindgen_malloc, wasm.__wbindgen_realloc);
261
+ const len1 = WASM_VECTOR_LEN;
262
+ getDataViewMemory0().setInt32(arg0 + 4 * 1, len1, true);
263
+ getDataViewMemory0().setInt32(arg0 + 4 * 0, ptr1, true);
264
+ }
265
+ export function __wbindgen_init_externref_table() {
266
+ const table = wasm.__wbindgen_externrefs;
267
+ const offset = table.grow(4);
268
+ table.set(0, undefined);
269
+ table.set(offset + 0, undefined);
270
+ table.set(offset + 1, null);
271
+ table.set(offset + 2, true);
272
+ table.set(offset + 3, false);
273
+ }
274
+ const WasmModelFinalization = (typeof FinalizationRegistry === 'undefined')
275
+ ? { register: () => {}, unregister: () => {} }
276
+ : new FinalizationRegistry(ptr => wasm.__wbg_wasmmodel_free(ptr >>> 0, 1));
277
+
278
+ let cachedDataViewMemory0 = null;
279
+ function getDataViewMemory0() {
280
+ if (cachedDataViewMemory0 === null || cachedDataViewMemory0.buffer.detached === true || (cachedDataViewMemory0.buffer.detached === undefined && cachedDataViewMemory0.buffer !== wasm.memory.buffer)) {
281
+ cachedDataViewMemory0 = new DataView(wasm.memory.buffer);
282
+ }
283
+ return cachedDataViewMemory0;
284
+ }
285
+
286
+ function getStringFromWasm0(ptr, len) {
287
+ ptr = ptr >>> 0;
288
+ return decodeText(ptr, len);
289
+ }
290
+
291
+ let cachedUint8ArrayMemory0 = null;
292
+ function getUint8ArrayMemory0() {
293
+ if (cachedUint8ArrayMemory0 === null || cachedUint8ArrayMemory0.byteLength === 0) {
294
+ cachedUint8ArrayMemory0 = new Uint8Array(wasm.memory.buffer);
295
+ }
296
+ return cachedUint8ArrayMemory0;
297
+ }
298
+
299
+ function passArray8ToWasm0(arg, malloc) {
300
+ const ptr = malloc(arg.length * 1, 1) >>> 0;
301
+ getUint8ArrayMemory0().set(arg, ptr / 1);
302
+ WASM_VECTOR_LEN = arg.length;
303
+ return ptr;
304
+ }
305
+
306
+ function passStringToWasm0(arg, malloc, realloc) {
307
+ if (realloc === undefined) {
308
+ const buf = cachedTextEncoder.encode(arg);
309
+ const ptr = malloc(buf.length, 1) >>> 0;
310
+ getUint8ArrayMemory0().subarray(ptr, ptr + buf.length).set(buf);
311
+ WASM_VECTOR_LEN = buf.length;
312
+ return ptr;
313
+ }
314
+
315
+ let len = arg.length;
316
+ let ptr = malloc(len, 1) >>> 0;
317
+
318
+ const mem = getUint8ArrayMemory0();
319
+
320
+ let offset = 0;
321
+
322
+ for (; offset < len; offset++) {
323
+ const code = arg.charCodeAt(offset);
324
+ if (code > 0x7F) break;
325
+ mem[ptr + offset] = code;
326
+ }
327
+ if (offset !== len) {
328
+ if (offset !== 0) {
329
+ arg = arg.slice(offset);
330
+ }
331
+ ptr = realloc(ptr, len, len = offset + arg.length * 3, 1) >>> 0;
332
+ const view = getUint8ArrayMemory0().subarray(ptr + offset, ptr + len);
333
+ const ret = cachedTextEncoder.encodeInto(arg, view);
334
+
335
+ offset += ret.written;
336
+ ptr = realloc(ptr, len, offset, 1) >>> 0;
337
+ }
338
+
339
+ WASM_VECTOR_LEN = offset;
340
+ return ptr;
341
+ }
342
+
343
+ function takeFromExternrefTable0(idx) {
344
+ const value = wasm.__wbindgen_externrefs.get(idx);
345
+ wasm.__externref_table_dealloc(idx);
346
+ return value;
347
+ }
348
+
349
+ let cachedTextDecoder = new TextDecoder('utf-8', { ignoreBOM: true, fatal: true });
350
+ cachedTextDecoder.decode();
351
+ const MAX_SAFARI_DECODE_BYTES = 2146435072;
352
+ let numBytesDecoded = 0;
353
+ function decodeText(ptr, len) {
354
+ numBytesDecoded += len;
355
+ if (numBytesDecoded >= MAX_SAFARI_DECODE_BYTES) {
356
+ cachedTextDecoder = new TextDecoder('utf-8', { ignoreBOM: true, fatal: true });
357
+ cachedTextDecoder.decode();
358
+ numBytesDecoded = len;
359
+ }
360
+ return cachedTextDecoder.decode(getUint8ArrayMemory0().subarray(ptr, ptr + len));
361
+ }
362
+
363
+ const cachedTextEncoder = new TextEncoder();
364
+
365
+ if (!('encodeInto' in cachedTextEncoder)) {
366
+ cachedTextEncoder.encodeInto = function (arg, view) {
367
+ const buf = cachedTextEncoder.encode(arg);
368
+ view.set(buf);
369
+ return {
370
+ read: arg.length,
371
+ written: buf.length
372
+ };
373
+ };
374
+ }
375
+
376
+ let WASM_VECTOR_LEN = 0;
377
+
378
+
379
+ let wasm;
380
+ export function __wbg_set_wasm(val) {
381
+ wasm = val;
382
+ }
Binary file
package/package.json ADDED
@@ -0,0 +1,26 @@
1
+ {
2
+ "name": "@lib3mf-rs/lib3mf-wasm",
3
+ "type": "module",
4
+ "collaborators": [
5
+ "Steve Scargall <steve@example.com>"
6
+ ],
7
+ "description": "WebAssembly bindings for lib3mf-rs",
8
+ "version": "0.1.0",
9
+ "license": "MIT OR Apache-2.0",
10
+ "repository": {
11
+ "type": "git",
12
+ "url": "https://github.com/sscargal/lib3mf-rs"
13
+ },
14
+ "files": [
15
+ "lib3mf_wasm_bg.wasm",
16
+ "lib3mf_wasm.js",
17
+ "lib3mf_wasm_bg.js",
18
+ "lib3mf_wasm.d.ts"
19
+ ],
20
+ "main": "lib3mf_wasm.js",
21
+ "types": "lib3mf_wasm.d.ts",
22
+ "sideEffects": [
23
+ "./lib3mf_wasm.js",
24
+ "./snippets/*"
25
+ ]
26
+ }