@smoothbricks/lmao 0.2.2 → 0.2.3

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 (276) hide show
  1. package/dist/allocator.wasm +0 -0
  2. package/dist/index.d.ts +1 -0
  3. package/dist/index.d.ts.map +1 -1
  4. package/dist/index.js +1 -0
  5. package/dist/lib/JsBufferStrategy.d.ts +4 -3
  6. package/dist/lib/JsBufferStrategy.d.ts.map +1 -1
  7. package/dist/lib/JsBufferStrategy.js +18 -11
  8. package/dist/lib/arrow/lease.d.ts +10 -0
  9. package/dist/lib/arrow/lease.d.ts.map +1 -0
  10. package/dist/lib/arrow/lease.js +33 -0
  11. package/dist/lib/arrow/utils.d.ts +3 -4
  12. package/dist/lib/arrow/utils.d.ts.map +1 -1
  13. package/dist/lib/arrow/utils.js +12 -13
  14. package/dist/lib/arrow/vocabularyDictionary.d.ts +21 -0
  15. package/dist/lib/arrow/vocabularyDictionary.d.ts.map +1 -0
  16. package/dist/lib/arrow/vocabularyDictionary.js +95 -0
  17. package/dist/lib/bufferStrategy.d.ts +3 -2
  18. package/dist/lib/bufferStrategy.d.ts.map +1 -1
  19. package/dist/lib/cloudflare/traceRows.d.ts.map +1 -1
  20. package/dist/lib/cloudflare/traceRows.js +3 -2
  21. package/dist/lib/codegen/evaluatorGenerator.d.ts.map +1 -1
  22. package/dist/lib/codegen/evaluatorGenerator.js +12 -11
  23. package/dist/lib/codegen/fixedPositionWriterGenerator.d.ts +44 -82
  24. package/dist/lib/codegen/fixedPositionWriterGenerator.d.ts.map +1 -1
  25. package/dist/lib/codegen/fixedPositionWriterGenerator.js +223 -162
  26. package/dist/lib/codegen/spanLoggerClosureMaterializer.d.ts +63 -0
  27. package/dist/lib/codegen/spanLoggerClosureMaterializer.d.ts.map +1 -0
  28. package/dist/lib/codegen/spanLoggerClosureMaterializer.js +447 -0
  29. package/dist/lib/codegen/spanLoggerGenerator.d.ts +36 -19
  30. package/dist/lib/codegen/spanLoggerGenerator.d.ts.map +1 -1
  31. package/dist/lib/codegen/spanLoggerGenerator.js +164 -158
  32. package/dist/lib/convertToArrow.d.ts +7 -2
  33. package/dist/lib/convertToArrow.d.ts.map +1 -1
  34. package/dist/lib/convertToArrow.js +499 -285
  35. package/dist/lib/defineOpContext.js +1 -1
  36. package/dist/lib/enumMetadata.d.ts +19 -0
  37. package/dist/lib/enumMetadata.d.ts.map +1 -0
  38. package/dist/lib/enumMetadata.js +86 -0
  39. package/dist/lib/flushScheduler.d.ts.map +1 -1
  40. package/dist/lib/flushScheduler.js +13 -4
  41. package/dist/lib/library.d.ts +16 -98
  42. package/dist/lib/library.d.ts.map +1 -1
  43. package/dist/lib/library.js +32 -525
  44. package/dist/lib/logBinding.d.ts +21 -27
  45. package/dist/lib/logBinding.d.ts.map +1 -1
  46. package/dist/lib/op.d.ts +30 -30
  47. package/dist/lib/op.d.ts.map +1 -1
  48. package/dist/lib/op.js +15 -22
  49. package/dist/lib/opContext/createOpGroup.d.ts +1 -1
  50. package/dist/lib/opContext/createOpGroup.d.ts.map +1 -1
  51. package/dist/lib/opContext/createOpGroup.js +22 -29
  52. package/dist/lib/opContext/defineOp.d.ts +4 -4
  53. package/dist/lib/opContext/defineOp.d.ts.map +1 -1
  54. package/dist/lib/opContext/defineOp.js +53 -10
  55. package/dist/lib/opContext/opGroupTypes.d.ts +2 -2
  56. package/dist/lib/opContext/opTypes.d.ts +1 -1
  57. package/dist/lib/opContext/opTypes.d.ts.map +1 -1
  58. package/dist/lib/opContext/spanContextTypes.d.ts +29 -14
  59. package/dist/lib/opContext/spanContextTypes.d.ts.map +1 -1
  60. package/dist/lib/opContext/types.d.ts +4 -4
  61. package/dist/lib/opContext/types.d.ts.map +1 -1
  62. package/dist/lib/physicalLayoutPlan.d.ts +89 -0
  63. package/dist/lib/physicalLayoutPlan.d.ts.map +1 -0
  64. package/dist/lib/physicalLayoutPlan.js +442 -0
  65. package/dist/lib/resolveMessage.d.ts +9 -0
  66. package/dist/lib/resolveMessage.d.ts.map +1 -0
  67. package/dist/lib/resolveMessage.js +63 -0
  68. package/dist/lib/result.d.ts +5 -7
  69. package/dist/lib/result.d.ts.map +1 -1
  70. package/dist/lib/result.js +19 -26
  71. package/dist/lib/runtimeHint.d.ts +35 -0
  72. package/dist/lib/runtimeHint.d.ts.map +1 -0
  73. package/dist/lib/runtimeHint.js +79 -0
  74. package/dist/lib/schema/types.d.ts +1 -1
  75. package/dist/lib/schema/types.d.ts.map +1 -1
  76. package/dist/lib/span-buffer/aot/v1.d.ts +2 -0
  77. package/dist/lib/span-buffer/aot/v1.d.ts.map +1 -0
  78. package/dist/lib/span-buffer/aot/v1.js +24 -0
  79. package/dist/lib/spanBuffer.d.ts +33 -4
  80. package/dist/lib/spanBuffer.d.ts.map +1 -1
  81. package/dist/lib/spanBuffer.js +583 -91
  82. package/dist/lib/spanBufferStats.d.ts +1 -1
  83. package/dist/lib/spanBufferStats.d.ts.map +1 -1
  84. package/dist/lib/spanContext.d.ts +27 -51
  85. package/dist/lib/spanContext.d.ts.map +1 -1
  86. package/dist/lib/spanContext.js +822 -344
  87. package/dist/lib/sqlite/sqlite-common.d.ts.map +1 -1
  88. package/dist/lib/sqlite/sqlite-common.js +20 -21
  89. package/dist/lib/testing/bun-harness.d.ts +8 -3
  90. package/dist/lib/testing/bun-harness.d.ts.map +1 -1
  91. package/dist/lib/testing/bun-harness.js +30 -18
  92. package/dist/lib/testing/extractFacts.d.ts +1 -1
  93. package/dist/lib/testing/extractFacts.d.ts.map +1 -1
  94. package/dist/lib/testing/extractFacts.js +55 -63
  95. package/dist/lib/testing/facts.d.ts +1 -1
  96. package/dist/lib/testing/facts.d.ts.map +1 -1
  97. package/dist/lib/testing/queryable-span.d.ts +1 -1
  98. package/dist/lib/testing/queryable-span.d.ts.map +1 -1
  99. package/dist/lib/testing/queryable-span.js +26 -28
  100. package/dist/lib/testing/span-query.d.ts.map +1 -1
  101. package/dist/lib/testing/span-query.js +19 -22
  102. package/dist/lib/testing/stdio-replay.d.ts.map +1 -1
  103. package/dist/lib/testing/stdio-replay.js +19 -8
  104. package/dist/lib/threadId.d.ts.map +1 -1
  105. package/dist/lib/threadId.js +6 -3
  106. package/dist/lib/traceRoot.d.ts +30 -8
  107. package/dist/lib/traceRoot.d.ts.map +1 -1
  108. package/dist/lib/traceRoot.es.d.ts +17 -15
  109. package/dist/lib/traceRoot.es.d.ts.map +1 -1
  110. package/dist/lib/traceRoot.es.js +68 -34
  111. package/dist/lib/traceRoot.js +11 -0
  112. package/dist/lib/traceRoot.node.d.ts +17 -19
  113. package/dist/lib/traceRoot.node.d.ts.map +1 -1
  114. package/dist/lib/traceRoot.node.js +66 -38
  115. package/dist/lib/traceTopology.d.ts +42 -0
  116. package/dist/lib/traceTopology.d.ts.map +1 -0
  117. package/dist/lib/traceTopology.js +260 -0
  118. package/dist/lib/tracer.d.ts +9 -5
  119. package/dist/lib/tracer.d.ts.map +1 -1
  120. package/dist/lib/tracer.js +65 -71
  121. package/dist/lib/tracers/ArrayQueueTracer.d.ts +1 -1
  122. package/dist/lib/tracers/ArrayQueueTracer.js +1 -1
  123. package/dist/lib/tracers/StdioTracer.d.ts.map +1 -1
  124. package/dist/lib/tracers/StdioTracer.js +15 -13
  125. package/dist/lib/tracers/TestTracer.d.ts +4 -6
  126. package/dist/lib/tracers/TestTracer.d.ts.map +1 -1
  127. package/dist/lib/tracers/TestTracer.js +6 -8
  128. package/dist/lib/types.d.ts +49 -32
  129. package/dist/lib/types.d.ts.map +1 -1
  130. package/dist/lib/vocabularyRegistry.d.ts +25 -0
  131. package/dist/lib/vocabularyRegistry.d.ts.map +1 -0
  132. package/dist/lib/vocabularyRegistry.js +372 -0
  133. package/dist/lib/wasm/WasmBufferStrategy.d.ts +6 -10
  134. package/dist/lib/wasm/WasmBufferStrategy.d.ts.map +1 -1
  135. package/dist/lib/wasm/WasmBufferStrategy.js +44 -37
  136. package/dist/lib/wasm/wasmAllocator.d.ts +27 -22
  137. package/dist/lib/wasm/wasmAllocator.d.ts.map +1 -1
  138. package/dist/lib/wasm/wasmAllocator.js +66 -44
  139. package/dist/lib/wasm/wasmPhysicalLayout.d.ts +67 -0
  140. package/dist/lib/wasm/wasmPhysicalLayout.d.ts.map +1 -0
  141. package/dist/lib/wasm/wasmPhysicalLayout.js +179 -0
  142. package/dist/lib/wasm/wasmSpanBuffer.d.ts +79 -31
  143. package/dist/lib/wasm/wasmSpanBuffer.d.ts.map +1 -1
  144. package/dist/lib/wasm/wasmSpanBuffer.js +442 -272
  145. package/dist/lib/wasm/wasmTraceRoot.d.ts +24 -22
  146. package/dist/lib/wasm/wasmTraceRoot.d.ts.map +1 -1
  147. package/dist/lib/wasm/wasmTraceRoot.js +110 -71
  148. package/dist/vocabulary/register/v1.d.ts +2 -0
  149. package/dist/vocabulary/register/v1.d.ts.map +1 -0
  150. package/dist/vocabulary/register/v1.js +1 -0
  151. package/dist/wasm.d.ts +5 -0
  152. package/dist/wasm.d.ts.map +1 -0
  153. package/dist/wasm.js +4 -0
  154. package/package.json +54 -6
  155. package/src/index.ts +1 -0
  156. package/src/lib/JsBufferStrategy.ts +27 -9
  157. package/src/lib/__tests__/arrow-builder-integration/arrow-conversion.test.ts +335 -37
  158. package/src/lib/__tests__/arrow-builder-integration/binary-arrow-conversion.test.ts +97 -26
  159. package/src/lib/__tests__/arrow-builder-integration/binary-comparison.test.ts +75 -15
  160. package/src/lib/__tests__/arrow-builder-integration/binary-format-compliance.test.ts +30 -6
  161. package/src/lib/__tests__/arrow-builder-integration/buffer-chaining.test.ts +18 -21
  162. package/src/lib/__tests__/arrow-builder-integration/foundation.test.ts +5 -2
  163. package/src/lib/__tests__/arrow-builder-integration/integration.test.ts +25 -7
  164. package/src/lib/__tests__/arrow-builder-integration/lazy-columns.test.ts +3 -2
  165. package/src/lib/__tests__/buffer-overflow.test.ts +24 -18
  166. package/src/lib/__tests__/capability-span-context.test.ts +505 -0
  167. package/src/lib/__tests__/capacity-tuning.test.ts +5 -3
  168. package/src/lib/__tests__/eager-columns.test.ts +345 -0
  169. package/src/lib/__tests__/enum-metadata-aot.test.ts +153 -0
  170. package/src/lib/__tests__/enum-metadata.test.ts +177 -0
  171. package/src/lib/__tests__/fixtures/empty-vocabulary-runtime.fixture.ts +15 -0
  172. package/src/lib/__tests__/flushScheduler.test.ts +5 -1
  173. package/src/lib/__tests__/identity-view.property.test.ts +179 -0
  174. package/src/lib/__tests__/library.test.ts +19 -235
  175. package/src/lib/__tests__/message-buffer-families.test.ts +878 -0
  176. package/src/lib/__tests__/nested-library-tasks.test.ts +14 -15
  177. package/src/lib/__tests__/op-metadata.test.ts +1 -1
  178. package/src/lib/__tests__/op-type-safety.test.ts +5 -16
  179. package/src/lib/__tests__/physical-layout-plan.test.ts +333 -0
  180. package/src/lib/__tests__/remap-descriptor-integration.test.ts +381 -0
  181. package/src/lib/__tests__/root-context-construction.test.ts +414 -0
  182. package/src/lib/__tests__/span-auto-runtime.test.ts +386 -0
  183. package/src/lib/__tests__/span-buffer-segment-stats.test.ts +221 -0
  184. package/src/lib/__tests__/span-lifecycle.test.ts +53 -25
  185. package/src/lib/__tests__/spanbuffer-materializer-parity.test.ts +266 -0
  186. package/src/lib/__tests__/sync-async-trace.test.ts +24 -23
  187. package/src/lib/__tests__/tag-writer-aot.test.ts +214 -0
  188. package/src/lib/__tests__/test-helpers.ts +67 -12
  189. package/src/lib/__tests__/threadId.test.ts +62 -0
  190. package/src/lib/__tests__/timestamp.test.ts +221 -93
  191. package/src/lib/__tests__/traceTopology.property.test.ts +383 -0
  192. package/src/lib/__tests__/type-narrowing.test.ts +63 -0
  193. package/src/lib/__tests__/vocabulary-dense-decoding.test.ts +405 -0
  194. package/src/lib/__tests__/vocabularyRegistry.test.ts +313 -0
  195. package/src/lib/arrow/lease.ts +43 -0
  196. package/src/lib/arrow/utils.ts +10 -14
  197. package/src/lib/arrow/vocabularyDictionary.ts +123 -0
  198. package/src/lib/bufferStrategy.ts +9 -1
  199. package/src/lib/cloudflare/traceRows.ts +3 -2
  200. package/src/lib/codegen/__tests__/evaluatorGenerator.test.ts +21 -141
  201. package/src/lib/codegen/__tests__/fixedPositionWriter-parity.test.ts +273 -0
  202. package/src/lib/codegen/__tests__/fixedPositionWriter-uint64.test.ts +61 -34
  203. package/src/lib/codegen/__tests__/nullBitmap.test.ts +33 -37
  204. package/src/lib/codegen/__tests__/spanLoggerGenerator.test.ts +34 -18
  205. package/src/lib/codegen/__tests__/spanLoggerMaterializerParity.test.ts +503 -0
  206. package/src/lib/codegen/evaluatorGenerator.ts +18 -13
  207. package/src/lib/codegen/fixedPositionWriterGenerator.ts +329 -245
  208. package/src/lib/codegen/spanLoggerClosureMaterializer.ts +598 -0
  209. package/src/lib/codegen/spanLoggerGenerator.ts +241 -177
  210. package/src/lib/convertToArrow.ts +598 -311
  211. package/src/lib/defineOpContext.ts +1 -1
  212. package/src/lib/enumMetadata.ts +121 -0
  213. package/src/lib/flushScheduler.ts +11 -5
  214. package/src/lib/library.ts +37 -595
  215. package/src/lib/logBinding.ts +21 -27
  216. package/src/lib/op.ts +75 -18
  217. package/src/lib/opContext/__tests__/retry.test.ts +27 -21
  218. package/src/lib/opContext/createOpGroup.ts +33 -31
  219. package/src/lib/opContext/defineOp.ts +88 -11
  220. package/src/lib/opContext/opGroupTypes.ts +2 -2
  221. package/src/lib/opContext/opTypes.ts +1 -1
  222. package/src/lib/opContext/spanContextTypes.ts +98 -17
  223. package/src/lib/opContext/types.ts +7 -3
  224. package/src/lib/physicalLayoutPlan.ts +598 -0
  225. package/src/lib/resolveMessage.ts +67 -0
  226. package/src/lib/result.ts +20 -33
  227. package/src/lib/runtimeHint.ts +98 -0
  228. package/src/lib/schema/__tests__/featureFlags.test.ts +2 -1
  229. package/src/lib/schema/builder.ts +1 -1
  230. package/src/lib/schema/types.ts +1 -1
  231. package/src/lib/span-buffer/aot/v1.ts +26 -0
  232. package/src/lib/spanBuffer.ts +658 -92
  233. package/src/lib/spanBufferStats.ts +1 -1
  234. package/src/lib/spanContext.ts +1350 -861
  235. package/src/lib/sqlite/sqlite-common.ts +20 -31
  236. package/src/lib/testing/__tests__/topology-traversal.test.ts +174 -0
  237. package/src/lib/testing/__tests__/vitest-harness.test.ts +2 -1
  238. package/src/lib/testing/bun-harness.ts +94 -43
  239. package/src/lib/testing/extractFacts.ts +64 -78
  240. package/src/lib/testing/facts.ts +1 -1
  241. package/src/lib/testing/queryable-span.ts +23 -30
  242. package/src/lib/testing/span-query.ts +16 -20
  243. package/src/lib/testing/stdio-replay.ts +25 -8
  244. package/src/lib/testing/vitest-harness.ts +1 -1
  245. package/src/lib/threadId.ts +6 -3
  246. package/src/lib/traceRoot.es.ts +77 -42
  247. package/src/lib/traceRoot.node.ts +75 -46
  248. package/src/lib/traceRoot.ts +41 -14
  249. package/src/lib/traceTopology.ts +279 -0
  250. package/src/lib/tracer.ts +109 -98
  251. package/src/lib/tracers/ArrayQueueTracer.ts +1 -1
  252. package/src/lib/tracers/StdioTracer.ts +15 -13
  253. package/src/lib/tracers/TestTracer.ts +6 -8
  254. package/src/lib/tracers/__tests__/ArrayQueueTracer.test.ts +15 -10
  255. package/src/lib/tracers/__tests__/TestTracer.test.ts +23 -13
  256. package/src/lib/types.ts +64 -33
  257. package/src/lib/vocabularyRegistry.ts +439 -0
  258. package/src/lib/wasm/WasmBufferStrategy.ts +56 -48
  259. package/src/lib/wasm/__tests__/WasmBufferStrategy.test.ts +215 -11
  260. package/src/lib/wasm/__tests__/allocatorFreeBlockClamp.test.ts +86 -0
  261. package/src/lib/wasm/__tests__/wasm-identity-view.test.ts +311 -0
  262. package/src/lib/wasm/__tests__/wasm-integration.test.ts +417 -44
  263. package/src/lib/wasm/__tests__/wasm-message-buffer-families.test.ts +588 -0
  264. package/src/lib/wasm/__tests__/wasmAllocator.test.ts +100 -668
  265. package/src/lib/wasm/__tests__/wasmSpanBuffer.test.ts +419 -63
  266. package/src/lib/wasm/__tests__/wasmTraceRoot.test.ts +24 -29
  267. package/src/lib/wasm/__tests__/wasmTracer.test.ts.disabled +2 -2
  268. package/src/lib/wasm/wasmAllocator.ts +131 -96
  269. package/src/lib/wasm/wasmPhysicalLayout.ts +309 -0
  270. package/src/lib/wasm/wasmSpanBuffer.ts +597 -341
  271. package/src/lib/wasm/wasmTraceRoot.ts +140 -78
  272. package/src/vocabulary/register/v1.ts +1 -0
  273. package/src/wasm.ts +19 -0
  274. package/src/lib/__tests__/remapped-buffer-view-integration.test.ts +0 -319
  275. package/src/lib/__tests__/remapped-buffer-view.test.ts +0 -635
  276. package/src/lib/wasm/allocator.zig +0 -948
