@openbim/ifcx 0.0.0-stage → 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/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 point-grey
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md CHANGED
@@ -1,3 +1,208 @@
1
- # Temporary Holding Version
1
+ # @openbim/ifcx
2
2
 
3
- This version is a temporary placeholder for this package. An operational version to replace this has been submitted for review and is awaiting a staged release.
3
+ Read, write, validate and compose **IFC5 / IFCX** files, and export them as
4
+ GLB, from JavaScript and TypeScript. A WebAssembly build of the
5
+ [`openbim-ifcx`](https://crates.io/crates/openbim-ifcx) Rust crate, from the
6
+ `openbim-ifcx-wasm` crate in [openbimrs/ifcx](https://github.com/openbimrs/ifcx).
7
+
8
+ Published to npm as `@openbim/ifcx`: one package for Node 18 and later,
9
+ bundlers (webpack, Rollup) and plain browser pages, with TypeScript
10
+ declarations for each. Every build is tested from the packed tarball,
11
+ the browser builds in headless Chrome.
12
+
13
+ Targets the `ifcx_alpha` draft of buildingSMART's IFC5. IFCX is still a
14
+ moving draft; see the
15
+ [capabilities](https://github.com/openbimrs/ifcx/blob/main/docs/capabilities.md).
16
+
17
+ ## Install
18
+
19
+ ```sh
20
+ npm install @openbim/ifcx
21
+ ```
22
+
23
+ ## Entry points
24
+
25
+ | Import | Build | Loads the wasm module |
26
+ | --- | --- | --- |
27
+ | `@openbim/ifcx` in Node (`require` or `import`) | CommonJS (`wasm-bindgen --target nodejs`) | synchronously, on load |
28
+ | `@openbim/ifcx` in a bundler | ES module (`--target bundler`) | through the bundler's WebAssembly support, e.g. webpack 5 `experiments.asyncWebAssembly` |
29
+ | `@openbim/ifcx/web` | ES module (`--target web`) | when you `await init()` |
30
+
31
+ `package.json` `exports` picks the build: the `node` condition gets the
32
+ CommonJS build, everything else the bundler build. Use `@openbim/ifcx/web`
33
+ for a page without a bundler, and for bundlers without WebAssembly ES
34
+ module integration, such as Vite (or add `vite-plugin-wasm`). All three
35
+ export the same API; the `web` build adds the default `init` export (and
36
+ `initSync`).
37
+
38
+ ## Node
39
+
40
+ ```js
41
+ const { readFileSync, writeFileSync } = require("node:fs");
42
+ const { IfcxFile, compose, exportGlb } = require("@openbim/ifcx"); // or import
43
+
44
+ const model = readFileSync("model.ifcx"); // a Buffer is a Uint8Array
45
+ const file = IfcxFile.parse(model); // or a string
46
+ console.log(file.header.id, file.nodeCount);
47
+
48
+ const report = file.validate();
49
+ for (const f of report.failures) {
50
+ console.log(f.node, f.attribute, f.pointer, f.kind, f.message);
51
+ }
52
+ const text = file.write(); // JSON text, unchanged from the input
53
+
54
+ // Layers weakest first: the last layer's opinions win.
55
+ const tree = compose([model, readFileSync("overlay.ifcx")]);
56
+ console.log(Object.keys(tree.children)); // the root nodes
57
+
58
+ // Any glTF viewer opens the result.
59
+ writeFileSync("model.glb", exportGlb([model, readFileSync("overlay.ifcx")]));
60
+ ```
61
+
62
+ ## Browser
63
+
64
+ With a bundler, import the package as usual; the bundler loads the wasm
65
+ module:
66
+
67
+ ```js
68
+ import { compose, exportGlb, fetchImports } from "@openbim/ifcx";
69
+ ```
70
+
71
+ Without one, serve the package's `web/` directory, map the name in an
72
+ import map (or import the file by URL), and call `init()` once before
73
+ anything else. `init()` fetches `openbim_ifcx_wasm_bg.wasm` from next to
74
+ the module; pass `init({ module_or_path: url })` to load it from elsewhere.
75
+
76
+ ```html
77
+ <script type="importmap">
78
+ { "imports": { "@openbim/ifcx/web": "/node_modules/@openbim/ifcx/web/openbim_ifcx_wasm.js" } }
79
+ </script>
80
+ <script type="module">
81
+ import init, { IfcxFile, exportGlb, fetchImports, validate } from "@openbim/ifcx/web";
82
+
83
+ await init();
84
+ const url = new URL("models/house.ifcx", location.href);
85
+ const model = new Uint8Array(await (await fetch(url)).arrayBuffer());
86
+
87
+ // Fetch what the model imports, relative to its own URL, then use it.
88
+ const imports = await fetchImports(model, { baseUrl: url });
89
+ const report = validate(model, { imports });
90
+ const glb = exportGlb(model, { imports }); // e.g. for three.js's GLTFLoader.parse
91
+ console.log(IfcxFile.parse(model).header.id, report.valid, glb.length);
92
+ </script>
93
+ ```
94
+
95
+ ## API
96
+
97
+ | Call | Returns |
98
+ | --- | --- |
99
+ | `IfcxFile.parse(input)` | an `IfcxFile`; `input` is JSON text or UTF-8 bytes |
100
+ | `file.write(pretty?)` | the file as JSON text, lossless |
101
+ | `file.toJSON()` | the file as a plain object (`JSON.stringify(file)` works) |
102
+ | `file.header` | the `header` object |
103
+ | `file.nodeCount` | number of entries in `data` |
104
+ | `file.validate()` | a `ValidationReport` against the file's own `schemas` |
105
+ | `compose(layers, options?)` | the composed tree as a `ComposedNode` |
106
+ | `validate(layers, options?)` | a `ValidationReport` over all layers and resolved imports |
107
+ | `exportGlb(layers, options?)` | the composed model as binary glTF 2.0, a `Uint8Array` |
108
+ | `fetchImports(layers, options?)` | a promise of a `Map` from import `uri` to file, for `options.imports` |
109
+ | `init(input?)` | `@openbim/ifcx/web` only: loads the wasm module; await it once first |
110
+
111
+ `layers` is one input or an array of them, weakest first. TypeScript
112
+ declarations ship with the package.
113
+
114
+ **Lossless round trip.** `write()` keeps key order, fields the draft does
115
+ not define, `null` deletions and every number exactly as read. A plain
116
+ object from `toJSON()` cannot promise that: JavaScript lists integer-like
117
+ keys first and rounds integers beyond 2^53. Keep the `IfcxFile` and call
118
+ `write()` to save a file.
119
+
120
+ **Composed tree.** `compose` returns the artificial root (path `""`) over
121
+ every root node. Each node is `{ path, attributes, children }`, the shape of
122
+ upstream's `PostCompositionNode`, with `children` keyed by child name. Nodes
123
+ shared through inheritance appear once per place they are used.
124
+
125
+ **Imports.** Without `options.imports`, imports are not resolved: the layers'
126
+ schemas and data are concatenated in order, as upstream's `Federate` does.
127
+ With `options.imports` (an object or a `Map` from the exact import `uri` to
128
+ a file), the layers become the imports of a synthetic main layer, as
129
+ upstream's `ifcx compose` command builds it, and every import must be
130
+ supplied. `integrity` values are checked against the supplied bytes. In
131
+ upstream order an import overrides the layer that imports it, and a later
132
+ layer overrides both. The WebAssembly module never touches the network or
133
+ the filesystem.
134
+
135
+ **Fetching imports.** `fetchImports(layers, options?)` collects them for
136
+ you, in JavaScript: it reads each layer's `imports`, fetches every `uri`
137
+ (recursively, each once, concurrently) and resolves to a `Map` keyed by
138
+ the exact import `uri`, ready for `options.imports`.
139
+
140
+ - `baseUrl`: what relative URIs of the given layers resolve against;
141
+ `location.href` by default in a browser. A fetched file's own imports
142
+ resolve against its URL.
143
+ - `fetch`: the function that loads a URL, the global `fetch` by default.
144
+ Pass your own for credentials, a cache, a mirror, or Node files:
145
+ `{ fetch: async (url) => readFile(new URL(url)) }`. It may return a
146
+ `Response`, a `Uint8Array`, an `ArrayBuffer` or a string. Without a
147
+ `baseUrl` it receives a relative `uri` unchanged.
148
+ - `imports`: files already at hand, never fetched again.
149
+ - `signal`: an `AbortSignal` passed to every fetch.
150
+
151
+ The in-memory map keys a file by its exact `uri`, as upstream's
152
+ `InMemoryLayerProvider` does, so two different files imported under the
153
+ same relative `uri` from different directories cannot both be supplied;
154
+ the first one fetched is used. Anything that cannot be fetched rejects with
155
+ an `IfcxError` with code `fetch`; `integrity` and import cycles are checked
156
+ by `compose`, `validate` and `exportGlb`.
157
+
158
+ **GLB export.** `exportGlb` writes the render scene of the composed tree:
159
+ transformed, instanced meshes, lines and points with their materials, one
160
+ glTF node per instance named by its IFCX path. The root node turns IFCX's
161
+ Z-up into glTF's Y-up (`yUp: false` keeps Z-up) and carries the scene
162
+ origin, so georeferenced models keep their coordinates
163
+ (`originOnRoot: false` centres the model on the glTF origin instead).
164
+ Geometry values that do not decode are left out. `options.imports` works
165
+ as for `compose`.
166
+
167
+ **Validation report.** `{ valid, failures }`, each failure
168
+ `{ node, attribute, pointer, kind, message }`: the node path, the attribute
169
+ id, an RFC 6901 pointer into the value (`""` for the value itself), a stable
170
+ `kind` such as `missing-schema`, `type-mismatch`, `not-an-integer`,
171
+ `not-an-option`, `missing-key`, `too-few-elements` or `too-many-elements`,
172
+ and a human message.
173
+
174
+ ## Errors
175
+
176
+ Every failure throws an `Error` with `name === "IfcxError"` and a stable
177
+ `code`:
178
+
179
+ | `code` | Meaning |
180
+ | --- | --- |
181
+ | `read` | not an IFCX file: invalid JSON or the wrong shape; the message gives line and column |
182
+ | `write` | the file could not be written |
183
+ | `layer` | imports could not be resolved: a missing file, an import cycle, an `integrity` mismatch |
184
+ | `compose` | a reference cycle, or a reference to a node no layer defines |
185
+ | `invalid-argument` | an argument of the wrong type, or no layers |
186
+ | `glb` | the scene could not be written as GLB (over 4 GiB) |
187
+ | `fetch` | `fetchImports` could not fetch an import, or got no file back |
188
+
189
+ A code is never renamed or reused.
190
+
191
+ ## Building
192
+
193
+ ```sh
194
+ cargo install wasm-bindgen-cli --version 0.2.128 --locked
195
+ crates/openbim-ifcx-wasm/scripts/build-npm-pkg.sh # builds pkg/, runs every check
196
+ ```
197
+
198
+ The script binds one release build three times (`pkg/`, `pkg/bundler/`,
199
+ `pkg/web/`), runs the Node suite, then `npm pack`s the package and checks
200
+ each entry point from the tarball: Node `require` and `import`, a webpack
201
+ bundle, and the `web` and bundler builds in headless Chrome, which parse,
202
+ validate, compose, fetch imports and export GLB from the repository's
203
+ fixtures. It finds Chrome through `CHROME_BIN`, `PATH` or a Playwright
204
+ download; `IFCX_SKIP_BROWSER=1` skips the browser part with a warning.
205
+
206
+ ## License
207
+
208
+ MIT, like the rest of `openbimrs/ifcx`.
@@ -0,0 +1,53 @@
1
+ import type { IfcxInput } from "./openbim_ifcx_wasm.js";
2
+
3
+ /** The part of a `fetch` `Response` that `fetchImports` reads. */
4
+ export interface FetchedResponse {
5
+ ok?: boolean;
6
+ status?: number;
7
+ statusText?: string;
8
+ arrayBuffer(): Promise<ArrayBuffer>;
9
+ }
10
+
11
+ /**
12
+ * Loads one imported file. `fetch` itself fits; a custom function may also
13
+ * return the file directly. `url` is the import `uri` resolved against the
14
+ * importing file's URL (or `baseUrl`); when a relative `uri` has no base to
15
+ * resolve against, a custom function receives the `uri` unchanged.
16
+ */
17
+ export type ImportFetcher = (
18
+ url: string,
19
+ init?: { signal?: AbortSignal },
20
+ ) => Promise<FetchedResponse | IfcxInput | ArrayBuffer>;
21
+
22
+ /** Options for `fetchImports`. */
23
+ export interface FetchImportsOptions {
24
+ /**
25
+ * URL that relative import URIs of the given layers resolve against.
26
+ * Defaults to `location.href` in a browser. Imports of a fetched file
27
+ * resolve against that file's URL.
28
+ */
29
+ baseUrl?: string | URL;
30
+ /** Loads each file. Defaults to the global `fetch`. */
31
+ fetch?: ImportFetcher;
32
+ /** Files already at hand, keyed by import `uri`; never fetched again. */
33
+ imports?: Record<string, IfcxInput> | Map<string, IfcxInput>;
34
+ /** Passed to every fetch call. */
35
+ signal?: AbortSignal;
36
+ }
37
+
38
+ /**
39
+ * Fetch every file the layers import, recursively and each `uri` once, and
40
+ * return them keyed by the exact import `uri`: the `imports` option of
41
+ * `compose`, `validate` and `exportGlb`. Network access happens here, in
42
+ * JavaScript; the WebAssembly module never fetches anything. A file that
43
+ * cannot be fetched rejects with an `IfcxError` whose `code` is `"fetch"`.
44
+ *
45
+ * ```js
46
+ * const imports = await fetchImports(layers, { baseUrl: "https://example.org/model/" });
47
+ * const tree = compose(layers, { imports });
48
+ * ```
49
+ */
50
+ export function fetchImports(
51
+ layers: IfcxInput | IfcxInput[],
52
+ options?: FetchImportsOptions,
53
+ ): Promise<Map<string, IfcxInput>>;
@@ -0,0 +1,137 @@
1
+ // Import resolution over `fetch`, for the `imports` option of `compose`,
2
+ // `validate` and `exportGlb`.
3
+ //
4
+ // Plain JavaScript on purpose: the Rust crates perform no network access
5
+ // (ADR 0002), so the binding resolves imports in memory only and this
6
+ // module does the fetching on the JavaScript side. It reads each layer's
7
+ // `imports` list, fetches every named file (recursively, each URI once) and
8
+ // returns the files keyed by their exact import `uri`, which is what the
9
+ // in-memory `imports` option expects. Integrity values are checked later,
10
+ // by the binding, against the fetched bytes.
11
+ //
12
+ // This file is the ES module source. scripts/build-npm-pkg.sh copies it into
13
+ // the `web` and `bundler` builds and derives the CommonJS copy for the Node
14
+ // build from it, so it must stay self-contained: no imports, and every
15
+ // export a top-level `export async function` or `export function`.
16
+
17
+ /** The imports of an IFCX file, or none when it is not readable JSON. */
18
+ function importUris(input) {
19
+ let text = input;
20
+ if (typeof input !== "string") {
21
+ text = new TextDecoder().decode(input);
22
+ }
23
+ let file;
24
+ try {
25
+ file = JSON.parse(text);
26
+ } catch {
27
+ // Not JSON: the binding call that receives it reports where.
28
+ return [];
29
+ }
30
+ const imports = file !== null && typeof file === "object" ? file.imports : undefined;
31
+ if (!Array.isArray(imports)) return [];
32
+ return imports
33
+ .map((entry) => (entry !== null && typeof entry === "object" ? entry.uri : undefined))
34
+ .filter((uri) => typeof uri === "string");
35
+ }
36
+
37
+ function fetchError(message, cause) {
38
+ const error = new Error(message, cause === undefined ? undefined : { cause });
39
+ error.name = "IfcxError";
40
+ error.code = "fetch";
41
+ return error;
42
+ }
43
+
44
+ function isInput(value) {
45
+ return typeof value === "string" || value instanceof Uint8Array;
46
+ }
47
+
48
+ /** The URL `uri` names, relative to `base`; `undefined` if it cannot be resolved. */
49
+ function resolveUrl(uri, base) {
50
+ try {
51
+ return base === undefined ? new URL(uri).href : new URL(uri, base).href;
52
+ } catch {
53
+ return undefined;
54
+ }
55
+ }
56
+
57
+ /** What a fetch function returned, as an IFCX input. */
58
+ async function readResponse(response, uri, url) {
59
+ if (isInput(response)) return response;
60
+ if (response instanceof ArrayBuffer) return new Uint8Array(response);
61
+ if (response !== null && typeof response === "object" && typeof response.arrayBuffer === "function") {
62
+ if (response.ok === false) {
63
+ const status = [response.status, response.statusText].filter(Boolean).join(" ");
64
+ throw fetchError(`could not fetch import ${JSON.stringify(uri)} from ${url}: HTTP ${status}`);
65
+ }
66
+ return new Uint8Array(await response.arrayBuffer());
67
+ }
68
+ throw fetchError(
69
+ `the fetch function returned neither a Response, a Uint8Array, an ArrayBuffer nor a string for ${JSON.stringify(uri)}`,
70
+ );
71
+ }
72
+
73
+ /**
74
+ * Fetch every file the layers import, recursively, keyed by the exact
75
+ * import `uri`, for the `imports` option of `compose`, `validate` and
76
+ * `exportGlb`.
77
+ */
78
+ export async function fetchImports(layers, options = {}) {
79
+ const list = Array.isArray(layers) ? layers : [layers];
80
+ for (const layer of list) {
81
+ if (!isInput(layer)) {
82
+ throw fetchError("every layer must be a string or a Uint8Array");
83
+ }
84
+ }
85
+ const fetcher = options.fetch ?? globalThis.fetch;
86
+ if (typeof fetcher !== "function") {
87
+ throw fetchError("no fetch function: pass options.fetch");
88
+ }
89
+ const custom = options.fetch !== undefined;
90
+ const base =
91
+ options.baseUrl !== undefined ? String(options.baseUrl) : globalThis.location?.href;
92
+ const init = options.signal === undefined ? undefined : { signal: options.signal };
93
+
94
+ const found = new Map();
95
+ if (options.imports !== undefined) {
96
+ const known = options.imports instanceof Map ? options.imports : Object.entries(options.imports);
97
+ for (const [uri, file] of known) found.set(uri, file);
98
+ }
99
+
100
+ // Breadth first; each level's fetches run concurrently. `url` is where a
101
+ // layer came from, so its relative imports resolve against it.
102
+ let level = list.map((file) => ({ file, url: base }));
103
+ for (const [, file] of found) level.push({ file, url: base });
104
+ while (level.length > 0) {
105
+ const pending = [];
106
+ for (const { file, url: importer } of level) {
107
+ for (const uri of importUris(file)) {
108
+ if (found.has(uri)) continue;
109
+ found.set(uri, undefined); // claimed: fetch each URI once
110
+ const url = resolveUrl(uri, importer);
111
+ if (url === undefined && !custom) {
112
+ throw fetchError(
113
+ `cannot resolve import ${JSON.stringify(uri)} to a URL: pass options.baseUrl`,
114
+ );
115
+ }
116
+ pending.push(
117
+ (async () => {
118
+ let response;
119
+ try {
120
+ response = await fetcher(url ?? uri, init);
121
+ } catch (error) {
122
+ throw fetchError(
123
+ `could not fetch import ${JSON.stringify(uri)} from ${url ?? uri}: ${error?.message ?? error}`,
124
+ error,
125
+ );
126
+ }
127
+ const fetched = await readResponse(response, uri, url ?? uri);
128
+ found.set(uri, fetched);
129
+ return { file: fetched, url: url ?? importer };
130
+ })(),
131
+ );
132
+ }
133
+ }
134
+ level = await Promise.all(pending);
135
+ }
136
+ return found;
137
+ }
@@ -0,0 +1,139 @@
1
+ /* tslint:disable */
2
+ /* eslint-disable */
3
+
4
+ /** An IFCX file as JSON text or as its UTF-8 bytes. */
5
+ export type IfcxInput = string | Uint8Array;
6
+
7
+ /** Options for `compose` and `validate`. */
8
+ export interface LayerOptions {
9
+ /**
10
+ * Files that imports name, keyed by the exact import `uri`. When given
11
+ * (even empty), imports are resolved as upstream does and every import
12
+ * must be here; without it, imports are not resolved.
13
+ */
14
+ imports?: Record<string, IfcxInput> | Map<string, IfcxInput>;
15
+ }
16
+
17
+ /** One node of a composed tree. The root has path `""`. */
18
+ export interface ComposedNode {
19
+ path: string;
20
+ attributes: Record<string, unknown>;
21
+ children: Record<string, ComposedNode>;
22
+ }
23
+
24
+ /** The `kind` of a validation failure. */
25
+ export type ValidationFailureKind =
26
+ | "missing-schema"
27
+ | "unknown-inherited-schema"
28
+ | "inheritance-cycle"
29
+ | "type-mismatch"
30
+ | "not-an-integer"
31
+ | "not-an-option"
32
+ | "missing-key"
33
+ | "too-few-elements"
34
+ | "too-many-elements"
35
+ | "missing-restrictions"
36
+ | "unknown-data-type"
37
+ | "other";
38
+
39
+ /** One attribute value that does not match its schema. */
40
+ export interface ValidationFailure {
41
+ /** Path of the node carrying the attribute. */
42
+ node: string;
43
+ /** Attribute id, which is also the schema id. */
44
+ attribute: string;
45
+ /** RFC 6901 JSON pointer into the value; `""` for the value itself. */
46
+ pointer: string;
47
+ kind: ValidationFailureKind;
48
+ message: string;
49
+ }
50
+
51
+ export interface ValidationReport {
52
+ valid: boolean;
53
+ failures: ValidationFailure[];
54
+ }
55
+
56
+ /**
57
+ * Compose layers, weakest first, into a tree of plain objects: the
58
+ * artificial root (path `""`) over every root node.
59
+ */
60
+ export function compose(layers: IfcxInput | IfcxInput[], options?: LayerOptions): ComposedNode;
61
+
62
+ /**
63
+ * Check the attributes of the layers, weakest first, against the schemas of
64
+ * every layer (and of resolved imports), after merging nodes that share a
65
+ * path.
66
+ */
67
+ export function validate(layers: IfcxInput | IfcxInput[], options?: LayerOptions): ValidationReport;
68
+
69
+ /** Options for `exportGlb`. */
70
+ export interface GlbOptions extends LayerOptions {
71
+ /** Rotate IFCX's Z-up axes to glTF's Y-up. Default `true`. */
72
+ yUp?: boolean;
73
+ /**
74
+ * Keep the model's coordinates by putting the scene origin on the root
75
+ * node. With `false` the model sits around the glTF origin, which is then
76
+ * only recorded in the root's `extras.ifcxOrigin`. Default `true`.
77
+ */
78
+ originOnRoot?: boolean;
79
+ }
80
+
81
+ /**
82
+ * Compose layers, weakest first, and write their render scene as a binary
83
+ * glTF 2.0 (`.glb`) file: transformed, instanced meshes, lines and points
84
+ * with their materials, one glTF node per instance named by its IFCX path.
85
+ */
86
+ export function exportGlb(layers: IfcxInput | IfcxInput[], options?: GlbOptions): Uint8Array;
87
+
88
+ /** The `code` of an `IfcxError`. */
89
+ export type IfcxErrorCode =
90
+ | "read"
91
+ | "write"
92
+ | "layer"
93
+ | "compose"
94
+ | "invalid-argument"
95
+ | "glb"
96
+ | "fetch";
97
+
98
+
99
+
100
+ /**
101
+ * One IFCX file. Writing it back is lossless: key order, unknown fields,
102
+ * `null` deletions and every number stay as read.
103
+ *
104
+ * A newtype because `wasm_bindgen` can only export a type defined in this
105
+ * crate; all state and behaviour are the core's.
106
+ */
107
+ export class IfcxFile {
108
+ private constructor();
109
+ free(): void;
110
+ [Symbol.dispose](): void;
111
+ /**
112
+ * Read an IFCX file from its JSON text or UTF-8 bytes.
113
+ */
114
+ static parse(input: IfcxInput): IfcxFile;
115
+ /**
116
+ * The file as a plain object, so `JSON.stringify(file)` works. Unlike
117
+ * `write`, a plain object may reorder integer-like keys and round
118
+ * integers beyond 2^53.
119
+ */
120
+ toJSON(): Record<string, unknown>;
121
+ /**
122
+ * Check every attribute against this file's own `schemas`. Imports are
123
+ * not resolved; use the `validate` function to include them.
124
+ */
125
+ validate(): ValidationReport;
126
+ /**
127
+ * The file as JSON text; `pretty` indents it by two spaces.
128
+ */
129
+ write(pretty?: boolean | null): string;
130
+ /**
131
+ * The `header` object.
132
+ */
133
+ readonly header: Record<string, unknown>;
134
+ /**
135
+ * Number of entries in `data`; several may share a path.
136
+ */
137
+ readonly nodeCount: number;
138
+ }
139
+ export * from "./fetch-imports.js";
@@ -0,0 +1,10 @@
1
+ /* @ts-self-types="./openbim_ifcx_wasm.d.ts" */
2
+ import * as wasm from "./openbim_ifcx_wasm_bg.wasm";
3
+ import { __wbg_set_wasm } from "./openbim_ifcx_wasm_bg.js";
4
+
5
+ __wbg_set_wasm(wasm);
6
+
7
+ export {
8
+ IfcxFile, compose, exportGlb, validate
9
+ } from "./openbim_ifcx_wasm_bg.js";
10
+ export { fetchImports } from "./fetch-imports.js";