@bldrs-ai/conway 1.399.1223 → 1.401.1237

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.
Files changed (56) hide show
  1. package/compiled/Dist/ConwayGeomWasmNode.js +0 -0
  2. package/compiled/Dist/ConwayGeomWasmNodeMT.js +0 -0
  3. package/compiled/Dist/ConwayGeomWasmWeb.js +0 -0
  4. package/compiled/Dist/ConwayGeomWasmWebMT.js +1 -1
  5. package/compiled/Dist/ConwayGeomWasmWebMT.wasm +0 -0
  6. package/compiled/dependencies/conway-geom/Dist/ConwayGeomWasmNode.js +0 -0
  7. package/compiled/dependencies/conway-geom/Dist/ConwayGeomWasmNodeMT.js +0 -0
  8. package/compiled/dependencies/conway-geom/Dist/ConwayGeomWasmWeb.js +0 -0
  9. package/compiled/dependencies/conway-geom/Dist/ConwayGeomWasmWebMT.js +1 -1
  10. package/compiled/dependencies/conway-geom/Dist/ConwayGeomWasmWebMT.wasm +0 -0
  11. package/compiled/examples/browser-bundled.cjs +184 -11
  12. package/compiled/examples/cli-bundled.cjs +213 -40
  13. package/compiled/examples/cli-step-bundled.cjs +105 -37
  14. package/compiled/examples/validator-bundled.cjs +184 -11
  15. package/compiled/src/ifc/ifc_step_model.d.ts +5 -2
  16. package/compiled/src/ifc/ifc_step_model.d.ts.map +1 -1
  17. package/compiled/src/ifc/ifc_step_model.js +5 -3
  18. package/compiled/src/ifc/ifc_step_parser.d.ts +36 -0
  19. package/compiled/src/ifc/ifc_step_parser.d.ts.map +1 -1
  20. package/compiled/src/ifc/ifc_step_parser.js +40 -0
  21. package/compiled/src/ifc/ifc_stream_model.test.d.ts +2 -0
  22. package/compiled/src/ifc/ifc_stream_model.test.d.ts.map +1 -0
  23. package/compiled/src/ifc/ifc_stream_model.test.js +70 -0
  24. package/compiled/src/parsing/parsing_buffer.d.ts +23 -0
  25. package/compiled/src/parsing/parsing_buffer.d.ts.map +1 -1
  26. package/compiled/src/parsing/parsing_buffer.js +31 -0
  27. package/compiled/src/step/parsing/byte_source.d.ts +55 -0
  28. package/compiled/src/step/parsing/byte_source.d.ts.map +1 -0
  29. package/compiled/src/step/parsing/byte_source.js +35 -0
  30. package/compiled/src/step/parsing/incremental_type_index.d.ts +64 -0
  31. package/compiled/src/step/parsing/incremental_type_index.d.ts.map +1 -0
  32. package/compiled/src/step/parsing/incremental_type_index.js +92 -0
  33. package/compiled/src/step/parsing/incremental_type_index.test.d.ts +2 -0
  34. package/compiled/src/step/parsing/incremental_type_index.test.d.ts.map +1 -0
  35. package/compiled/src/step/parsing/incremental_type_index.test.js +55 -0
  36. package/compiled/src/step/parsing/step_parser.d.ts +19 -0
  37. package/compiled/src/step/parsing/step_parser.d.ts.map +1 -1
  38. package/compiled/src/step/parsing/step_parser.js +48 -4
  39. package/compiled/src/step/parsing/streaming_index_builder.d.ts +70 -0
  40. package/compiled/src/step/parsing/streaming_index_builder.d.ts.map +1 -0
  41. package/compiled/src/step/parsing/streaming_index_builder.js +125 -0
  42. package/compiled/src/step/parsing/streaming_index_builder.test.d.ts +2 -0
  43. package/compiled/src/step/parsing/streaming_index_builder.test.d.ts.map +1 -0
  44. package/compiled/src/step/parsing/streaming_index_builder.test.js +107 -0
  45. package/compiled/src/step/parsing/streaming_record_dispatcher.d.ts +55 -0
  46. package/compiled/src/step/parsing/streaming_record_dispatcher.d.ts.map +1 -0
  47. package/compiled/src/step/parsing/streaming_record_dispatcher.js +67 -0
  48. package/compiled/src/step/parsing/streaming_record_dispatcher.test.d.ts +2 -0
  49. package/compiled/src/step/parsing/streaming_record_dispatcher.test.d.ts.map +1 -0
  50. package/compiled/src/step/parsing/streaming_record_dispatcher.test.js +66 -0
  51. package/compiled/src/step/step_model_base.d.ts +9 -3
  52. package/compiled/src/step/step_model_base.d.ts.map +1 -1
  53. package/compiled/src/step/step_model_base.js +9 -3
  54. package/compiled/src/version/version.js +1 -1
  55. package/compiled/tsconfig.tsbuildinfo +1 -1
  56. package/package.json +1 -1