@@ -0,0 +1,439 @@
1
+ export const REGISTER_VOCABULARY = Symbol.for('@smoothbricks/lmao/vocabulary/register/v1');
2
+
3
+ const VOCABULARY_STATE = Symbol.for('@smoothbricks/lmao/vocabulary/state/v1');
4
+ const SCHEMA_VERSION = 1;
5
+ const ID_ALGORITHM = 'sha256-24-v1';
6
+ const MAX_VOCABULARY_ID = 0x00ff_ffff;
7
+ const CONTENT_HASH_PATTERN = /^[0-9a-f]{64}$/;
8
+ const SHA256_INITIAL = new Uint32Array([
9
+ 0x6a09e667, 0xbb67ae85, 0x3c6ef372, 0xa54ff53a, 0x510e527f, 0x9b05688c, 0x1f83d9ab, 0x5be0cd19,
10
+ ]);
11
+ const SHA256_ROUND = new Uint32Array([
12
+ 0x428a2f98, 0x71374491, 0xb5c0fbcf, 0xe9b5dba5, 0x3956c25b, 0x59f111f1, 0x923f82a4, 0xab1c5ed5, 0xd807aa98,
13
+ 0x12835b01, 0x243185be, 0x550c7dc3, 0x72be5d74, 0x80deb1fe, 0x9bdc06a7, 0xc19bf174, 0xe49b69c1, 0xefbe4786,
14
+ 0x0fc19dc6, 0x240ca1cc, 0x2de92c6f, 0x4a7484aa, 0x5cb0a9dc, 0x76f988da, 0x983e5152, 0xa831c66d, 0xb00327c8,
15
+ 0xbf597fc7, 0xc6e00bf3, 0xd5a79147, 0x06ca6351, 0x14292967, 0x27b70a85, 0x2e1b2138, 0x4d2c6dfc, 0x53380d13,
16
+ 0x650a7354, 0x766a0abb, 0x81c2c92e, 0x92722c85, 0xa2bfe8a1, 0xa81a664b, 0xc24b8b70, 0xc76c51a3, 0xd192e819,
17
+ 0xd6990624, 0xf40e3585, 0x106aa070, 0x19a4c116, 0x1e376c08, 0x2748774c, 0x34b0bcb5, 0x391c0cb3, 0x4ed8aa4a,
18
+ 0x5b9cca4f, 0x682e6ff3, 0x748f82ee, 0x78a5636f, 0x84c87814, 0x8cc70208, 0x90befffa, 0xa4506ceb, 0xbef9a3f7,
19
+ 0xc67178f2,
20
+ ]);
21
+
22
+ export interface VocabularyFragment {
23
+ readonly schemaVersion: 1;
24
+ readonly idAlgorithm: 'sha256-24-v1';
25
+ readonly contentHash: string;
26
+ readonly ids: Uint32Array;
27
+ readonly kindTags: Uint8Array;
28
+ readonly utf8: Uint8Array;
29
+ readonly offsets: Int32Array;
30
+ }
31
+
32
+ export type VocabularyBinding = Uint32Array;
33
+
34
+ export interface VocabularyGeneration {
35
+ readonly generation: number;
36
+ readonly ids: Uint32Array;
37
+ readonly kindTags: Uint8Array;
38
+ readonly records: Uint8Array;
39
+ readonly offsets: Int32Array;
40
+ }
41
+
42
+ type RegisteredFragment = {
43
+ readonly ids: Uint32Array;
44
+ readonly kindTags: Uint8Array;
45
+ readonly utf8: Uint8Array;
46
+ readonly offsets: Int32Array;
47
+ readonly binding: VocabularyBinding;
48
+ };
49
+
50
+ type RegisteredEntry = {
51
+ readonly kindTag: number;
52
+ readonly record: Uint8Array;
53
+ readonly denseIndex: number;
54
+ };
55
+
56
+ type VocabularyState = {
57
+ readonly entriesById: Map<number, RegisteredEntry>;
58
+ readonly fragmentsByHash: Map<string, RegisteredFragment>;
59
+ readonly generations: VocabularyGeneration[];
60
+ current: VocabularyGeneration;
61
+ };
62
+
63
+ export class VocabularyRegistrationError extends Error {
64
+ override readonly name = 'VocabularyRegistrationError';
65
+ }
66
+
67
+ const decoder = new TextDecoder('utf-8', { fatal: true });
68
+ const algorithmBytes = new TextEncoder().encode(ID_ALGORITHM);
69
+
70
+ function fail(code: string, detail: string): never {
71
+ throw new VocabularyRegistrationError(`${code}: ${detail}`);
72
+ }
73
+
74
+ function rotateRight(value: number, count: number): number {
75
+ return (value >>> count) | (value << (32 - count));
76
+ }
77
+
78
+ function sha256(input: Uint8Array): Uint8Array {
79
+ const bitLength = input.length * 8;
80
+ const paddedLength = (input.length + 9 + 63) & ~63;
81
+ const padded = new Uint8Array(paddedLength);
82
+ padded.set(input);
83
+ padded[input.length] = 0x80;
84
+ const view = new DataView(padded.buffer);
85
+ view.setUint32(paddedLength - 8, Math.floor(bitLength / 0x1_0000_0000), false);
86
+ view.setUint32(paddedLength - 4, bitLength >>> 0, false);
87
+
88
+ const hash = SHA256_INITIAL.slice();
89
+ const words = new Uint32Array(64);
90
+ for (let block = 0; block < paddedLength; block += 64) {
91
+ for (let index = 0; index < 16; index++) words[index] = view.getUint32(block + index * 4, false);
92
+ for (let index = 16; index < 64; index++) {
93
+ const x = words[index - 15];
94
+ const y = words[index - 2];
95
+ const sigma0 = rotateRight(x, 7) ^ rotateRight(x, 18) ^ (x >>> 3);
96
+ const sigma1 = rotateRight(y, 17) ^ rotateRight(y, 19) ^ (y >>> 10);
97
+ words[index] = (words[index - 16] + sigma0 + words[index - 7] + sigma1) >>> 0;
98
+ }
99
+ let a = hash[0];
100
+ let b = hash[1];
101
+ let c = hash[2];
102
+ let d = hash[3];
103
+ let e = hash[4];
104
+ let f = hash[5];
105
+ let g = hash[6];
106
+ let h = hash[7];
107
+ for (let index = 0; index < 64; index++) {
108
+ const sum1 = rotateRight(e, 6) ^ rotateRight(e, 11) ^ rotateRight(e, 25);
109
+ const choose = (e & f) ^ (~e & g);
110
+ const temp1 = (h + sum1 + choose + SHA256_ROUND[index] + words[index]) >>> 0;
111
+ const sum0 = rotateRight(a, 2) ^ rotateRight(a, 13) ^ rotateRight(a, 22);
112
+ const majority = (a & b) ^ (a & c) ^ (b & c);
113
+ const temp2 = (sum0 + majority) >>> 0;
114
+ h = g;
115
+ g = f;
116
+ f = e;
117
+ e = (d + temp1) >>> 0;
118
+ d = c;
119
+ c = b;
120
+ b = a;
121
+ a = (temp1 + temp2) >>> 0;
122
+ }
123
+ hash[0] = (hash[0] + a) >>> 0;
124
+ hash[1] = (hash[1] + b) >>> 0;
125
+ hash[2] = (hash[2] + c) >>> 0;
126
+ hash[3] = (hash[3] + d) >>> 0;
127
+ hash[4] = (hash[4] + e) >>> 0;
128
+ hash[5] = (hash[5] + f) >>> 0;
129
+ hash[6] = (hash[6] + g) >>> 0;
130
+ hash[7] = (hash[7] + h) >>> 0;
131
+ }
132
+ const result = new Uint8Array(32);
133
+ const resultView = new DataView(result.buffer);
134
+ for (let index = 0; index < hash.length; index++) resultView.setUint32(index * 4, hash[index], false);
135
+ return result;
136
+ }
137
+
138
+ function hex(bytes: Uint8Array): string {
139
+ let result = '';
140
+ for (const byte of bytes) result += byte.toString(16).padStart(2, '0');
141
+ return result;
142
+ }
143
+
144
+ function equalBytes(
145
+ left: Uint8Array | Uint32Array | Int32Array,
146
+ right: Uint8Array | Uint32Array | Int32Array,
147
+ ): boolean {
148
+ if (left.constructor !== right.constructor || left.length !== right.length) return false;
149
+ for (let index = 0; index < left.length; index++) if (left[index] !== right[index]) return false;
150
+ return true;
151
+ }
152
+
153
+ function samePayload(registered: RegisteredFragment, fragment: VocabularyFragment): boolean {
154
+ return (
155
+ equalBytes(registered.ids, fragment.ids) &&
156
+ equalBytes(registered.kindTags, fragment.kindTags) &&
157
+ equalBytes(registered.utf8, fragment.utf8) &&
158
+ equalBytes(registered.offsets, fragment.offsets)
159
+ );
160
+ }
161
+
162
+ function recordAt(fragment: VocabularyFragment, ordinal: number): Uint8Array {
163
+ return fragment.utf8.subarray(fragment.offsets[ordinal], fragment.offsets[ordinal + 1]);
164
+ }
165
+
166
+ function validateRecord(record: Uint8Array, ordinal: number): void {
167
+ const view = new DataView(record.buffer, record.byteOffset, record.byteLength);
168
+ if (record.length < 6) fail('LMAO_VOCABULARY_FRAGMENT_INVALID', `record ${ordinal} is truncated`);
169
+ let offset = 0;
170
+ const textLength = view.getUint32(offset, true);
171
+ offset += 4;
172
+ if (textLength > record.length - offset - 2)
173
+ fail('LMAO_VOCABULARY_FRAGMENT_INVALID', `record ${ordinal} text is truncated`);
174
+ try {
175
+ decoder.decode(record.subarray(offset, offset + textLength));
176
+ } catch {
177
+ fail('LMAO_VOCABULARY_FRAGMENT_INVALID', `record ${ordinal} text is not valid UTF-8`);
178
+ }
179
+ offset += textLength;
180
+ const fieldCount = view.getUint16(offset, true);
181
+ offset += 2;
182
+ for (let field = 0; field < fieldCount; field++) {
183
+ if (offset + 2 > record.length)
184
+ fail('LMAO_VOCABULARY_FRAGMENT_INVALID', `record ${ordinal} field ${field} name length is truncated`);
185
+ const nameLength = view.getUint16(offset, true);
186
+ offset += 2;
187
+ if (offset + nameLength + 2 > record.length)
188
+ fail('LMAO_VOCABULARY_FRAGMENT_INVALID', `record ${ordinal} field ${field} name is truncated`);
189
+ try {
190
+ decoder.decode(record.subarray(offset, offset + nameLength));
191
+ } catch {
192
+ fail('LMAO_VOCABULARY_FRAGMENT_INVALID', `record ${ordinal} field ${field} name is not valid UTF-8`);
193
+ }
194
+ offset += nameLength;
195
+ const columnLength = view.getUint16(offset, true);
196
+ offset += 2;
197
+ if (offset + columnLength > record.length)
198
+ fail('LMAO_VOCABULARY_FRAGMENT_INVALID', `record ${ordinal} field ${field} column is truncated`);
199
+ try {
200
+ decoder.decode(record.subarray(offset, offset + columnLength));
201
+ } catch {
202
+ fail('LMAO_VOCABULARY_FRAGMENT_INVALID', `record ${ordinal} field ${field} column is not valid UTF-8`);
203
+ }
204
+ offset += columnLength;
205
+ }
206
+ if (offset !== record.length) fail('LMAO_VOCABULARY_FRAGMENT_INVALID', `record ${ordinal} has trailing bytes`);
207
+ }
208
+
209
+ function contentHash(fragment: VocabularyFragment): string {
210
+ const size =
211
+ 1 +
212
+ 2 +
213
+ algorithmBytes.length +
214
+ 4 +
215
+ fragment.ids.length * 4 +
216
+ 4 +
217
+ fragment.kindTags.length +
218
+ 4 +
219
+ fragment.utf8.length +
220
+ 4 +
221
+ fragment.offsets.length * 4;
222
+ const bytes = new Uint8Array(size);
223
+ const view = new DataView(bytes.buffer);
224
+ let offset = 0;
225
+ bytes[offset++] = fragment.schemaVersion;
226
+ view.setUint16(offset, algorithmBytes.length, true);
227
+ offset += 2;
228
+ bytes.set(algorithmBytes, offset);
229
+ offset += algorithmBytes.length;
230
+ view.setUint32(offset, fragment.ids.length, true);
231
+ offset += 4;
232
+ for (const id of fragment.ids) {
233
+ view.setUint32(offset, id, true);
234
+ offset += 4;
235
+ }
236
+ view.setUint32(offset, fragment.kindTags.length, true);
237
+ offset += 4;
238
+ bytes.set(fragment.kindTags, offset);
239
+ offset += fragment.kindTags.length;
240
+ view.setUint32(offset, fragment.utf8.length, true);
241
+ offset += 4;
242
+ bytes.set(fragment.utf8, offset);
243
+ offset += fragment.utf8.length;
244
+ view.setUint32(offset, fragment.offsets.length, true);
245
+ offset += 4;
246
+ for (const boundary of fragment.offsets) {
247
+ view.setInt32(offset, boundary, true);
248
+ offset += 4;
249
+ }
250
+ return hex(sha256(bytes));
251
+ }
252
+
253
+ function createState(): VocabularyState {
254
+ const initial: VocabularyGeneration = Object.freeze({
255
+ generation: 0,
256
+ ids: new Uint32Array(0),
257
+ kindTags: new Uint8Array(0),
258
+ records: new Uint8Array(0),
259
+ offsets: new Int32Array([0]),
260
+ });
261
+ return { entriesById: new Map(), fragmentsByHash: new Map(), generations: [initial], current: initial };
262
+ }
263
+
264
+ function globalState(): VocabularyState {
265
+ const globals = globalThis as typeof globalThis & { [VOCABULARY_STATE]?: VocabularyState };
266
+ let state = globals[VOCABULARY_STATE];
267
+ if (state === undefined) {
268
+ state = createState();
269
+ Object.defineProperty(globals, VOCABULARY_STATE, {
270
+ value: state,
271
+ configurable: false,
272
+ enumerable: false,
273
+ writable: false,
274
+ });
275
+ }
276
+ return state;
277
+ }
278
+
279
+ function validateFragmentStructure(fragment: VocabularyFragment): void {
280
+ if (fragment.schemaVersion !== SCHEMA_VERSION || fragment.idAlgorithm !== ID_ALGORITHM)
281
+ fail('LMAO_VOCABULARY_ABI_UNAVAILABLE', 'unsupported schema version or ID algorithm');
282
+ if (!CONTENT_HASH_PATTERN.test(fragment.contentHash))
283
+ fail('LMAO_VOCABULARY_FRAGMENT_INVALID', `invalid contentHash ${JSON.stringify(fragment.contentHash)}`);
284
+ if (
285
+ !(fragment.ids instanceof Uint32Array) ||
286
+ !(fragment.kindTags instanceof Uint8Array) ||
287
+ !(fragment.utf8 instanceof Uint8Array) ||
288
+ !(fragment.offsets instanceof Int32Array)
289
+ )
290
+ fail('LMAO_VOCABULARY_FRAGMENT_INVALID', 'fragment arrays use incorrect types');
291
+ if (fragment.kindTags.length !== fragment.ids.length || fragment.offsets.length !== fragment.ids.length + 1)
292
+ fail('LMAO_VOCABULARY_FRAGMENT_INVALID', 'parallel lengths differ');
293
+ if (fragment.offsets[0] !== 0 || fragment.offsets[fragment.offsets.length - 1] !== fragment.utf8.length)
294
+ fail('LMAO_VOCABULARY_FRAGMENT_INVALID', 'offsets do not cover bytes');
295
+ const localOrdinals = new Map<number, number>();
296
+ for (let ordinal = 0; ordinal < fragment.ids.length; ordinal++) {
297
+ const id = fragment.ids[ordinal];
298
+ const kindTag = fragment.kindTags[ordinal];
299
+ if (id === 0 || id > MAX_VOCABULARY_ID) fail('LMAO_VOCABULARY_FRAGMENT_INVALID', `stable ID ${id}`);
300
+ if (kindTag !== 1 && kindTag !== 2) fail('LMAO_VOCABULARY_FRAGMENT_INVALID', `kind tag ${kindTag}`);
301
+ if (fragment.offsets[ordinal] > fragment.offsets[ordinal + 1])
302
+ fail('LMAO_VOCABULARY_FRAGMENT_INVALID', 'offsets are not monotonic');
303
+ const record = recordAt(fragment, ordinal);
304
+ validateRecord(record, ordinal);
305
+ const idInput = new Uint8Array(record.length + 1);
306
+ idInput[0] = kindTag;
307
+ idInput.set(record, 1);
308
+ const digest = sha256(idInput);
309
+ const derivedId = (digest[0] << 16) | (digest[1] << 8) | digest[2];
310
+ if (id !== derivedId || id === 0)
311
+ fail('LMAO_VOCABULARY_FRAGMENT_INVALID', `stable ID ${id} does not match record ${ordinal}`);
312
+ const previousOrdinal = localOrdinals.get(id);
313
+ if (
314
+ previousOrdinal !== undefined &&
315
+ (fragment.kindTags[previousOrdinal] !== kindTag || !equalBytes(recordAt(fragment, previousOrdinal), record))
316
+ ) {
317
+ fail('LMAO_VOCABULARY_ID_COLLISION', `stable ID ${id} within fragment`);
318
+ }
319
+ localOrdinals.set(id, ordinal);
320
+ }
321
+ }
322
+
323
+ function appendGeneration(
324
+ state: VocabularyState,
325
+ ids: readonly number[],
326
+ kindTags: readonly number[],
327
+ records: readonly Uint8Array[],
328
+ ): void {
329
+ if (ids.length === 0) return;
330
+ const previous = state.current;
331
+ let byteLength = previous.records.length;
332
+ for (const record of records) byteLength += record.length;
333
+ const generationIds = new Uint32Array(previous.ids.length + ids.length);
334
+ generationIds.set(previous.ids);
335
+ generationIds.set(ids, previous.ids.length);
336
+ const generationKinds = new Uint8Array(previous.kindTags.length + kindTags.length);
337
+ generationKinds.set(previous.kindTags);
338
+ generationKinds.set(kindTags, previous.kindTags.length);
339
+ const generationRecords = new Uint8Array(byteLength);
340
+ generationRecords.set(previous.records);
341
+ const generationOffsets = new Int32Array(generationIds.length + 1);
342
+ generationOffsets.set(previous.offsets);
343
+ let byteOffset = previous.records.length;
344
+ for (let index = 0; index < records.length; index++) {
345
+ generationRecords.set(records[index], byteOffset);
346
+ byteOffset += records[index].length;
347
+ generationOffsets[previous.ids.length + index + 1] = byteOffset;
348
+ }
349
+ const generation: VocabularyGeneration = Object.freeze({
350
+ generation: previous.generation + 1,
351
+ ids: generationIds,
352
+ kindTags: generationKinds,
353
+ records: generationRecords,
354
+ offsets: generationOffsets,
355
+ });
356
+ state.generations.push(generation);
357
+ state.current = generation;
358
+ }
359
+
360
+ function registerVocabulary(fragment: VocabularyFragment): VocabularyBinding {
361
+ validateFragmentStructure(fragment);
362
+ const state = globalState();
363
+ const sameHash = state.fragmentsByHash.get(fragment.contentHash);
364
+ if (sameHash !== undefined) {
365
+ if (!samePayload(sameHash, fragment)) fail('LMAO_VOCABULARY_CONTENT_HASH_COLLISION', fragment.contentHash);
366
+ return sameHash.binding;
367
+ }
368
+ if (contentHash(fragment) !== fragment.contentHash) {
369
+ fail('LMAO_VOCABULARY_CONTENT_HASH_MISMATCH', fragment.contentHash);
370
+ }
371
+ for (let ordinal = 0; ordinal < fragment.ids.length; ordinal++) {
372
+ const existing = state.entriesById.get(fragment.ids[ordinal]);
373
+ if (
374
+ existing !== undefined &&
375
+ (existing.kindTag !== fragment.kindTags[ordinal] || !equalBytes(existing.record, recordAt(fragment, ordinal)))
376
+ )
377
+ fail('LMAO_VOCABULARY_ID_COLLISION', `stable ID ${fragment.ids[ordinal]}`);
378
+ }
379
+
380
+ const binding = new Uint32Array(fragment.ids.length);
381
+ const appendedIds: number[] = [];
382
+ const appendedKinds: number[] = [];
383
+ const appendedRecords: Uint8Array[] = [];
384
+ for (let ordinal = 0; ordinal < fragment.ids.length; ordinal++) {
385
+ const id = fragment.ids[ordinal];
386
+ let existing = state.entriesById.get(id);
387
+ if (existing === undefined) {
388
+ const record = recordAt(fragment, ordinal).slice();
389
+ existing = {
390
+ kindTag: fragment.kindTags[ordinal],
391
+ record,
392
+ denseIndex: state.current.ids.length + appendedIds.length,
393
+ };
394
+ state.entriesById.set(id, existing);
395
+ appendedIds.push(id);
396
+ appendedKinds.push(existing.kindTag);
397
+ appendedRecords.push(record);
398
+ }
399
+ binding[ordinal] = existing.denseIndex;
400
+ }
401
+ appendGeneration(state, appendedIds, appendedKinds, appendedRecords);
402
+ state.fragmentsByHash.set(fragment.contentHash, {
403
+ ids: fragment.ids.slice(),
404
+ kindTags: fragment.kindTags.slice(),
405
+ utf8: fragment.utf8.slice(),
406
+ offsets: fragment.offsets.slice(),
407
+ binding,
408
+ });
409
+ return binding;
410
+ }
411
+
412
+ export type RegisterVocabulary = (fragment: VocabularyFragment) => VocabularyBinding;
413
+
414
+ function isRegisterVocabulary(value: unknown): value is RegisterVocabulary {
415
+ return typeof value === 'function';
416
+ }
417
+
418
+ function installVocabularyRegistration(): RegisterVocabulary {
419
+ const globals = globalThis as typeof globalThis & { [REGISTER_VOCABULARY]?: unknown };
420
+ const installed = globals[REGISTER_VOCABULARY];
421
+ if (installed !== undefined) {
422
+ if (!isRegisterVocabulary(installed))
423
+ fail('LMAO_VOCABULARY_ABI_UNAVAILABLE', 'global symbol contains a non-function');
424
+ return installed;
425
+ }
426
+ Object.defineProperty(globals, REGISTER_VOCABULARY, {
427
+ value: registerVocabulary,
428
+ configurable: false,
429
+ enumerable: false,
430
+ writable: false,
431
+ });
432
+ return registerVocabulary;
433
+ }
434
+
435
+ export const registerVocabularyFragment = installVocabularyRegistration();
436
+
437
+ export function getVocabularyGeneration(): VocabularyGeneration {
438
+ return globalState().current;
439
+ }
@@ -18,8 +18,9 @@ import type { BufferStrategy } from '../bufferStrategy.js';
18
18
  import { convertSpanTreeToArrowTable } from '../convertToArrow.js';
