@bldrs-ai/conway 1.409.1251 → 1.413.1255

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 (29) 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 +1 -1
  12. package/compiled/examples/cli-bundled.cjs +28 -28
  13. package/compiled/examples/cli-step-bundled.cjs +28 -28
  14. package/compiled/examples/validator-bundled.cjs +1 -1
  15. package/compiled/src/core/demand_residency_pump.d.ts +93 -0
  16. package/compiled/src/core/demand_residency_pump.d.ts.map +1 -0
  17. package/compiled/src/core/demand_residency_pump.js +120 -0
  18. package/compiled/src/core/demand_residency_pump.test.d.ts +2 -0
  19. package/compiled/src/core/demand_residency_pump.test.d.ts.map +1 -0
  20. package/compiled/src/core/demand_residency_pump.test.js +148 -0
  21. package/compiled/src/core/geometry_tile_bindings.d.ts +110 -0
  22. package/compiled/src/core/geometry_tile_bindings.d.ts.map +1 -0
  23. package/compiled/src/core/geometry_tile_bindings.js +76 -0
  24. package/compiled/src/core/geometry_tile_bindings.test.d.ts +2 -0
  25. package/compiled/src/core/geometry_tile_bindings.test.d.ts.map +1 -0
  26. package/compiled/src/core/geometry_tile_bindings.test.js +211 -0
  27. package/compiled/src/version/version.js +1 -1
  28. package/compiled/tsconfig.tsbuildinfo +1 -1
  29. package/package.json +1 -1
