@ifc-lite/sdk 9.5.0 → 9.6.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 +28 -16
- package/dist/cost-reference-scan.d.ts.map +1 -1
- package/dist/cost-reference-scan.js +10 -9
- package/dist/cost-reference-scan.js.map +1 -1
- package/dist/headless/align-backend.d.ts +5 -0
- package/dist/headless/align-backend.d.ts.map +1 -0
- package/dist/headless/align-backend.js +15 -0
- package/dist/headless/align-backend.js.map +1 -0
- package/dist/headless/authored-attribute-value.d.ts +3 -0
- package/dist/headless/authored-attribute-value.d.ts.map +1 -0
- package/dist/headless/authored-attribute-value.js +34 -0
- package/dist/headless/authored-attribute-value.js.map +1 -0
- package/dist/headless/backend-query-relationships.d.ts +5 -0
- package/dist/headless/backend-query-relationships.d.ts.map +1 -0
- package/dist/headless/backend-query-relationships.js +55 -0
- package/dist/headless/backend-query-relationships.js.map +1 -0
- package/dist/headless/backend-query.d.ts +20 -0
- package/dist/headless/backend-query.d.ts.map +1 -0
- package/dist/headless/backend-query.js +359 -0
- package/dist/headless/backend-query.js.map +1 -0
- package/dist/headless/backend.d.ts +80 -0
- package/dist/headless/backend.d.ts.map +1 -0
- package/dist/headless/backend.js +317 -0
- package/dist/headless/backend.js.map +1 -0
- package/dist/headless/capabilities.d.ts +22 -0
- package/dist/headless/capabilities.d.ts.map +1 -0
- package/dist/headless/capabilities.js +52 -0
- package/dist/headless/capabilities.js.map +1 -0
- package/dist/headless/effective-entity-membership.d.ts +8 -0
- package/dist/headless/effective-entity-membership.d.ts.map +1 -0
- package/dist/headless/effective-entity-membership.js +23 -0
- package/dist/headless/effective-entity-membership.js.map +1 -0
- package/dist/headless/headless-backend-cost-store.d.ts +14 -0
- package/dist/headless/headless-backend-cost-store.d.ts.map +1 -0
- package/dist/headless/headless-backend-cost-store.js +18 -0
- package/dist/headless/headless-backend-cost-store.js.map +1 -0
- package/dist/headless/headless-backend-modelling.d.ts +50 -0
- package/dist/headless/headless-backend-modelling.d.ts.map +1 -0
- package/dist/headless/headless-backend-modelling.js +128 -0
- package/dist/headless/headless-backend-modelling.js.map +1 -0
- package/dist/headless/headless-backend-room.d.ts +9 -0
- package/dist/headless/headless-backend-room.d.ts.map +1 -0
- package/dist/headless/headless-backend-room.js +22 -0
- package/dist/headless/headless-backend-room.js.map +1 -0
- package/dist/headless/headless-backend-store-stubs.d.ts +13 -0
- package/dist/headless/headless-backend-store-stubs.d.ts.map +1 -0
- package/dist/headless/headless-backend-store-stubs.js +9 -0
- package/dist/headless/headless-backend-store-stubs.js.map +1 -0
- package/dist/headless/headless-backend-store.d.ts +8 -0
- package/dist/headless/headless-backend-store.d.ts.map +1 -0
- package/dist/headless/headless-backend-store.js +42 -0
- package/dist/headless/headless-backend-store.js.map +1 -0
- package/dist/headless/headless-backend-structural-store.d.ts +21 -0
- package/dist/headless/headless-backend-structural-store.d.ts.map +1 -0
- package/dist/headless/headless-backend-structural-store.js +18 -0
- package/dist/headless/headless-backend-structural-store.js.map +1 -0
- package/dist/headless/headless-backend-structural.d.ts +4 -0
- package/dist/headless/headless-backend-structural.d.ts.map +1 -0
- package/dist/headless/headless-backend-structural.js +24 -0
- package/dist/headless/headless-backend-structural.js.map +1 -0
- package/dist/headless/mutation-base.d.ts +5 -0
- package/dist/headless/mutation-base.d.ts.map +1 -0
- package/dist/headless/mutation-base.js +31 -0
- package/dist/headless/mutation-base.js.map +1 -0
- package/dist/headless/options.d.ts +11 -0
- package/dist/headless/options.d.ts.map +1 -0
- package/dist/headless/options.js +13 -0
- package/dist/headless/options.js.map +1 -0
- package/dist/headless/overlay.d.ts +170 -0
- package/dist/headless/overlay.d.ts.map +1 -0
- package/dist/headless/overlay.js +285 -0
- package/dist/headless/overlay.js.map +1 -0
- package/dist/headless/schema-tables.d.ts +42 -0
- package/dist/headless/schema-tables.d.ts.map +1 -0
- package/dist/headless/schema-tables.js +77 -0
- package/dist/headless/schema-tables.js.map +1 -0
- package/dist/headless/session.d.ts +23 -0
- package/dist/headless/session.d.ts.map +1 -0
- package/dist/headless/session.js +125 -0
- package/dist/headless/session.js.map +1 -0
- package/dist/headless.d.ts +13 -0
- package/dist/headless.d.ts.map +1 -0
- package/dist/headless.js +13 -0
- package/dist/headless.js.map +1 -0
- package/dist/index.d.ts +2 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/namespaces/bcf-viewpoint.d.ts +19 -0
- package/dist/namespaces/bcf-viewpoint.d.ts.map +1 -1
- package/dist/namespaces/bcf-viewpoint.js.map +1 -1
- package/dist/namespaces/bcf.d.ts.map +1 -1
- package/dist/namespaces/bcf.js +17 -0
- package/dist/namespaces/bcf.js.map +1 -1
- package/dist/namespaces/viewer.d.ts +20 -1
- package/dist/namespaces/viewer.d.ts.map +1 -1
- package/dist/namespaces/viewer.js +40 -0
- package/dist/namespaces/viewer.js.map +1 -1
- package/dist/section-plane.d.ts +12 -0
- package/dist/section-plane.d.ts.map +1 -0
- package/dist/section-plane.js +5 -0
- package/dist/section-plane.js.map +1 -0
- package/dist/types.d.ts +6 -6
- package/dist/types.d.ts.map +1 -1
- package/package.json +20 -15
|
@@ -0,0 +1,170 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The pending-mutation overlay, in the shape the server's read surface needs to
|
|
3
|
+
* read it. One reader for every tool: `model_diff` (#2000) and, since #2004, the
|
|
4
|
+
* query backend, `model_info` and `count_entities`.
|
|
5
|
+
*
|
|
6
|
+
* A `model_id` on this server does not name a file — it names a session model,
|
|
7
|
+
* and `entity_set_property` / `entity_set_attribute` / `entity_create` /
|
|
8
|
+
* `entity_delete` queue their edits in a `MutablePropertyView` overlay that the
|
|
9
|
+
* parsed `IfcDataStore` never sees (the store's buffer and index are immutable;
|
|
10
|
+
* the overlay materialises at `export_ifc` / `model_save` time).
|
|
11
|
+
*
|
|
12
|
+
* Read straight from the store, a tool therefore answers about the file as it
|
|
13
|
+
* was parsed. That is the wrong answer for the caller most likely to ask: an
|
|
14
|
+
* agent edits, reads back to confirm, and is told its edit did not happen — and
|
|
15
|
+
* the natural recovery from that is to edit again.
|
|
16
|
+
*
|
|
17
|
+
* **Reads fold the overlay in silently; the payload then says how many edits are
|
|
18
|
+
* queued.** Folding is what makes "what is there now" answerable at all, and
|
|
19
|
+
* `query_entities`' filters make it non-negotiable: a query for the value an
|
|
20
|
+
* agent just wrote has to find it, and a tool that reported the divergence
|
|
21
|
+
* instead of folding would still return the pre-edit row set. Reporting is what
|
|
22
|
+
* keeps the answer honest about not being on disk yet — `pendingMutations` is
|
|
23
|
+
* absent, not zero, on a session that has never been edited, so nothing changes
|
|
24
|
+
* for a read-only caller.
|
|
25
|
+
*/
|
|
26
|
+
import { type PropertySet, type QuantitySet } from '@ifc-lite/data';
|
|
27
|
+
import { type MutablePropertyView } from '@ifc-lite/mutations';
|
|
28
|
+
import { effectiveRelationshipEdges, type IfcDataStore } from '@ifc-lite/parser';
|
|
29
|
+
/** An entity that exists only in the overlay (`entity_create`). */
|
|
30
|
+
export interface CreatedEntity {
|
|
31
|
+
expressId: number;
|
|
32
|
+
/** IFC class as authored, e.g. `IfcWall`. */
|
|
33
|
+
ifcType: string;
|
|
34
|
+
/** Empty for a created entity that carries none — an `IfcCartesianPoint`, or
|
|
35
|
+
* a root the caller left unidentified. Never empty in {@link
|
|
36
|
+
* PendingOverlay.created}, which is the cross-model-identity list. */
|
|
37
|
+
globalId: string;
|
|
38
|
+
name?: string;
|
|
39
|
+
description?: string;
|
|
40
|
+
/** The positional STEP attributes exactly as authored. */
|
|
41
|
+
attributes: readonly unknown[];
|
|
42
|
+
}
|
|
43
|
+
/**
|
|
44
|
+
* Attribute writes queued for an entity, keyed by IFC attribute name.
|
|
45
|
+
*
|
|
46
|
+
* **Not a fixed set of fields, deliberately.** This used to project the three
|
|
47
|
+
* names the fingerprint hashes (`Name` / `Description` / `ObjectType`) while
|
|
48
|
+
* `entity_set_attribute` accepted four, so a written `Tag` was queued and never
|
|
49
|
+
* read back (#2014). A projection that enumerates has to be kept in step with
|
|
50
|
+
* the write tool's enum by hand, and was not. Passing every queued write through
|
|
51
|
+
* means the read surface cannot fall behind the write surface again: whatever
|
|
52
|
+
* the enum grows, the readback carries.
|
|
53
|
+
*/
|
|
54
|
+
type AttributeOverrides = ReadonlyMap<string, string>;
|
|
55
|
+
/** The overlay's read surface, as every folding tool consumes it. */
|
|
56
|
+
export interface PendingOverlay {
|
|
57
|
+
/** Express ids tombstoned by `entity_delete`. Empty is the common case.
|
|
58
|
+
* Since #2012 this includes an entity the session created and then deleted:
|
|
59
|
+
* `deleteEntity` tombstones it as well as forgetting it, so that "was this
|
|
60
|
+
* deleted" has a true answer to find. A count therefore has to intersect
|
|
61
|
+
* this with the store rather than subtracting its size — see
|
|
62
|
+
* {@link foldedEntityCount}. */
|
|
63
|
+
readonly deleted: ReadonlySet<number>;
|
|
64
|
+
/** Entities queued by `entity_create` that carry a GlobalId, in creation
|
|
65
|
+
* order. The identity list: `model_diff` compares on it, and
|
|
66
|
+
* `get_entity(global_id)` resolves against it. A created entity without one
|
|
67
|
+
* is reachable through {@link createdEntity} by expressId. */
|
|
68
|
+
readonly created: readonly CreatedEntity[];
|
|
69
|
+
/** Every queued entity, GlobalId-bearing or not, in creation order. What a
|
|
70
|
+
* *count* is about: a created `IfcCartesianPoint` has no identity to compare
|
|
71
|
+
* but is still one more entity in the model. `entity_delete` on a queued
|
|
72
|
+
* entity drops it from this list, so a created-then-deleted entity appears
|
|
73
|
+
* here not at all and in {@link deleted} once. */
|
|
74
|
+
readonly createdAll: readonly CreatedEntity[];
|
|
75
|
+
/** Queued mutations on this model — the same number `mutation_diff` reports.
|
|
76
|
+
* Echoed back so a caller can tell an answer that includes uncommitted edits
|
|
77
|
+
* from one taken over the file as parsed. */
|
|
78
|
+
readonly pendingMutations: number;
|
|
79
|
+
/** Any queued entity by expressId, GlobalId-bearing or not. Null when the id
|
|
80
|
+
* is not one this session created. */
|
|
81
|
+
createdEntity(expressId: number): CreatedEntity | null;
|
|
82
|
+
/** The class a queued `setEntityType` gives the entity (`IfcWall` spelling),
|
|
83
|
+
* or null when it keeps its authored class. Every read that names an
|
|
84
|
+
* entity's type must go through this, or it disagrees with what export
|
|
85
|
+
* writes (#5009 review). */
|
|
86
|
+
effectiveType(expressId: number): string | null;
|
|
87
|
+
/** Sparse queued class changes for effective whole-model enumeration. */
|
|
88
|
+
typeMutations?(): ReadonlyMap<number, {
|
|
89
|
+
readonly newType: string;
|
|
90
|
+
}>;
|
|
91
|
+
/** Sparse retype intents for aggregate class counts; absent in older read shims. */
|
|
92
|
+
getTypeMutations?(): ReadonlyMap<number, {
|
|
93
|
+
readonly newType: string;
|
|
94
|
+
}>;
|
|
95
|
+
attributes(expressId: number): AttributeOverrides;
|
|
96
|
+
positionalAttributes(expressId: number): ReadonlyMap<number, unknown>;
|
|
97
|
+
/** Every entity with a queued attribute write, keyed by expressId. For loops
|
|
98
|
+
* over a whole model: `attributes(id)` builds a map per call, which is fine
|
|
99
|
+
* for one entity and not for a million. */
|
|
100
|
+
attributesByEntity(): ReadonlyMap<number, AttributeOverrides>;
|
|
101
|
+
/** Base property sets with the overlay's edits applied. */
|
|
102
|
+
propertySets(expressId: number): PropertySet[];
|
|
103
|
+
/** Base quantity sets with the overlay's edits applied. */
|
|
104
|
+
quantitySets(expressId: number): QuantitySet[];
|
|
105
|
+
relationshipEdges(expressId: number, ifcRelType?: string): ReturnType<typeof effectiveRelationshipEdges>;
|
|
106
|
+
readonly supersededRelationshipIds: ReadonlySet<number>;
|
|
107
|
+
}
|
|
108
|
+
/**
|
|
109
|
+
* The model's pending overlay, or `null` when it has none.
|
|
110
|
+
*
|
|
111
|
+
* Null is the answer for every model that was only ever read: the backend
|
|
112
|
+
* creates the view lazily on the first mutation, so an unedited session pays
|
|
113
|
+
* nothing and every caller keeps its original store-only path.
|
|
114
|
+
*/
|
|
115
|
+
export declare function pendingOverlay(model: {
|
|
116
|
+
backend: {
|
|
117
|
+
pendingOverlay(): PendingOverlay | null;
|
|
118
|
+
};
|
|
119
|
+
}): PendingOverlay | null;
|
|
120
|
+
/**
|
|
121
|
+
* The same reader, built from the view directly.
|
|
122
|
+
*
|
|
123
|
+
* The backend owns its `MutablePropertyView` and folds the overlay into its own
|
|
124
|
+
* query adapter, so it cannot go through `pendingOverlay` — that would need a
|
|
125
|
+
* `LoadedModel`, which is the registry entry wrapped *around* the backend.
|
|
126
|
+
*/
|
|
127
|
+
export declare function overlayFromView(view: MutablePropertyView | null, store: IfcDataStore): PendingOverlay | null;
|
|
128
|
+
/**
|
|
129
|
+
* Entity count per STEP type key, with queued creates, deletes and retypes
|
|
130
|
+
* applied by the shared aggregate accessor. A created entity is counted under
|
|
131
|
+
* its effective uppercase class, matching the parsed type buckets.
|
|
132
|
+
*/
|
|
133
|
+
export declare function foldedTypeCounts(store: IfcDataStore, overlay: PendingOverlay | null): Map<string, number>;
|
|
134
|
+
/**
|
|
135
|
+
* The model's entity count with the session's creates and deletes applied.
|
|
136
|
+
*
|
|
137
|
+
* Adjusts the parser's own total rather than re-summing `byType`, so an unedited
|
|
138
|
+
* model's number is byte-for-byte what it always was.
|
|
139
|
+
*
|
|
140
|
+
* Only tombstones that name a STORE entity are subtracted. A created-then-
|
|
141
|
+
* deleted entity is already absent from `createdAll`, so counting its tombstone
|
|
142
|
+
* too would subtract it a second time and report one entity fewer than the file
|
|
143
|
+
* has (#2012). This also makes a tombstone on an id the store never had — which
|
|
144
|
+
* `entity_delete` rejects, but nothing here relies on that — unable to drive the
|
|
145
|
+
* count below the truth.
|
|
146
|
+
*/
|
|
147
|
+
export declare function foldedEntityCount(store: IfcDataStore, overlay: PendingOverlay | null): number;
|
|
148
|
+
/**
|
|
149
|
+
* `{ pendingMutations: n }` when any of the sessions has queued edits, `{}`
|
|
150
|
+
* otherwise — spread into a tool payload so an unedited session's shape is
|
|
151
|
+
* untouched.
|
|
152
|
+
*
|
|
153
|
+
* Always a scalar, at every level of every payload. `model_diff` used to publish
|
|
154
|
+
* a `{ base, head }` object under the same name inside `contentDiff`, so one
|
|
155
|
+
* field name meant two shapes depending on where a caller found it; the split is
|
|
156
|
+
* now `pendingMutationsBySide`.
|
|
157
|
+
*/
|
|
158
|
+
export declare function pendingMutationsField(...overlays: Array<PendingOverlay | null>): Record<string, number>;
|
|
159
|
+
/**
|
|
160
|
+
* Unwrap one authored STEP attribute to plain text.
|
|
161
|
+
*
|
|
162
|
+
* `StoreEditor.addEntity` documents both `"'literal'"` and a bare string as
|
|
163
|
+
* ways to write a string attribute, so the quotes have to come off before the
|
|
164
|
+
* value is hashed — otherwise a created entity could never content-match the
|
|
165
|
+
* same entity read back out of a file. `$` (unset), `*` (derived) and `#42`
|
|
166
|
+
* (a reference) are not text.
|
|
167
|
+
*/
|
|
168
|
+
export declare function stepText(value: unknown): string | undefined;
|
|
169
|
+
export {};
|
|
170
|
+
//# sourceMappingURL=overlay.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"overlay.d.ts","sourceRoot":"","sources":["../../src/headless/overlay.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,285 @@
|
|
|
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
|
+
* The pending-mutation overlay, in the shape the server's read surface needs to
|
|
6
|
+
* read it. One reader for every tool: `model_diff` (#2000) and, since #2004, the
|
|
7
|
+
* query backend, `model_info` and `count_entities`.
|
|
8
|
+
*
|
|
9
|
+
* A `model_id` on this server does not name a file — it names a session model,
|
|
10
|
+
* and `entity_set_property` / `entity_set_attribute` / `entity_create` /
|
|
11
|
+
* `entity_delete` queue their edits in a `MutablePropertyView` overlay that the
|
|
12
|
+
* parsed `IfcDataStore` never sees (the store's buffer and index are immutable;
|
|
13
|
+
* the overlay materialises at `export_ifc` / `model_save` time).
|
|
14
|
+
*
|
|
15
|
+
* Read straight from the store, a tool therefore answers about the file as it
|
|
16
|
+
* was parsed. That is the wrong answer for the caller most likely to ask: an
|
|
17
|
+
* agent edits, reads back to confirm, and is told its edit did not happen — and
|
|
18
|
+
* the natural recovery from that is to edit again.
|
|
19
|
+
*
|
|
20
|
+
* **Reads fold the overlay in silently; the payload then says how many edits are
|
|
21
|
+
* queued.** Folding is what makes "what is there now" answerable at all, and
|
|
22
|
+
* `query_entities`' filters make it non-negotiable: a query for the value an
|
|
23
|
+
* agent just wrote has to find it, and a tool that reported the divergence
|
|
24
|
+
* instead of folding would still return the pre-edit row set. Reporting is what
|
|
25
|
+
* keeps the answer honest about not being on disk yet — `pendingMutations` is
|
|
26
|
+
* absent, not zero, on a session that has never been edited, so nothing changes
|
|
27
|
+
* for a read-only caller.
|
|
28
|
+
*/
|
|
29
|
+
import { countEffectiveEntityTypes } from '@ifc-lite/data';
|
|
30
|
+
import { storeHasSourceEntity } from '@ifc-lite/mutations';
|
|
31
|
+
import { getInheritanceChainAcrossSchemas, effectiveRelationshipEdges, resolveEffectiveEntityRecord, resolveEffectiveRelationshipOverlay, normalizeIfcTypeName, } from '@ifc-lite/parser';
|
|
32
|
+
import { effectiveSourceIds, pendingEntityMembership } from './effective-entity-membership.js';
|
|
33
|
+
const NO_POSITIONAL_ATTRIBUTES = new Map();
|
|
34
|
+
/**
|
|
35
|
+
* The model's pending overlay, or `null` when it has none.
|
|
36
|
+
*
|
|
37
|
+
* Null is the answer for every model that was only ever read: the backend
|
|
38
|
+
* creates the view lazily on the first mutation, so an unedited session pays
|
|
39
|
+
* nothing and every caller keeps its original store-only path.
|
|
40
|
+
*/
|
|
41
|
+
export function pendingOverlay(model) {
|
|
42
|
+
return model.backend.pendingOverlay();
|
|
43
|
+
}
|
|
44
|
+
/**
|
|
45
|
+
* The same reader, built from the view directly.
|
|
46
|
+
*
|
|
47
|
+
* The backend owns its `MutablePropertyView` and folds the overlay into its own
|
|
48
|
+
* query adapter, so it cannot go through `pendingOverlay` — that would need a
|
|
49
|
+
* `LoadedModel`, which is the registry entry wrapped *around* the backend.
|
|
50
|
+
*/
|
|
51
|
+
export function overlayFromView(view, store) {
|
|
52
|
+
if (!view || !view.hasPendingChanges())
|
|
53
|
+
return null;
|
|
54
|
+
return new ViewOverlay(view, store);
|
|
55
|
+
}
|
|
56
|
+
/**
|
|
57
|
+
* **Everything derived is lazy, and the point lookup does not derive at all.**
|
|
58
|
+
*
|
|
59
|
+
* A fresh `ViewOverlay` is built on every overlay read — that is what keeps it
|
|
60
|
+
* honest, because there is no revision counter on `MutablePropertyView` to cache
|
|
61
|
+
* against and `getMutations().length` is not one (`mutation_undo` splices the
|
|
62
|
+
* history, so a later write can land back on the same length). A stale overlay
|
|
63
|
+
* after a mutation is a correctness bug; rebuilding is not.
|
|
64
|
+
*
|
|
65
|
+
* So the rebuild is made cheap instead. The constructor does no work, each
|
|
66
|
+
* derived collection is computed once per instance on first use, and
|
|
67
|
+
* `createdEntity` — the one called per entity by `entityData` — goes straight to
|
|
68
|
+
* the view's own id map rather than materialising the whole created list.
|
|
69
|
+
* Measured on a 20k-wall model with 2,000 queued creates, a 1,000-id
|
|
70
|
+
* `get_entities_bulk` went from ~1,960ms to ~7ms (median of five), and
|
|
71
|
+
* `getMutations()` (which copies the whole history) is no longer touched unless
|
|
72
|
+
* a payload reports the count.
|
|
73
|
+
*/
|
|
74
|
+
class ViewOverlay {
|
|
75
|
+
view;
|
|
76
|
+
store;
|
|
77
|
+
tombstones = null;
|
|
78
|
+
all = null;
|
|
79
|
+
identified = null;
|
|
80
|
+
count = null;
|
|
81
|
+
effectiveRelations = null;
|
|
82
|
+
constructor(view, store) {
|
|
83
|
+
this.view = view;
|
|
84
|
+
this.store = store;
|
|
85
|
+
}
|
|
86
|
+
get deleted() {
|
|
87
|
+
if (!this.tombstones)
|
|
88
|
+
this.tombstones = this.view.getTombstones();
|
|
89
|
+
return this.tombstones;
|
|
90
|
+
}
|
|
91
|
+
/** The created entity as export writes it: effective class, name-relaid attributes, edits applied. */
|
|
92
|
+
effectiveCreatedEntity(entity) {
|
|
93
|
+
const record = resolveEffectiveEntityRecord(entity, {
|
|
94
|
+
retype: this.view.getEntityTypeMutation(entity.expressId)?.newType,
|
|
95
|
+
named: this.view.getAttributeMutationsForEntity(entity.expressId).map(({ name, value }) => [name, value]),
|
|
96
|
+
positional: this.view.getPositionalMutationsForEntity(entity.expressId) ?? [],
|
|
97
|
+
}, this.store.schemaVersion);
|
|
98
|
+
return toCreatedEntity({ ...entity, type: record.type, attributes: record.attributes });
|
|
99
|
+
}
|
|
100
|
+
get createdAll() {
|
|
101
|
+
if (!this.all)
|
|
102
|
+
this.all = this.view.getNewEntities().map(entity => this.effectiveCreatedEntity(entity));
|
|
103
|
+
return this.all;
|
|
104
|
+
}
|
|
105
|
+
get created() {
|
|
106
|
+
if (!this.identified)
|
|
107
|
+
this.identified = this.createdAll.filter((entity) => entity.globalId !== '');
|
|
108
|
+
return this.identified;
|
|
109
|
+
}
|
|
110
|
+
get pendingMutations() {
|
|
111
|
+
// `getMutations()` copies the whole history, so it is paid once per overlay
|
|
112
|
+
// and only when something actually reports the number.
|
|
113
|
+
if (this.count === null)
|
|
114
|
+
this.count = this.view.getMutations().length;
|
|
115
|
+
return this.count;
|
|
116
|
+
}
|
|
117
|
+
createdEntity(expressId) {
|
|
118
|
+
// The view already indexes new entities by id; going through `createdAll`
|
|
119
|
+
// here made every `entityData` call O(number of queued creates).
|
|
120
|
+
const raw = this.view.getNewEntity(expressId);
|
|
121
|
+
return raw ? this.effectiveCreatedEntity(raw) : null;
|
|
122
|
+
}
|
|
123
|
+
effectiveType(expressId) {
|
|
124
|
+
const retype = this.view.getEntityTypeMutation(expressId)?.newType;
|
|
125
|
+
return retype ? normalizeIfcTypeName(retype) : null;
|
|
126
|
+
}
|
|
127
|
+
typeMutations() {
|
|
128
|
+
return this.view.getTypeMutations();
|
|
129
|
+
}
|
|
130
|
+
getTypeMutations() {
|
|
131
|
+
return this.view.getTypeMutations();
|
|
132
|
+
}
|
|
133
|
+
attributes(expressId) {
|
|
134
|
+
// Every queued write, not a chosen few. Last write wins, which is what
|
|
135
|
+
// `getAttributeMutationsForEntity` already orders for us.
|
|
136
|
+
const out = new Map();
|
|
137
|
+
for (const { name, value } of this.view.getAttributeMutationsForEntity(expressId)) {
|
|
138
|
+
out.set(name, value);
|
|
139
|
+
}
|
|
140
|
+
return out;
|
|
141
|
+
}
|
|
142
|
+
positionalAttributes(expressId) {
|
|
143
|
+
return this.view.getPositionalMutationsForEntity(expressId) ?? NO_POSITIONAL_ATTRIBUTES;
|
|
144
|
+
}
|
|
145
|
+
attributesByEntity() {
|
|
146
|
+
return this.view.getAttributeMutationsByEntity();
|
|
147
|
+
}
|
|
148
|
+
get relationshipOverlay() {
|
|
149
|
+
if (!this.effectiveRelations)
|
|
150
|
+
this.effectiveRelations = resolveEffectiveRelationshipOverlay(this.store, {
|
|
151
|
+
createdEntities: () => this.view.getNewEntities(),
|
|
152
|
+
mutatedEntityIds: () => this.view.getEffectiveChanges().map(change => change.entityId),
|
|
153
|
+
namedAttributes: id => this.view.getAttributeMutationsForEntity(id).map(({ name, value }) => [name, value]),
|
|
154
|
+
positionalAttributes: id => this.view.getPositionalMutationsForEntity(id) ?? [],
|
|
155
|
+
entityType: id => this.view.getEntityTypeMutation(id)?.newType,
|
|
156
|
+
isDeleted: id => this.view.isDeleted(id),
|
|
157
|
+
});
|
|
158
|
+
return this.effectiveRelations;
|
|
159
|
+
}
|
|
160
|
+
relationshipEdges(expressId, ifcRelType) {
|
|
161
|
+
return effectiveRelationshipEdges(this.relationshipOverlay, id => this.view.isDeleted(id), expressId, ifcRelType);
|
|
162
|
+
}
|
|
163
|
+
get supersededRelationshipIds() {
|
|
164
|
+
return this.relationshipOverlay.supersededSourceIds;
|
|
165
|
+
}
|
|
166
|
+
propertySets(expressId) {
|
|
167
|
+
return this.view.getForEntity(expressId);
|
|
168
|
+
}
|
|
169
|
+
quantitySets(expressId) {
|
|
170
|
+
return this.view.getQuantitiesForEntity(expressId);
|
|
171
|
+
}
|
|
172
|
+
}
|
|
173
|
+
/**
|
|
174
|
+
* Entity count per STEP type key, with queued creates, deletes and retypes
|
|
175
|
+
* applied by the shared aggregate accessor. A created entity is counted under
|
|
176
|
+
* its effective uppercase class, matching the parsed type buckets.
|
|
177
|
+
*/
|
|
178
|
+
export function foldedTypeCounts(store, overlay) {
|
|
179
|
+
return countEffectiveEntityTypes(store, pendingEntityMembership(overlay), effectiveSourceIds(store));
|
|
180
|
+
}
|
|
181
|
+
/**
|
|
182
|
+
* The model's entity count with the session's creates and deletes applied.
|
|
183
|
+
*
|
|
184
|
+
* Adjusts the parser's own total rather than re-summing `byType`, so an unedited
|
|
185
|
+
* model's number is byte-for-byte what it always was.
|
|
186
|
+
*
|
|
187
|
+
* Only tombstones that name a STORE entity are subtracted. A created-then-
|
|
188
|
+
* deleted entity is already absent from `createdAll`, so counting its tombstone
|
|
189
|
+
* too would subtract it a second time and report one entity fewer than the file
|
|
190
|
+
* has (#2012). This also makes a tombstone on an id the store never had — which
|
|
191
|
+
* `entity_delete` rejects, but nothing here relies on that — unable to drive the
|
|
192
|
+
* count below the truth.
|
|
193
|
+
*/
|
|
194
|
+
export function foldedEntityCount(store, overlay) {
|
|
195
|
+
if (!overlay)
|
|
196
|
+
return store.entityCount;
|
|
197
|
+
let deletedFromStore = 0;
|
|
198
|
+
for (const id of overlay.deleted) {
|
|
199
|
+
if (storeHasSourceEntity(store, id))
|
|
200
|
+
deletedFromStore++;
|
|
201
|
+
}
|
|
202
|
+
return store.entityCount + overlay.createdAll.length - deletedFromStore;
|
|
203
|
+
}
|
|
204
|
+
/**
|
|
205
|
+
* `{ pendingMutations: n }` when any of the sessions has queued edits, `{}`
|
|
206
|
+
* otherwise — spread into a tool payload so an unedited session's shape is
|
|
207
|
+
* untouched.
|
|
208
|
+
*
|
|
209
|
+
* Always a scalar, at every level of every payload. `model_diff` used to publish
|
|
210
|
+
* a `{ base, head }` object under the same name inside `contentDiff`, so one
|
|
211
|
+
* field name meant two shapes depending on where a caller found it; the split is
|
|
212
|
+
* now `pendingMutationsBySide`.
|
|
213
|
+
*/
|
|
214
|
+
export function pendingMutationsField(...overlays) {
|
|
215
|
+
if (overlays.every((overlay) => overlay === null))
|
|
216
|
+
return {};
|
|
217
|
+
return { pendingMutations: overlays.reduce((total, overlay) => total + (overlay?.pendingMutations ?? 0), 0) };
|
|
218
|
+
}
|
|
219
|
+
/**
|
|
220
|
+
* Read the IfcRoot header of an overlay-created entity.
|
|
221
|
+
*
|
|
222
|
+
* `NewEntity.attributes` is the positional STEP list the caller authored, so
|
|
223
|
+
* slots 0/2/3 are `GlobalId`/`Name`/`Description` **for an `IfcRoot` subtype**
|
|
224
|
+
* in every schema version — that part of the layout is fixed. Nothing further
|
|
225
|
+
* along is: slot 4 is `ObjectType` on an `IfcObject` but `ApplicableOccurrence`
|
|
226
|
+
* on an `IfcTypeObject`, so `objectType` and `PredefinedType` are read from the
|
|
227
|
+
* store for store entities and simply absent for created ones.
|
|
228
|
+
*
|
|
229
|
+
* **The `IfcRoot` half of that sentence is a precondition, not a description**,
|
|
230
|
+
* and `entity_create` accepts any IFC class. Slot 0 of an
|
|
231
|
+
* `IfcPropertySingleValue` is its `Name`, so reading it positionally turned
|
|
232
|
+
* `entity_create('IfcPropertySingleValue', ["'Width'", …])` into an entity whose
|
|
233
|
+
* GlobalId was `Width` — which then joined the cross-model identity list, so
|
|
234
|
+
* `get_entity(global_id: 'Width')` resolved to a property value, `model_diff`
|
|
235
|
+
* reported `Width` as an added entity, and `get_entities_bulk` could call it an
|
|
236
|
+
* ambiguous GlobalId. The columnar parser hits the same hazard when it keys an
|
|
237
|
+
* `IfcMaterial` on the Name in slot 0.
|
|
238
|
+
*
|
|
239
|
+
* So the header is read only when the class actually derives from `IfcRoot`,
|
|
240
|
+
* cross-schema (#2003) so an IFC2X3- or IFC4X3-only root is not judged by the
|
|
241
|
+
* IFC4 pin. A class no bundled schema declares has no chain to prove it is a
|
|
242
|
+
* root, so it keeps an empty header — the same deliberate vendor gap
|
|
243
|
+
* `diff-scope.ts` documents, and the safe direction: an unidentified entity is
|
|
244
|
+
* merely unreachable by GlobalId, whereas a wrongly identified one corrupts the
|
|
245
|
+
* identity space every other tool shares.
|
|
246
|
+
*
|
|
247
|
+
* `globalId` also comes back empty when slot 0 holds none — a created
|
|
248
|
+
* `IfcCartesianPoint` has no cross-model identity, and neither has a created
|
|
249
|
+
* root the caller left unidentified. Those are excluded from `created` (nothing
|
|
250
|
+
* can compare or look them up by key) but stay reachable by expressId, because
|
|
251
|
+
* `get_entity(express_id)` on an id this session just handed out must not answer
|
|
252
|
+
* "not found", and they are still counted.
|
|
253
|
+
*/
|
|
254
|
+
function toCreatedEntity(entity) {
|
|
255
|
+
const rooted = getInheritanceChainAcrossSchemas(entity.type).includes('IfcRoot');
|
|
256
|
+
return {
|
|
257
|
+
expressId: entity.expressId,
|
|
258
|
+
ifcType: entity.type,
|
|
259
|
+
globalId: (rooted ? stepText(entity.attributes[0]) : undefined) ?? '',
|
|
260
|
+
name: rooted ? stepText(entity.attributes[2]) : undefined,
|
|
261
|
+
description: rooted ? stepText(entity.attributes[3]) : undefined,
|
|
262
|
+
attributes: entity.attributes,
|
|
263
|
+
};
|
|
264
|
+
}
|
|
265
|
+
/**
|
|
266
|
+
* Unwrap one authored STEP attribute to plain text.
|
|
267
|
+
*
|
|
268
|
+
* `StoreEditor.addEntity` documents both `"'literal'"` and a bare string as
|
|
269
|
+
* ways to write a string attribute, so the quotes have to come off before the
|
|
270
|
+
* value is hashed — otherwise a created entity could never content-match the
|
|
271
|
+
* same entity read back out of a file. `$` (unset), `*` (derived) and `#42`
|
|
272
|
+
* (a reference) are not text.
|
|
273
|
+
*/
|
|
274
|
+
export function stepText(value) {
|
|
275
|
+
if (typeof value !== 'string')
|
|
276
|
+
return undefined;
|
|
277
|
+
const trimmed = value.trim();
|
|
278
|
+
if (trimmed === '' || trimmed === '$' || trimmed === '*' || trimmed.startsWith('#'))
|
|
279
|
+
return undefined;
|
|
280
|
+
if (trimmed.length >= 2 && trimmed.startsWith("'") && trimmed.endsWith("'")) {
|
|
281
|
+
return trimmed.slice(1, -1).replace(/''/g, "'") || undefined;
|
|
282
|
+
}
|
|
283
|
+
return trimmed;
|
|
284
|
+
}
|
|
285
|
+
//# sourceMappingURL=overlay.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"overlay.js","sourceRoot":"","sources":["../../src/headless/overlay.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The bundled IFC schema tables, kept per schema rather than merged.
|
|
3
|
+
*
|
|
4
|
+
* The parser's `getAttributeNamesAcrossSchemas` answers from the IFC4 codegen
|
|
5
|
+
* pin whenever the pin knows the class, and from a merged union otherwise —
|
|
6
|
+
* which is the right default for a reader that does not know which schema it is
|
|
7
|
+
* looking at. Two callers here do know: `schema_describe` is told the class, and
|
|
8
|
+
* the query backend is told the model. For them a merged answer can be the wrong
|
|
9
|
+
* schema's answer:
|
|
10
|
+
*
|
|
11
|
+
* - `IfcControl` has five attributes in IFC2X3 and six in IFC4 (which added
|
|
12
|
+
* `Identification`), so subtracting the merged count from an IFC2X3 leaf like
|
|
13
|
+
* `IfcScheduleTimeControl` ate one of the leaf's own attributes.
|
|
14
|
+
* - `IfcRelCoversSpaces` names slot 4 `RelatedSpace` in IFC2X3 and
|
|
15
|
+
* `RelatingSpace` in IFC4, so reading a created one back in an IFC2X3 model
|
|
16
|
+
* reported an attribute name the file will not serialise.
|
|
17
|
+
*
|
|
18
|
+
* Later schemas still win when no schema is named, matching the parser's union.
|
|
19
|
+
*/
|
|
20
|
+
import { type IfcEntityInfo } from '@ifc-lite/data';
|
|
21
|
+
/** One schema's row for a class, or undefined. `schema` is matched case- and
|
|
22
|
+
* punctuation-insensitively, so `Ifc4x3` and `IFC4X3` both resolve. */
|
|
23
|
+
export declare function entityInfoInSchema(type: string, schema: string | undefined): IfcEntityInfo | undefined;
|
|
24
|
+
/**
|
|
25
|
+
* A class's row and the bundled schema that answered for it, later schemas
|
|
26
|
+
* winning — the same precedence the parser's union map applies.
|
|
27
|
+
*/
|
|
28
|
+
export declare function entityInfoAcrossSchemas(type: string): {
|
|
29
|
+
info: IfcEntityInfo;
|
|
30
|
+
schema: string;
|
|
31
|
+
} | undefined;
|
|
32
|
+
/**
|
|
33
|
+
* Attribute names in STEP positional order, preferring the named schema's
|
|
34
|
+
* spelling.
|
|
35
|
+
*
|
|
36
|
+
* Falls back to the parser's cross-schema answer when the model's schema does
|
|
37
|
+
* not declare the class — a vendor extension, or a class the file's declared
|
|
38
|
+
* schema does not have. That fallback is the pre-existing behaviour and is
|
|
39
|
+
* strictly better than nothing.
|
|
40
|
+
*/
|
|
41
|
+
export declare function attributeNamesForSchema(type: string, schema: string | undefined): readonly string[];
|
|
42
|
+
//# sourceMappingURL=schema-tables.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"schema-tables.d.ts","sourceRoot":"","sources":["../../src/headless/schema-tables.ts"],"names":[],"mappings":"AAIA;;;;;;;;;;;;;;;;;;GAkBG;AAEH,OAAO,EAA2D,KAAK,aAAa,EAAE,MAAM,gBAAgB,CAAC;AAuB7G;wEACwE;AACxE,wBAAgB,kBAAkB,CAAC,IAAI,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,GAAG,SAAS,GAAG,aAAa,GAAG,SAAS,CAGtG;AAED;;;GAGG;AACH,wBAAgB,uBAAuB,CAAC,IAAI,EAAE,MAAM,GAAG;IAAE,IAAI,EAAE,aAAa,CAAC;IAAC,MAAM,EAAE,MAAM,CAAA;CAAE,GAAG,SAAS,CAQzG;AAED;;;;;;;;GAQG;AACH,wBAAgB,uBAAuB,CAAC,IAAI,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,GAAG,SAAS,GAAG,SAAS,MAAM,EAAE,CAGnG"}
|
|
@@ -0,0 +1,77 @@
|
|
|
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
|
+
* The bundled IFC schema tables, kept per schema rather than merged.
|
|
6
|
+
*
|
|
7
|
+
* The parser's `getAttributeNamesAcrossSchemas` answers from the IFC4 codegen
|
|
8
|
+
* pin whenever the pin knows the class, and from a merged union otherwise —
|
|
9
|
+
* which is the right default for a reader that does not know which schema it is
|
|
10
|
+
* looking at. Two callers here do know: `schema_describe` is told the class, and
|
|
11
|
+
* the query backend is told the model. For them a merged answer can be the wrong
|
|
12
|
+
* schema's answer:
|
|
13
|
+
*
|
|
14
|
+
* - `IfcControl` has five attributes in IFC2X3 and six in IFC4 (which added
|
|
15
|
+
* `Identification`), so subtracting the merged count from an IFC2X3 leaf like
|
|
16
|
+
* `IfcScheduleTimeControl` ate one of the leaf's own attributes.
|
|
17
|
+
* - `IfcRelCoversSpaces` names slot 4 `RelatedSpace` in IFC2X3 and
|
|
18
|
+
* `RelatingSpace` in IFC4, so reading a created one back in an IFC2X3 model
|
|
19
|
+
* reported an attribute name the file will not serialise.
|
|
20
|
+
*
|
|
21
|
+
* Later schemas still win when no schema is named, matching the parser's union.
|
|
22
|
+
*/
|
|
23
|
+
import { ENTITIES_IFC2X3, ENTITIES_IFC4_EXPRESS, ENTITIES_IFC4X3 } from '@ifc-lite/data';
|
|
24
|
+
import { getAttributeNamesAcrossSchemas } from '@ifc-lite/parser';
|
|
25
|
+
/** Bundled schemas, oldest first. */
|
|
26
|
+
const SCHEMA_TABLES = [
|
|
27
|
+
['IFC2X3', ENTITIES_IFC2X3],
|
|
28
|
+
// Checked against the IFC4 EXPRESS registry, so an agent asking about IFC4
|
|
29
|
+
// is not told about entities or attributes IFC4 lacks (#5204).
|
|
30
|
+
['IFC4', ENTITIES_IFC4_EXPRESS],
|
|
31
|
+
['IFC4X3', ENTITIES_IFC4X3],
|
|
32
|
+
];
|
|
33
|
+
let bySchema = null;
|
|
34
|
+
function tables() {
|
|
35
|
+
if (!bySchema) {
|
|
36
|
+
bySchema = new Map(SCHEMA_TABLES.map(([schema, list]) => [
|
|
37
|
+
schema,
|
|
38
|
+
new Map(list.map((entity) => [entity.name.toUpperCase(), entity])),
|
|
39
|
+
]));
|
|
40
|
+
}
|
|
41
|
+
return bySchema;
|
|
42
|
+
}
|
|
43
|
+
/** One schema's row for a class, or undefined. `schema` is matched case- and
|
|
44
|
+
* punctuation-insensitively, so `Ifc4x3` and `IFC4X3` both resolve. */
|
|
45
|
+
export function entityInfoInSchema(type, schema) {
|
|
46
|
+
if (!schema)
|
|
47
|
+
return undefined;
|
|
48
|
+
return tables().get(schema.toUpperCase().replace(/[^A-Z0-9]/g, ''))?.get(type.toUpperCase());
|
|
49
|
+
}
|
|
50
|
+
/**
|
|
51
|
+
* A class's row and the bundled schema that answered for it, later schemas
|
|
52
|
+
* winning — the same precedence the parser's union map applies.
|
|
53
|
+
*/
|
|
54
|
+
export function entityInfoAcrossSchemas(type) {
|
|
55
|
+
const upper = type.toUpperCase();
|
|
56
|
+
for (let i = SCHEMA_TABLES.length - 1; i >= 0; i--) {
|
|
57
|
+
const schema = SCHEMA_TABLES[i][0];
|
|
58
|
+
const info = tables().get(schema)?.get(upper);
|
|
59
|
+
if (info)
|
|
60
|
+
return { info, schema };
|
|
61
|
+
}
|
|
62
|
+
return undefined;
|
|
63
|
+
}
|
|
64
|
+
/**
|
|
65
|
+
* Attribute names in STEP positional order, preferring the named schema's
|
|
66
|
+
* spelling.
|
|
67
|
+
*
|
|
68
|
+
* Falls back to the parser's cross-schema answer when the model's schema does
|
|
69
|
+
* not declare the class — a vendor extension, or a class the file's declared
|
|
70
|
+
* schema does not have. That fallback is the pre-existing behaviour and is
|
|
71
|
+
* strictly better than nothing.
|
|
72
|
+
*/
|
|
73
|
+
export function attributeNamesForSchema(type, schema) {
|
|
74
|
+
const info = entityInfoInSchema(type, schema);
|
|
75
|
+
return info ? info.attributes : getAttributeNamesAcrossSchemas(type);
|
|
76
|
+
}
|
|
77
|
+
//# sourceMappingURL=schema-tables.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"schema-tables.js","sourceRoot":"","sources":["../../src/headless/schema-tables.ts"],"names":[],"mappings":"AAAA;;+DAE+D;AAE/D;;;;;;;;;;;;;;;;;;GAkBG;AAEH,OAAO,EAAE,eAAe,EAAE,qBAAqB,EAAE,eAAe,EAAsB,MAAM,gBAAgB,CAAC;AAC7G,OAAO,EAAE,8BAA8B,EAAE,MAAM,kBAAkB,CAAC;AAElE,qCAAqC;AACrC,MAAM,aAAa,GAA+D;IAChF,CAAC,QAAQ,EAAE,eAAe,CAAC;IAC3B,2EAA2E;IAC3E,+DAA+D;IAC/D,CAAC,MAAM,EAAE,qBAAqB,CAAC;IAC/B,CAAC,QAAQ,EAAE,eAAe,CAAC;CAC5B,CAAC;AAEF,IAAI,QAAQ,GAAmD,IAAI,CAAC;AACpE,SAAS,MAAM;IACb,IAAI,CAAC,QAAQ,EAAE,CAAC;QACd,QAAQ,GAAG,IAAI,GAAG,CAAC,aAAa,CAAC,GAAG,CAAC,CAAC,CAAC,MAAM,EAAE,IAAI,CAAC,EAAE,EAAE,CAAC;YACvD,MAAM;YACN,IAAI,GAAG,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,MAAM,EAAE,EAAE,CAAC,CAAC,MAAM,CAAC,IAAI,CAAC,WAAW,EAAE,EAAE,MAAM,CAAC,CAAC,CAAC;SACnE,CAAC,CAAC,CAAC;IACN,CAAC;IACD,OAAO,QAAQ,CAAC;AAClB,CAAC;AAED;wEACwE;AACxE,MAAM,UAAU,kBAAkB,CAAC,IAAY,EAAE,MAA0B;IACzE,IAAI,CAAC,MAAM;QAAE,OAAO,SAAS,CAAC;IAC9B,OAAO,MAAM,EAAE,CAAC,GAAG,CAAC,MAAM,CAAC,WAAW,EAAE,CAAC,OAAO,CAAC,YAAY,EAAE,EAAE,CAAC,CAAC,EAAE,GAAG,CAAC,IAAI,CAAC,WAAW,EAAE,CAAC,CAAC;AAC/F,CAAC;AAED;;;GAGG;AACH,MAAM,UAAU,uBAAuB,CAAC,IAAY;IAClD,MAAM,KAAK,GAAG,IAAI,CAAC,WAAW,EAAE,CAAC;IACjC,KAAK,IAAI,CAAC,GAAG,aAAa,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC,IAAI,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC;QACnD,MAAM,MAAM,GAAG,aAAa,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;QACnC,MAAM,IAAI,GAAG,MAAM,EAAE,CAAC,GAAG,CAAC,MAAM,CAAC,EAAE,GAAG,CAAC,KAAK,CAAC,CAAC;QAC9C,IAAI,IAAI;YAAE,OAAO,EAAE,IAAI,EAAE,MAAM,EAAE,CAAC;IACpC,CAAC;IACD,OAAO,SAAS,CAAC;AACnB,CAAC;AAED;;;;;;;;GAQG;AACH,MAAM,UAAU,uBAAuB,CAAC,IAAY,EAAE,MAA0B;IAC9E,MAAM,IAAI,GAAG,kBAAkB,CAAC,IAAI,EAAE,MAAM,CAAC,CAAC;IAC9C,OAAO,IAAI,CAAC,CAAC,CAAC,IAAI,CAAC,UAAU,CAAC,CAAC,CAAC,8BAA8B,CAAC,IAAI,CAAC,CAAC;AACvE,CAAC"}
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import { type ParseModelOptions, type ParsedModel } from '@ifc-lite/parser';
|
|
2
|
+
import { type BimContext } from '../context.js';
|
|
3
|
+
import { type HeadlessCapability, type HeadlessOperation } from './capabilities.js';
|
|
4
|
+
import type { HeadlessBackendOptions } from './options.js';
|
|
5
|
+
export declare class HeadlessSessionClosedError extends Error {
|
|
6
|
+
readonly code = "SESSION_CLOSED";
|
|
7
|
+
constructor();
|
|
8
|
+
}
|
|
9
|
+
export interface OpenModelOptions extends ParseModelOptions {
|
|
10
|
+
modelId?: string;
|
|
11
|
+
modelName?: string;
|
|
12
|
+
backend?: HeadlessBackendOptions;
|
|
13
|
+
}
|
|
14
|
+
export interface HeadlessModelSession extends ParsedModel {
|
|
15
|
+
readonly bim: BimContext;
|
|
16
|
+
readonly closed: boolean;
|
|
17
|
+
capability(operation: HeadlessOperation): HeadlessCapability;
|
|
18
|
+
/** Idempotent. Releases host preparation caches and refuses later backend calls. */
|
|
19
|
+
close(): void;
|
|
20
|
+
}
|
|
21
|
+
/** Open STEP/IFCZIP bytes for headless queries, mutations and export. No I/O or renderer is created. */
|
|
22
|
+
export declare function openModel(input: Uint8Array | ArrayBuffer, options?: OpenModelOptions): Promise<HeadlessModelSession>;
|
|
23
|
+
//# sourceMappingURL=session.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"session.d.ts","sourceRoot":"","sources":["../../src/headless/session.ts"],"names":[],"mappings":"AAIA,OAAO,EAAc,KAAK,iBAAiB,EAAE,KAAK,WAAW,EAAE,MAAM,kBAAkB,CAAC;AACxF,OAAO,EAAoB,KAAK,UAAU,EAAE,MAAM,eAAe,CAAC;AAElE,OAAO,EAAmD,KAAK,kBAAkB,EAAE,KAAK,iBAAiB,EAAE,MAAM,mBAAmB,CAAC;AACrI,OAAO,KAAK,EAAE,sBAAsB,EAAE,MAAM,cAAc,CAAC;AAG3D,qBAAa,0BAA2B,SAAQ,KAAK;IACnD,QAAQ,CAAC,IAAI,oBAAoB;;CAElC;AACD,MAAM,WAAW,gBAAiB,SAAQ,iBAAiB;IACzD,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,OAAO,CAAC,EAAE,sBAAsB,CAAC;CAClC;AACD,MAAM,WAAW,oBAAqB,SAAQ,WAAW;IACvD,QAAQ,CAAC,GAAG,EAAE,UAAU,CAAC;IACzB,QAAQ,CAAC,MAAM,EAAE,OAAO,CAAC;IACzB,UAAU,CAAC,SAAS,EAAE,iBAAiB,GAAG,kBAAkB,CAAC;IAC7D,oFAAoF;IACpF,KAAK,IAAI,IAAI,CAAC;CACf;AAmED,wGAAwG;AACxG,wBAAsB,SAAS,CAAC,KAAK,EAAE,UAAU,GAAG,WAAW,EAAE,OAAO,GAAE,gBAAqB,GAAG,OAAO,CAAC,oBAAoB,CAAC,CAoC9H"}
|