@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
@@ -14,11 +14,24 @@ import type { ColumnValueType } from '@smoothbricks/arrow-builder';
14
14
  import { getSchemaType } from '@smoothbricks/arrow-builder';
15
15
  import { checkCapacityTuning } from '../capacityTuning.js';
16
16
  import type { OpMetadata } from '../op.js';
17
+ import type { EagerColumnDescriptor } from '../physicalLayoutPlan.js';
18
+ import type { MessageLayoutFamily, MessagePhysicalLayout } from '../runtimeHint.js';
17
19
  import type { LogSchema } from '../schema/LogSchema.js';
18
20
  import type { SpanBufferStats } from '../spanBufferStats.js';
19
21
  import type { ITraceRoot } from '../traceRoot.js';
20
- import type { AnySpanBuffer } from '../types.js';
21
- import type { WasmAllocator } from './wasmAllocator.js';
22
+ import type { SpanBuffer } from '../types.js';
23
+ import { getVocabularyGeneration, type VocabularyGeneration } from '../vocabularyRegistry.js';
24
+ import {
25
+ WASM_NO_LAYOUT_OFFSET,
26
+ WASM_SPAN_IDENTITY_CHILD,
27
+ WASM_SPAN_IDENTITY_ROOT,
28
+ type WasmAllocator,
29
+ } from './wasmAllocator.js';
30
+ import {
31
+ getWasmPhysicalLayout,
32
+ type WasmNumericFamily,
33
+ type WasmPhysicalLayoutDescriptor,
34
+ } from './wasmPhysicalLayout.js';
22
35
 
23
36
  // Spec link (88): realizes specs/lmao/01q_wasm_memory_architecture.md#smoo/lmao!n/wasm-mem (SpanBuffer + SpanLogger codegen).
24
37
  //#region smoo/lmao!n/wasm-mem.spanbuffer
@@ -33,45 +46,95 @@ import type { WasmAllocator } from './wasmAllocator.js';
33
46
  * Internal fields (_traceRoot, _scopeValues, _opMetadata, _callsiteMetadata) are passed separately
34
47
  * by the create* functions.
35
48
  */
