@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
@@ -25,15 +25,23 @@
25
25
  * @module spanBuffer
26
26
  */
27
27
 
28
- import { type ColumnBufferExtension, DEFAULT_BUFFER_CAPACITY, getColumnBufferClass } from '@smoothbricks/arrow-builder';
28
+ import {
29
+ activeMaterializerMode,
30
+ type ColumnBufferExtension,
31
+ DEFAULT_BUFFER_CAPACITY,
32
+ getColumnBufferClass,
33
+ type SchemaWithMetadata,
34
+ } from '@smoothbricks/arrow-builder';
29
35
  import { checkCapacityTuning } from './capacityTuning.js';
30
36
  import type { OpMetadata } from './opContext/opTypes.js';
31
- import type { LogSchema } from './schema/LogSchema.js';
32
- import { textEncoder } from './spanBufferHelpers.js';
37
+ import type { EagerColumnDescriptor } from './physicalLayoutPlan.js';
38
+ import type { MessageLayoutFamily, MessagePhysicalLayout } from './runtimeHint.js';
39
+ import { LogSchema } from './schema/LogSchema.js';
33
40
  import type { SpanBufferStats } from './spanBufferStats.js';
34
- import { writeThreadIdToUint64Array } from './threadId.js';
41
+ import { copyThreadIdTo, getThreadId } from './threadId.js';
35
42
  import type { ITraceRoot } from './traceRoot.js';
36
43
  import type { AnySpanBuffer, SpanBuffer } from './types.js';
44
+ import { getVocabularyGeneration, type VocabularyGeneration } from './vocabularyRegistry.js';
37
45
 
38
46
  // Re-export ITraceRoot for external consumers (and to satisfy linter - it's used in generated code)
39
47
  export type { ITraceRoot };
@@ -44,8 +52,6 @@ export type { ITraceRoot };
44
52
  */
45
53
  export const EMPTY_SCOPE: Readonly<Record<string, unknown>> = Object.freeze({});
46
54
 
47
- const traceIdDecoder = new TextDecoder();
48
-
49
55
  // ============================================================================
50
56
  // Thread-local state (generated once per process/worker)
51
57
  // These are used by the generated class code via the constructorPreamble
@@ -132,11 +138,15 @@ export interface SpanBufferConstructor<T extends LogSchema = LogSchema> {
132
138
  callsiteMetadata: OpMetadata | undefined,
133
139
  opMetadata: OpMetadata | undefined,
134
140
  traceRoot: ITraceRoot,
141
+ vocabularyGeneration: VocabularyGeneration,
135
142
  ): SpanBuffer<T>;
136
143
 
137
144
  // Static properties added after class generation
138
145
  readonly schema: T;
139
146
  readonly stats: SpanBufferStats;
147
+ readonly messageLayoutFamily: MessageLayoutFamily;
148
+ readonly messagePhysicalLayout: MessagePhysicalLayout;
149
+ readonly eagerColumns: EagerColumnDescriptor;
140
150
  }
141
151
  //#endregion smoo/lmao!n/spanbuffer-layout.class-signature
142
152
 
@@ -148,6 +158,7 @@ type GeneratedSpanBufferClass<T extends LogSchema> = new (
148
158
  callsiteMetadata: OpMetadata | undefined,
149
159
  opMetadata: OpMetadata | undefined,
150
160
  traceRoot: ITraceRoot,
161
+ vocabularyGeneration: VocabularyGeneration,
151
162
  ) => SpanBuffer<T>;
152
163
 