@@ -0,0 +1,125 @@
1
+ import ParsingBuffer from "../../parsing/parsing_buffer.js";
2
+ import { ParseResult, } from "./step_parser.js";
3
+ /**
4
+ * Build the entity index by streaming a `ByteSource` through a fixed-size
5
+ * moving window, instead of parsing one resident buffer. The parse loop is
6
+ * byte-for-byte the resident one (`StepParser.parseDataBlockStreamed` drives
7
+ * the same generator); this coordinator only owns the window: it fills it,
8
+ * parses the header from the first fill, then slides the window forward at
9
+ * top-level record boundaries as the parser advances.
10
+ *
11
+ * Because the parser records file-absolute addresses and its rewind stack is
12
+ * empty at every top-level boundary, sliding there is transparent: the
13
+ * unconsumed tail is copied to the front, fresh bytes are appended, and the
14
+ * buffer is rebased so `address` keeps its file-absolute value. Peak JS heap
15
+ * for the index build is therefore `window + index columns`, independent of
16
+ * file size.
17
+ *
18
+ * The window slides only once the cursor passes `pool / 2`, bounding the
19
+ * memmove frequency; a record up to `pool / 2` bytes always fits after a
20
+ * slide. If a single record exceeds that (never on the current corpus, whose
21
+ * largest STEP record is ~25 KB), the whole parse restarts from the
22
+ * beginning with the window doubled, and repeats until every record fits —
23
+ * correctness over the pathological case, at the cost of a re-scan. (M1's
24
+ * production path will instead grow in place / restart from the last
25
+ * boundary; from-scratch keeps the spike simple.)
26
+ *
27
+ * @param source The byte source.
28
+ * @param parser The STEP parser (typed to the schema).
29
+ * @param pool Target window size in bytes.
30
+ * @param onRecordIndexed Optional per-record event, invoked live as each
31
+ * top-level record is indexed with its (localID, expressID, typeID) — the
32
+ * seam for incremental semantic consumers (M2). localIDs are dense and
33
+ * assigned in parse order from 0. On the rare grow-and-restart (a single
34
+ * record larger than the window — never on real files, whose largest STEP
35
+ * record is ~25 KB), the parse re-runs from the start and records re-fire
36
+ * from localID 0; consumers must therefore be idempotent by localID /
37
+ * expressID (the standard consumers — type index keyed by localID, roots
38
+ * registry keyed by expressID — are). Must be synchronous and cheap;
39
+ * expensive work belongs on a demand queue, not the parse path.
40
+ * @return {StreamingIndexResult} The index, header, result and diagnostics.
41
+ */
42
+ export function buildIndexStreaming(source, parser, pool, onRecordIndexed) {
43
+ const fileSize = source.byteLength;
44
+ // Grow-and-restart loop: the body runs a full streamed parse at the current
45
+ // window size. It only re-runs if a single record couldn't fit the window
46
+ // (false end-of-file before true EOF), doubling the window each time.
47
+ let windowBytes = Math.max(pool, MIN_WINDOW);
48
+ for (;;) {
49
+ const window = new Uint8Array(windowBytes);
50
+ let windowStartFile = 0;
51
+ let windowLen = source.read(0, windowBytes, window, 0);
52
+ let bytesRead = windowLen;
53
+ const input = new ParsingBuffer(window, 0, windowLen);
54
+ const [header, headerResult] = parser.parseHeader(input);
55
+ if (headerResult !== ParseResult.COMPLETE) {
56
+ // Header didn't complete in the first window: either malformed, or a
57
+ // header larger than the pool. Report it as-is — the resident parse
58
+ // would surface the same header result.
59
+ return {
60
+ header,
61
+ elements: [],
62
+ result: headerResult,
63
+ stats: { pool, windowBytes, slides: 0, maxRecordLen: 0, bytesRead },
64
+ };
65
+ }
66
+ // Slide once the cursor is past half the window; a record up to this much
67
+ // headroom always fits without a false EOF.
68
+ const slideThreshold = windowBytes >> 1;
69
+ let slides = 0;
70
+ let maxRecordLen = 0;
71
+ let prevBoundaryFile = windowStartFile + input.cursor;
72
+ const onRecordBoundary = (buffer) => {
73
+ const cursor = buffer.cursor;
74
+ const recordFileStart = windowStartFile + cursor;
75
+ const recordLen = recordFileStart - prevBoundaryFile;
76
+ if (recordLen > maxRecordLen) {
77
+ maxRecordLen = recordLen;
78
+ }
79
+ prevBoundaryFile = recordFileStart;
80
+ // Window already spans to EOF — nothing left to slide in.
81
+ if (windowStartFile + windowLen >= fileSize) {
82
+ return;
83
+ }
84
+ // Enough headroom remains; defer the slide to bound memmove frequency.
85
+ if (cursor < slideThreshold) {
86
+ return;
87
+ }
88
+ // Slide: move the unconsumed tail to the front, refill behind it.
89
+ const tail = windowLen - cursor;
90
+ window.copyWithin(0, cursor, windowLen);
91
+ const want = windowBytes - tail;
92
+ const got = source.read(windowStartFile + windowLen, want, window, tail);
93
+ bytesRead += got;
94
+ windowLen = tail + got;
95
+ windowStartFile = recordFileStart;
96
+ buffer.rebaseWindow(window, 0, windowLen, windowStartFile);
97
+ ++slides;
98
+ };
99
+ const [index, result] = parser.parseDataBlockStreamed(input, onRecordBoundary, onRecordIndexed);
100
+ // A parse that stopped short of true EOF, with the window not spanning to
101
+ // EOF, means a single record overflowed the window: grow and retry.
102
+ const stoppedShort = result !== ParseResult.COMPLETE;
103
+ const notAtEof = windowStartFile + windowLen < fileSize;
104
+ if (stoppedShort && notAtEof) {
105
+ windowBytes *= 2;
106
+ continue;
107
+ }
108
+ // Capture the final record's length (no boundary fires after the last).
109
+ const lastRecordLen = (windowStartFile + input.cursor) - prevBoundaryFile;
110
+ if (lastRecordLen > maxRecordLen) {
111
+ maxRecordLen = lastRecordLen;
112
+ }
113
+ return {
114
+ header,
115
+ elements: index.elements,
116
+ result,
117
+ stats: { pool, windowBytes, slides, maxRecordLen, bytesRead },
118
+ };
119
+ }
120
+ }
121
+ // Floor on the window so a pathologically tiny pool still holds the header
122
+ // and a record with slide headroom. Tests drive pools down to this to force
123
+ // many slides on a small fixture; the pool sweep uses ≥ 128 KB anyway.
124
+ // eslint-disable-next-line no-magic-numbers
125
+ const MIN_WINDOW = 4 * 1024;
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=streaming_index_builder.test.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"streaming_index_builder.test.d.ts","sourceRoot":"","sources":["../../../../src/step/parsing/streaming_index_builder.test.ts"],"names":[],"mappings":""}
@@ -0,0 +1,107 @@
1
+ /* eslint-disable no-magic-numbers */
2
+ // M0 spike: streaming index build must be byte-identical to the resident
3
+ // parse. The parser reads file-absolute addresses and only rewinds within a
4
+ // single top-level record, so feeding it from a moving window that slides at
5
+ // record boundaries must produce the exact same index columns — at every
6
+ // pool size, including one small enough to force many slides + straddles.
7
+ import * as fs from "fs";
8
+ import { beforeAll, describe, expect, test } from "@jest/globals";
9
+ import ParsingBuffer from "../../parsing/parsing_buffer.js";
10
+ import IfcStepParser from "../../ifc/ifc_step_parser.js";
11
+ import { BufferByteSource } from "./byte_source.js";
12
+ import { buildIndexStreaming } from "./streaming_index_builder.js";
13
+ import { ParseResult } from "./step_parser.js";
14
+ /**
15
+ * Flatten the index (top-level records + their inline / multi-mapping
16
+ * children) into a stable, comparable column dump: every field the streaming
17
+ * parse must reproduce identically.
18
+ *
19
+ * @param elements The index elements.
20
+ * @return {object[]} A flat list of column tuples in traversal order.
21
+ */
22
+ function columns(elements) {
23
+ const out = [];
24
+ const visit = (e) => {
25
+ out.push({
26
+ address: e.address,
27
+ length: e.length,
28
+ typeID: e.typeID,
29
+ expressID: e.expressID ?? null,
30
+ });
31
+ for (const child of e.inlineEntities ?? []) {
32
+ visit(child);
33
+ }
34
+ for (const child of e.multiMapping ?? []) {
35
+ visit(child);
36
+ }
37
+ };
38
+ for (const e of elements) {
39
+ visit(e);
40
+ }
41
+ return out;
42
+ }
43
+ let bytes;
44
+ let residentColumns;
45
+ let residentCount;
46
+ beforeAll(() => {
47
+ bytes = new Uint8Array(fs.readFileSync("data/index.ifc"));
48
+ const input = new ParsingBuffer(bytes);
49
+ IfcStepParser.Instance.parseHeader(input);
50
+ const [index, result] = IfcStepParser.Instance.parseDataBlock(input);
51
+ expect(result).toBe(ParseResult.COMPLETE);
52
+ residentColumns = columns(index.elements);
53
+ residentCount = index.elements.length;
54
+ });
55
+ describe("buildIndexStreaming", () => {
56
+ // A tiny pool relative to the 18 KB fixture forces repeated slides and at
57
+ // least one record straddling a window boundary; a large one takes the
58
+ // whole file in a single window (no slide). Both must match the resident
59
+ // parse exactly.
60
+ const POOLS = [4 * 1024, 8 * 1024, 16 * 1024, 128 * 1024];
61
+ test.each(POOLS)("produces the resident index at pool=%d bytes", (pool) => {
62
+ const source = new BufferByteSource(bytes);
63
+ const result = buildIndexStreaming(source, IfcStepParser.Instance, pool);
64
+ expect(result.result).toBe(ParseResult.COMPLETE);
65
+ expect(result.elements.length).toBe(residentCount);
66
+ expect(columns(result.elements)).toEqual(residentColumns);
67
+ });
68
+ test("the small pool actually slides the window (straddle exercised)", () => {
69
+ const source = new BufferByteSource(bytes);
70
+ const result = buildIndexStreaming(source, IfcStepParser.Instance, 4 * 1024);
71
+ // 18 KB fixture through a 4 KB window must slide several times.
72
+ expect(result.stats.slides).toBeGreaterThan(1);
73
+ // Physical window never grew past the pool (no record exceeds it).
74
+ expect(result.stats.windowBytes).toBe(4 * 1024);
75
+ // Every byte read once (no growth re-reads) — bounded to file size.
76
+ expect(result.stats.bytesRead).toBe(bytes.length);
77
+ });
78
+ test("the large pool takes the whole file in one window (no slide)", () => {
79
+ const source = new BufferByteSource(bytes);
80
+ const result = buildIndexStreaming(source, IfcStepParser.Instance, 128 * 1024);
81
+ expect(result.stats.slides).toBe(0);
82
+ expect(result.stats.maxRecordLen).toBeGreaterThan(0);
83
+ });
84
+ test("grows the window and restarts when a single record exceeds the pool", () => {
85
+ // Synthetic file with one record far larger than a tiny pool — the
86
+ // corpus never hits this (largest STEP record ~25 KB), so exercise the
87
+ // grow-and-restart safety valve directly. A long text literal makes one
88
+ // record ~12 KB; a 4 KB pool must grow past it and still index it.
89
+ const bigValue = "X".repeat(12000);
90
+ const text = "ISO-10303-21;\nHEADER;\nENDSEC;\nDATA;\n" +
91
+ "#1=IFCWALL('a');\n" +
92
+ `#2=IFCPROPERTYSINGLEVALUE('${bigValue}');\n` +
93
+ "#3=IFCWALL('c');\nENDSEC;\n";
94
+ const synthetic = new TextEncoder().encode(text);
95
+ const resident = new ParsingBuffer(synthetic);
96
+ IfcStepParser.Instance.parseHeader(resident);
97
+ const [residentIndex, residentResult] = IfcStepParser.Instance.parseDataBlock(resident);
98
+ expect(residentResult).toBe(ParseResult.COMPLETE);
99
+ const streamed = buildIndexStreaming(new BufferByteSource(synthetic), IfcStepParser.Instance, 4 * 1024);
100
+ expect(streamed.result).toBe(ParseResult.COMPLETE);
101
+ // Window grew past the 4 KB pool to fit the ~12 KB record.
102
+ expect(streamed.stats.windowBytes).toBeGreaterThan(4 * 1024);
103
+ expect(streamed.stats.maxRecordLen).toBeGreaterThan(12000);
104
+ // ...and the index is still byte-identical to the resident parse.
105
+ expect(columns(streamed.elements)).toEqual(columns(residentIndex.elements));
106
+ });
107
+ });
@@ -0,0 +1,55 @@
1
+ import { StepEntityConstructorAbstract } from "../step_entity_constructor.js";
2
+ /**
3
+ * A per-record event handler. Receives a record's dense `localID` (assigned
4
+ * in parse order from 0), its `expressID`, and its `typeID` (0 for
5
+ * external-mapping records).
6
+ */
7
+ export type RecordHandler<TypeIDType> = (localID: number, expressID: number, typeID: TypeIDType | undefined) => void;
8
+ /**
9
+ * Routes the streaming parse's per-record events (M2) to consumers that
10
+ * subscribe by **type set**. A subscription's type set is the subtype closure
11
+ * of the entity constructors it names — the same `query` closure the type
12
+ * index and `expressIDsOfTypes` use (conway #383) — so `on([IfcRoot], …)`
13
+ * matches every product, relationship, property set and quantity, and any
14
+ * future IfcRoot subtype, with no whitelist to maintain.
15
+ *
16
+ * Feed `dispatcher.onRecordIndexed` to `buildIndexStreaming` /
17
+ * `parseDataBlockStreamed`. Handlers run synchronously in the parse path, so
18
+ * they must be cheap (copy ids into a compact structure); anything expensive
19
+ * belongs on a demand queue, not here.
20
+ *
21
+ * Because subscriptions match on the raw parse `typeID`, external-mapping /
22
+ * complex records (typeID 0) are only delivered via `onAnyRecord` — resolving
23
+ * their concrete type is the incremental-type-index consumer's job (it reads
24
+ * `multiMapping`), a follow-on. For the overwhelming majority of records
25
+ * (simple, one type each) type-set delivery is exact.
26
+ */
27
+ export declare class StreamingRecordDispatcher<TypeIDType extends number> {
28
+ private readonly typed;
29
+ private readonly any;
30
+ /**
31
+ * Subscribe to records of the given entity types (including their
32
+ * subtypes).
33
+ *
34
+ * @param types The entity constructors whose subtype closures to match.
35
+ * @param handler Called for each matching record.
36
+ */
37
+ on(types: StepEntityConstructorAbstract<TypeIDType>[], handler: RecordHandler<TypeIDType>): void;
38
+ /**
39
+ * Subscribe to every record regardless of type (the firehose — including
40
+ * external-mapping records).
41
+ *
42
+ * @param handler Called for every record.
43
+ */
44
+ onAnyRecord(handler: RecordHandler<TypeIDType>): void;
45
+ /**
46
+ * The per-record callback to hand to the streaming parse. Dispatches each
47
+ * record to the matching subscribers. Bound so it can be passed directly.
48
+ *
49
+ * @param localID The record's dense local ID.
50
+ * @param expressID The record's express ID.
51
+ * @param typeID The record's type ID (0 for external-mapping records).
52
+ */
53
+ readonly onRecordIndexed: RecordHandler<TypeIDType>;
54
+ }
55
+ //# sourceMappingURL=streaming_record_dispatcher.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"streaming_record_dispatcher.d.ts","sourceRoot":"","sources":["../../../../src/step/parsing/streaming_record_dispatcher.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,6BAA6B,EAAE,MAAM,4BAA4B,CAAA;AAG1E;;;;GAIG;AACH,MAAM,MAAM,aAAa,CAAC,UAAU,IAClC,CAAE,OAAO,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,EAAE,MAAM,EAAE,UAAU,GAAG,SAAS,KAAM,IAAI,CAAA;AAGhF;;;;;;;;;;;;;;;;;;GAkBG;AACH,qBAAa,yBAAyB,CAAC,UAAU,SAAS,MAAM;IAE9D,OAAO,CAAC,QAAQ,CAAC,KAAK,CAGf;IAEP,OAAO,CAAC,QAAQ,CAAC,GAAG,CAAkC;IAEtD;;;;;;OAMG;IACI,EAAE,CACL,KAAK,EAAE,6BAA6B,CAAC,UAAU,CAAC,EAAE,EAClD,OAAO,EAAE,aAAa,CAAC,UAAU,CAAC,GAAI,IAAI;IAQ9C;;;;;OAKG;IACI,WAAW,CAAE,OAAO,EAAE,aAAa,CAAC,UAAU,CAAC,GAAI,IAAI;IAI9D;;;;;;;OAOG;IACH,SAAgB,eAAe,EAAE,aAAa,CAAC,UAAU,CAAC,CAcvD;CACJ"}
@@ -0,0 +1,67 @@
1
+ /**
2
+ * Routes the streaming parse's per-record events (M2) to consumers that
3
+ * subscribe by **type set**. A subscription's type set is the subtype closure
4
+ * of the entity constructors it names — the same `query` closure the type
5
+ * index and `expressIDsOfTypes` use (conway #383) — so `on([IfcRoot], …)`
6
+ * matches every product, relationship, property set and quantity, and any
7
+ * future IfcRoot subtype, with no whitelist to maintain.
8
+ *
9
+ * Feed `dispatcher.onRecordIndexed` to `buildIndexStreaming` /
10
+ * `parseDataBlockStreamed`. Handlers run synchronously in the parse path, so
11
+ * they must be cheap (copy ids into a compact structure); anything expensive
12
+ * belongs on a demand queue, not here.
13
+ *
14
+ * Because subscriptions match on the raw parse `typeID`, external-mapping /
15
+ * complex records (typeID 0) are only delivered via `onAnyRecord` — resolving
16
+ * their concrete type is the incremental-type-index consumer's job (it reads
17
+ * `multiMapping`), a follow-on. For the overwhelming majority of records
18
+ * (simple, one type each) type-set delivery is exact.
19
+ */
20
+ export class StreamingRecordDispatcher {
21
+ constructor() {
22
+ this.typed = [];
23
+ this.any = [];
24
+ /**
25
+ * The per-record callback to hand to the streaming parse. Dispatches each
26
+ * record to the matching subscribers. Bound so it can be passed directly.
27
+ *
28
+ * @param localID The record's dense local ID.
29
+ * @param expressID The record's express ID.
30
+ * @param typeID The record's type ID (0 for external-mapping records).
31
+ */
32
+ this.onRecordIndexed = (localID, expressID, typeID) => {
33
+ for (const handler of this.any) {
34
+ handler(localID, expressID, typeID);
35
+ }
36
+ if (typeID !== void 0) {
37
+ for (const { set, handler } of this.typed) {
38
+ if (set.has(typeID)) {
39
+ handler(localID, expressID, typeID);
40
+ }
41
+ }
42
+ }
43
+ };
44
+ }
45
+ /**
46
+ * Subscribe to records of the given entity types (including their
47
+ * subtypes).
48
+ *
49
+ * @param types The entity constructors whose subtype closures to match.
50
+ * @param handler Called for each matching record.
51
+ */
52
+ on(types, handler) {
53
+ this.typed.push({
54
+ set: new Set(types.flatMap((type) => type.query)),
55
+ handler,
56
+ });
57
+ }
58
+ /**
59
+ * Subscribe to every record regardless of type (the firehose — including
60
+ * external-mapping records).
61
+ *
62
+ * @param handler Called for every record.
63
+ */
64
+ onAnyRecord(handler) {
65
+ this.any.push(handler);
66
+ }
67
+ }
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=streaming_record_dispatcher.test.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"streaming_record_dispatcher.test.d.ts","sourceRoot":"","sources":["../../../../src/step/parsing/streaming_record_dispatcher.test.ts"],"names":[],"mappings":""}
@@ -0,0 +1,66 @@
1
+ /* eslint-disable no-magic-numbers */
2
+ // M2 core: record events emitted live from the streaming parse, routed by a
3
+ // type-set subscription, let a consumer build a semantic structure
4
+ // incrementally — and it must match what the finished model's type index
5
+ // yields. Here: a roots registry collected from the event stream equals
6
+ // `expressIDsOfTypes(IfcRoot)` on the resident model.
7
+ import * as fs from "fs";
8
+ import { beforeAll, describe, expect, test } from "@jest/globals";
9
+ import ParsingBuffer from "../../parsing/parsing_buffer.js";
10
+ import IfcStepParser from "../../ifc/ifc_step_parser.js";
11
+ import { BufferByteSource } from "./byte_source.js";
12
+ import { buildIndexStreaming } from "./streaming_index_builder.js";
13
+ import { StreamingRecordDispatcher } from "./streaming_record_dispatcher.js";
14
+ import { ParseResult } from "./step_parser.js";
15
+ import { IfcRoot, IfcProduct } from "../../ifc/ifc4_gen/index.js";
16
+ let bytes;
17
+ let residentRoots;
18
+ let residentProducts;
19
+ beforeAll(() => {
20
+ bytes = new Uint8Array(fs.readFileSync("data/index.ifc"));
21
+ const input = new ParsingBuffer(bytes);
22
+ IfcStepParser.Instance.parseHeader(input);
23
+ const [, model] = IfcStepParser.Instance.parseDataToModel(input);
24
+ residentRoots = new Set(model.expressIDsOfTypes(IfcRoot));
25
+ residentProducts = new Set(model.expressIDsOfTypes(IfcProduct));
26
+ });
27
+ describe("StreamingRecordDispatcher", () => {
28
+ test("a roots registry built live from the event stream matches the type index", () => {
29
+ const roots = [];
30
+ const products = new Set();
31
+ const dispatcher = new StreamingRecordDispatcher();
32
+ dispatcher.on([IfcRoot], (_localID, expressID) => roots.push(expressID));
33
+ dispatcher.on([IfcProduct], (_localID, expressID) => products.add(expressID));
34
+ const result = buildIndexStreaming(new BufferByteSource(bytes), IfcStepParser.Instance, 4 * 1024, // tiny pool → the parse slides; events still fire per record
35
+ dispatcher.onRecordIndexed);
36
+ expect(result.result).toBe(ParseResult.COMPLETE);
37
+ // index.ifc has no external-mapping records, so raw-typeID delivery is
38
+ // exact vs. the type index.
39
+ expect(new Set(roots)).toEqual(residentRoots);
40
+ expect(products).toEqual(residentProducts);
41
+ // Products are a strict subset of roots (IfcProduct ⊂ IfcRoot).
42
+ expect(residentProducts.size).toBeLessThan(residentRoots.size);
43
+ for (const p of products) {
44
+ expect(residentRoots.has(p)).toBe(true);
45
+ }
46
+ });
47
+ test("localIDs arrive dense and in ascending parse order", () => {
48
+ const localIDs = [];
49
+ const dispatcher = new StreamingRecordDispatcher();
50
+ dispatcher.onAnyRecord((localID) => localIDs.push(localID));
51
+ buildIndexStreaming(new BufferByteSource(bytes), IfcStepParser.Instance, 128 * 1024, dispatcher.onRecordIndexed);
52
+ expect(localIDs.length).toBeGreaterThan(10);
53
+ // 0,1,2,…,n-1 in order.
54
+ expect(localIDs).toEqual(localIDs.map((_v, i) => i));
55
+ });
56
+ test("onAnyRecord sees every record; a type filter sees a subset", () => {
57
+ let all = 0;
58
+ let rootsCount = 0;
59
+ const dispatcher = new StreamingRecordDispatcher();
60
+ dispatcher.onAnyRecord(() => ++all);
61
+ dispatcher.on([IfcRoot], () => ++rootsCount);
62
+ buildIndexStreaming(new BufferByteSource(bytes), IfcStepParser.Instance, 128 * 1024, dispatcher.onRecordIndexed);
63
+ expect(all).toBeGreaterThan(rootsCount);
64
+ expect(rootsCount).toBe(residentRoots.size);
65
+ });
66
+ });
@@ -1,5 +1,5 @@
1
1
  import { StepIndexEntry } from "./parsing/step_parser.js";