@@ -0,0 +1,211 @@
1
+ /* eslint-disable no-magic-numbers */
2
+ // Phase B: the wasm-backed tile composition — TS accounting over the module's
3
+ // physical pool — and the segment-walk payload reader, verified against a
4
+ // faithful in-memory fake of the embind surface (same layout, same chunking,
5
+ // same non-contiguous segments a fragmented pool produces).
6
+ import { describe, expect, test } from "@jest/globals";
7
+ import { DemandGeometryQueue } from "./demand_geometry_queue.js";
8
+ import { createWasmTileBackend, readGeometryTilePayload, } from "./geometry_tile_bindings.js";
9
+ const HEADER_BYTES = 8;
10
+ /**
11
+ * A faithful in-memory fake of the wasm tile pool bindings: a byte "heap"
12
+ * carved into chunks with a LIFO freelist, tiles committed as (possibly
13
+ * non-contiguous) chunk lists — the same observable behaviour as the C++
14
+ * TilePool through the embind surface.
15
+ *
16
+ * @return {object} `{ bindings, heap, commitPayload }` where commitPayload
17
+ * scatter-commits a payload built from header+floats+indices.
18
+ */
19
+ function fakeWasmPool() {
20
+ const HEAP_BYTES = 64 * 1024;
21
+ const heap = new Uint8Array(HEAP_BYTES);
22
+ let chunkBytes = 0;
23
+ let regionBase = 0;
24
+ let freeList = [];
25
+ const tiles = new Map();
26
+ let failedCommits = 0;
27
+ let totalChunks = 0;
28
+ const bindings = {
29
+ initGeometryTilePool: (budget, chunk) => {
30
+ if (chunk < 16 || budget < chunk) {
31
+ return false;
32
+ }
33
+ chunkBytes = chunk;
34
+ totalChunks = Math.floor(budget / chunk);
35
+ regionBase = 1024; // nonzero base, like a real heap allocation
36
+ freeList = [];
37
+ for (let c = totalChunks - 1; c >= 0; --c) {
38
+ freeList.push(c);
39
+ }
40
+ tiles.clear();
41
+ return true;
42
+ },
43
+ geometryTilePoolInitialized: () => chunkBytes > 0,
44
+ commitGeometryTileBytes: (assetID, sourceAddress, byteLength) => {
45
+ const needed = Math.ceil(byteLength / chunkBytes);
46
+ if (tiles.has(assetID) || needed > freeList.length) {
47
+ ++failedCommits;
48
+ return false;
49
+ }
50
+ const chunks = [];
51
+ let copied = 0;
52
+ for (let where = 0; where < needed; ++where) {
53
+ const chunk = freeList.pop();
54
+ chunks.push(chunk);
55
+ const span = Math.min(byteLength - copied, chunkBytes);
56
+ heap.set(heap.subarray(sourceAddress + copied, sourceAddress + copied + span), regionBase + chunk * chunkBytes);
57
+ copied += span;
58
+ }
59
+ tiles.set(assetID, { chunks, byteSize: byteLength, refCount: 1 });
60
+ return true;
61
+ },
62
+ retainGeometryTile: (assetID) => {
63
+ const tile = tiles.get(assetID);
64
+ if (tile === void 0) {
65
+ return false;
66
+ }
67
+ ++tile.refCount;
68
+ return true;
69
+ },
70
+ releaseGeometryTile: (assetID) => {
71
+ const tile = tiles.get(assetID);
72
+ if (tile === void 0) {
73
+ return false;
74
+ }
75
+ if (--tile.refCount > 0) {
76
+ return false;
77
+ }
78
+ freeList.push(...tile.chunks);
79
+ tiles.delete(assetID);
80
+ return true;
81
+ },
82
+ geometryTileResident: (assetID) => tiles.has(assetID),
83
+ geometryTileRefCount: (assetID) => tiles.get(assetID)?.refCount ?? 0,
84
+ geometryTileByteSize: (assetID) => tiles.get(assetID)?.byteSize ?? 0,
85
+ geometryTileSegmentCount: (assetID) => tiles.get(assetID)?.chunks.length ?? 0,
86
+ geometryTileSegmentAddress: (assetID, segment) => {
87
+ const tile = tiles.get(assetID);
88
+ if (tile === void 0 || segment >= tile.chunks.length) {
89
+ return 0;
90
+ }
91
+ return regionBase + tile.chunks[segment] * chunkBytes;
92
+ },
93
+ geometryTileSegmentByteLength: (assetID, segment) => {
94
+ const tile = tiles.get(assetID);
95
+ if (tile === void 0 || segment >= tile.chunks.length) {
96
+ return 0;
97
+ }
98
+ return Math.min(tile.byteSize - segment * chunkBytes, chunkBytes);
99
+ },
100
+ geometryTileVertexByteLength: (assetID) => {
101
+ const address = bindings.geometryTileSegmentAddress(assetID, 0);
102
+ return address === 0 ?
103
+ 0 : new DataView(heap.buffer).getUint32(address, true);
104
+ },
105
+ geometryTileIndexByteLength: (assetID) => {
106
+ const address = bindings.geometryTileSegmentAddress(assetID, 0);
107
+ return address === 0 ?
108
+ 0 : new DataView(heap.buffer).getUint32(address + 4, true);
109
+ },
110
+ geometryTilePoolBytesInUse: () => (totalChunks - freeList.length) * chunkBytes,
111
+ geometryTilePoolTotalBytes: () => totalChunks * chunkBytes,
112
+ geometryTilePoolFreeChunks: () => freeList.length,
113
+ geometryTilePoolFailedCommits: () => failedCommits,
114
+ };
115
+ // Stage a payload high in the fake heap and scatter-commit it.
116
+ const commitPayload = (assetID, floats, indices) => {
117
+ const staging = HEAP_BYTES - 8192;
118
+ const view = new DataView(heap.buffer, staging);
119
+ view.setUint32(0, floats.length * 4, true);
120
+ view.setUint32(4, indices.length * 4, true);
121
+ floats.forEach((value, i) => view.setFloat32(HEADER_BYTES + i * 4, value, true));
122
+ indices.forEach((value, i) => view.setUint32(HEADER_BYTES + floats.length * 4 + i * 4, value, true));
123
+ return bindings.commitGeometryTileBytes(assetID, staging, HEADER_BYTES + floats.length * 4 + indices.length * 4);
124
+ };
125
+ return { bindings, heap, commitPayload };
126
+ }
127
+ describe("createWasmTileBackend", () => {
128
+ test("composes TS accounting over the wasm pool end to end with the queue", () => {
129
+ const { bindings, commitPayload } = fakeWasmPool();
130
+ // Two products share one representation; extraction commits real bytes.
131
+ const composition = {
132
+ 1: [{ assetID: 100, byteSize: 200 }],
133
+ 2: [{ assetID: 100, byteSize: 200 }, { assetID: 200, byteSize: 100 }],
134
+ };
135
+ const extracted = [];
136
+ const extractor = {
137
+ assetsOf: (id) => composition[id] ?? [],
138
+ extractIntoTile: (assetID) => {
139
+ extracted.push(assetID);
140
+ if (!commitPayload(assetID, [1, 2, 3], [0, 1, 2])) {
141
+ throw new Error("commit failed");
142
+ }
143
+ },
144
+ };
145
+ const backend = createWasmTileBackend(bindings, extractor, 4096, 64);
146
+ const queue = new DemandGeometryQueue(backend.tiles, 4096);
147
+ queue.request(1, 10);
148
+ queue.request(2, 20);
149
+ queue.pump();
150
+ // Shared asset extracted once; both tiles resident wasm-side.
151
+ expect(extracted).toEqual([100, 200]);
152
+ expect(bindings.geometryTileResident(100)).toBe(true);
153
+ expect(bindings.geometryTileResident(200)).toBe(true);
154
+ // Evicting product 2 releases only its unique asset (100 is shared).
155
+ queue.evictAll();
156
+ expect(bindings.geometryTileResident(100)).toBe(false);
157
+ expect(bindings.geometryTileResident(200)).toBe(false);
158
+ expect(bindings.geometryTilePoolBytesInUse()).toBe(0);
159
+ });
160
+ test("rejects a config the wasm pool refuses", () => {
161
+ const { bindings } = fakeWasmPool();
162
+ const extractor = {
163
+ assetsOf: () => [],
164
+ extractIntoTile: () => { },
165
+ };
166
+ expect(() => createWasmTileBackend(bindings, extractor, 4096, 8))
167
+ .toThrow(/rejected init/);
168
+ });
169
+ });
170
+ describe("readGeometryTilePayload", () => {
171
+ test("gathers a multi-segment payload back to exact typed arrays", () => {
172
+ const { bindings, heap, commitPayload } = fakeWasmPool();
173
+ bindings.initGeometryTilePool(4096, 64);
174
+ // 40 floats + 20 indices + header = 248 bytes over 64-byte chunks →
175
+ // 4 non-contiguous segments with a partial tail.
176
+ const floats = Array.from({ length: 40 }, (_v, i) => i * 0.5);
177
+ const indices = Array.from({ length: 20 }, (_v, i) => i * 3);
178
+ expect(commitPayload(7, floats, indices)).toBe(true);
179
+ expect(bindings.geometryTileSegmentCount(7)).toBe(4);
180
+ const payload = readGeometryTilePayload(bindings, heap, 7);
181
+ expect(Array.from(payload.vertexData)).toEqual(floats);
182
+ expect(Array.from(payload.indexData)).toEqual(indices);
183
+ expect(bindings.geometryTileVertexByteLength(7)).toBe(160);
184
+ expect(bindings.geometryTileIndexByteLength(7)).toBe(80);
185
+ });
186
+ test("survives freelist fragmentation (interleaved tiles)", () => {
187
+ const { bindings, heap, commitPayload } = fakeWasmPool();
188
+ bindings.initGeometryTilePool(4096, 64);
189
+ commitPayload(1, [1, 1, 1, 1], [1]);
190
+ commitPayload(2, Array(30).fill(2), [2, 2]);
191
+ bindings.releaseGeometryTile(1);
192
+ // Tile 3's chunks interleave with tile 2's.
193
+ const floats = Array.from({ length: 25 }, (_v, i) => i + 0.25);
194
+ commitPayload(3, floats, [9, 8, 7]);
195
+ const payload = readGeometryTilePayload(bindings, heap, 3);
196
+ expect(Array.from(payload.vertexData)).toEqual(floats);
197
+ expect(Array.from(payload.indexData)).toEqual([9, 8, 7]);
198
+ });
199
+ test("rejects a non-resident tile and a corrupt header", () => {
200
+ const { bindings, heap, commitPayload } = fakeWasmPool();
201
+ bindings.initGeometryTilePool(4096, 64);
202
+ expect(() => readGeometryTilePayload(bindings, heap, 42))
203
+ .toThrow(/not resident/);
204
+ commitPayload(5, [1], [2]);
205
+ // Corrupt the header's vertex byte length in place.
206
+ const address = bindings.geometryTileSegmentAddress(5, 0);
207
+ new DataView(heap.buffer).setUint32(address, 9999, true);
208
+ expect(() => readGeometryTilePayload(bindings, heap, 5))
209
+ .toThrow(/inconsistent/);
210
+ });
211
+ });
@@ -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.409.1251';
8
+ const versionString = 'Conway v1.413.1255';
9
9
  export { versionString };