153
164
  function isGeneratedSpanBufferClass<T extends LogSchema>(
@@ -160,6 +171,8 @@ function isGeneratedSpanBufferClass<T extends LogSchema>(
160
171
 
161
172
  return (
162
173
  typeof Reflect.get(prototype, 'getOrCreateOverflow') === 'function' &&
174
+ typeof Reflect.get(prototype, '_sealStats') === 'function' &&
175
+ typeof Reflect.get(prototype, '_sealStatsChain') === 'function' &&
163
176
  typeof Reflect.get(prototype, 'isParentOf') === 'function' &&
164
177
  typeof Reflect.get(prototype, 'isChildOf') === 'function'
165
178
  );
@@ -186,23 +199,438 @@ function getSpanBufferConstructorForBuffer<T extends LogSchema>(buffer: SpanBuff
186
199
 
187
200
  function createSpanBufferConstructor<T extends LogSchema>(
188
201
  schema: T,
202
+ messageLayoutFamily: MessageLayoutFamily,
203
+ messagePhysicalLayout: MessagePhysicalLayout,
189
204
  generatedClass: GeneratedSpanBufferClass<T>,
205
+ eagerColumns: EagerColumnDescriptor,
190
206
  ): SpanBufferConstructor<T> {
191
- return Object.assign(generatedClass, {
192
- schema,
207
+ Object.defineProperties(generatedClass, {
208
+ schema: { value: schema, enumerable: true },
209
+ messageLayoutFamily: { value: messageLayoutFamily, enumerable: true },
210
+ messagePhysicalLayout: { value: messagePhysicalLayout, enumerable: true },
211
+ eagerColumns: { value: eagerColumns, enumerable: true },
193
212
  stats: {
194
- capacity: DEFAULT_BUFFER_CAPACITY,
195
- totalWrites: 0,
196
- spansCreated: 0,
197
- } satisfies SpanBufferStats,
213
+ value: {
214
+ capacity: DEFAULT_BUFFER_CAPACITY,
215
+ totalWrites: 0,
216
+ spansCreated: 0,
217
+ } satisfies SpanBufferStats,
218
+ enumerable: true,
219
+ },
198
220
  });
221
+
222
+ if (!isSpanBufferConstructorForSchema(generatedClass, schema)) {
223
+ throw new TypeError('Generated column buffer constructor initialization failed');
224
+ }
225
+ return generatedClass;
199
226
  }
200
227
 
201
228
  /**
202
229
  * Cache for generated SpanBuffer classes per schema.
203
230
  * Key is the schema object reference (WeakMap for GC).
204
231
  */
205
- const spanBufferClassCache = new WeakMap<LogSchema, SpanBufferConstructor>();
232
+ const spanBufferClassCache = new WeakMap<LogSchema, Map<string, object>>();
233
+ const messageStorageSchemas = new WeakMap<LogSchema, Map<string, LogSchema>>();
234
+ const EMPTY_EAGER_COLUMNS: EagerColumnDescriptor = Object.freeze({
235
+ names: Object.freeze([]),
236
+ words: Object.freeze([]),
237
+ key: '',
238
+ });
239
+
240
+ function resolveCompiledEagerColumns(schema: LogSchema, requestedNames: readonly string[]): EagerColumnDescriptor {
241
+ if (requestedNames.length === 0) return EMPTY_EAGER_COLUMNS;
242
+ const requested = new Set(requestedNames);
243
+ const names: string[] = [];
244
+ const words = new Array<number>(Math.ceil(schema._columnNames.length / 32)).fill(0);
245
+ for (let columnIndex = 0; columnIndex < schema._columnNames.length; columnIndex++) {
246
+ const name = schema._columnNames[columnIndex];
247
+ if (!requested.delete(name)) continue;
248
+ names.push(name);
249
+ const wordIndex = columnIndex >>> 5;
250
+ words[wordIndex] = (words[wordIndex] | (1 << (columnIndex & 31))) >>> 0;
251
+ }
252
+ if (requested.size !== 0) {
253
+ throw new TypeError(
254
+ `Unknown compiled eager column${requested.size === 1 ? '' : 's'}: ${[...requested].join(', ')}`,
255
+ );
256
+ }
257
+ while (words.length !== 0 && words[words.length - 1] === 0) words.pop();
258
+ const frozenWords = Object.freeze(words);
259
+ return Object.freeze({
260
+ names: Object.freeze(names),
261
+ words: frozenWords,
262
+ key: frozenWords.map((word) => word.toString(16).padStart(8, '0')).join(''),
263
+ });
264
+ }
265
+
266
+ function getStorageSchema(
267
+ schema: LogSchema,
268
+ messageLayoutFamily: MessageLayoutFamily,
269
+ messagePhysicalLayout: MessagePhysicalLayout,
270
+ ): LogSchema {
271
+ let byLayout = messageStorageSchemas.get(schema);
272
+ const key = `${messageLayoutFamily}:${messagePhysicalLayout}`;
273
+ let storageSchema = byLayout?.get(key);
274
+ if (storageSchema === undefined) {
275
+ const fields: Record<string, SchemaWithMetadata> = {};
276
+ for (const [name, field] of schema._columns) {
277
+ if (name !== 'message') fields[name] = field;
278
+ }
279
+ storageSchema = new LogSchema(fields);
280
+ byLayout ??= new Map();
281
+ byLayout.set(key, storageSchema);
282
+ messageStorageSchemas.set(schema, byLayout);
283
+ }
284
+ return storageSchema;
285
+ }
286
+
287
+ /**
288
+ * Canonicalize a ttsc-emitted SpanBuffer class into the same schema/layout cache
289
+ * used by the dynamic fallback. The factory is lazy so an already materialized
290
+ * class wins without allocating a duplicate constructor.
291
+ */
292
+ export function materializeCompiledSpanBufferClass<T extends LogSchema>(
293
+ schema: T,
294
+ messageLayoutFamily: MessageLayoutFamily,
295
+ messagePhysicalLayout: MessagePhysicalLayout,
296
+ eagerColumnNames: readonly string[],
297
+ factory: () => new (...args: never[]) => unknown,
298
+ ): SpanBufferConstructor<T> {
299
+ const eagerColumns = resolveCompiledEagerColumns(schema, eagerColumnNames);
300
+ // Mode-prefixed like arrow-builder's class caches: the materializer mode is fixed for a process,
301
+ // but the parity suite flips it via override and must never see the other mode's class.
302
+ const cacheKey = `${activeMaterializerMode()}:${messageLayoutFamily}:${messagePhysicalLayout}:${eagerColumns.key}`;
303
+ let familyClasses = spanBufferClassCache.get(schema);
304
+ const cached = familyClasses?.get(cacheKey);
305
+ if (
306
+ isSpanBufferConstructorForSchema(cached, schema) &&
307
+ cached.messageLayoutFamily === messageLayoutFamily &&
308
+ cached.messagePhysicalLayout === messagePhysicalLayout
309
+ ) {
310
+ return cached;
311
+ }
312
+
313
+ const compiledClass = factory();
314
+ if (!isGeneratedSpanBufferClass<T>(compiledClass)) {
315
+ throw new TypeError('Compiled SpanBuffer class is missing the required buffer contract');
316
+ }
317
+ const SpanBufferClass = createSpanBufferConstructor(
318
+ schema,
319
+ messageLayoutFamily,
320
+ messagePhysicalLayout,
321
+ compiledClass,
322
+ eagerColumns,
323
+ );
324
+ familyClasses ??= new Map();
325
+ familyClasses.set(cacheKey, SpanBufferClass);
326
+ spanBufferClassCache.set(schema, familyClasses);
327
+ return SpanBufferClass;
328
+ }
329
+
330
+ // ============================================================================
331
+ // No-eval (closure-composed) extension counterparts
332
+ // ============================================================================
333
+ // Production workerd forbids code generation from strings, so arrow-builder
334
+ // materializes the buffer class from closures there (see arrow-builder's
335
+ // closureMaterializers). The two factories below are the EXACT no-eval
336
+ // re-expressions of the code-string constructorPreamble/methods passed in the
337
+ // extension inside getSpanBufferClass — any change to one side MUST be
338
+ // mirrored on the other (spanbuffer-materializer-parity.test.ts compares the
339
+ // resulting buffers byte for byte).
340
+
341
+ /** Prototype/instance view used by the closure-composed members. */
342
+ type ClosureSelf = AnySpanBuffer & Record<string, unknown>;
343
+
344
+ // Thread-local span counter contract shared with the generated constructorPreamble
345
+ // (`globalThis.globalSpanCounter`), see threadId.ts docs.
346
+ declare global {
347
+ var globalSpanCounter: number | undefined;
348
+ }
349
+
350
+ function isSpanBufferConstructorValue(value: unknown): value is SpanBufferConstructor {
351
+ return typeof value === 'function';
352
+ }
353
+
354
+ function spanBufferStatics(self: AnySpanBuffer): SpanBufferConstructor {
355
+ const ctor = self.constructor;
356
+ if (!isSpanBufferConstructorValue(ctor)) throw new TypeError('SpanBuffer constructor is not initialized');
357
+ return ctor;
358
+ }
359
+
360
+ /** Constructor arguments arrive untyped through the materializer; every real buffer/traceRoot is an object. */
361
+ function isSpanBufferValue(value: unknown): value is AnySpanBuffer {
362
+ return typeof value === 'object' && value !== null;
363
+ }
364
+
365
+ function isTraceRootValue(value: unknown): value is ITraceRoot {
366
+ return typeof value === 'object' && value !== null;
367
+ }
368
+
369
+ function requireParent(parent: AnySpanBuffer | undefined): AnySpanBuffer {
370
+ if (parent === undefined) throw new TypeError('Chained/child SpanBuffer construction requires a parent buffer');
371
+ return parent;
372
+ }
373
+
374
+ /** Every runtime SpanBuffer instance satisfies the typed SpanBuffer contract (dynamic column lanes included). */
375
+ function isTypedSpanBuffer(value: AnySpanBuffer): value is SpanBuffer<LogSchema> {
376
+ return typeof value === 'object' && value !== null;
377
+ }
378
+
379
+ /** Mirrors the generated `_sealStats` body (shared with getOrCreateOverflow). */
380
+ function sealStatsFor(self: AnySpanBuffer): void {
381
+ if (self._statsSealed) return;
382
+ const completedRows = self._writeIndex - self._statsReservedRows;
383
+ if (completedRows > 0) spanBufferStatics(self).stats.totalWrites += completedRows;
384
+ self._statsSealed = true;
385
+ }
386
+
387
+ /**
388
+ * No-eval counterpart of the extension `constructorPreamble` (and its
389
+ * `constructorParams` signature): identical buffer-type branches (root /
390
+ * child / chained), identical physical system layout math, identical
391
+ * property-assignment order (V8 in-object slots).
392
+ */
393
+ function createSpanBufferClosureInit(
394
+ messageLayoutFamily: MessageLayoutFamily,
395
+ messagePhysicalLayout: MessagePhysicalLayout,
396
+ ): NonNullable<ColumnBufferExtension['closureInit']> {
397
+ return (self, requestedCapacity, ctorArgs) => {
398
+ // ctorArgs mirror constructorParams: stats, parent, isChained, callsiteMetadata, opMetadata, traceRoot, vocabularyGeneration
399
+ const parent = isSpanBufferValue(ctorArgs[1]) ? ctorArgs[1] : undefined;
400
+ const isChained = Boolean(ctorArgs[2]);
401
+ const callsiteMetadata = ctorArgs[3];
402
+ const opMetadata = ctorArgs[4];
403
+ const traceRootArg = ctorArgs[5];
404
+ if (!isTraceRootValue(traceRootArg)) throw new TypeError('SpanBuffer construction requires a traceRoot');
405
+ const traceRoot = traceRootArg;
406
+ const vocabularyGeneration = ctorArgs[6];
407
+
408
+ // Thread-local span counter (per-process/worker, see threadId.ts docs)
409
+ globalThis.globalSpanCounter = (globalThis.globalSpanCounter ?? 0) + 1;
410
+ const spanId = globalThis.globalSpanCounter;
411
+ const threadId = isChained ? requireParent(parent).thread_id : getThreadId();
412
+
413
+ // Calculate exact physical system storage (same math as the preamble variants).
414
+ let systemSize: number;
415
+ let rowHeaderOffset = 0;
416
+ let messageIdOffset = 0;
417
+ let logHeaderOffset = 0;
418
+ if (messagePhysicalLayout === 'packed') {
419
+ rowHeaderOffset = requestedCapacity * 8;
420
+ systemSize = (requestedCapacity * 12 + 7) & ~7;
421
+ } else if (messageLayoutFamily === 'dynamic-only') {
422
+ systemSize = (requestedCapacity * 9 + 7) & ~7;
423
+ } else if (messagePhysicalLayout === 'current') {
424
+ messageIdOffset = (requestedCapacity * 9 + 1) & ~1;
425
+ systemSize = (messageIdOffset + requestedCapacity * 2 + 7) & ~7;
426
+ } else {
427
+ logHeaderOffset = (requestedCapacity * 9 + 3) & ~3;
428
+ systemSize = (logHeaderOffset + requestedCapacity * 4 + 7) & ~7;
429
+ }
430
+
431
+ // CHAINED: shares parent._identity; CHILD: identity without traceId; ROOT: identity with traceId bytes.
432
+ let systemBuffer: ArrayBuffer;
433
+ let identityView: Uint8Array;
434
+ if (isChained) {
435
+ systemBuffer = new ArrayBuffer(systemSize);
436
+ identityView = requireParent(parent)._identity;
437
+ } else if (parent) {
438
+ const identitySize = 12;
439
+ systemBuffer = new ArrayBuffer(systemSize + identitySize);
440
+ identityView = new Uint8Array(systemBuffer, systemSize, identitySize);
441
+ copyThreadIdTo(identityView, 0);
442
+ identityView[8] = spanId;
443
+ identityView[9] = spanId >>> 8;
444
+ identityView[10] = spanId >>> 16;
445
+ identityView[11] = spanId >>> 24;
446
+ } else {
447
+ const traceIdBytes = traceRoot._traceIdBytes;
448
+ const identitySize = 13 + traceIdBytes.length;
449
+ systemBuffer = new ArrayBuffer(systemSize + identitySize);
450
+ identityView = new Uint8Array(systemBuffer, systemSize, identitySize);
451
+ copyThreadIdTo(identityView, 0);
452
+ identityView[8] = spanId;
453
+ identityView[9] = spanId >>> 8;
454
+ identityView[10] = spanId >>> 16;
455
+ identityView[11] = spanId >>> 24;
456
+ identityView[12] = traceIdBytes.length;
457
+ identityView.set(traceIdBytes, 13);
458
+ }
459
+
460
+ // Assign properties in the optimal V8 in-object slot order (same as the preamble).
461
+ self._writeIndex = 0;
462
+ self._capacity = requestedCapacity;
463
+ self._overflow = undefined;
464
+ self.timestamp = new BigInt64Array(systemBuffer, 0, requestedCapacity);
465
+ if (messagePhysicalLayout === 'packed') {
466
+ self._rowHeaders = new Uint32Array(systemBuffer, rowHeaderOffset, requestedCapacity);
467
+ } else {
468
+ self.entry_type = new Uint8Array(systemBuffer, requestedCapacity * 8, requestedCapacity);
469
+ if (messageLayoutFamily !== 'dynamic-only') {
470
+ if (messagePhysicalLayout === 'current') {
471
+ self._messageIds = new Uint16Array(systemBuffer, messageIdOffset, requestedCapacity);
472
+ } else {
473
+ self._logHeaders = new Uint32Array(systemBuffer, logHeaderOffset, requestedCapacity);
474
+ }
475
+ }
476
+ }
477
+ self._vocabularyGeneration = vocabularyGeneration;
478
+ if (messageLayoutFamily === 'static-only') {
479
+ self._spanName = undefined;
480
+ self._terminalMessage = undefined;
481
+ } else if (messageLayoutFamily === 'dynamic-only') {
482
+ self._spanName = undefined;
483
+ }
484
+ self._nodeIndex = 4294967295;
485
+ self._topologyGeneration = 0;
486
+ self._parent = isChained ? requireParent(parent)._parent : parent;
487
+ self._traceRoot = traceRoot;
488
+ self._scopeValues = parent ? parent._scopeValues : EMPTY_SCOPE;
489
+ self._threadId = threadId;
490
+ self._identity = identityView;
491
+ self._system = systemBuffer;
492
+ self._callsiteMetadata = callsiteMetadata;
493
+ self._opMetadata = opMetadata;
494
+ self._statsSealed = false;
495
+ self._statsReservedRows = isChained ? 0 : 2;
496
+ if (messageLayoutFamily !== 'static-only') {
497
+ self.message_values = new Array(requestedCapacity);
498
+ }
499
+ };
500
+ }
501
+
502
+ /**
503
+ * No-eval counterpart of the extension `methods` string: same members, same
504
+ * kinds (getter vs method), installed after the generated per-column members
505
+ * so same-named members override exactly like the compiled class body.
506
+ */
507
+ function createSpanBufferClosureMethods(
508
+ messageLayoutFamily: MessageLayoutFamily,
509
+ ): NonNullable<ColumnBufferExtension['closureMethods']> {
510
+ return (prototype) => {
511
+ const defineMethod = (name: string, value: unknown): void => {
512
+ Object.defineProperty(prototype, name, { value, writable: true, configurable: true, enumerable: false });
513
+ };
514
+ const defineGetter = (name: string, get: (this: ClosureSelf) => unknown): void => {
515
+ Object.defineProperty(prototype, name, { get, configurable: true, enumerable: false });
516
+ };
517
+
518
+ defineGetter('span_id', function () {
519
+ const b = this._identity;
520
+ return b ? (b[8] | (b[9] << 8) | (b[10] << 16) | (b[11] << 24)) >>> 0 : 0;
521
+ });
522
+ defineGetter('thread_id', function () {
523
+ return this._threadId;
524
+ });
525
+ defineGetter('trace_id', function () {
526
+ return this._traceRoot.trace_id;
527
+ });
528
+ defineGetter('_spanStartTime', function () {
529
+ return this.timestamp[0];
530
+ });
531
+ defineGetter('_lastLoggedTime', function () {
532
+ const chain: AnySpanBuffer[] = [];
533
+ let current: AnySpanBuffer | undefined = this;
534
+ while (current) {
535
+ chain.push(current);
536
+ current = current._overflow;
537
+ }
538
+ for (let i = chain.length - 1; i >= 0; i--) {
539
+ const buffer = chain[i];
540
+ for (let row = buffer._writeIndex - 1; row >= 0; row--) {
541
+ const ts = buffer.timestamp[row];
542
+ if (ts !== 0n) {
543
+ return ts;
544
+ }
545
+ }
546
+ }
547
+ return null;
548
+ });
549
+ defineGetter('_hasParent', function () {
550
+ return this._parent !== undefined;
551
+ });
552
+ defineGetter('parent_span_id', function () {
553
+ return this._parent?.span_id ?? 0;
554
+ });
555
+ defineGetter('parent_thread_id', function () {
556
+ return this._parent?.thread_id ?? 0n;
557
+ });
558
+ defineMethod('isParentOf', function (this: ClosureSelf, other: AnySpanBuffer) {
559
+ return this === other._parent;
560
+ });
561
+ defineMethod('isChildOf', function (this: ClosureSelf, other: AnySpanBuffer) {
562
+ return this._parent === other;
563
+ });
564
+ defineMethod('copyThreadIdTo', function (this: ClosureSelf, dest: Uint8Array, offset: number) {
565
+ const source = this._identity;
566
+ if (source) {
567
+ dest[offset] = source[0];
568
+ dest[offset + 1] = source[1];
569
+ dest[offset + 2] = source[2];
570
+ dest[offset + 3] = source[3];
571
+ dest[offset + 4] = source[4];
572
+ dest[offset + 5] = source[5];
573
+ dest[offset + 6] = source[6];
574
+ dest[offset + 7] = source[7];
575
+ } else {
576
+ dest.fill(0, offset, offset + 8);
577
+ }
578
+ });
579
+ defineMethod('copyParentThreadIdTo', function (this: ClosureSelf, dest: Uint8Array, offset: number) {
580
+ if (this._parent) this._parent.copyThreadIdTo(dest, offset);
581
+ else dest.fill(0, offset, offset + 8);
582
+ });
583
+ defineGetter('_logSchema', function () {
584
+ return spanBufferStatics(this).schema;
585
+ });
586
+ defineGetter('_messageLayoutFamily', function () {
587
+ return spanBufferStatics(this).messageLayoutFamily;
588
+ });
589
+ defineGetter('_messagePhysicalLayout', function () {
590
+ return spanBufferStatics(this).messagePhysicalLayout;
591
+ });
592
+ defineGetter('_columns', function () {
593
+ return spanBufferStatics(this).schema._columns;
594
+ });
595
+ defineGetter('_stats', function () {
596
+ return spanBufferStatics(this).stats;
597
+ });
598
+ defineMethod('_sealStats', function (this: ClosureSelf) {
599
+ sealStatsFor(this);
600
+ });
601
+ defineMethod('_sealStatsChain', function (this: ClosureSelf) {
602
+ let current: AnySpanBuffer | undefined = this;
603
+ while (current) {
604
+ sealStatsFor(current);
605
+ current = current._overflow;
606
+ }
607
+ });
608
+ defineMethod('getOrCreateOverflow', function (this: ClosureSelf) {
609
+ if (this._overflow) return this._overflow;
610
+ sealStatsFor(this);
611
+ const tracer = this._traceRoot.tracer;
612
+ tracer.onStatsWillResetFor(this);
613
+ checkCapacityTuning(spanBufferStatics(this).stats);
614
+ if (!isTypedSpanBuffer(this)) throw new TypeError('SpanBuffer instance is not initialized');
615
+ return tracer.bufferStrategy.createOverflowBuffer(this);
616
+ });
617
+ if (messageLayoutFamily === 'static-only') {
618
+ defineMethod('message', function (this: ClosureSelf, pos: number, val: string) {
619
+ if (pos === 0) this._spanName = val;
620
+ else if (pos === 1) this._terminalMessage = val;
621
+ else throw new RangeError('Static-only buffers only accept raw system messages at rows 0 and 1');
622
+ return this;
623
+ });
624
+ } else {
625
+ defineMethod('message', function (this: ClosureSelf, pos: number, val: string) {
626
+ const lane: unknown = this.message_values;
627
+ if (!Array.isArray(lane)) throw new TypeError('message_values lane is not allocated');
628
+ lane[pos] = val;
629
+ return this;
630
+ });
631
+ }
632
+ };
633
+ }
206
634
 
207
635
  /**
208
636
  * Get or create a SpanBuffer class for the given schema.
@@ -217,9 +645,20 @@ const spanBufferClassCache = new WeakMap<LogSchema, SpanBufferConstructor>();
217
645
  * @returns SpanBufferConstructor with static schema and stats properties
218
646
  */
219
647
  //#region smoo/lmao!n/spanbuffer-layout.constructor
220
- export function getSpanBufferClass<T extends LogSchema>(schema: T): SpanBufferConstructor<T> {
221
- const cached = spanBufferClassCache.get(schema);
222
- if (isSpanBufferConstructorForSchema(cached, schema)) {
648
+ export function getSpanBufferClass<T extends LogSchema>(
649
+ schema: T,
650
+ messageLayoutFamily: MessageLayoutFamily = 'mixed',
651
+ messagePhysicalLayout: MessagePhysicalLayout = 'current',
652
+ eagerColumns: EagerColumnDescriptor = EMPTY_EAGER_COLUMNS,
653
+ ): SpanBufferConstructor<T> {
654
+ const cacheKey = `${activeMaterializerMode()}:${messageLayoutFamily}:${messagePhysicalLayout}:${eagerColumns.key}`;
655
+ let familyClasses = spanBufferClassCache.get(schema);
656
+ const cached = familyClasses?.get(cacheKey);
657
+ if (
658
+ isSpanBufferConstructorForSchema(cached, schema) &&
659
+ cached.messageLayoutFamily === messageLayoutFamily &&
660
+ cached.messagePhysicalLayout === messagePhysicalLayout
661
+ ) {
223
662
  return cached;
224
663
  }
225
664
 
@@ -243,7 +682,7 @@ export function getSpanBufferClass<T extends LogSchema>(schema: T): SpanBufferCo
243
682
  // 2. _capacity - compared on EVERY write (overflow check)
244
683
  // 3. _overflow - checked when overflow occurs
245
684
  // 4. timestamp/entry_type - TypedArray refs accessed on EVERY write
246
- // 5. _children/_parent - modified/accessed during span creation
685
+ // 5. _nodeIndex/_parent - modified/accessed during span creation
247
686
  // 6. _traceRoot - accessed for timestamp anchors on EVERY write
248
687
  // 7. _identity/_logBinding - accessed during span operations
249
688
  // 8-N. Cold properties - only accessed during Arrow conversion
@@ -251,14 +690,20 @@ export function getSpanBufferClass<T extends LogSchema>(schema: T): SpanBufferCo
251
690
 
252
691
  // Define extension for arrow-builder's class generator
253
692
  const extension: ColumnBufferExtension = {
254
- constructorParams: 'stats, parent, isChained, callsiteMetadata, opMetadata, traceRoot',
693
+ preallocatedColumns: eagerColumns.names,
694
+ constructorParams: 'stats, parent, isChained, callsiteMetadata, opMetadata, traceRoot, vocabularyGeneration',
255
695
  dependencies: {
256
- writeThreadIdToUint64Array,
257
- textDecoder: traceIdDecoder,
258
- textEncoder,
696
+ copyThreadIdTo,
697
+ getThreadId,
259
698
  EMPTY_SCOPE,
260
699
  checkCapacityTuning,
261
700
  },
701
+ // No-eval counterparts consumed by arrow-builder's closure-composed
702
+ // materializer on runtimes without string codegen (workerd). They MUST
703
+ // mirror constructorPreamble/methods below exactly — see the factories
704
+ // above and spanbuffer-materializer-parity.test.ts.
705
+ closureInit: createSpanBufferClosureInit(messageLayoutFamily, messagePhysicalLayout),
706
+ closureMethods: createSpanBufferClosureMethods(messageLayoutFamily),
262
707
  // ==========================================================================
263
708
  // GENERATED CONSTRUCTOR PREAMBLE
264
709
  // ==========================================================================
@@ -284,12 +729,11 @@ export function getSpanBufferClass<T extends LogSchema>(schema: T): SpanBufferCo
284
729
  // V8 IN-OBJECT PROPERTY OPTIMIZATION:
285
730
  // Property assignment order determines which properties get fast in-object slots.
286
731
  // First ~10-12 properties are stored directly on the object (fastest access).
287
- // Slots 1-3: HOTTEST (every log entry) - _writeIndex, _capacity, _overflow
288
- // Slots 4-5: HOT (TypedArray refs) - timestamp, entry_type
289
- // Slots 6-7: WARM (tree structure) - _children, _parent
290
- // Slots 8-10: WARM (context) - _traceRoot, _scopeValues, _identity
291
- // Slots 11: WARM (system) - _system
292
- // Slots 12+: COLD (Arrow conversion only) - _callsiteMetadata, _opMetadata
732
+ // Slots 0-2: HOTTEST (every log entry) - _writeIndex, _capacity, _overflow
733
+ // Slots 3-6: HOT (storage refs) - timestamp, entry_type/header, message identity, vocabulary
734
+ // Slots 7-10: WARM (tree structure) - _nodeIndex, _topologyGeneration, _parent, _traceRoot
735
+ // Slots 11-14: WARM (context/identity) - _scopeValues, _threadId, _identity, _system
736
+ // Slots 15+: COLD (Arrow/stats/raw message) - metadata, stats, message_values
293
737
  // ==========================================================================
294
738
  constructorPreamble:
295
739
  // Thread-local span counter (per-process/worker, see threadId.ts docs)
@@ -297,84 +741,133 @@ export function getSpanBufferClass<T extends LogSchema>(schema: T): SpanBufferCo
297
741
  globalThis.globalSpanCounter = 0;
298
742
  }
299
743
  const spanId = ++globalThis.globalSpanCounter;
744
+ const threadId = isChained ? parent.thread_id : getThreadId();
300
745
  ` +
301
- // Calculate system buffer size: timestamps (8 bytes * cap) + entry_type (1 byte * cap)
302
- // Align to 8 bytes so identity's BigUint64Array offset is aligned
303
- `const rawSystemSize = requestedCapacity * 9;
304
- const systemSize = (rawSystemSize + 7) & ~7;
746
+ // Calculate exact physical system storage once for the generated class.
747
+ (messagePhysicalLayout === 'packed'
748
+ ? `const rowHeaderOffset = requestedCapacity * 8;
749
+ const systemSize = (requestedCapacity * 12 + 7) & ~7;
305
750
  let systemBuffer;
306
751
  let identityView;
307
- ` +
752
+ `
753
+ : messageLayoutFamily === 'dynamic-only'
754
+ ? `const systemSize = (requestedCapacity * 9 + 7) & ~7;
755
+ let systemBuffer;
756
+ let identityView;
757
+ `
758
+ : messagePhysicalLayout === 'current'
759
+ ? `const messageIdOffset = (requestedCapacity * 9 + 1) & ~1;
760
+ const systemSize = (messageIdOffset + requestedCapacity * 2 + 7) & ~7;
761
+ let systemBuffer;
762
+ let identityView;
763
+ `
764
+ : `const logHeaderOffset = (requestedCapacity * 9 + 3) & ~3;
765
+ const systemSize = (logHeaderOffset + requestedCapacity * 4 + 7) & ~7;
766
+ let systemBuffer;
767
+ let identityView;
768
+ `) +
308
769
  // CHAINED BUFFER: overflow storage for same logical span - shares parent identity
309
770
  `if (isChained) {
310
771
  systemBuffer = new ArrayBuffer(systemSize);
311
772
  identityView = parent._identity;
312
773
  }` +
313
774
  // CHILD BUFFER: new span in existing trace - gets identity without traceId
314
- // (traceId accessed via parent chain walk in trace_id getter)
315
775
  ` else if (parent) {
316
776
  const identitySize = 12;
317
777
  systemBuffer = new ArrayBuffer(systemSize + identitySize);
318
778
  identityView = new Uint8Array(systemBuffer, systemSize, identitySize);
319
- const threadIdArray = new BigUint64Array(systemBuffer, systemSize, 1);
320
- writeThreadIdToUint64Array(threadIdArray, 0);
321
- new DataView(systemBuffer, systemSize).setUint32(8, spanId, true);
779
+ copyThreadIdTo(identityView, 0);
780
+ identityView[8] = spanId;
781
+ identityView[9] = spanId >>> 8;
782
+ identityView[10] = spanId >>> 16;
783
+ identityView[11] = spanId >>> 24;
322
784
  }` +
323
- // ROOT BUFFER: new trace - gets identity with traceId from TraceRoot
785
+ // ROOT BUFFER: new trace - copies the trace-owned canonical bytes once into its public identity.
324
786
  ` else {
325
- const traceIdUtf8 = textEncoder.encode(traceRoot.trace_id);
326
- const identitySize = 13 + traceIdUtf8.length;
787
+ const traceIdBytes = traceRoot._traceIdBytes;
788
+ const identitySize = 13 + traceIdBytes.length;
327
789
  systemBuffer = new ArrayBuffer(systemSize + identitySize);
328
790
  identityView = new Uint8Array(systemBuffer, systemSize, identitySize);
329
- const view = new DataView(systemBuffer, systemSize);
330
- const threadIdArray = new BigUint64Array(systemBuffer, systemSize, 1);
331
- writeThreadIdToUint64Array(threadIdArray, 0);
332
- view.setUint32(8, spanId, true);
333
- identityView[12] = traceIdUtf8.length;
334
- identityView.set(traceIdUtf8, 13);
791
+ copyThreadIdTo(identityView, 0);
792
+ identityView[8] = spanId;
793
+ identityView[9] = spanId >>> 8;
794
+ identityView[10] = spanId >>> 16;
795
+ identityView[11] = spanId >>> 24;
796
+ identityView[12] = traceIdBytes.length;
797
+ identityView.set(traceIdBytes, 13);
335
798
  }
336
799
  ` +
337
- // Create TypedArray views for timestamps and entry_type
338
- `const timestampView = new BigInt64Array(systemBuffer, 0, requestedCapacity);
800
+ // Create exact physical views. Packed classes never create split entry/header views.
801
+ (messagePhysicalLayout === 'packed'
802
+ ? `const timestampView = new BigInt64Array(systemBuffer, 0, requestedCapacity);
803
+ const rowHeaderView = new Uint32Array(systemBuffer, rowHeaderOffset, requestedCapacity);
804
+ `
805
+ : `const timestampView = new BigInt64Array(systemBuffer, 0, requestedCapacity);
339
806
  const entryTypeView = new Uint8Array(systemBuffer, requestedCapacity * 8, requestedCapacity);
340
- timestampView.fill(0n);
341
- entryTypeView.fill(0);
342
807
  ` +
343
- // Assign properties in optimal order for V8 in-object slots (see comment above)
808
+ (messageLayoutFamily === 'dynamic-only'
809
+ ? ''
810
+ : messagePhysicalLayout === 'current'
811
+ ? ` const messageIdView = new Uint16Array(systemBuffer, messageIdOffset, requestedCapacity);
812
+ `
813
+ : ` const logHeaderView = new Uint32Array(systemBuffer, logHeaderOffset, requestedCapacity);
814
+ `)) +
815
+ // Assign properties in optimal order for V8 in-object slots.
344
816
  `this._writeIndex = 0;
345
817
  this._capacity = requestedCapacity;
346
818
  this._overflow = undefined;
347
819
  this.timestamp = timestampView;
348
- this.entry_type = entryTypeView;
349
- this._children = [];
820
+ ` +
821
+ (messagePhysicalLayout === 'packed'
822
+ ? ` this._rowHeaders = rowHeaderView;
823
+ `
824
+ : ` this.entry_type = entryTypeView;
825
+ ` +
826
+ (messageLayoutFamily === 'dynamic-only'
827
+ ? ''
828
+ : messagePhysicalLayout === 'current'
829
+ ? ` this._messageIds = messageIdView;
830
+ `
831
+ : ` this._logHeaders = logHeaderView;
832
+ `)) +
833
+ ` this._vocabularyGeneration = vocabularyGeneration;
834
+ ` +
835
+ (messageLayoutFamily === 'static-only'
836
+ ? ` this._spanName = undefined;
837
+ this._terminalMessage = undefined;
838
+ `
839
+ : messageLayoutFamily === 'dynamic-only'
840
+ ? ` this._spanName = undefined;
841
+ `
842
+ : '') +
843
+ ` this._nodeIndex = 4294967295;
844
+ this._topologyGeneration = 0;
350
845
  this._parent = isChained ? parent._parent : parent;
351
846
  this._traceRoot = traceRoot;
352
847
  this._scopeValues = parent ? parent._scopeValues : EMPTY_SCOPE;
848
+ this._threadId = threadId;
353
849
  this._identity = identityView;
354
850
  this._system = systemBuffer;
355
851
  this._callsiteMetadata = callsiteMetadata;
356
852
  this._opMetadata = opMetadata;
357
- `,
358
- // Generated methods for SpanBuffer - no comments in output
359
- // span_id: extract from identity bytes [8:12]
360
- // thread_id: extract from identity bytes [0:8]
361
- // trace_id: walk up parent chain to root, decode trace_id from identity
362
- // parent_span_id/parent_thread_id: delegate to parent
363
- // isParentOf/isChildOf: pointer comparison
364
- // copyThreadIdTo/copyParentThreadIdTo: copy thread_id bytes to destination
853
+ this._statsSealed = false;
854
+ this._statsReservedRows = isChained ? 0 : 2;
855
+ ` +
856
+ (messageLayoutFamily === 'static-only'
857
+ ? ''
858
+ : ` this.message_values = new Array(requestedCapacity);
859
+ `) +
860
+ ' ',
861
+ // Generated identity methods use canonical primitives/direct offsets without temporary views.
365
862
  methods: `get span_id() {
366
- return this._identity ? new DataView(this._identity.buffer, this._identity.byteOffset + 8).getUint32(0, true) : 0;
863
+ const b = this._identity;
864
+ return b ? (b[8] | (b[9] << 8) | (b[10] << 16) | (b[11] << 24)) >>> 0 : 0;
367
865
  }
368
866
  get thread_id() {
369
- return this._identity ? new DataView(this._identity.buffer, this._identity.byteOffset).getBigUint64(0, true) : 0n;
867
+ return this._threadId;
370
868
  }
371
869
  get trace_id() {
372
- let current = this;
373
- while (current._parent) { current = current._parent; }
374
- if (!current._identity) { return undefined; }
375
- const len = current._identity[12];
376
- const traceIdBytes = current._identity.subarray(13, 13 + len);
377
- return textDecoder.decode(traceIdBytes);
870
+ return this._traceRoot.trace_id;
378
871
  }
379
872
  get _spanStartTime() {
380
873
  return this.timestamp[0];
@@ -403,42 +896,90 @@ export function getSpanBufferClass<T extends LogSchema>(schema: T): SpanBufferCo
403
896
  isParentOf(other) { return this === other._parent; }
404
897
  isChildOf(other) { return this._parent === other; }
405
898
  copyThreadIdTo(dest, offset) {
406
- if (this._identity) {
407
- const view = new DataView(this._identity.buffer, this._identity.byteOffset);
408
- const threadId = view.getBigUint64(0, true);
409
- new DataView(dest.buffer, dest.byteOffset + offset).setBigUint64(0, threadId, true);
899
+ const source = this._identity;
900
+ if (source) {
901
+ dest[offset] = source[0];
902
+ dest[offset + 1] = source[1];
903
+ dest[offset + 2] = source[2];
904
+ dest[offset + 3] = source[3];
905
+ dest[offset + 4] = source[4];
906
+ dest[offset + 5] = source[5];
907
+ dest[offset + 6] = source[6];
908
+ dest[offset + 7] = source[7];
410
909
  } else {
411
- new DataView(dest.buffer, dest.byteOffset + offset).setBigUint64(0, 0n, true);
910
+ dest.fill(0, offset, offset + 8);
412
911
  }
413
912
  }
414
913
  copyParentThreadIdTo(dest, offset) {
415
- this._parent?.copyThreadIdTo(dest, offset) ?? new DataView(dest.buffer, dest.byteOffset + offset).setBigUint64(0, 0n, true);
914
+ if (this._parent) this._parent.copyThreadIdTo(dest, offset);
915
+ else dest.fill(0, offset, offset + 8);
416
916
  }
417
917
  get _logSchema() { return this.constructor.schema; }
918
+ get _messageLayoutFamily() { return this.constructor.messageLayoutFamily; }
919
+ get _messagePhysicalLayout() { return this.constructor.messagePhysicalLayout; }
418
920
  get _columns() { return this.constructor.schema._columns; }
419
921
  get _stats() { return this.constructor.stats; }
922
+ _sealStats() {
923
+ if (this._statsSealed) return;
924
+ const completedRows = this._writeIndex - this._statsReservedRows;
925
+ if (completedRows > 0) this.constructor.stats.totalWrites += completedRows;
926
+ this._statsSealed = true;
927
+ }
928
+ _sealStatsChain() {
929
+ let current = this;
930
+ while (current) {
931
+ current._sealStats();
932
+ current = current._overflow;
933
+ }
934
+ }
420
935
  getOrCreateOverflow() {
421
936
  if (this._overflow) return this._overflow;
937
+ this._sealStats();
422
938
  const tracer = this._traceRoot.tracer;
423
939
  tracer.onStatsWillResetFor(this);
424
940
  checkCapacityTuning(this.constructor.stats);
425
941
  return tracer.bufferStrategy.createOverflowBuffer(this);
426
942
  }
943
+ ${
944
+ messageLayoutFamily === 'static-only'
945
+ ? `message(pos, val) {
946
+ if (pos === 0) this._spanName = val;
947
+ else if (pos === 1) this._terminalMessage = val;
948
+ else throw new RangeError('Static-only buffers only accept raw system messages at rows 0 and 1');
949
+ return this;
950
+ }`
951
+ : `message(pos, val) {
952
+ this.message_values[pos] = val;
953
+ return this;
954
+ }`
955
+ }
427
956
  `,
428
957
  };
429
958
 
959
+ // Storage schema identity partitions source schema/family/physical classes; the extension cache key
960
+ // partitions eager-column selections without adding another dedicated cache object.
961
+ const storageSchema = getStorageSchema(schema, messageLayoutFamily, messagePhysicalLayout);
962
+
430
963
  // Generate class with arrow-builder
431
- const generatedClass = getColumnBufferClass(schema, extension);
964
+ const generatedClass = getColumnBufferClass(storageSchema, extension, storageSchema);
432
965
  if (!isGeneratedSpanBufferClass<T>(generatedClass)) {
433
966
  throw new TypeError('Generated column buffer is missing SpanBuffer methods');
434
967
  }
435
968
 
436
- // Add static properties to the generated class
437
- // These are shared across all instances from the same defineOpContext
438
- const SpanBufferClass = createSpanBufferConstructor(schema, generatedClass);
969
+ // Initialize immutable static discriminators before publishing the constructor in the LMAO cache.
970
+ // The mutable stats object remains shared by every instance of this exact class dimension.
971
+ const SpanBufferClass = createSpanBufferConstructor(
972
+ schema,
973
+ messageLayoutFamily,
974
+ messagePhysicalLayout,
975
+ generatedClass,
976
+ eagerColumns,
977
+ );
439
978
 
440
979
  // Cache for future use
441
- spanBufferClassCache.set(schema, SpanBufferClass);
980
+ familyClasses ??= new Map();
981
+ familyClasses.set(cacheKey, SpanBufferClass);
982
+ spanBufferClassCache.set(schema, familyClasses);
442
983
 
443
984
  return SpanBufferClass;
444
985
  }
@@ -460,7 +1001,15 @@ export function getSpanBufferClass<T extends LogSchema>(schema: T): SpanBufferCo
460
1001
  *
461
1002
  * @returns SpanBuffer with typed setters for schema fields
462
1003
  */
463
- const MIN_CAPACITY = 8;
1004
+ export const MIN_ADAPTIVE_BUFFER_CAPACITY = 8;
1005
+
1006
+ export function resolveSpanBufferCapacity(explicitCapacity: number | undefined, adaptiveCapacity: number): number {
1007
+ if (explicitCapacity === undefined) return Math.max(MIN_ADAPTIVE_BUFFER_CAPACITY, adaptiveCapacity);
1008
+ if (!Number.isSafeInteger(explicitCapacity) || explicitCapacity < 2) {
1009
+ throw new RangeError('Explicit span buffer capacity must be a safe integer of at least 2');
1010
+ }
1011
+ return explicitCapacity;
1012
+ }
464
1013
 
465
1014
  //#region smoo/lmao!n/spanbuffer-layout.create-root
466
1015
  export function createSpanBuffer<T extends LogSchema>(
@@ -468,27 +1017,39 @@ export function createSpanBuffer<T extends LogSchema>(
468
1017
  traceRoot: ITraceRoot,
469
1018
  opMetadata: OpMetadata,
470
1019
  capacity?: number,
1020
+ plannedClass?: SpanBufferConstructor<T>,
471
1021
  ): SpanBuffer<T> {
472
- const SpanBufferClass = getSpanBufferClass(schema);
1022
+ const metadataPlan = opMetadata._physicalLayoutPlan;
1023
+ const metadataSpanBufferClass = metadataPlan?.SpanBufferClass;
1024
+ if (metadataSpanBufferClass !== undefined && !isSpanBufferConstructorForSchema(metadataSpanBufferClass, schema)) {
1025
+ throw new TypeError('Planned SpanBuffer class does not match schema');
1026
+ }
1027
+ const plannedSpanBufferClass = plannedClass ?? metadataSpanBufferClass;
1028
+ const SpanBufferClass = plannedSpanBufferClass ?? getSpanBufferClass(schema);
473
1029
  const stats = SpanBufferClass.stats;
474
1030
 
475
- // Use provided capacity or default from class stats, enforce minimum
476
- const actualCapacity = Math.max(MIN_CAPACITY, capacity ?? stats.capacity);
1031
+ // Compiler-planned capacities are validated when the runtime hint is decoded. Keep direct external calls strict.
1032
+ const actualCapacity =
1033
+ plannedSpanBufferClass === undefined
1034
+ ? resolveSpanBufferCapacity(capacity, stats.capacity)
1035
+ : (capacity ?? Math.max(MIN_ADAPTIVE_BUFFER_CAPACITY, stats.capacity));
477
1036
 
478
1037
  // Track non-chained buffer creation for capacity tuning
479
1038
  stats.spansCreated++;
480
1039
 
481
- // Create root buffer (no parent)
482
- // Root spans use same metadata for both callsite and op (no distinction at root)
483
- return new SpanBufferClass(
1040
+ // Create root buffer (no parent), then register its stable logical node.
1041
+ const buffer = new SpanBufferClass(
484
1042
  actualCapacity,
485
1043
  stats,
486
- undefined, // no parent
487
- false, // not chained
488
- opMetadata, // callsiteMetadata for row 0
489
- opMetadata, // opMetadata for rows 1+ (same as callsite for root)
490
- traceRoot, // pre-built TraceRoot with trace_id, anchors, tracer
1044
+ undefined,
1045
+ false,
1046
+ opMetadata,
1047
+ opMetadata,
1048
+ traceRoot,
1049
+ metadataPlan?.vocabularyGeneration ?? getVocabularyGeneration(),
491
1050
  );
1051
+ traceRoot._topology.registerRoot(buffer);
1052
+ return buffer;
492
1053
  }
493
1054
  //#endregion smoo/lmao!n/spanbuffer-layout.create-root
494
1055
 
@@ -520,10 +1081,12 @@ export function createOverflowBuffer<T extends LogSchema>(buffer: SpanBuffer<T>)
520
1081
  buffer._callsiteMetadata,
521
1082
  buffer._opMetadata,
522
1083
  buffer._traceRoot, // traceRoot from parent
1084
+ buffer._vocabularyGeneration,
523
1085
  );
524
1086
 
525
1087
  // Link current buffer to next
526
1088
  buffer._overflow = nextBuffer;
1089
+ buffer._traceRoot._topology.adoptOverflow(buffer, nextBuffer);
527
1090
 
528
1091
  return nextBuffer;
529
1092
  }
@@ -554,7 +1117,8 @@ export function createChildSpanBuffer<T extends LogSchema>(
554
1117
  capacity?: number,
555
1118
  ): SpanBuffer<T> {
556
1119
  const stats = SpanBufferClass.stats;
557
- const actualCapacity = capacity ?? stats.capacity;
1120
+ // Child capacities arrive through a validated callsite plan; preserve exact values including 2..7.
1121
+ const actualCapacity = capacity ?? Math.max(MIN_ADAPTIVE_BUFFER_CAPACITY, stats.capacity);
558
1122
 
559
1123
  // Track non-chained buffer creation for capacity tuning
560
1124
  stats.spansCreated++;
@@ -568,8 +1132,10 @@ export function createChildSpanBuffer<T extends LogSchema>(
568
1132
  callsiteMetadata, // callsiteMetadata - CALLER's op metadata (for row 0)
569
1133
  opMetadata, // opMetadata - EXECUTING op metadata (for rows 1+)
570
1134
  parentBuffer._traceRoot, // traceRoot from parent
1135
+ opMetadata._physicalLayoutPlan?.vocabularyGeneration ?? getVocabularyGeneration(),
571
1136
  );
572
1137
 
1138
+ parentBuffer._traceRoot._topology.registerChild(parentBuffer, childBuffer);
573
1139
  return childBuffer;
574
1140
  }
575
1141
  //#endregion smoo/lmao!n/spanbuffer-layout.create-child