2
- import { StepExternalByteStore } from "./step_buffer_provider.js";
2
+ import { StepBufferProvider, StepExternalByteStore } from "./step_buffer_provider.js";
3
3
  import StepEntityBase from "./step_entity_base.js";
4
4
  import StepEntitySchema from "./step_entity_schema.js";
5
5
  import StepEntityInternalReference from "./step_entity_internal_reference.js";
@@ -52,11 +52,17 @@ export default abstract class StepModelBase<EntityTypeIDs extends number, BaseEn
52
52
  * Construct this step model with its matching schema, a buffer to read from and an element index.
53
53
  *
54
54
  * @param schema The Step schema this is based on.
55
- * @param buffer_ The buffer to read this from.
55
+ * @param buffer_ The buffer to read this from. Pass `undefined` together
56
+ * with `provider` to build a model whose source is windowed from
57
+ * construction (e.g. a streaming open — see buildModelStreaming); the
58
+ * synchronous read paths (geometry extraction) then require the relevant
59
+ * ranges to be resident, exactly as after `spillSourceToExternalStore`.
56
60
  * @param elementIndex The element index for this, parsed or deserialized - note this takes
57
61
  * ownership of this array in the sense it will modify values/unfold inline elements etc.
62
+ * @param provider Optional pre-built buffer provider. When omitted, a
63
+ * resident provider over `buffer_` is used (the classic path).
58
64
  */
