@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
@@ -3,12 +3,17 @@
3
3
  */
4
4
 
5
5
  import { afterEach, beforeAll, describe, expect, it } from 'bun:test';
6
+ import { getColumnValue, getRawTimestamp } from '../../__tests__/arrow-test-helpers.js';
7
+ import { convertToLeasedArrowTable } from '../../convertToArrow.js';
6
8
  import { createOpMetadata } from '../../opContext/defineOp.js';
9
+ import { resolveMessage } from '../../resolveMessage.js';
7
10
  import { S } from '../../schema/builder.js';
8
11
  import { defineLogSchema } from '../../schema/defineLogSchema.js';
9
12
  import type { TracerLifecycleHooks } from '../../traceRoot.js';
13
+ import { iterateSpanChildren, iterateSpanTree, NO_NODE } from '../../traceTopology.js';
10
14
  import { WasmBufferStrategy } from '../WasmBufferStrategy.js';
11
15
  import { createWasmAllocator } from '../wasmAllocator.js';
16
+ import { isWasmSpanBufferInstance, type WasmSpanBufferInstance } from '../wasmSpanBuffer.js';
12
17
  import { createWasmTraceRoot } from '../wasmTraceRoot.js';
13
18
 
14
19
  describe('WasmBufferStrategy', () => {
@@ -23,9 +28,16 @@ describe('WasmBufferStrategy', () => {
23
28
  // Test metadata for buffer creation
24
29
  const testMetadata = createOpMetadata('test-op', 'test-package', 'test.ts', 'abc123', 1);
25
30
 
31
+ function requireWasmSpanBuffer<T extends typeof testSchema>(value: unknown): WasmSpanBufferInstance<T> {
32
+ if (!isWasmSpanBufferInstance<T>(value)) {
33
+ throw new Error('expected WasmSpanBufferInstance');
34
+ }
35
+ return value;
36
+ }
37
+
26
38
  // Mock tracer lifecycle hooks for WasmTraceRoot
27
39
  let strategy: WasmBufferStrategy<typeof testSchema>;
28
- let mockTracer: TracerLifecycleHooks;
40
+ let mockTracer: TracerLifecycleHooks<typeof testSchema>;
29
41
 
30
42
  beforeAll(async () => {
31
43
  strategy = await WasmBufferStrategy.create<typeof testSchema>({
@@ -42,6 +54,7 @@ describe('WasmBufferStrategy', () => {
42
54
  onSpanStart: () => {},
43
55
  onSpanEnd: () => {},
44
56
  onStatsWillResetFor: () => {},
57
+ getFlagEvaluatorForContext: () => undefined,
45
58
  bufferStrategy: strategy,
46
59
  };
47
60
  });
@@ -81,10 +94,11 @@ describe('WasmBufferStrategy', () => {
81
94
  const buffer = strategy.createSpanBuffer(testSchema, traceRoot, testMetadata);
82
95
  // Caller must set span name via message() - simulating writeSpanStart behavior
83
96
  buffer.message(0, 'test-span');
97
+ expect('message_nulls' in buffer).toBe(false);
84
98
 
85
99
  expect(buffer).toBeDefined();
86
100
  expect(buffer._capacity).toBe(64);
87
- expect(buffer.message_values[0]).toBe('test-span');
101
+ expect(resolveMessage(buffer, 0)).toBe('test-span');
88
102
  expect(String(buffer.trace_id)).toBe('test-trace-id');
89
103
  });
90
104
 
@@ -121,14 +135,19 @@ describe('WasmBufferStrategy', () => {
121
135
  const child = strategy.createChildSpanBuffer(parent, testMetadata, testMetadata);
122
136
  // Caller must set span name via message() - simulating writeSpanStart behavior
123
137
  child.message(0, 'child-span');
138
+ expect('message_nulls' in child).toBe(false);
124
139
 
125
140
  expect(child).toBeDefined();
126
- expect(child.message_values[0]).toBe('child-span');
141
+ expect(resolveMessage(child, 0)).toBe('child-span');
127
142
  expect(child.trace_id).toBe(parent.trace_id);
128
143
  expect(child.parent_span_id).toBe(parent.span_id);
129
- // Note: parent._children is NOT populated by the strategy - SpanContext handles that.
130
- // The strategy only sets up the parent-child relationship via child._parent.
131
144
  expect(child._parent).toBe(parent);
145
+
146
+ const children = iterateSpanChildren(parent);
147
+ const registered = children.next();
148
+ if (registered.done) throw new Error('topology did not register the child span');
149
+ expect(registered.value).toBe(child);
150
+ expect(children.next().done).toBe(true);
132
151
  });
133
152
 
134
153
  it('inherits capacity from parent by default', async () => {
@@ -167,10 +186,50 @@ describe('WasmBufferStrategy', () => {
167
186
 
168
187
  expect(overflow).toBeDefined();
169
188
  expect(overflow._capacity).toBe(buffer._capacity);
170
- // Note: Currently overflow allocates its own identity block with new span_id
171
- // TODO: Overflow should share identity with parent buffer
172
189
  expect(overflow.trace_id).toBe(buffer.trace_id);
190
+ expect(overflow.span_id).toBe(buffer.span_id);
191
+ expect(overflow.parent_span_id).toBe(buffer.parent_span_id);
192
+ expect(overflow._nodeIndex).toBe(buffer._nodeIndex);
193
+ expect(overflow._topologyGeneration).toBe(buffer._topologyGeneration);
194
+ expect(traceRoot._topology.count).toBe(1);
173
195
  expect(buffer._overflow).toBe(overflow);
196
+ expect(Array.from(iterateSpanTree(buffer))).toEqual([buffer, overflow]);
197
+ });
198
+ });
199
+
200
+ describe('trace topology', () => {
201
+ it('registers many siblings in insertion order while growing every topology lane', async () => {
202
+ const traceRoot = createWasmTraceRoot(strategy.allocator, 'many-siblings-trace', mockTracer);
203
+ const root = strategy.createSpanBuffer(testSchema, traceRoot, testMetadata);
204
+ const siblingCount = 257;
205
+ const legacyChildLane = ['_', 'children'].join('');
206
+ expect(Reflect.has(root, legacyChildLane)).toBe(false);
207
+
208
+ for (let index = 0; index < siblingCount; index++) {
209
+ const child = strategy.createChildSpanBuffer(root, testMetadata, testMetadata);
210
+ child.message(0, `child-${index}`);
211
+ expect('message_nulls' in child).toBe(false);
212
+ expect(child._nodeIndex).toBe(index + 1);
213
+ }
214
+
215
+ const topology = traceRoot._topology;
216
+ expect(topology.count).toBe(siblingCount + 1);
217
+ expect(topology.buffers.length).toBeGreaterThanOrEqual(siblingCount + 1);
218
+ expect(topology.firstChild.length).toBe(topology.buffers.length);
219
+ expect(topology.lastChild.length).toBe(topology.buffers.length);
220
+ expect(topology.nextSibling.length).toBe(topology.buffers.length);
221
+ expect(topology.firstChild[root._nodeIndex]).toBe(1);
222
+ expect(topology.lastChild[root._nodeIndex]).toBe(siblingCount);
223
+ expect(topology.nextSibling[siblingCount]).toBe(NO_NODE);
224
+
225
+ let observed = 0;
226
+ for (const child of iterateSpanChildren(root)) {
227
+ expect(child._nodeIndex).toBe(observed + 1);
228
+ expect(resolveMessage(child, 0)).toBe(`child-${observed}`);
229
+ expect(Reflect.has(child, legacyChildLane)).toBe(false);
230
+ observed++;
231
+ }
232
+ expect(observed).toBe(siblingCount);
174
233
  });
175
234
  });
176
235
 
@@ -218,6 +277,54 @@ describe('WasmBufferStrategy', () => {
218
277
  // Should have freed both buffers
219
278
  expect(statsAfter.freeCount).toBeGreaterThan(statsBefore.freeCount);
220
279
  });
280
+
281
+ it('releases and resets chains deeper than recursive walkers can traverse', async () => {
282
+ const deepSchema = defineLogSchema({});
283
+ const deepStrategy = await WasmBufferStrategy.create<typeof deepSchema>({
284
+ capacity: 8,
285
+ initialPages: 16,
286
+ maxPages: 256,
287
+ });
288
+ const deepTracer: TracerLifecycleHooks<typeof deepSchema> = {
289
+ onTraceStart: () => {},
290
+ onTraceEnd: () => {},
291
+ onSpanStart: () => {},
292
+ onSpanEnd: () => {},
293
+ onStatsWillResetFor: () => {},
294
+ getFlagEvaluatorForContext: () => undefined,
295
+ bufferStrategy: deepStrategy,
296
+ };
297
+ const depth = 20_000;
298
+ const buildChain = (traceId: string) => {
299
+ const traceRoot = createWasmTraceRoot(deepStrategy.allocator, traceId, deepTracer);
300
+ const root = deepStrategy.createSpanBuffer(deepSchema, traceRoot, testMetadata);
301
+ let leaf = root;
302
+ for (let level = 0; level < depth; level++) {
303
+ leaf = deepStrategy.createChildSpanBuffer(leaf, testMetadata, testMetadata);
304
+ }
305
+ return { leaf, root, topology: traceRoot._topology };
306
+ };
307
+
308
+ const released = buildChain('deep-release-trace');
309
+ const releaseGeneration = released.topology.generation;
310
+ expect(released.topology.count).toBe(depth + 1);
311
+ deepStrategy.releaseBuffer(released.root);
312
+ expect(released.topology.count).toBe(0);
313
+ expect(released.topology.root).toBe(NO_NODE);
314
+ expect(released.topology.generation).toBe(releaseGeneration + 1);
315
+ expect(() => released.topology.assertLive(released.leaf)).toThrow(/stale/);
316
+ expect(() => deepStrategy.releaseBuffer(released.root)).toThrow(/stale/);
317
+
318
+ const reset = buildChain('deep-reset-trace');
319
+ const resetGeneration = reset.topology.generation;
320
+ expect(reset.topology.count).toBe(depth + 1);
321
+ deepStrategy.reset();
322
+ expect(reset.topology.count).toBe(0);
323
+ expect(reset.topology.root).toBe(NO_NODE);
324
+ expect(reset.topology.generation).toBe(resetGeneration + 1);
325
+ expect(() => reset.topology.assertLive(reset.leaf)).toThrow(/stale/);
326
+ expect(() => iterateSpanChildren(reset.root).next()).toThrow(/stale/);
327
+ });
221
328
  });
222
329
 
223
330
  describe('getStats()', () => {
@@ -237,11 +344,11 @@ describe('WasmBufferStrategy', () => {
237
344
 
238
345
  describe('reset()', () => {
239
346
  it('resets allocator state', async () => {
240
- const traceRoot = createWasmTraceRoot(strategy.allocator, 'test-trace-id', mockTracer);
347
+ const firstTraceRoot = createWasmTraceRoot(strategy.allocator, 'first-test-trace-id', mockTracer);
348
+ const secondTraceRoot = createWasmTraceRoot(strategy.allocator, 'second-test-trace-id', mockTracer);
241
349
 
242
- // Create some buffers
243
- strategy.createSpanBuffer(testSchema, traceRoot, testMetadata);
244
- strategy.createSpanBuffer(testSchema, traceRoot, testMetadata);
350
+ strategy.createSpanBuffer(testSchema, firstTraceRoot, testMetadata);
351
+ strategy.createSpanBuffer(testSchema, secondTraceRoot, testMetadata);
245
352
 
246
353
  const statsBeforeReset = strategy.getStats();
247
354
  expect(statsBeforeReset.allocCount).toBeGreaterThan(0);
@@ -252,5 +359,102 @@ describe('WasmBufferStrategy', () => {
252
359
  expect(statsAfterReset.allocCount).toBe(0);
253
360
  expect(statsAfterReset.freeCount).toBe(0);
254
361
  });
362
+
363
+ it('invalidates root, child, and overflow generations before offsets are reused', async () => {
364
+ const traceRoot = createWasmTraceRoot(strategy.allocator, 'test-trace-id', mockTracer);
365
+ const root = strategy.createSpanBuffer(testSchema, traceRoot, testMetadata);
366
+ const child = strategy.createChildSpanBuffer(root, testMetadata, testMetadata);
367
+ const overflow = strategy.createOverflowBuffer(root);
368
+ root.timestamp[0] = 11n;
369
+ child.timestamp[0] = 22n;
370
+ overflow.timestamp[0] = 33n;
371
+ const topologyGeneration = traceRoot._topology.generation;
372
+
373
+ strategy.reset();
374
+
375
+ expect(() => root.timestamp).toThrow(/generation .* released/);
376
+ expect(() => child.timestamp).toThrow(/generation .* released/);
377
+ expect(() => overflow.timestamp).toThrow(/generation .* released/);
378
+ expect(() => traceRoot._topology.assertLive(root)).toThrow(/stale/);
379
+ expect(() => traceRoot._topology.assertLive(child)).toThrow(/stale/);
380
+ expect(() => traceRoot._topology.assertLive(overflow)).toThrow(/stale/);
381
+ expect(() => iterateSpanChildren(root).next()).toThrow(/stale/);
382
+ expect(traceRoot._topology.generation).toBe(topologyGeneration + 1);
383
+ expect(traceRoot._topology.count).toBe(0);
384
+ expect(traceRoot._topology.root).toBe(NO_NODE);
385
+ expect(strategy.getStats()).toEqual({ allocCount: 0, freeCount: 0, bumpPtr: 192, capacity: 64 });
386
+
387
+ expect(() => strategy.releaseBuffer(root)).toThrow(/stale/);
388
+ expect(strategy.getStats()).toEqual({ allocCount: 0, freeCount: 0, bumpPtr: 192, capacity: 64 });
389
+ });
390
+ });
391
+
392
+ describe('leased Arrow conversion', () => {
393
+ it('survives a WASM memory epoch change, releases safely, and reuses freed slabs', async () => {
394
+ const leasedStrategy = await WasmBufferStrategy.create<typeof testSchema>({
395
+ capacity: 8,
396
+ initialPages: 1,
397
+ maxPages: 18,
398
+ });
399
+ const leasedTracer: TracerLifecycleHooks<typeof testSchema> = {
400
+ onTraceStart: () => {},
401
+ onTraceEnd: () => {},
402
+ onSpanStart: () => {},
403
+ onSpanEnd: () => {},
404
+ onStatsWillResetFor: () => {},
405
+ getFlagEvaluatorForContext: () => undefined,
406
+ bufferStrategy: leasedStrategy,
407
+ };
408
+ const traceRoot = createWasmTraceRoot(leasedStrategy.allocator, 'leased-wasm', leasedTracer);
409
+ const root = requireWasmSpanBuffer(leasedStrategy.createSpanBuffer(testSchema, traceRoot, testMetadata, 8));
410
+ root.timestamp[0] = 9_001n;
411
+ {
412
+ const entryTypes = root.entry_type;
413
+ if (entryTypes === undefined) throw new Error('Expected split entry-type lane');
414
+ entryTypes[0] = 8;
415
+ }
416
+ root.message(0, 'leased-wasm-row');
417
+ expect('message_nulls' in root).toBe(false);
418
+ root.latency(0, 17.5);
419
+ root.success(0, true);
420
+ root.operation(0, 1);
421
+ root._writeIndex = 1;
422
+
423
+ const f64Pointer = root._familyPtrs.f64;
424
+ const topologyGeneration = traceRoot._topology.generation;
425
+ const memoryVersion = leasedStrategy.allocator.memoryVersion;
426
+ const memoryBuffer = leasedStrategy.allocator.memory.buffer;
427
+ const lease = convertToLeasedArrowTable(root);
428
+ expect(lease.released).toBe(false);
429
+ expect(getColumnValue(lease.table, 'latency', 0)).toBe(17.5);
430
+ expect(getColumnValue(lease.table, 'success', 0)).toBe(true);
431
+ expect(getColumnValue(lease.table, 'operation', 0)).toBe('READ');
432
+
433
+ const bytesToGrow = memoryBuffer.byteLength - leasedStrategy.getStats().bumpPtr + 8;
434
+ const growth = leasedStrategy.allocator.allocExact(bytesToGrow, 8);
435
+ expect(growth).toBeGreaterThan(0);
436
+ expect(leasedStrategy.allocator.memory.buffer).not.toBe(memoryBuffer);
437
+ expect(leasedStrategy.allocator.memoryVersion).toBeGreaterThan(memoryVersion);
438
+ expect(getRawTimestamp(lease.table, 0)).toBe(9_001n);
439
+ expect(getColumnValue(lease.table, 'latency', 0)).toBe(17.5);
440
+ expect(getColumnValue(lease.table, 'message', 0)).toBe('leased-wasm-row');
441
+ expect(traceRoot._topology.generation).toBe(topologyGeneration);
442
+
443
+ leasedStrategy.releaseBuffer(root);
444
+ expect(traceRoot._topology.generation).toBe(topologyGeneration);
445
+ expect(() => traceRoot._topology.assertLive(root)).toThrow(/stale/);
446
+ expect(root.timestamp[0]).toBe(9_001n);
447
+ expect(getColumnValue(lease.table, 'latency', 0)).toBe(17.5);
448
+ lease.release();
449
+ expect(lease.released).toBe(true);
450
+ expect(traceRoot._topology.generation).toBe(topologyGeneration + 1);
451
+ expect(() => root.timestamp).toThrow(/generation .* released/);
452
+ expect(() => lease.release()).not.toThrow();
453
+
454
+ const nextTraceRoot = createWasmTraceRoot(leasedStrategy.allocator, 'leased-wasm-next', leasedTracer);
455
+ const next = requireWasmSpanBuffer(leasedStrategy.createSpanBuffer(testSchema, nextTraceRoot, testMetadata, 8));
456
+ expect(next._familyPtrs.f64).toBe(f64Pointer);
457
+ leasedStrategy.releaseBuffer(next);
458
+ });
255
459
  });
256
460
  });
@@ -0,0 +1,86 @@
1
+ import { beforeEach, describe, expect, it } from 'bun:test';
2
+ import { createWasmAllocator, type WasmAllocator } from '../wasmAllocator.js';
3
+
4
+ /**
5
+ * Exact slabs may be smaller than the allocator's intrusive free-list metadata.
6
+ * Releasing such a slab must reserve bookkeeping storage internally rather than
7
+ * overwrite an adjacent live slab.
8
+ */
9
+ describe('sub-metadata exact slab safety', () => {
10
+ let allocator: WasmAllocator;
11
+
12
+ beforeEach(async () => {
13
+ allocator = await createWasmAllocator({ capacity: 64 });
14
+ allocator.reset();
15
+ });
16
+
17
+ function churnAndAssertNoCorruption(byteLength: 9 | 18) {
18
+ const batch = 8;
19
+ const rounds = 25;
20
+ let nextMarker = 1;
21
+ const live = new Map<number, number>();
22
+
23
+ function allocAndMark(): number {
24
+ const offset = allocator.allocExact(byteLength, 1);
25
+ expect(offset).toBeGreaterThan(0);
26
+ const marker = nextMarker++ & 0xff;
27
+ allocator.u8[offset + byteLength - 1] = marker;
28
+ live.set(offset, marker);
29
+ return offset;
30
+ }
31
+
32
+ function assertAllLiveIntact(context: string) {
33
+ for (const [offset, marker] of live) {
34
+ expect(allocator.u8[offset + byteLength - 1], `exact slab at ${offset} corrupted (${context})`).toBe(marker);
35
+ }
36
+ }
37
+
38
+ for (let i = 0; i < batch; i++) allocAndMark();
39
+ assertAllLiveIntact('after initial batch');
40
+
41
+ for (let round = 0; round < rounds; round++) {
42
+ const offsets = [...live.keys()];
43
+ for (let i = 0; i < offsets.length; i += 2) {
44
+ allocator.freeExact(offsets[i], byteLength, 1);
45
+ live.delete(offsets[i]);
46
+ }
47
+ assertAllLiveIntact(`round ${round} after frees`);
48
+ while (live.size < batch) allocAndMark();
49
+ assertAllLiveIntact(`round ${round} after refill`);
50
+ }
51
+ }
52
+
53
+ it('9-byte exact alloc/free/realloc churn never corrupts a live neighbor', () => {
54
+ churnAndAssertNoCorruption(9);
55
+ });
56
+
57
+ it('18-byte exact alloc/free/realloc churn never corrupts a live neighbor', () => {
58
+ churnAndAssertNoCorruption(18);
59
+ });
60
+
61
+ it('never returns an address that still has a live exact owner', () => {
62
+ const byteLength = 9;
63
+ const live = new Set<number>();
64
+ let allocs = 0;
65
+ let frees = 0;
66
+
67
+ for (let i = 0; i < 300; i++) {
68
+ const shouldFree = live.size > 0 && (i % 3 === 0 || live.size > 40);
69
+ if (shouldFree) {
70
+ const victim = [...live][i % live.size];
71
+ allocator.freeExact(victim, byteLength, 1);
72
+ live.delete(victim);
73
+ frees++;
74
+ } else {
75
+ const offset = allocator.allocExact(byteLength, 1);
76
+ expect(offset).toBeGreaterThan(0);
77
+ expect(live.has(offset), `allocator returned live exact offset ${offset}`).toBe(false);
78
+ live.add(offset);
79
+ allocs++;
80
+ }
81
+ }
82
+
83
+ expect(allocs).toBeGreaterThan(0);
84
+ expect(frees).toBeGreaterThan(0);
85
+ });
86
+ });