19
19
  import type { OpMetadata } from '../opContext/opTypes.js';
20
20
  import type { LogSchema } from '../schema/LogSchema.js';
21
- import { EMPTY_SCOPE } from '../spanBuffer.js';
21
+ import { EMPTY_SCOPE, type SpanBufferConstructor } from '../spanBuffer.js';
22
22
  import type { ITraceRoot } from '../traceRoot.js';
23
+ import { walkSpanTree } from '../traceTopology.js';
23
24
  import type { AnySpanBuffer, SpanBuffer } from '../types.js';
24
25
  import { createWasmAllocator, type WasmAllocator, type WasmAllocatorOptions } from './wasmAllocator.js';
25
26
  import {
@@ -29,14 +30,13 @@ import {
29
30
  isWasmSpanBufferInstance,
30
31
  type WasmSpanBufferInstance,
31
32
  } from './wasmSpanBuffer.js';
33
+ import { WasmTraceRoot } from './wasmTraceRoot.js';
32
34
 
33
35
  // Spec link (88): realizes specs/lmao/01q_wasm_memory_architecture.md#smoo/lmao!n/wasm-mem (OpContext integration + trace completion).
34
36
  //#region smoo/lmao!n/wasm-mem.strategy
35
37
 
36
- function requireWasmSpanBuffer(buffer: AnySpanBuffer): WasmSpanBufferInstance {
37
- if (!isWasmSpanBufferInstance(buffer)) {
38
- throw new Error('Expected WASM-backed span buffer');
39
- }
38
+ function requireWasmSpanBuffer<T extends LogSchema>(buffer: unknown): WasmSpanBufferInstance<T> {
39
+ if (!isWasmSpanBufferInstance<T>(buffer)) throw new Error('Expected WASM-backed span buffer');
40
40
  return buffer;
41
41
  }
42
42
 
@@ -75,6 +75,7 @@ export class WasmBufferStrategy<T extends LogSchema = LogSchema> implements Buff
75
75
  * Shared across all buffers created by this strategy.
76
76
  */
77
77
  readonly allocator: WasmAllocator;
78
+ private readonly liveRoots = new Set<WasmSpanBufferInstance<T>>();
78
79
 
79
80
  /**
80
81
  * Private constructor - use static create() method.
@@ -98,28 +99,34 @@ export class WasmBufferStrategy<T extends LogSchema = LogSchema> implements Buff
98
99
  return new WasmBufferStrategy<T>(allocator);
99
100
  }
100
101
 
101
- createSpanBuffer(schema: T, traceRoot: ITraceRoot, opMetadata: OpMetadata, capacity?: number): SpanBuffer<T> {
102
+ createSpanBuffer(
103
+ schema: T,
104
+ traceRoot: ITraceRoot<T>,
105
+ opMetadata: OpMetadata,
106
+ capacity?: number,
107
+ plannedClass?: SpanBufferConstructor<T>,
108
+ ): SpanBuffer<T> {
102
109
  const effectiveCapacity = capacity ?? this.allocator.capacity;
103
-
110
+ const messageLayoutFamily = plannedClass?.messageLayoutFamily ?? 'mixed';
111
+ const messagePhysicalLayout = plannedClass?.messagePhysicalLayout ?? 'current';
104
112
  // Create WASM buffer
105
113
  const wasmBuffer = createWasmSpanBuffer(
106
114
  schema,
107
115
  {
108
116
  allocator: this.allocator,
109
117
  capacity: effectiveCapacity,
110
- trace_id: traceRoot.trace_id,
111
- // thread_id and span_id come from WASM (global header and allocator respectively)
112
- thread_id: 0n, // Will be read from WASM header
113
- span_id: 0, // Will be assigned by WASM allocator
118
+ messageLayoutFamily,
119
+ messagePhysicalLayout,
114
120
  },
115
121
  traceRoot, // _traceRoot
116
122
  EMPTY_SCOPE, // _scopeValues
117
123
  opMetadata, // _opMetadata
118
124
  opMetadata, // _callsiteMetadata (same as opMetadata for root)
119
125
  );
126
+ traceRoot._topology.registerRoot(wasmBuffer);
127
+ this.liveRoots.add(wasmBuffer);
120
128
 
121
- // eslint-disable-next-line @typescript-eslint/no-unsafe-type-assertion -- generated WASM buffers implement the SpanBuffer contract for the same schema.
122
- return wasmBuffer as unknown as SpanBuffer<T>;
129
+ return wasmBuffer;
123
130
  }
124
131
 
125
132
  createChildSpanBuffer(
@@ -128,34 +135,37 @@ export class WasmBufferStrategy<T extends LogSchema = LogSchema> implements Buff
128
135
  opMetadata: OpMetadata,
129
136
  capacity?: number,
130
137
  schema?: T,
138
+ plannedClass?: SpanBufferConstructor<T>,
131
139
  ): SpanBuffer<T> {
132
- const wasmParent = requireWasmSpanBuffer(parentBuffer);
140
+ const wasmParent = requireWasmSpanBuffer<T>(parentBuffer);
133
141
  const effectiveCapacity = capacity ?? wasmParent._capacity;
134
142
 
135
143
  // Use provided schema (for cross-library calls) or parent's schema
136
- const childSchema = schema ?? (parentBuffer._logSchema as T);
144
+ const childSchema = schema ?? parentBuffer._logSchema;
145
+ const messageLayoutFamily = plannedClass?.messageLayoutFamily ?? wasmParent._messageLayoutFamily;
146
+ const messagePhysicalLayout = plannedClass?.messagePhysicalLayout ?? wasmParent._messagePhysicalLayout;
137
147
 
138
148
  const child = createWasmChildSpanBuffer(
139
149
  wasmParent,
140
150
  {
141
151
  allocator: this.allocator,
142
152
  capacity: effectiveCapacity,
143
- thread_id: 0n, // Will be read from WASM header
144
- span_id: 0, // Will be assigned by WASM allocator
145
153
  schema: childSchema, // Pass schema for correct buffer class
154
+ messageLayoutFamily,
155
+ messagePhysicalLayout,
146
156
  },
147
157
  parentBuffer._traceRoot, // _traceRoot (inherit from parent)
148
158
  parentBuffer._scopeValues, // _scopeValues (inherit from parent)
149
159
  opMetadata, // _opMetadata
150
160
  callsiteMetadata, // _callsiteMetadata
151
161
  );
162
+ parentBuffer._traceRoot._topology.registerChild(parentBuffer, child);
152
163
 
153
- // eslint-disable-next-line @typescript-eslint/no-unsafe-type-assertion -- child buffers preserve the requested schema contract.
154
- return child as unknown as SpanBuffer<T>;
164
+ return child;
155
165
  }
156
166
 
157
167
  createOverflowBuffer(buffer: SpanBuffer<T>): SpanBuffer<T> {
158
- const wasmBuffer = requireWasmSpanBuffer(buffer);
168
+ const wasmBuffer = requireWasmSpanBuffer<T>(buffer);
159
169
  const overflow = createWasmOverflowBuffer(
160
170
  wasmBuffer,
161
171
  buffer._traceRoot, // _traceRoot (same as original)
@@ -163,9 +173,9 @@ export class WasmBufferStrategy<T extends LogSchema = LogSchema> implements Buff
163
173
  buffer._opMetadata, // _opMetadata (same as original)
164
174
  buffer._callsiteMetadata ?? buffer._opMetadata, // _callsiteMetadata (same as original, fallback to opMetadata)
165
175
  );
176
+ buffer._traceRoot._topology.adoptOverflow(buffer, overflow);
166
177
 
167
- // eslint-disable-next-line @typescript-eslint/no-unsafe-type-assertion -- overflow buffers are schema-compatible continuations of the same span.
168
- return overflow as unknown as SpanBuffer<T>;
178
+ return overflow;
169
179
  }
170
180
 
171
181
  toArrowTable(buffer: AnySpanBuffer): Table {
@@ -174,37 +184,35 @@ export class WasmBufferStrategy<T extends LogSchema = LogSchema> implements Buff
174
184
  }
175
185
 
176
186
  releaseBuffer(buffer: AnySpanBuffer): void {
177
- // Walk the span tree and free all WASM memory
178
- this.freeSpanTree(requireWasmSpanBuffer(buffer));
179
- }
180
-
181
- /**
182
- * Recursively free all WASM memory for a span tree.
183
- */
184
- private freeSpanTree(buffer: WasmSpanBufferInstance): void {
185
- // Free children first (depth-first)
186
- if (buffer._children) {
187
- for (const child of buffer._children) {
188
- this.freeSpanTree(child);
187
+ const root = requireWasmSpanBuffer<T>(buffer);
188
+ const segments: WasmSpanBufferInstance<T>[] = [];
189
+ walkSpanTree(root, (segment) => segments.push(requireWasmSpanBuffer<T>(segment)));
190
+ const topology = root._traceRoot._topology;
191
+ topology.release(() => {
192
+ for (const segment of segments) segment.free();
193
+ if (root._descriptor.kind === 'root' && root._traceRoot instanceof WasmTraceRoot) {
194
+ root._traceRoot.free();
189
195
  }
190
- }
191
-
192
- // Free overflow chain
193
- if (buffer._overflow) {
194
- this.freeSpanTree(buffer._overflow);
195
- }
196
+ });
197
+ this.liveRoots.delete(root);
198
+ }
196
199
 
197
- // Free this buffer's WASM memory
198
- if (typeof buffer.free === 'function') {
199
- buffer.free();
200
- }
200
+ private invalidateSpanTree(buffer: WasmSpanBufferInstance<T>): void {
201
+ walkSpanTree(buffer, (segment) => {
202
+ const wasmSegment = requireWasmSpanBuffer<T>(segment);
203
+ wasmSegment._columnPtrs.fill(-1);
204
+ wasmSegment._descriptor.state = 'freed';
205
+ });
201
206
  }
202
207
 
203
- /**
204
- * Reset the allocator (for testing/benchmarking).
205
- * WARNING: This invalidates ALL buffers created by this strategy.
206
- */
208
+ /** Reset invalidates every live handle before allocator offsets are reused. */
207
209
  reset(): void {
210
+ for (const root of this.liveRoots) {
211
+ this.invalidateSpanTree(root);
212
+ if (root._traceRoot instanceof WasmTraceRoot) root._traceRoot.invalidate();
213
+ root._traceRoot._topology.release();
214
+ }
215
+ this.liveRoots.clear();
208
216
  this.allocator.reset();
209
217
  }
210
218