36
- export interface WasmSpanBufferOptions {
49
+ export type WasmBufferState = 'live' | 'freed';
50
+ export type WasmIdentityMode = 'root' | 'child' | 'overflow';
51
+
52
+ export interface WasmBufferDescriptor {
53
+ readonly generation: number;
54
+ readonly kind: WasmIdentityMode;
55
+ state: WasmBufferState;
56
+ readonly layout: WasmPhysicalLayoutDescriptor;
57
+ readonly allocationPtr: number;
58
+ readonly allocationByteLength: number;
59
+ readonly systemPtr: number;
60
+ readonly familyPtrs: Readonly<Record<WasmNumericFamily, number>>;
61
+ readonly identityPtr: number;
62
+ readonly ownsIdentity: boolean;
63
+ memoryVersion: number;
64
+ readonly parent?: WasmBufferDescriptor;
65
+ overflow?: WasmBufferDescriptor;
66
+ }
67
+
68
+ const EMPTY_EAGER_COLUMNS: EagerColumnDescriptor = Object.freeze({
69
+ names: Object.freeze([]),
70
+ words: Object.freeze([]),
71
+ key: '',
72
+ });
73
+ let nextWasmBufferGeneration = 0;
74
+
75
+ export interface WasmSpanBufferOptions<T extends LogSchema = LogSchema> {
37
76
  allocator: WasmAllocator;
38
77
  capacity: number;
39
- trace_id: string;
40
- thread_id: bigint;
41
- span_id: number;
42
- parent_thread_id?: bigint;
43
- parent_span_id?: number;
44
- logSchema: LogSchema;
45
- _traceRoot: ITraceRoot;
78
+ messageLayoutFamily?: MessageLayoutFamily;
79
+ messagePhysicalLayout?: MessagePhysicalLayout;
80
+ logSchema: T;
81
+ _traceRoot: ITraceRoot<T>;
46
82
  _scopeValues: Readonly<Record<string, unknown>>;
47
83
  _opMetadata: OpMetadata;
48
84
  _callsiteMetadata: OpMetadata;
85
+ _vocabularyGeneration: VocabularyGeneration;
86
+ _layout: WasmPhysicalLayoutDescriptor;
87
+ _identityMode?: WasmIdentityMode;
88
+ _identitySource?: WasmSpanBufferInstance<T>;
89
+ _parent?: WasmSpanBufferInstance<T>;
90
+ _generation?: number;
49
91
  }
50
92
 
51
93
  /**
52
- * WASM SpanBuffer instance interface.
53
- * Extends AnySpanBuffer with WASM-specific pointer properties.
94
+ * Extends the schema-specific SpanBuffer shape with WASM pointer ownership.
54
95
  */
55
- export interface WasmSpanBufferInstance extends AnySpanBuffer {
96
+ export type WasmSpanBufferInstance<T extends LogSchema = LogSchema> = SpanBuffer<T> & {
56
97
  // ===========================================================================
57
98
  // WASM pointers (internal implementation details)
58
99
  // ===========================================================================
59
100
  /** Byte offset into WASM memory for system columns (timestamp + entry_type) */
60
101
  readonly _systemPtr: number;
102
+ /** Owning root/child/overflow superblock returned by the allocator. */
103
+ readonly _allocationPtr: number;
104
+ readonly _allocationByteLength: number;
61
105
  /** Byte offset into WASM memory for identity block (writeIndex, span_id, trace_id) */
62
106
  readonly _identityPtr: number;
63
107
  /** Array of byte offsets for each column (-1 = not allocated) */
64
108
  readonly _columnPtrs: Int32Array;
65
109
  /** Reference to the WASM allocator */
66
110
  readonly _allocator: WasmAllocator;
67
-
68
- // Override tree structure with WASM-typed versions
69
- _parent?: WasmSpanBufferInstance;
70
- _children: WasmSpanBufferInstance[];
71
- _overflow?: WasmSpanBufferInstance;
72
-
73
- // Override message column - WASM stores as JS array (not WASM memory)
74
- readonly _message: string[];
111
+ readonly _descriptor: WasmBufferDescriptor;
112
+ readonly _layout: WasmPhysicalLayoutDescriptor;
113
+ readonly _familyPtrs: Readonly<Record<WasmNumericFamily, number>>;
114
+ _viewVersion: number;
115
+ _timestampView: BigInt64Array;
116
+ _entryTypeView?: Uint8Array;
117
+ _messageIds?: Uint16Array;
118
+ _logHeaders?: Uint32Array;
119
+ _rowHeaders?: Uint32Array;
120
+ _identityView: Uint8Array;
121
+ _identityData: DataView;
122
+ _identitySource?: WasmSpanBufferInstance<T>;
123
+ readonly _threadId: bigint;
124
+ readonly _spanId: number;
125
+ _refreshViews(version: number): void;
126
+ _ensureWasmViews(): void;
127
+ _getWasmColumnValue(columnIndex: number): ColumnValueType | undefined;
128
+ _getWasmColumnNulls(columnIndex: number): Uint8Array | undefined;
129
+ readonly _identityOwner: boolean;
130
+ _overflowWriteIndex: number;
131
+
132
+ // Preserve WASM-typed parent and overflow links.
133
+ _parent?: WasmSpanBufferInstance<T>;
134
+ _overflow?: WasmSpanBufferInstance<T>;
135
+
136
+ // Present only for dynamic-only and mixed physical families.
137
+ readonly _message?: string[];
75
138
 
76
139
  // ===========================================================================
77
140
  // WASM-specific methods
@@ -87,15 +150,21 @@ export interface WasmSpanBufferInstance extends AnySpanBuffer {
87
150
 
88
151
  /** Get column nulls array if allocated */
89
152
  getNullsIfAllocated(columnName: string): Uint8Array | undefined;
90
- }
153
+ };
154
+
155
+ type WasmRawMessageBuffer = WasmSpanBufferInstance & { readonly _message: string[] };
156
+ type WasmUnpackedMessageBuffer = WasmSpanBufferInstance & { readonly _entryTypeView: Uint8Array };
91
157
 
92
158
  /**
93
159
  * Constructor type for generated WASM SpanBuffer classes.
94
160
  */
95
- export interface WasmSpanBufferConstructor {
96
- new (opts: WasmSpanBufferOptions): WasmSpanBufferInstance;
97
- readonly schema: LogSchema;
98
- stats: SpanBufferStats; // Mutable stats shared across all instances
161
+ export interface WasmSpanBufferConstructor<T extends LogSchema = LogSchema> {
162
+ new (opts: WasmSpanBufferOptions<T>): WasmSpanBufferInstance<T>;
163
+ readonly schema: T;
164
+ readonly messageLayoutFamily: MessageLayoutFamily;
165
+ readonly messagePhysicalLayout: MessagePhysicalLayout;
166
+ readonly eagerColumns: EagerColumnDescriptor;
167
+ stats: SpanBufferStats;
99
168
  }
100
169
 
101
170
  // =============================================================================
@@ -119,8 +188,10 @@ interface ColumnMeta {
119
188
  schemaType: string;
120
189
  /** Index in the _columnPtrs array */
121
190
  columnIndex: number;
122
- /** Whether this column is eager (always allocated) */
123
- isEager: boolean;
191
+ /** Schema-level eager storage suppresses null bitmaps by declaration. */
192
+ isSchemaEager: boolean;
193
+ /** Compile-proven eager storage is preallocated but remains nullable. */
194
+ isPreallocated: boolean;
124
195
  /** Enum values if schemaType === 'enum' */
125
196
  enumValues?: readonly string[];
126
197
  }
@@ -137,39 +208,34 @@ function getFieldEnumValues(value: unknown): readonly string[] | undefined {
137
208
  return Array.isArray(enumValues) ? enumValues : undefined;
138
209
  }
139
210
 
140
- function isColumnValueType(value: unknown): value is ColumnValueType {
141
- return value === undefined || Array.isArray(value) || ArrayBuffer.isView(value);
142
- }
143
-
144
- function getFreeMethod(
145
- sizeClass: Exclude<SizeClass, 'string'>,
146
- ): keyof Pick<WasmAllocator, 'free1B' | 'free4B' | 'free8B'> {
147
- switch (sizeClass) {
148
- case '1b':
149
- return 'free1B';
150
- case '4b':
151
- return 'free4B';
152
- case '8b':
153
- return 'free8B';
154
- }
155
- }
156
-
157
- function isWasmSpanBufferConstructor(value: unknown): value is WasmSpanBufferConstructor {
211
+ function isWasmSpanBufferConstructor<T extends LogSchema = LogSchema>(
212
+ value: unknown,
213
+ schema?: T,
214
+ messageLayoutFamily?: MessageLayoutFamily,
215
+ messagePhysicalLayout?: MessagePhysicalLayout,
216
+ ): value is WasmSpanBufferConstructor<T> {
158
217
  return (
159
218
  typeof value === 'function' &&
160
219
  typeof Reflect.get(value, 'schema') === 'object' &&
161
220
  Reflect.get(value, 'schema') !== null &&
221
+ (schema === undefined || Reflect.get(value, 'schema') === schema) &&
222
+ (messageLayoutFamily === undefined || Reflect.get(value, 'messageLayoutFamily') === messageLayoutFamily) &&
223
+ (messagePhysicalLayout === undefined || Reflect.get(value, 'messagePhysicalLayout') === messagePhysicalLayout) &&
162
224
  typeof Reflect.get(value, 'stats') === 'object' &&
163
225
  Reflect.get(value, 'stats') !== null
164
226
  );
165
227
  }
166
228
 
167
- export function isWasmSpanBufferInstance(value: unknown): value is WasmSpanBufferInstance {
229
+ export function isWasmSpanBufferInstance<T extends LogSchema = LogSchema>(
230
+ value: unknown,
231
+ ): value is WasmSpanBufferInstance<T> {
168
232
  return typeof value === 'object' && value !== null && typeof Reflect.get(value, '_systemPtr') === 'number';
169
233
  }
170
234
 
171
- function getWasmSpanBufferConstructor(instance: { constructor: unknown }): WasmSpanBufferConstructor {
172
- if (!isWasmSpanBufferConstructor(instance.constructor)) {
235
+ function getWasmSpanBufferConstructor<T extends LogSchema>(
236
+ instance: WasmSpanBufferInstance<T>,
237
+ ): WasmSpanBufferConstructor<T> {
238
+ if (!isWasmSpanBufferConstructor(instance.constructor, instance._logSchema)) {
173
239
  throw new Error('Expected generated WasmSpanBuffer constructor');
174
240
  }
175
241
  return instance.constructor;
@@ -183,15 +249,16 @@ function isWasmSpanBufferFactory(value: unknown): value is () => WasmSpanBufferC
183
249
  * Build column metadata array from schema.
184
250
  * Maps each schema field to its storage characteristics.
185
251
  */
186
- function buildColumnMeta(schema: LogSchema): ColumnMeta[] {
252
+ function buildColumnMeta(schema: LogSchema, eagerColumns: EagerColumnDescriptor): ColumnMeta[] {
187
253
  const result: ColumnMeta[] = [];
188
254
  const fields = schema.fields;
255
+ const preallocatedColumns = new Set(eagerColumns.names);
189
256
  let columnIndex = 0;
190
257
 
191
258
  for (const name of schema._columnNames) {
192
259
  const field = fields[name];
193
260
  const schemaType = getSchemaType(field);
194
- const isEager = getFieldEager(field);
261
+ const isSchemaEager = getFieldEager(field);
195
262
  const enumValues = getFieldEnumValues(field);
196
263
 
197
264
  let sizeClass: SizeClass;
@@ -217,7 +284,8 @@ function buildColumnMeta(schema: LogSchema): ColumnMeta[] {
217
284
  sizeClass,
218
285
  schemaType: schemaType ?? 'unknown',
219
286
  columnIndex,
220
- isEager,
287
+ isSchemaEager,
288
+ isPreallocated: !isSchemaEager && preallocatedColumns.has(name),
221
289
  enumValues,
222
290
  });
223
291
  columnIndex++;
@@ -230,48 +298,48 @@ function buildColumnMeta(schema: LogSchema): ColumnMeta[] {
230
298
  // Common WasmSpanBuffer Methods (shared via prototype)
231
299
  // =============================================================================
232
300
 
233
- /**
234
- * Get timestamp column view from WASM memory.
235
- * Assigned to prototype after class generation.
236
- */
301
+ function wasmEnsureViews(this: WasmSpanBufferInstance): void {
302
+ assertWasmBufferLive(this);
303
+ const memoryVersion = this._allocator.refreshViews();
304
+ if (memoryVersion !== this._viewVersion) this._refreshViews(memoryVersion);
305
+ }
306
+
237
307
  function wasmGetTimestamp(this: WasmSpanBufferInstance): BigInt64Array {
238
- return new BigInt64Array(this._allocator.memory.buffer, this._systemPtr, this._capacity);
308
+ wasmEnsureViews.call(this);
309
+ return this._timestampView;
239
310
  }
240
311
 
241
- /**
242
- * Get entry_type column view from WASM memory.
243
- * Assigned to prototype after class generation.
244
- */
245
- function wasmGetEntryType(this: WasmSpanBufferInstance): Uint8Array {
246
- return new Uint8Array(this._allocator.memory.buffer, this._systemPtr + this._capacity * 8, this._capacity);
312
+ function wasmGetEntryType(this: WasmUnpackedMessageBuffer): Uint8Array {
313
+ wasmEnsureViews.call(this);
314
+ return this._entryTypeView;
315
+ }
316
+
317
+ function assertWasmBufferLive(buffer: WasmSpanBufferInstance): void {
318
+ if (buffer._descriptor.state !== 'live') {
319
+ const generation = buffer._descriptor.generation;
320
+ throw new Error(
321
+ `Cannot access released WASM buffer generation ${generation}; generation ${generation} has been released`,
322
+ );
323
+ }
247
324
  }
248
325
 
249
- /**
250
- * Get writeIndex from WASM identity block.
251
- */
252
326
  function wasmGetWriteIndex(this: WasmSpanBufferInstance): number {
253
- return this._allocator.readWriteIndex(this._identityPtr);
327
+ wasmEnsureViews.call(this);
328
+ return this._identityOwner ? this._identityData.getUint32(0, true) : this._overflowWriteIndex;
254
329
  }
255
330
 
256
- /**
257
- * Set writeIndex in WASM identity block.
258
- */
259
331
  function wasmSetWriteIndex(this: WasmSpanBufferInstance, value: number): void {
260
- new DataView(this._allocator.memory.buffer).setUint32(this._identityPtr, value, true);
332
+ wasmEnsureViews.call(this);
333
+ if (this._identityOwner) {
334
+ this._identityData.setUint32(0, value, true);
335
+ } else {
336
+ this._overflowWriteIndex = value;
337
+ }
261
338
  }
262
339
 
263
- /**
264
- * Get trace_id from WASM identity block.
265
- */
266
340
  function wasmGetTraceId(this: WasmSpanBufferInstance): string {
267
- const len = this._allocator.readIdentityTraceIdLen(this._identityPtr);
268
- if (len === 0) {
269
- let p = this._parent;
270
- while (p?._parent) p = p._parent;
271
- return p ? p.trace_id : '';
272
- }
273
- const traceIdPtr = this._allocator.getIdentityTraceIdPtr(this._identityPtr);
274
- return new TextDecoder().decode(new Uint8Array(this._allocator.memory.buffer, traceIdPtr, len));
341
+ assertWasmBufferLive(this);
342
+ return this._traceRoot.trace_id;
275
343
  }
276
344
 
277
345
  function wasmGetSpanStartTime(this: WasmSpanBufferInstance): bigint {
@@ -303,16 +371,13 @@ function wasmGetLastLoggedTime(this: WasmSpanBufferInstance): bigint | null {
303
371
  * Get thread_id from WASM identity block.
304
372
  */
305
373
  function wasmGetThreadId(this: WasmSpanBufferInstance): bigint {
306
- const high = this._allocator.getThreadIdHigh();
307
- const low = this._allocator.getThreadIdLow();
308
- return (BigInt(high) << 32n) | BigInt(low);
374
+ assertWasmBufferLive(this);
375
+ return this._threadId;
309
376
  }
310
377
 
311
- /**
312
- * Get span_id from WASM identity block.
313
- */
314
378
  function wasmGetSpanId(this: WasmSpanBufferInstance): number {
315
- return this._allocator.readIdentitySpanId(this._identityPtr);
379
+ assertWasmBufferLive(this);
380
+ return this._spanId;
316
381
  }
317
382
 
318
383
  /**
@@ -336,102 +401,106 @@ function wasmGetHasParent(this: WasmSpanBufferInstance): boolean {
336
401
  return this._parent !== null;
337
402
  }
338
403
 
339
- /**
340
- * Message column setter.
341
- */
342
- function wasmMessage(this: WasmSpanBufferInstance, idx: number, value: string): WasmSpanBufferInstance {
404
+ function wasmRawMessage(this: WasmRawMessageBuffer, idx: number, value: string): WasmRawMessageBuffer {
405
+ assertWasmBufferLive(this);
343
406
  this._message[idx] = value;
344
407
  return this;
345
408
  }
346
409
 
410
+ function wasmStaticMessage(this: WasmSpanBufferInstance, idx: number, value: string): WasmSpanBufferInstance {
411
+ assertWasmBufferLive(this);
412
+ if (idx === 0) this._spanName = value;
413
+ else if (idx === 1) this._terminalMessage = value;
414
+ else throw new RangeError('Static-only buffers only accept raw system messages at rows 0 and 1');
415
+ return this;
416
+ }
417
+
347
418
  /**
348
419
  * Get message column values.
349
420
  */
350
- function wasmGetMessageValues(this: WasmSpanBufferInstance): string[] {
421
+ function wasmGetMessageValues(this: WasmRawMessageBuffer): string[] {
422
+ assertWasmBufferLive(this);
351
423
  return this._message;
352
424
  }
353
425
 
354
- /**
355
- * Get message column nulls (always undefined for eager column).
356
- */
357
- function wasmGetMessageNulls(this: WasmSpanBufferInstance): undefined {
358
- return undefined;
426
+ function wasmGetMessageLayoutFamily(this: WasmSpanBufferInstance): MessageLayoutFamily {
427
+ return getWasmSpanBufferConstructor(this).messageLayoutFamily;
359
428
  }
360
429
 
361
- /**
362
- * Free all WASM memory for this buffer.
363
- */
364
- function wasmFree(this: WasmSpanBufferInstance): void {
365
- // Free identity block
366
- this._allocator.freeIdentity(this._identityPtr);
430
+ function wasmGetMessagePhysicalLayout(this: WasmSpanBufferInstance): MessagePhysicalLayout {
431
+ return getWasmSpanBufferConstructor(this).messagePhysicalLayout;
432
+ }
367
433
 
368
- // Free system block
369
- this._allocator.freeSpanSystem(this._systemPtr);
434
+ /** Free the one owned root/child/overflow superblock exactly once. */
435
+ function wasmFree(this: WasmSpanBufferInstance): void {
436
+ if (this._descriptor.state === 'freed') return;
437
+ if (this._viewVersion !== 0) this._sealStats();
370
438
 
371
- // Free column blocks
372
- const columnMeta = buildColumnMeta(this._logSchema);
373
- for (const col of columnMeta) {
374
- if (col.sizeClass !== 'string' && this._columnPtrs[col.columnIndex] >= 0) {
375
- const freeMethod = getFreeMethod(col.sizeClass);
376
- this._allocator[freeMethod](this._columnPtrs[col.columnIndex]);
377
- }
378
- }
439
+ this._allocator.freeSpanSuperblock(this._allocationPtr, this._allocationByteLength);
440
+ this._descriptor.state = 'freed';
379
441
  }
380
442
 
381
443
  /**
382
444
  * Check if column is allocated.
383
445
  */
384
446
  function wasmIsColumnAllocated(this: WasmSpanBufferInstance, columnIndex: number): boolean {
385
- return this._columnPtrs[columnIndex] >= 0;
447
+ assertWasmBufferLive(this);
448
+ wasmEnsureViews.call(this);
449
+ return this._columnPtrs[columnIndex] >= 0 || this._getWasmColumnValue(columnIndex) !== undefined;
386
450
  }
387
451
 
388
- /**
389
- * Get column values if allocated.
390
- */
452
+ /** Get a canonical column value view or JS sidecar without accessor reflection. */
391
453
  function wasmGetColumnIfAllocated(this: WasmSpanBufferInstance, columnName: string): ColumnValueType | undefined {
454
+ assertWasmBufferLive(this);
392
455
  const idx = this._logSchema._columnNames.indexOf(columnName);
393
456
  if (idx === -1) return undefined;
394
- const getter = Object.getOwnPropertyDescriptor(Object.getPrototypeOf(this), `${columnName}_values`);
395
- if (getter?.get) {
396
- const value = getter.get.call(this);
397
- return isColumnValueType(value) ? value : undefined;
398
- }
399
- return undefined;
457
+ wasmEnsureViews.call(this);
458
+ return this._getWasmColumnValue(idx);
400
459
  }
401
460
 
402
- /**
403
- * Get column nulls if allocated.
404
- */
461
+ /** Get a canonical column null-bitmap view without accessor reflection. */
405
462
  function wasmGetNullsIfAllocated(this: WasmSpanBufferInstance, columnName: string): Uint8Array | undefined {
406
- const getter = Object.getOwnPropertyDescriptor(Object.getPrototypeOf(this), `${columnName}_nulls`);
407
- if (getter?.get) {
408
- return getter.get.call(this);
409
- }
410
- return undefined;
463
+ assertWasmBufferLive(this);
464
+ const idx = this._logSchema._columnNames.indexOf(columnName);
465
+ if (idx === -1) return undefined;
466
+ wasmEnsureViews.call(this);
467
+ return this._getWasmColumnNulls(idx);
411
468
  }
412
469
 
413
- /**
414
- * Get system ArrayBuffer for compatibility with AnySpanBuffer.
415
- * WASM uses shared memory, so this returns the entire WASM memory buffer.
416
- */
470
+ /** Get the current WASM memory backing store. */
417
471
  function wasmGetSystem(this: WasmSpanBufferInstance): ArrayBuffer {
472
+ wasmEnsureViews.call(this);
418
473
  return this._allocator.memory.buffer;
419
474
  }
420
475
 
421
- /**
422
- * Get identity Uint8Array for compatibility with AnySpanBuffer.
423
- * Returns a view of the identity block (48 bytes) in WASM memory.
424
- */
476
+ /** Get the canonical identity view for the current memory version. */
425
477
  function wasmGetIdentity(this: WasmSpanBufferInstance): Uint8Array {
426
- // Identity block is 48 bytes: writeIndex(4) + reserved(4) + span_id(4) + trace_id_len(1) + trace_id(up to 35)
427
- return new Uint8Array(this._allocator.memory.buffer, this._identityPtr, 48);
478
+ wasmEnsureViews.call(this);
479
+ return this._identityView;
480
+ }
481
+
482
+ function wasmSealStats(this: WasmSpanBufferInstance): void {
483
+ if (this._statsSealed) return;
484
+ const completedRows = this._writeIndex - this._statsReservedRows;
485
+ if (completedRows > 0) getWasmSpanBufferConstructor(this).stats.totalWrites += completedRows;
486
+ this._statsSealed = true;
487
+ }
488
+
489
+ function wasmSealStatsChain(this: WasmSpanBufferInstance): void {
490
+ let current: WasmSpanBufferInstance | undefined = this;
491
+ while (current) {
492
+ current._sealStats();
493
+ current = current._overflow;
494
+ }
428
495
  }
429
496
 
430
497
  /**
431
498
  * Get or create overflow buffer.
432
499
  */
433
500
  function wasmGetOrCreateOverflow(this: WasmSpanBufferInstance): WasmSpanBufferInstance {
501
+ assertWasmBufferLive(this);
434
502
  if (this._overflow) return this._overflow;
503
+ this._sealStats();
435
504
  const tracer = this._traceRoot.tracer;
436
505
  tracer.onStatsWillResetFor(this);
437
506
  checkCapacityTuning(getWasmSpanBufferConstructor(this).stats);
@@ -472,69 +541,24 @@ function wasmInspect(this: WasmSpanBufferInstance): string {
472
541
  * Generate eager column initialization code for constructor.
473
542
  * Eager columns are pre-allocated, lazy columns start as undefined/-1.
474
543
  */
475
- function generateEagerColumnInit(columnMeta: ColumnMeta[]): string {
544
+ function generateColumnInit(columnMeta: ColumnMeta[]): string {
476
545
  const lines: string[] = [];
477
-
478
546
  for (const col of columnMeta) {
479
- if (col.name === 'message') continue; // Message handled separately
480
-
481
- if (col.sizeClass === 'string') {
482
- if (col.isEager) {
483
- lines.push(`this._${col.name}_values = new Array(this._capacity);`);
484
- } else {
485
- lines.push(`this._${col.name}_values = undefined;`);
486
- lines.push(`this._${col.name}_nulls = undefined;`);
487
- }
488
- } else if (col.isEager) {
489
- lines.push(`this._columnPtrs[${col.columnIndex}] = this._allocator.alloc${col.sizeClass.toUpperCase()}();`);
547
+ if (col.name === 'message' || col.sizeClass !== 'string') continue;
548
+ if (col.isSchemaEager) {
549
+ lines.push(`this._${col.name}_values = new Array(this._capacity);`);
550
+ } else if (col.isPreallocated) {
551
+ lines.push(`this._${col.name}_values = new Array(this._capacity);`);
552
+ lines.push(`this._${col.name}_nulls = new Uint8Array((this._capacity + 7) >>> 3);`);
553
+ } else {
554
+ lines.push(`this._${col.name}_values = undefined;`);
555
+ lines.push(`this._${col.name}_nulls = undefined;`);
490
556
  }
491
557
  }
492
-
493
558
  return lines.join('\n ');
494
559
  }
495
560
 
496
- /**
497
- * Generate setter method for a numeric column (stored in WASM).
498
- * Calls WASM exports directly for hot path optimization.
499
- */
500
- function generateNumericSetter(col: ColumnMeta): string {
501
- const allocMethod = `alloc${col.sizeClass.toUpperCase()}`;
502
-
503
- // Map to WASM export names
504
- const writeExportName =
505
- col.schemaType === 'number' || col.schemaType === 'bigUint64'
506
- ? 'write_col_f64'
507
- : col.sizeClass === '1b'
508
- ? 'write_col_u8'
509
- : col.sizeClass === '4b'
510
- ? 'write_col_u32'
511
- : 'write_col_f64';
512
-
513
- if (col.isEager) {
514
- // Eager: column is pre-allocated, just write value (call WASM export directly)
515
- return `${col.name}(idx, value) {
516
- const ptr = this._columnPtrs[${col.columnIndex}];
517
- this._allocator.exports.${writeExportName}(ptr, idx, value, this._capacity);
518
- return this;
519
- }`;
520
- }
521
-
522
- // Lazy: allocate on first write (call WASM export directly)
523
- return `${col.name}(idx, value) {
524
- let ptr = this._columnPtrs[${col.columnIndex}];
525
- if (ptr < 0) {
526
- ptr = this._allocator.${allocMethod}();
527
- this._columnPtrs[${col.columnIndex}] = ptr;
528
- }
529
- this._allocator.exports.${writeExportName}(ptr, idx, value, this._capacity);
530
- return this;
531
- }`;
532
- }
533
-
534
- /**
535
- * Generate getter for numeric column values (creates view on demand).
536
- */
537
- function generateNumericValuesGetter(col: ColumnMeta): string {
561
+ function generateNumericViewBinding(col: ColumnMeta, layoutIndex: number): string {
538
562
  const arrayType =
539
563
  col.schemaType === 'number'
540
564
  ? 'Float64Array'
@@ -545,65 +569,160 @@ function generateNumericValuesGetter(col: ColumnMeta): string {
545
569
  : col.sizeClass === '4b'
546
570
  ? 'Uint32Array'
547
571
  : 'Float64Array';
572
+ return `const column = this._layout.columns[${layoutIndex}];
573
+ const familyPtr = this._familyPtrs[column.family];
574
+ this._${col.name}_nulls = new Uint8Array(memory, familyPtr + column.nullOffset, column.nullByteLength);
575
+ this._${col.name}_values = new ${arrayType}(memory, familyPtr + column.valueOffset, this._capacity);
576
+ this._columnPtrs[${col.columnIndex}] = familyPtr + column.nullOffset;`;
577
+ }
548
578
 
549
- const bytesPerElement = col.sizeClass === '1b' ? 1 : col.sizeClass === '4b' ? 4 : 8;
579
+ function generateEnsureNumericView(col: ColumnMeta, layoutIndex: number): string {
580
+ if (col.isSchemaEager || col.isPreallocated) return '';
581
+ return `if (this._columnPtrs[${col.columnIndex}] === -1) {
582
+ const memory = this._allocator.memory.buffer;
583
+ ${generateNumericViewBinding(col, layoutIndex)}
584
+ }`;
585
+ }
550
586
 
551
- return `get ${col.name}_values() {
552
- const ptr = this._columnPtrs[${col.columnIndex}];
553
- if (ptr < 0) return ${col.isEager ? 'null' : 'undefined'};
554
- const nullBitmapSize = Math.ceil(this._capacity / 8);
555
- const valueOffset = ptr + nullBitmapSize;
556
- // Align offset for typed array
557
- const alignedOffset = (valueOffset + ${bytesPerElement - 1}) & ~${bytesPerElement - 1};
558
- return new ${arrayType}(
559
- this._allocator.memory.buffer,
560
- alignedOffset,
561
- this._capacity
562
- );
587
+ function generateNumericSetter(col: ColumnMeta, layoutIndex: number): string {
588
+ const ensureView = generateEnsureNumericView(col, layoutIndex);
589
+ const validity = col.isSchemaEager
590
+ ? `this._${col.name}_values[idx] = value;`
591
+ : `if (value == null) {
592
+ this._${col.name}_nulls[idx >>> 3] &= ~(1 << (idx & 7));
593
+ } else {
594
+ this._${col.name}_nulls[idx >>> 3] |= 1 << (idx & 7);
595
+ this._${col.name}_values[idx] = value;
596
+ }`;
597
+ return `${col.name}(idx, value) {
598
+ this._ensureWasmViews();
599
+ ${ensureView}
600
+ ${validity}
601
+ return this;
563
602
  }`;
564
603
  }
565
604
 
566
- /**
567
- * Generate getter for numeric column nulls bitmap.
568
- */
569
- function generateNumericNullsGetter(col: ColumnMeta): string {
570
- if (col.isEager) {
571
- // Eager columns don't have null bitmaps
572
- return `get ${col.name}_nulls() {
573
- return undefined;
605
+ function generateNumericValuesGetter(col: ColumnMeta, layoutIndex: number): string {
606
+ return `get ${col.name}_values() {
607
+ this._ensureWasmViews();
608
+ ${generateEnsureNumericView(col, layoutIndex)}
609
+ return this._${col.name}_values;
574
610
  }`;
575
- }
611
+ }
576
612
 
613
+ function generateNumericNullsGetter(col: ColumnMeta, layoutIndex: number): string {
614
+ if (col.isSchemaEager) {
615
+ return `get ${col.name}_nulls() { return undefined; }`;
616
+ }
577
617
  return `get ${col.name}_nulls() {
578
- const ptr = this._columnPtrs[${col.columnIndex}];
579
- if (ptr < 0) return undefined;
580
- const nullBitmapSize = Math.ceil(this._capacity / 8);
581
- return new Uint8Array(
582
- this._allocator.memory.buffer,
583
- ptr,
584
- nullBitmapSize
585
- );
618
+ this._ensureWasmViews();
619
+ ${generateEnsureNumericView(col, layoutIndex)}
620
+ return this._${col.name}_nulls;
586
621
  }`;
587
622
  }
588
623
 
624
+ function generateViewRefresh(
625
+ columnMeta: ColumnMeta[],
626
+ messageLayoutFamily: MessageLayoutFamily,
627
+ messagePhysicalLayout: MessagePhysicalLayout,
628
+ ): string {
629
+ const lines = [
630
+ 'const memory = this._allocator.memory.buffer;',
631
+ 'this._timestampView = new BigInt64Array(memory, this._systemPtr, this._capacity);',
632
+ ];
633
+ if (messagePhysicalLayout === 'packed') {
634
+ lines.push(
635
+ 'this._rowHeaders = new Uint32Array(memory, this._systemPtr + this._layout.system.rowHeaderOffset, this._capacity);',
636
+ );
637
+ } else {
638
+ lines.push(
639
+ 'this._entryTypeView = new Uint8Array(memory, this._systemPtr + this._layout.system.entryTypeOffset, this._capacity);',
640
+ );
641
+ if (messageLayoutFamily !== 'dynamic-only') {
642
+ if (messagePhysicalLayout === 'current') {
643
+ lines.push(
644
+ 'this._messageIds = new Uint16Array(memory, this._systemPtr + this._layout.system.messageIdOffset, this._capacity);',
645
+ );
646
+ } else {
647
+ lines.push(
648
+ 'this._logHeaders = new Uint32Array(memory, this._systemPtr + this._layout.system.messageDenseIndexOffset, this._capacity);',
649
+ );
650
+ }
651
+ }
652
+ }
653
+ lines.push(`if (this._identityOwner) {
654
+ this._identityView = new Uint8Array(memory, this._identityPtr, 128);
655
+ this._identityData = new DataView(memory, this._identityPtr, 128);
656
+ } else {
657
+ this._identitySource._ensureWasmViews();
658
+ this._identityView = this._identitySource._identityView;
659
+ this._identityData = this._identitySource._identityData;
660
+ }`);
661
+ let layoutIndex = 0;
662
+ for (const col of columnMeta) {
663
+ if (col.sizeClass === 'string' || col.name === 'message') continue;
664
+ const binding = generateNumericViewBinding(col, layoutIndex);
665
+ if (col.isSchemaEager || col.isPreallocated) {
666
+ lines.push(`{ ${binding} }`);
667
+ } else {
668
+ lines.push(`if (this._columnPtrs[${col.columnIndex}] !== -1) { ${binding} }`);
669
+ }
670
+ layoutIndex++;
671
+ }
672
+ lines.push('this._viewVersion = version;', 'this._descriptor.memoryVersion = version;');
673
+ return lines.join('\n ');
674
+ }
675
+
676
+ function generateColumnLookup(columnMeta: ColumnMeta[], nulls: boolean): string {
677
+ const cases: string[] = [];
678
+ for (const col of columnMeta) {
679
+ if (col.name === 'message') continue;
680
+ if (nulls && col.isSchemaEager) cases.push(`case ${col.columnIndex}: return undefined;`);
681
+ else {
682
+ const property = nulls ? `_${col.name}_nulls` : `_${col.name}_values`;
683
+ cases.push(`case ${col.columnIndex}: return this.${property};`);
684
+ }
685
+ }
686
+ return `switch (columnIndex) { ${cases.join(' ')} default: return undefined; }`;
687
+ }
688
+
689
+ function generateEnsureStringStorage(col: ColumnMeta): string {
690
+ if (col.isSchemaEager || col.isPreallocated) return '';
691
+ return `if (this._${col.name}_values === undefined) {
692
+ this._${col.name}_values = new Array(this._capacity);
693
+ this._${col.name}_nulls = new Uint8Array((this._capacity + 7) >>> 3);
694
+ }`;
695
+ }
696
+
589
697
  /**
590
698
  * Generate setter method for a string column (stored in JS).
591
699
  */
592
700
  function generateStringSetter(col: ColumnMeta): string {
593
- if (col.isEager) {
701
+ const ensureStorage = generateEnsureStringStorage(col);
702
+ if (col.isSchemaEager) {
594
703
  return `${col.name}(idx, value) {
704
+ if (this._descriptor.state !== 'live') throw new Error('Cannot write a released WASM buffer');
595
705
  this._${col.name}_values[idx] = value;
596
706
  return this;
597
707
  }`;
598
708
  }
599
709
 
710
+ if (col.isPreallocated) {
711
+ return `${col.name}(idx, value) {
712
+ if (this._descriptor.state !== 'live') throw new Error('Cannot write a released WASM buffer');
713
+ if (value == null) this._${col.name}_nulls[idx >>> 3] &= ~(1 << (idx & 7));
714
+ else {
715
+ this._${col.name}_values[idx] = value;
716
+ this._${col.name}_nulls[idx >>> 3] |= 1 << (idx & 7);
717
+ }
718
+ return this;
719
+ }`;
720
+ }
721
+
600
722
  // Lazy string column: allocate arrays on first write
601
723
  return `${col.name}(idx, value) {
602
- if (this._${col.name}_values === undefined) {
603
- this._${col.name}_values = new Array(this._capacity);
604
- const nullBitmapSize = Math.ceil(this._capacity / 8);
605
- this._${col.name}_nulls = new Uint8Array(nullBitmapSize);
606
- }
724
+ if (this._descriptor.state !== 'live') throw new Error('Cannot write a released WASM buffer');
725
+ ${ensureStorage}
607
726
  if (value == null) {
608
727
  // Clear validity bit
609
728
  this._${col.name}_nulls[idx >>> 3] &= ~(1 << (idx & 7));
@@ -620,13 +739,10 @@ function generateStringSetter(col: ColumnMeta): string {
620
739
  * Generate getter for string column values.
621
740
  */
622
741
  function generateStringValuesGetter(col: ColumnMeta): string {
623
- if (col.isEager) {
624
- return `get ${col.name}_values() {
625
- return this._${col.name}_values;
626
- }`;
627
- }
628
-
742
+ const ensureStorage = generateEnsureStringStorage(col);
629
743
  return `get ${col.name}_values() {
744
+ if (this._descriptor.state !== 'live') throw new Error('Cannot read a released WASM buffer');
745
+ ${ensureStorage}
630
746
  return this._${col.name}_values;
631
747
  }`;
632
748
  }
@@ -635,13 +751,16 @@ function generateStringValuesGetter(col: ColumnMeta): string {
635
751
  * Generate getter for string column nulls.
636
752
  */
637
753
  function generateStringNullsGetter(col: ColumnMeta): string {
638
- if (col.isEager) {
754
+ if (col.isSchemaEager) {
639
755
  return `get ${col.name}_nulls() {
640
756
  return undefined;
641
757
  }`;
642
758
  }
643
759
 
760
+ const ensureStorage = generateEnsureStringStorage(col);
644
761
  return `get ${col.name}_nulls() {
762
+ if (this._descriptor.state !== 'live') throw new Error('Cannot read a released WASM buffer');
763
+ ${ensureStorage}
645
764
  return this._${col.name}_nulls;
646
765
  }`;
647
766
  }
@@ -652,21 +771,19 @@ function generateStringNullsGetter(col: ColumnMeta): string {
652
771
  */
653
772
  function generateColumnMethods(columnMeta: ColumnMeta[]): string {
654
773
  const methods: string[] = [];
774
+ let layoutIndex = 0;
655
775
 
656
776
  for (const col of columnMeta) {
657
- // Skip 'message' - handled specially by generateMessageMethods()
658
- if (col.name === 'message') {
659
- continue;
660
- }
661
-
777
+ if (col.name === 'message') continue;
662
778
  if (col.sizeClass === 'string') {
663
779
  methods.push(generateStringSetter(col));
664
780
  methods.push(generateStringValuesGetter(col));
665
781
  methods.push(generateStringNullsGetter(col));
666
782
  } else {
667
- methods.push(generateNumericSetter(col));
668
- methods.push(generateNumericValuesGetter(col));
669
- methods.push(generateNumericNullsGetter(col));
783
+ methods.push(generateNumericSetter(col, layoutIndex));
784
+ methods.push(generateNumericValuesGetter(col, layoutIndex));
785
+ methods.push(generateNumericNullsGetter(col, layoutIndex));
786
+ layoutIndex++;
670
787
  }
671
788
  }
672
789
 
@@ -677,11 +794,8 @@ function generateColumnMethods(columnMeta: ColumnMeta[]): string {
677
794
  // Class Cache and Generation
678
795
  // =============================================================================
679
796
 
680
- /**
681
- * Cache for generated WASM SpanBuffer classes.
682
- * Key is the schema object reference (WeakMap for GC).
683
- */
684
- const wasmSpanBufferClassCache = new WeakMap<LogSchema, WasmSpanBufferConstructor>();
797
+ /** Cache for generated WASM SpanBuffer classes, partitioned by schema and message family. */
798
+ const wasmSpanBufferClassCache = new WeakMap<LogSchema, Map<string, object>>();
685
799
 
686
800
  /**
687
801
  * Generate a WASM SpanBuffer class for the given schema.
@@ -691,61 +805,141 @@ const wasmSpanBufferClassCache = new WeakMap<LogSchema, WasmSpanBufferConstructo
691
805
  * @param schema - LogSchema defining the buffer structure
692
806
  * @returns WasmSpanBufferConstructor for creating buffer instances
693
807
  */
694
- export function getWasmSpanBufferClass(schema: LogSchema): WasmSpanBufferConstructor {
695
- const cached = wasmSpanBufferClassCache.get(schema);
696
- if (cached) {
808
+ export function getWasmSpanBufferClass<T extends LogSchema>(
809
+ schema: T,
810
+ messageLayoutFamily: MessageLayoutFamily = 'mixed',
811
+ messagePhysicalLayout: MessagePhysicalLayout = 'current',
812
+ eagerColumns: EagerColumnDescriptor = EMPTY_EAGER_COLUMNS,
813
+ ): WasmSpanBufferConstructor<T> {
814
+ const cacheKey = `${messageLayoutFamily}:${messagePhysicalLayout}:${eagerColumns.key}`;
815
+ let familyClasses = wasmSpanBufferClassCache.get(schema);
816
+ const cached = familyClasses?.get(cacheKey);
817
+ if (cached !== undefined && isWasmSpanBufferConstructor(cached, schema, messageLayoutFamily, messagePhysicalLayout))
697
818
  return cached;
698
- }
699
819
 
700
- // Pre-compute column metadata
701
- const columnMeta = buildColumnMeta(schema);
820
+ const columnMeta = buildColumnMeta(schema, eagerColumns);
702
821
 
703
822
  // Generate ONLY schema-specific code (constructor + column methods)
704
823
  const classCode = `
705
824
  class WasmSpanBuffer {
706
825
  constructor(opts) {
707
- // Store allocator and capacity
708
826
  this._allocator = opts.allocator;
709
827
  this._capacity = opts.capacity;
710
828
  this._logSchema = opts.logSchema;
711
-
712
- // Allocate identity block from WASM (root has trace_id, child does not)
713
- if (opts.trace_id) {
714
- const traceIdBytes = new TextEncoder().encode(opts.trace_id);
715
- const packed = opts.allocator.allocIdentityRootForJsWrite(traceIdBytes.length);
716
- this._identityPtr = Number(packed >> 32n);
717
- const traceIdOffset = Number(packed & 0xFFFFFFFFn);
718
- new Uint8Array(opts.allocator.memory.buffer).set(traceIdBytes, traceIdOffset);
719
- } else {
720
- this._identityPtr = opts.allocator.allocIdentityChild();
721
- }
722
-
723
- // Allocate system block from WASM
724
- this._systemPtr = opts.allocator.allocSpanSystem();
725
-
726
- // Initialize column pointers as unallocated (-1)
727
- this._columnPtrs = new Int32Array(${columnMeta.length}).fill(-1);
728
-
729
- // Tree structure
730
- this._parent = null;
731
- this._children = [];
829
+ this._parent = opts._parent ?? null;
830
+ this._nodeIndex = 4294967295;
831
+ this._topologyGeneration = 0;
732
832
  this._overflow = null;
833
+ this._overflowWriteIndex = 0;
834
+ this._layout = opts._layout;
835
+ if (this._layout.messageLayoutFamily !== '${messageLayoutFamily}') {
836
+ throw new TypeError('WASM layout descriptor does not match its generated message family');
837
+ }
838
+ if (this._layout.messagePhysicalLayout !== '${messagePhysicalLayout}') {
839
+ throw new TypeError('WASM layout descriptor does not match its generated physical layout');
840
+ }
733
841
 
734
- // Assign context properties from opts
735
842
  this._traceRoot = opts._traceRoot;
736
843
  this._scopeValues = opts._scopeValues;
737
844
  this._opMetadata = opts._opMetadata;
738
845
  this._callsiteMetadata = opts._callsiteMetadata;
846
+ this._vocabularyGeneration = opts._vocabularyGeneration;
847
+ this._statsSealed = false;
848
+ this._statsReservedRows = 2;
849
+ this._columnPtrs = new Int32Array(${columnMeta.length}).fill(-1);
850
+ ${
851
+ messageLayoutFamily === 'static-only'
852
+ ? `this._spanName = undefined;
853
+ this._terminalMessage = undefined;`
854
+ : messageLayoutFamily === 'dynamic-only'
855
+ ? `this._spanName = undefined;
856
+ this._message = new Array(opts.capacity);`
857
+ : 'this._message = new Array(opts.capacity);'
858
+ }
859
+ ${generateColumnInit(columnMeta)}
860
+
861
+ const identityMode = opts._identityMode ?? 'root';
862
+ this._identityOwner = identityMode !== 'overflow';
863
+ this._identitySource = identityMode === 'overflow' ? opts._identitySource : undefined;
864
+ const superblock = this._identityOwner ? this._layout.spanSuperblock : this._layout.overflowSuperblock;
865
+ this._allocationByteLength = superblock.byteLength;
866
+ if (identityMode === 'overflow') {
867
+ if (!this._identitySource) throw new Error('Overflow buffer requires an identity owner');
868
+ this._allocationPtr = opts.allocator.createOverflowSpan(superblock.byteLength);
869
+ this._identityPtr = this._identitySource._identityPtr;
870
+ } else {
871
+ const traceRootPtr = opts._traceRoot._traceRootPtr;
872
+ if (!Number.isInteger(traceRootPtr) || traceRootPtr <= 0) {
873
+ throw new TypeError('WASM span requires a live WasmTraceRoot');
874
+ }
875
+ const traceIdBytes = identityMode === 'root' ? opts._traceRoot._traceIdBytes : undefined;
876
+ this._allocationPtr = opts.allocator.createAndStartSpan(
877
+ identityMode === 'child' ? ${WASM_SPAN_IDENTITY_CHILD} : ${WASM_SPAN_IDENTITY_ROOT},
878
+ traceIdBytes?.length ?? 0,
879
+ superblock.byteLength,
880
+ superblock.systemOffset,
881
+ this._layout.system.entryTypeOffset ?? ${WASM_NO_LAYOUT_OFFSET},
882
+ this._layout.system.rowHeaderOffset ?? ${WASM_NO_LAYOUT_OFFSET},
883
+ traceRootPtr,
884
+ );
885
+ this._identityPtr = this._allocationPtr;
886
+ if (this._allocationPtr !== 0 && traceIdBytes !== undefined) {
887
+ opts.allocator.u8.set(traceIdBytes, this._identityPtr + 9);
888
+ }
889
+ }
890
+ if (this._allocationPtr === 0) throw new Error('WASM span superblock allocation failed');
739
891
 
740
- // Initialize message array
741
- this._message = new Array(opts.capacity);
892
+ this._systemPtr = this._allocationPtr + superblock.systemOffset;
893
+ const familyPtrs = { u8: 0, u32: 0, f64: 0 };
894
+ for (const family of ['u8', 'u32', 'f64']) {
895
+ const familyOffset = superblock.familyOffsets[family];
896
+ if (familyOffset !== 0) familyPtrs[family] = this._allocationPtr + familyOffset;
897
+ }
898
+ this._familyPtrs = Object.freeze(familyPtrs);
899
+ const allocatorWords = opts.allocator.u32;
900
+ this._threadId =
901
+ this._identitySource?.thread_id ?? ((BigInt(allocatorWords[7]) << 32n) | BigInt(allocatorWords[6]));
902
+ this._spanId = this._identitySource?.span_id ?? allocatorWords[(this._identityPtr + 4) >>> 2];
903
+ this._viewVersion = 0;
904
+ this._spanStartedAtAllocation = this._identityOwner;
905
+
906
+ this._descriptor = {
907
+ generation: opts._generation ?? 0,
908
+ kind: identityMode,
909
+ state: 'live',
910
+ layout: this._layout,
911
+ allocationPtr: this._allocationPtr,
912
+ allocationByteLength: this._allocationByteLength,
913
+ systemPtr: this._systemPtr,
914
+ familyPtrs: this._familyPtrs,
915
+ identityPtr: this._identityPtr,
916
+ ownsIdentity: this._identityOwner,
917
+ memoryVersion: 0,
918
+ parent: opts._parent?._descriptor,
919
+ };
920
+
921
+ try {
922
+ this._refreshViews(this._allocator.refreshViews());
923
+ } catch (error) {
924
+ this.free();
925
+ throw error;
926
+ }
927
+ }
928
+
929
+ _refreshViews(version) {
930
+ ${generateViewRefresh(columnMeta, messageLayoutFamily, messagePhysicalLayout)}
931
+ }
742
932
 
743
- // Initialize eager columns
744
- ${generateEagerColumnInit(columnMeta)}
933
+ _getWasmColumnValue(columnIndex) {
934
+ ${generateColumnLookup(columnMeta, false)}
935
+ }
936
+
937
+ _getWasmColumnNulls(columnIndex) {
938
+ ${generateColumnLookup(columnMeta, true)}
745
939
  }
746
940
 
747
- // Schema-specific column methods (ONLY part that differs per schema)
748
941
  ${generateColumnMethods(columnMeta)}
942
+
749
943
  }
750
944
 
751
945
  return WasmSpanBuffer;
@@ -764,11 +958,13 @@ return WasmSpanBuffer;
764
958
  enumerable: true,
765
959
  configurable: true,
766
960
  });
767
- Object.defineProperty(WasmSpanBufferClass.prototype, 'entry_type', {
768
- get: wasmGetEntryType,
769
- enumerable: true,
770
- configurable: true,
771
- });
961
+ if (messagePhysicalLayout !== 'packed') {
962
+ Object.defineProperty(WasmSpanBufferClass.prototype, 'entry_type', {
963
+ get: wasmGetEntryType,
964
+ enumerable: true,
965
+ configurable: true,
966
+ });
967
+ }
772
968
  Object.defineProperty(WasmSpanBufferClass.prototype, '_writeIndex', {
773
969
  get: wasmGetWriteIndex,
774
970
  set: wasmSetWriteIndex,
@@ -805,13 +1001,20 @@ return WasmSpanBuffer;
805
1001
  enumerable: true,
806
1002
  configurable: true,
807
1003
  });
808
- Object.defineProperty(WasmSpanBufferClass.prototype, 'message_values', {
809
- get: wasmGetMessageValues,
1004
+ if (messageLayoutFamily !== 'static-only') {
1005
+ Object.defineProperty(WasmSpanBufferClass.prototype, 'message_values', {
1006
+ get: wasmGetMessageValues,
1007
+ enumerable: true,
1008
+ configurable: true,
1009
+ });
1010
+ }
1011
+ Object.defineProperty(WasmSpanBufferClass.prototype, '_messageLayoutFamily', {
1012
+ get: wasmGetMessageLayoutFamily,
810
1013
  enumerable: true,
811
1014
  configurable: true,
812
1015
  });
813
- Object.defineProperty(WasmSpanBufferClass.prototype, 'message_nulls', {
814
- get: wasmGetMessageNulls,
1016
+ Object.defineProperty(WasmSpanBufferClass.prototype, '_messagePhysicalLayout', {
1017
+ get: wasmGetMessagePhysicalLayout,
815
1018
  enumerable: true,
816
1019
  configurable: true,
817
1020
  });
@@ -848,12 +1051,15 @@ return WasmSpanBuffer;
848
1051
  configurable: true,
849
1052
  });
850
1053
 
851
- WasmSpanBufferClass.prototype.message = wasmMessage;
1054
+ WasmSpanBufferClass.prototype._ensureWasmViews = wasmEnsureViews;
1055
+ WasmSpanBufferClass.prototype.message = messageLayoutFamily === 'static-only' ? wasmStaticMessage : wasmRawMessage;
852
1056
  WasmSpanBufferClass.prototype.free = wasmFree;
853
1057
  WasmSpanBufferClass.prototype.isColumnAllocated = wasmIsColumnAllocated;
854
1058
  WasmSpanBufferClass.prototype.getColumnIfAllocated = wasmGetColumnIfAllocated;
855
1059
  WasmSpanBufferClass.prototype.getNullsIfAllocated = wasmGetNullsIfAllocated;
856
1060
  WasmSpanBufferClass.prototype.getOrCreateOverflow = wasmGetOrCreateOverflow;
1061
+ WasmSpanBufferClass.prototype._sealStats = wasmSealStats;
1062
+ WasmSpanBufferClass.prototype._sealStatsChain = wasmSealStatsChain;
857
1063
  WasmSpanBufferClass.prototype[Symbol.for('nodejs.util.inspect.custom')] = wasmInspect;
858
1064
 
859
1065
  // Add static schema property
@@ -863,6 +1069,24 @@ return WasmSpanBuffer;
863
1069
  enumerable: true,
864
1070
  configurable: false,
865
1071
  });
1072
+ Object.defineProperty(WasmSpanBufferClass, 'messageLayoutFamily', {
1073
+ value: messageLayoutFamily,
1074
+ writable: false,
1075
+ enumerable: true,
1076
+ configurable: false,
1077
+ });
1078
+ Object.defineProperty(WasmSpanBufferClass, 'messagePhysicalLayout', {
1079
+ value: messagePhysicalLayout,
1080
+ writable: false,
1081
+ enumerable: true,
1082
+ configurable: false,
1083
+ });
1084
+ Object.defineProperty(WasmSpanBufferClass, 'eagerColumns', {
1085
+ value: eagerColumns,
1086
+ writable: false,
1087
+ enumerable: true,
1088
+ configurable: false,
1089
+ });
866
1090
 
867
1091
  // Add static stats property (required by SpanContext and arrow-builder's ColumnWriter)
868
1092
  // This matches the structure in spanBuffer.ts
@@ -879,8 +1103,13 @@ return WasmSpanBuffer;
879
1103
  configurable: true,
880
1104
  });
881
1105
 
882
- // Cache the class
883
- wasmSpanBufferClassCache.set(schema, WasmSpanBufferClass);
1106
+ if (!isWasmSpanBufferConstructor(WasmSpanBufferClass, schema)) {
1107
+ throw new Error('Generated WasmSpanBuffer constructor lost its schema identity');
1108
+ }
1109
+
1110
+ familyClasses ??= new Map();
1111
+ familyClasses.set(cacheKey, WasmSpanBufferClass);
1112
+ wasmSpanBufferClassCache.set(schema, familyClasses);
884
1113
 
885
1114
  return WasmSpanBufferClass;
886
1115
  }
@@ -898,24 +1127,40 @@ return WasmSpanBuffer;
898
1127
  * @param _scopeValues - Scope values for this span
899
1128
  * @param _opMetadata - Op metadata for attribution
900
1129
  * @param _callsiteMetadata - Callsite metadata for attribution
901
- * @returns WasmSpanBufferInstance
1130
+ * @returns Schema-specific WASM buffer instance
902
1131
  */
903
- export function createWasmSpanBuffer(
904
- schema: LogSchema,
905
- opts: Omit<WasmSpanBufferOptions, 'logSchema' | '_traceRoot' | '_scopeValues' | '_opMetadata' | '_callsiteMetadata'>,
906
- _traceRoot: ITraceRoot,
1132
+ export function createWasmSpanBuffer<T extends LogSchema>(
1133
+ schema: T,
1134
+ opts: Omit<
1135
+ WasmSpanBufferOptions<T>,
1136
+ | 'logSchema'
1137
+ | '_traceRoot'
1138
+ | '_scopeValues'
1139
+ | '_opMetadata'
1140
+ | '_callsiteMetadata'
1141
+ | '_vocabularyGeneration'
1142
+ | '_layout'
1143
+ >,
1144
+ _traceRoot: ITraceRoot<T>,
907
1145
  _scopeValues: Readonly<Record<string, unknown>>,
908
1146
  _opMetadata: OpMetadata,
909
1147
  _callsiteMetadata: OpMetadata,
910
- ): WasmSpanBufferInstance {
911
- const WasmSpanBufferClass = getWasmSpanBufferClass(schema);
1148
+ ): WasmSpanBufferInstance<T> {
1149
+ const messageLayoutFamily = opts.messageLayoutFamily ?? 'mixed';
1150
+ const messagePhysicalLayout = opts.messagePhysicalLayout ?? 'current';
1151
+ const eagerColumns = _opMetadata._physicalLayoutPlan?.eagerColumns ?? EMPTY_EAGER_COLUMNS;
1152
+ const WasmSpanBufferClass = getWasmSpanBufferClass(schema, messageLayoutFamily, messagePhysicalLayout, eagerColumns);
912
1153
  return new WasmSpanBufferClass({
913
1154
  ...opts,
1155
+ _identityMode: 'root',
1156
+ _generation: ++nextWasmBufferGeneration,
1157
+ _layout: getWasmPhysicalLayout(schema, opts.capacity, messageLayoutFamily, messagePhysicalLayout, eagerColumns),
914
1158
  logSchema: schema,
915
1159
  _traceRoot,
916
1160
  _scopeValues,
917
1161
  _opMetadata,
918
1162
  _callsiteMetadata,
1163
+ _vocabularyGeneration: _opMetadata._physicalLayoutPlan?.vocabularyGeneration ?? getVocabularyGeneration(),
919
1164
  });
920
1165
  }
921
1166
 
@@ -931,47 +1176,58 @@ export function createWasmSpanBuffer(
931
1176
  * @param _scopeValues - Scope values for this span
932
1177
  * @param _opMetadata - Op metadata for attribution
933
1178
  * @param _callsiteMetadata - Callsite metadata for attribution
934
- * @returns WasmSpanBufferInstance linked to parent
1179
+ * @returns Schema-specific child WASM buffer
935
1180
  */
936
- export function createWasmChildSpanBuffer(
937
- parent: WasmSpanBufferInstance,
1181
+ export function createWasmChildSpanBuffer<T extends LogSchema>(
1182
+ parent: WasmSpanBufferInstance<T>,
938
1183
  opts: Omit<
939
- WasmSpanBufferOptions,
1184
+ WasmSpanBufferOptions<T>,
940
1185
  | 'logSchema'
941
- | 'trace_id'
942
- | 'parent_thread_id'
943
- | 'parent_span_id'
944
1186
  | '_traceRoot'
945
1187
  | '_scopeValues'
946
1188
  | '_opMetadata'
947
1189
  | '_callsiteMetadata'
1190
+ | '_vocabularyGeneration'
1191
+ | '_layout'
948
1192
  > & {
949
- schema?: LogSchema;
1193
+ schema?: T;
950
1194
  },
951
- _traceRoot: ITraceRoot,
1195
+ _traceRoot: ITraceRoot<T>,
952
1196
  _scopeValues: Readonly<Record<string, unknown>>,
953
1197
  _opMetadata: OpMetadata,
954
1198
  _callsiteMetadata: OpMetadata,
955
- ): WasmSpanBufferInstance {
1199
+ ): WasmSpanBufferInstance<T> {
956
1200
  // Use provided schema (for cross-library calls) or parent's schema
957
1201
  const childSchema = opts.schema ?? parent._logSchema;
958
- const WasmSpanBufferClass = getWasmSpanBufferClass(childSchema);
959
-
1202
+ const messageLayoutFamily = opts.messageLayoutFamily ?? parent._messageLayoutFamily;
1203
+ const messagePhysicalLayout = opts.messagePhysicalLayout ?? parent._messagePhysicalLayout;
1204
+ const eagerColumns = _opMetadata._physicalLayoutPlan?.eagerColumns ?? EMPTY_EAGER_COLUMNS;
1205
+ const WasmSpanBufferClass = getWasmSpanBufferClass(
1206
+ childSchema,
1207
+ messageLayoutFamily,
1208
+ messagePhysicalLayout,
1209
+ eagerColumns,
1210
+ );
960
1211
  const child = new WasmSpanBufferClass({
961
1212
  ...opts,
962
- logSchema: childSchema, // Use the child's schema (may differ from parent)
963
- trace_id: parent.trace_id,
964
- parent_thread_id: parent.thread_id,
965
- parent_span_id: parent.span_id,
1213
+ logSchema: childSchema,
1214
+ _identityMode: 'child',
1215
+ _parent: parent,
1216
+ _generation: ++nextWasmBufferGeneration,
1217
+ _layout: getWasmPhysicalLayout(
1218
+ childSchema,
1219
+ opts.capacity,
1220
+ messageLayoutFamily,
1221
+ messagePhysicalLayout,
1222
+ eagerColumns,
1223
+ ),
966
1224
  _traceRoot,
967
1225
  _scopeValues,
968
1226
  _opMetadata,
969
1227
  _callsiteMetadata,
1228
+ _vocabularyGeneration: _opMetadata._physicalLayoutPlan?.vocabularyGeneration ?? getVocabularyGeneration(),
970
1229
  });
971
1230
 
972
- // Link to parent (SpanContext will push to parent._children with possible RemappedBufferView wrapper)
973
- child._parent = parent;
974
-
975
1231
  return child;
976
1232
  }
977
1233
 
@@ -983,35 +1239,35 @@ export function createWasmChildSpanBuffer(
983
1239
  * @param _scopeValues - Scope values for this span
984
1240
  * @param _opMetadata - Op metadata for attribution
985
1241
  * @param _callsiteMetadata - Callsite metadata for attribution
986
- * @returns WasmSpanBufferInstance linked as overflow
1242
+ * @returns Schema-specific overflow WASM buffer
987
1243
  */
988
- export function createWasmOverflowBuffer(
989
- buffer: WasmSpanBufferInstance,
990
- _traceRoot: ITraceRoot,
1244
+ export function createWasmOverflowBuffer<T extends LogSchema>(
1245
+ buffer: WasmSpanBufferInstance<T>,
1246
+ _traceRoot: ITraceRoot<T>,
991
1247
  _scopeValues: Readonly<Record<string, unknown>>,
992
1248
  _opMetadata: OpMetadata,
993
1249
  _callsiteMetadata: OpMetadata,
994
- ): WasmSpanBufferInstance {
995
- const WasmSpanBufferClass = getWasmSpanBufferClass(buffer._logSchema);
1250
+ ): WasmSpanBufferInstance<T> {
1251
+ const WasmSpanBufferClass = getWasmSpanBufferConstructor(buffer);
996
1252
 
997
1253
  const overflow = new WasmSpanBufferClass({
998
1254
  allocator: buffer._allocator,
999
1255
  capacity: buffer._capacity,
1000
- trace_id: buffer.trace_id,
1001
- thread_id: buffer.thread_id,
1002
- span_id: buffer.span_id,
1003
- parent_thread_id: buffer.parent_thread_id,
1004
- parent_span_id: buffer.parent_span_id,
1005
1256
  logSchema: buffer._logSchema,
1006
1257
  _traceRoot,
1007
1258
  _scopeValues,
1008
1259
  _opMetadata,
1009
1260
  _callsiteMetadata,
1261
+ _vocabularyGeneration: buffer._vocabularyGeneration,
1262
+ _identityMode: 'overflow',
1263
+ _identitySource: buffer._identitySource ?? buffer,
1264
+ _parent: buffer._parent,
1265
+ _generation: ++nextWasmBufferGeneration,
1266
+ _layout: buffer._layout,
1010
1267
  });
1011
-
1012
- // Link as overflow (not child - same logical span)
1013
- overflow._parent = buffer._parent;
1268
+ overflow._statsReservedRows = 0;
1014
1269
  buffer._overflow = overflow;
1270
+ buffer._descriptor.overflow = overflow._descriptor;
1015
1271
 
1016
1272
  return overflow;
1017
1273
  }