59
- constructor(schema: StepEntitySchema<EntityTypeIDs, BaseEntity>, buffer_: Uint8Array | undefined, elementIndex: StepIndexEntry<EntityTypeIDs>[]);
65
+ constructor(schema: StepEntitySchema<EntityTypeIDs, BaseEntity>, buffer_: Uint8Array | undefined, elementIndex: StepIndexEntry<EntityTypeIDs>[], provider?: StepBufferProvider);
60
66
  /**
61
67
  * Materialise the descriptor object for a local ID from the columns. Only the
62
68
  * persistent scalars are set; lazy fields (vtable, buffer, entity) are filled
@@ -1 +1 @@
1
- {"version":3,"file":"step_model_base.d.ts","sourceRoot":"","sources":["../../../src/step/step_model_base.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,cAAc,EAAE,MAAM,uBAAuB,CAAA;AAEtD,OAAO,EAGL,qBAAqB,EAEtB,MAAM,wBAAwB,CAAA;AAE/B,OAAO,cAAc,MAAM,oBAAoB,CAAA;AAC/C,OAAO,gBAAgB,MAAM,sBAAsB,CAAA;AACnD,OAAO,2BAAmE,MAAM,kCAAkC,CAAA;AAClH,OAAO,EAAE,eAAe,EAAE,MAAM,4BAA4B,CAAA;AAE5D,OAAO,EAAE,aAAa,EAAE,MAAM,6BAA6B,CAAA;AAC3D,OAAO,qBAAqB,EAAE,EAAE,6BAA6B,EAAE,MAAM,2BAA2B,CAAA;AAChG,OAAO,EAAE,KAAK,EAAE,aAAa,EAAE,MAAM,eAAe,CAAA;AACpD,OAAO,EAAE,mBAAmB,EAAE,MAAM,8BAA8B,CAAA;AAGlE,OAAO,EAAE,iBAAiB,EAAE,MAAM,4BAA4B,CAAA;AAC9D,OAAO,EAAE,iBAAiB,EAAE,MAAM,oBAAoB,CAAA;AACtD,OAAO,EAAE,aAAa,EAAE,MAAM,wBAAwB,CAAA;AACtD,OAAO,EAAE,cAAc,EAAE,MAAM,yBAAyB,CAAA;AAGxD;;GAEG;AACH,MAAM,CAAC,OAAO,CAAC,QAAQ,OAAO,aAAa,CACzC,aAAa,SAAS,MAAM,EAC5B,UAAU,SAAS,cAAc,CAAC,aAAa,CAAC,GAAG,cAAc,CAAC,aAAa,CAAC,CAClF,YAAW,QAAQ,CAAC,UAAU,CAAC,EAAE,KAAK;aAyElB,MAAM,EAAE,gBAAgB,CAAE,aAAa,EAAE,UAAU,CAAE;IACrE,OAAO,CAAC,OAAO;IAzEjB,kBAAyB,SAAS,EAAE,aAAa,CAAC,aAAa,CAAC,CAAA;IAChE,kBAAyB,mBAAmB,EAAE,qBAAqB,CAAE,aAAa,EAAE,UAAU,CAAE,CAAA;IAEhG,OAAO,CAAC,QAAQ,CAAC,cAAc,CAA6C;IAC5E,OAAO,CAAC,QAAQ,CAAC,aAAa,CAA4B;IAC1D,OAAO,CAAC,QAAQ,CAAC,iBAAiB,CAA4B;IAW9D,OAAO,CAAC,QAAQ,CAAC,QAAQ,CAAa;IACtC,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAa;IACrC,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAY;IACpC,OAAO,CAAC,QAAQ,CAAC,UAAU,CAAa;IACxC,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAQ;IAC/B,OAAO,CAAC,QAAQ,CAAC,mBAAmB,CAAQ;IAM5C,OAAO,CAAC,gBAAgB,CACgE;IAIxF,OAAO,CAAC,QAAQ,CAAC,eAAe,CAAC,CAC+C;IAQhF,OAAO,CAAC,eAAe,CAAoB;IAE3C;;;;;;;OAOG;IACI,kBAAkB,EAAE,OAAO,CAAO;IAEzC;;;OAGG;IACI,YAAY,EAAE,OAAO,CAAO;IAEnC,kBAAyB,SAAS,CAAC,EAAE,cAAc,CAAA;IAEnD,kBAAyB,QAAQ,CAAC,EAAE,aAAa,CAAA;IAEjD;;;;;;;OAOG;gBAEe,MAAM,EAAE,gBAAgB,CAAE,aAAa,EAAE,UAAU,CAAE,EAC7D,OAAO,EAAE,UAAU,GAAG,SAAS,EAAE,YAAY,EAAE,cAAc,CAAC,aAAa,CAAC,EAAE;IA+GxF;;;;;;;OAOG;IACH,OAAO,CAAC,cAAc;IActB;;;;;;;OAOG;IACH,OAAO,CAAC,KAAK;IAsBb;;;;;OAKG;IACI,UAAU,CAAE,UAAU,GAAE,OAAe,GAAI,IAAI;IAsEtD;;;;;OAKG;IACI,sBAAsB,CAC3B,OAAO,EAAE,2BAA2B,CAAE,aAAa,CAAE,GAAI,OAAO;IA+BlE;;;;;;;OAOG;IACI,mBAAmB,CAAC,OAAO,EAAE,MAAM,GAAG,OAAO;IAWpD;;;;;OAKG;IACI,mBAAmB,CAAE,OAAO,EAAE,MAAM,GAAI,IAAI;IAYnD;;;;OAIG;IACH,IAAW,cAAc,IAAI,MAAM,CAElC;IAED;;;;;OAKG;IACH,IAAW,gBAAgB,IAAI,OAAO,CAErC;IAED;;;;;OAKG;IACH,IAAW,mBAAmB,IAAI,MAAM,CAEvC;IAED;;;;;;;;;;;;;;;;;;;;OAoBG;IACI,0BAA0B,CAC7B,KAAK,EAAE,qBAAqB,EAC5B,UAAU,CAAC,EAAE,MAAM,EACnB,iBAAiB,CAAC,EAAE,MAAM,GAAI,IAAI;IActC;;;;;;;;;;OAUG;IACU,uBAAuB,CAAE,OAAO,EAAE,MAAM,GAAI,OAAO,CAAE,IAAI,CAAE;IAuBxE;;;;;;;;OAQG;IACU,yBAAyB,CAAE,SAAS,EAAE,MAAM,GAAI,OAAO,CAAE,IAAI,CAAE;IAgB5E;;;;OAIG;IACH,IAAW,IAAI,IAAI,MAAM,CAExB;IAED;;;;;OAKG;IACI,yBAAyB,CAAC,OAAO,EAAE,MAAM,GAAG,SAAS,GAAG,UAAU,GAAG,SAAS;IAcrF;;;;;;OAMG;IACI,8BAA8B,CAAC,CAAC,SAAS,6BAA6B,CAAE,aAAa,CAAE,EAAE,CAAC,SAAS,YAAY,CAAE,CAAC,CAAE,GAAG,UAAU,EAAG,OAAO,EAAE,MAAM,GAAG,SAAS,EAAE,IAAI,EAAE,CAAC,GAAI,CAAC,GAAG,SAAS;IAchM;;;;;;OAMG;IACI,qBAAqB,CAAC,SAAS,EAAE,MAAM,GAAG,UAAU,GAAG,SAAS;IAUvE;;;;;;;;OAQG;IACI,0BAA0B,CAAE,CAAC,SAAS,6BAA6B,CAAE,aAAa,CAAE,EAAE,CAAC,SAAS,YAAY,CAAE,CAAC,CAAE,GAAG,UAAU,EACnI,SAAS,EAAE,MAAM,EACjB,IAAI,EAAE,CAAC,GACP,CAAC,GAAG,SAAS;IAWf;;;;;;;;;OASG;IACI,wBAAwB,CAAE,CAAC,SAAS,6BAA6B,CAAE,aAAa,CAAE,EAAE,CAAC,SAAS,YAAY,CAAE,CAAC,CAAE,GAAG,UAAU,EACjI,OAAO,EAAE,MAAM,EACf,IAAI,EAAE,6BAA6B,CAAE,aAAa,CAAE,GACpD,CAAC,GAAG,SAAS;IAuFf;;;;;OAKG;IACI,uBAAuB,CAAE,IAAI,EAAE,mBAAmB,GAAI,WAAW;IASxE;;;;;;OAMG;IACI,qBAAqB,CAAC,OAAO,EAAE,MAAM,GAAG,MAAM,GAAG,SAAS;IAKjE;;;;;;;OAOG;IACI,mBAAmB,CAAC,OAAO,EAAE,MAAM,GAAG,UAAU,GAAG,SAAS;IAsFnE;;;;;;;OAOG;IACI,KAAK,CAAC,CAAC,SAAS,6BAA6B,CAAC,aAAa,CAAC,EAAE,EAAE,GAAG,KAAK,EAAE,CAAC,GAChF,gBAAgB,CAAC,YAAY,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC;IAS3C;;;;;;;;;OASG;IACI,SAAS,CAAC,CAAC,SAAS,6BAA6B,CAAC,aAAa,CAAC,EAAE,EACrE,GAAG,KAAK,EAAE,CAAC,GAAI,MAAM;IAOzB;;;;;;;;;;;;OAYG;IACK,iBAAiB,CAAC,CAAC,SAAS,6BAA6B,CAAC,aAAa,CAAC,EAAE,EAC9E,GAAG,KAAK,EAAE,CAAC,GAAI,gBAAgB,CAAC,MAAM,CAAC;IA8B3C;;;;OAIG;IACI,eAAe,IAAK,GAAG,CAAE,aAAa,CAAE;IAO/C;;;;OAIG;IACI,wBAAwB,IAAK,gBAAgB,CAAE,aAAa,CAAE;IAKrE;;;;;;;OAOG;IACI,OAAO,CAAC,GAAG,KAAK,EAAE,aAAa,EAAE,GAAG,gBAAgB,CAAC,UAAU,CAAC;IAOvE;;;;;;;OAOG;IACI,kBAAkB,CAAC,GAAG,KAAK,EAAE,aAAa,EAAE,GAAG,gBAAgB,CAAC,UAAU,CAAC;IAIlF;;;;;;;;OAQG;IACK,IAAI,CAAC,MAAM,EAAE,eAAe,EAAE,UAAU,GAAE,OAAe,GAAG,gBAAgB,CAAC,UAAU,CAAC;IAyBhG;;;;;;;OAOG;IACK,OAAO,CAAC,IAAI,EAAE,QAAQ,CAAC,MAAM,CAAC,GAAG,gBAAgB,CAAC,UAAU,CAAC;IAUrE;;;;;;OAMG;IACK,CAAC,MAAM,CAAC,QAAQ,CAAC,IAAI,gBAAgB,CAAC,UAAU,CAAC;IAWzD;;;;;;;OAOG;IACI,2BAA2B,CAAE,IAAI,EAAE,iBAAiB,GACzD,iBAAiB,GAAG,SAAS;IAK/B;;;;;;;OAOG;IACI,uBAAuB,CAAE,IAAI,EAAE,iBAAiB,GAAI,aAAa,GAAG,SAAS;CAIrF"}
1
+ {"version":3,"file":"step_model_base.d.ts","sourceRoot":"","sources":["../../../src/step/step_model_base.ts"],"names":[],"mappings":"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"}
@@ -12,11 +12,17 @@ export default class StepModelBase {
12
12
  * Construct this step model with its matching schema, a buffer to read from and an element index.
13
13
  *
14
14
  * @param schema The Step schema this is based on.
15
- * @param buffer_ The buffer to read this from.
15
+ * @param buffer_ The buffer to read this from. Pass `undefined` together
16
+ * with `provider` to build a model whose source is windowed from
17
+ * construction (e.g. a streaming open — see buildModelStreaming); the
18
+ * synchronous read paths (geometry extraction) then require the relevant
19
+ * ranges to be resident, exactly as after `spillSourceToExternalStore`.
16
20
  * @param elementIndex The element index for this, parsed or deserialized - note this takes
17
21
  * ownership of this array in the sense it will modify values/unfold inline elements etc.
22
+ * @param provider Optional pre-built buffer provider. When omitted, a
23
+ * resident provider over `buffer_` is used (the classic path).
18
24
  */
19
- constructor(schema, buffer_, elementIndex) {
25
+ constructor(schema, buffer_, elementIndex, provider) {
20
26
  this.schema = schema;
21
27
  this.buffer_ = buffer_;
22
28
  this.vtableBuilder_ = new StepVtableBuilder();
@@ -39,7 +45,7 @@ export default class StepModelBase {
39
45
  * error, and the field is optional, return null instead of throwing an exception.
40
46
  */
41
47
  this.nullOnErrors = true;
42
- this.bufferProvider_ = new ResidentStepBufferProvider(buffer_);
48
+ this.bufferProvider_ = provider ?? new ResidentStepBufferProvider(buffer_);
43
49
  const localElementIndex = elementIndex;
44
50
  let where = 0;
45
51
  const firstInlineElement = localElementIndex.length;
@@ -5,5 +5,5 @@
5
5
  // only the first segment (major) is meaningful and is the one CI carries forward.
6
6
  // Must stay in `vN.N.N` shape: the CI stamp regex, scripts/updateVersion.mjs, and
7
7
  // statistics.ts all match `v\d+\.\d+\.\d+`.
8
- const versionString = 'Conway v1.399.1223';
8
+ const versionString = 'Conway v1.401.1237';
9
9
  export { versionString };