@smoothbricks/lmao 0.2.2 → 0.3.0

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 (285) hide show
  1. package/README.md +206 -206
  2. package/dist/allocator.wasm +0 -0
  3. package/dist/index.d.ts +1 -0
  4. package/dist/index.d.ts.map +1 -1
  5. package/dist/index.js +1 -0
  6. package/dist/lib/JsBufferStrategy.d.ts +4 -3
  7. package/dist/lib/JsBufferStrategy.d.ts.map +1 -1
  8. package/dist/lib/JsBufferStrategy.js +18 -11
  9. package/dist/lib/arrow/lease.d.ts +10 -0
  10. package/dist/lib/arrow/lease.d.ts.map +1 -0
  11. package/dist/lib/arrow/lease.js +33 -0
  12. package/dist/lib/arrow/utils.d.ts +3 -4
  13. package/dist/lib/arrow/utils.d.ts.map +1 -1
  14. package/dist/lib/arrow/utils.js +12 -13
  15. package/dist/lib/arrow/vocabularyDictionary.d.ts +21 -0
  16. package/dist/lib/arrow/vocabularyDictionary.d.ts.map +1 -0
  17. package/dist/lib/arrow/vocabularyDictionary.js +95 -0
  18. package/dist/lib/bufferStrategy.d.ts +3 -2
  19. package/dist/lib/bufferStrategy.d.ts.map +1 -1
  20. package/dist/lib/cloudflare/traceRows.d.ts.map +1 -1
  21. package/dist/lib/cloudflare/traceRows.js +3 -2
  22. package/dist/lib/codegen/evaluatorGenerator.d.ts.map +1 -1
  23. package/dist/lib/codegen/evaluatorGenerator.js +12 -11
  24. package/dist/lib/codegen/fixedPositionWriterGenerator.d.ts +44 -82
  25. package/dist/lib/codegen/fixedPositionWriterGenerator.d.ts.map +1 -1
  26. package/dist/lib/codegen/fixedPositionWriterGenerator.js +223 -162
  27. package/dist/lib/codegen/spanLoggerClosureMaterializer.d.ts +63 -0
  28. package/dist/lib/codegen/spanLoggerClosureMaterializer.d.ts.map +1 -0
  29. package/dist/lib/codegen/spanLoggerClosureMaterializer.js +447 -0
  30. package/dist/lib/codegen/spanLoggerGenerator.d.ts +36 -19
  31. package/dist/lib/codegen/spanLoggerGenerator.d.ts.map +1 -1
  32. package/dist/lib/codegen/spanLoggerGenerator.js +164 -158
  33. package/dist/lib/convertToArrow.d.ts +7 -2
  34. package/dist/lib/convertToArrow.d.ts.map +1 -1
  35. package/dist/lib/convertToArrow.js +499 -285
  36. package/dist/lib/defineOpContext.js +1 -1
  37. package/dist/lib/enumMetadata.d.ts +19 -0
  38. package/dist/lib/enumMetadata.d.ts.map +1 -0
  39. package/dist/lib/enumMetadata.js +86 -0
  40. package/dist/lib/flushScheduler.d.ts.map +1 -1
  41. package/dist/lib/flushScheduler.js +13 -4
  42. package/dist/lib/library.d.ts +16 -98
  43. package/dist/lib/library.d.ts.map +1 -1
  44. package/dist/lib/library.js +32 -525
  45. package/dist/lib/logBinding.d.ts +21 -27
  46. package/dist/lib/logBinding.d.ts.map +1 -1
  47. package/dist/lib/op.d.ts +30 -30
  48. package/dist/lib/op.d.ts.map +1 -1
  49. package/dist/lib/op.js +15 -22
  50. package/dist/lib/opContext/createOpGroup.d.ts +1 -1
  51. package/dist/lib/opContext/createOpGroup.d.ts.map +1 -1
  52. package/dist/lib/opContext/createOpGroup.js +22 -29
  53. package/dist/lib/opContext/defineOp.d.ts +4 -4
  54. package/dist/lib/opContext/defineOp.d.ts.map +1 -1
  55. package/dist/lib/opContext/defineOp.js +53 -10
  56. package/dist/lib/opContext/opGroupTypes.d.ts +2 -2
  57. package/dist/lib/opContext/opTypes.d.ts +1 -1
  58. package/dist/lib/opContext/opTypes.d.ts.map +1 -1
  59. package/dist/lib/opContext/spanContextTypes.d.ts +33 -18
  60. package/dist/lib/opContext/spanContextTypes.d.ts.map +1 -1
  61. package/dist/lib/opContext/types.d.ts +4 -4
  62. package/dist/lib/opContext/types.d.ts.map +1 -1
  63. package/dist/lib/physicalLayoutPlan.d.ts +89 -0
  64. package/dist/lib/physicalLayoutPlan.d.ts.map +1 -0
  65. package/dist/lib/physicalLayoutPlan.js +442 -0
  66. package/dist/lib/resolveMessage.d.ts +9 -0
  67. package/dist/lib/resolveMessage.d.ts.map +1 -0
  68. package/dist/lib/resolveMessage.js +63 -0
  69. package/dist/lib/result.d.ts +5 -7
  70. package/dist/lib/result.d.ts.map +1 -1
  71. package/dist/lib/result.js +19 -26
  72. package/dist/lib/runtimeHint.d.ts +35 -0
  73. package/dist/lib/runtimeHint.d.ts.map +1 -0
  74. package/dist/lib/runtimeHint.js +79 -0
  75. package/dist/lib/schema/typeGuards.d.ts +7 -47
  76. package/dist/lib/schema/typeGuards.d.ts.map +1 -1
  77. package/dist/lib/schema/typeGuards.js +7 -123
  78. package/dist/lib/schema/types.d.ts +1 -1
  79. package/dist/lib/schema/types.d.ts.map +1 -1
  80. package/dist/lib/span-buffer/aot/v1.d.ts +2 -0
  81. package/dist/lib/span-buffer/aot/v1.d.ts.map +1 -0
  82. package/dist/lib/span-buffer/aot/v1.js +24 -0
  83. package/dist/lib/spanBuffer.d.ts +33 -4
  84. package/dist/lib/spanBuffer.d.ts.map +1 -1
  85. package/dist/lib/spanBuffer.js +583 -91
  86. package/dist/lib/spanBufferStats.d.ts +1 -1
  87. package/dist/lib/spanBufferStats.d.ts.map +1 -1
  88. package/dist/lib/spanContext.d.ts +27 -51
  89. package/dist/lib/spanContext.d.ts.map +1 -1
  90. package/dist/lib/spanContext.js +822 -344
  91. package/dist/lib/sqlite/sqlite-common.d.ts.map +1 -1
  92. package/dist/lib/sqlite/sqlite-common.js +20 -21
  93. package/dist/lib/testing/bun-harness.d.ts +8 -3
  94. package/dist/lib/testing/bun-harness.d.ts.map +1 -1
  95. package/dist/lib/testing/bun-harness.js +30 -18
  96. package/dist/lib/testing/extractFacts.d.ts +1 -1
  97. package/dist/lib/testing/extractFacts.d.ts.map +1 -1
  98. package/dist/lib/testing/extractFacts.js +55 -63
  99. package/dist/lib/testing/facts.d.ts +1 -1
  100. package/dist/lib/testing/facts.d.ts.map +1 -1
  101. package/dist/lib/testing/queryable-span.d.ts +1 -1
  102. package/dist/lib/testing/queryable-span.d.ts.map +1 -1
  103. package/dist/lib/testing/queryable-span.js +26 -28
  104. package/dist/lib/testing/span-query.d.ts.map +1 -1
  105. package/dist/lib/testing/span-query.js +19 -22
  106. package/dist/lib/testing/stdio-replay.d.ts.map +1 -1
  107. package/dist/lib/testing/stdio-replay.js +19 -8
  108. package/dist/lib/threadId.d.ts.map +1 -1
  109. package/dist/lib/threadId.js +6 -3
  110. package/dist/lib/traceContext.d.ts.map +1 -1
  111. package/dist/lib/traceContext.js +1 -4
  112. package/dist/lib/traceRoot.d.ts +30 -8
  113. package/dist/lib/traceRoot.d.ts.map +1 -1
  114. package/dist/lib/traceRoot.es.d.ts +17 -15
  115. package/dist/lib/traceRoot.es.d.ts.map +1 -1
  116. package/dist/lib/traceRoot.es.js +68 -34
  117. package/dist/lib/traceRoot.js +11 -0
  118. package/dist/lib/traceRoot.node.d.ts +17 -19
  119. package/dist/lib/traceRoot.node.d.ts.map +1 -1
  120. package/dist/lib/traceRoot.node.js +66 -38
  121. package/dist/lib/traceTopology.d.ts +42 -0
  122. package/dist/lib/traceTopology.d.ts.map +1 -0
  123. package/dist/lib/traceTopology.js +260 -0
  124. package/dist/lib/tracer.d.ts +9 -5
  125. package/dist/lib/tracer.d.ts.map +1 -1
  126. package/dist/lib/tracer.js +65 -71
  127. package/dist/lib/tracers/ArrayQueueTracer.d.ts +1 -1
  128. package/dist/lib/tracers/ArrayQueueTracer.js +1 -1
  129. package/dist/lib/tracers/StdioTracer.d.ts.map +1 -1
  130. package/dist/lib/tracers/StdioTracer.js +15 -13
  131. package/dist/lib/tracers/TestTracer.d.ts +4 -6
  132. package/dist/lib/tracers/TestTracer.d.ts.map +1 -1
  133. package/dist/lib/tracers/TestTracer.js +6 -8
  134. package/dist/lib/types.d.ts +49 -32
  135. package/dist/lib/types.d.ts.map +1 -1
  136. package/dist/lib/vocabularyRegistry.d.ts +25 -0
  137. package/dist/lib/vocabularyRegistry.d.ts.map +1 -0
  138. package/dist/lib/vocabularyRegistry.js +372 -0
  139. package/dist/lib/wasm/WasmBufferStrategy.d.ts +6 -10
  140. package/dist/lib/wasm/WasmBufferStrategy.d.ts.map +1 -1
  141. package/dist/lib/wasm/WasmBufferStrategy.js +44 -37
  142. package/dist/lib/wasm/wasmAllocator.d.ts +27 -22
  143. package/dist/lib/wasm/wasmAllocator.d.ts.map +1 -1
  144. package/dist/lib/wasm/wasmAllocator.js +66 -44
  145. package/dist/lib/wasm/wasmPhysicalLayout.d.ts +67 -0
  146. package/dist/lib/wasm/wasmPhysicalLayout.d.ts.map +1 -0
  147. package/dist/lib/wasm/wasmPhysicalLayout.js +179 -0
  148. package/dist/lib/wasm/wasmSpanBuffer.d.ts +79 -31
  149. package/dist/lib/wasm/wasmSpanBuffer.d.ts.map +1 -1
  150. package/dist/lib/wasm/wasmSpanBuffer.js +442 -272
  151. package/dist/lib/wasm/wasmTraceRoot.d.ts +24 -22
  152. package/dist/lib/wasm/wasmTraceRoot.d.ts.map +1 -1
  153. package/dist/lib/wasm/wasmTraceRoot.js +110 -71
  154. package/dist/vocabulary/register/v1.d.ts +2 -0
  155. package/dist/vocabulary/register/v1.d.ts.map +1 -0
  156. package/dist/vocabulary/register/v1.js +1 -0
  157. package/dist/wasm.d.ts +5 -0
  158. package/dist/wasm.d.ts.map +1 -0
  159. package/dist/wasm.js +4 -0
  160. package/package.json +54 -6
  161. package/src/index.ts +1 -0
  162. package/src/lib/JsBufferStrategy.ts +27 -9
  163. package/src/lib/__tests__/arrow-builder-integration/arrow-conversion.test.ts +335 -37
  164. package/src/lib/__tests__/arrow-builder-integration/binary-arrow-conversion.test.ts +97 -26
  165. package/src/lib/__tests__/arrow-builder-integration/binary-comparison.test.ts +75 -15
  166. package/src/lib/__tests__/arrow-builder-integration/binary-format-compliance.test.ts +30 -6
  167. package/src/lib/__tests__/arrow-builder-integration/buffer-chaining.test.ts +18 -21
  168. package/src/lib/__tests__/arrow-builder-integration/foundation.test.ts +5 -2
  169. package/src/lib/__tests__/arrow-builder-integration/integration.test.ts +25 -7
  170. package/src/lib/__tests__/arrow-builder-integration/lazy-columns.test.ts +3 -2
  171. package/src/lib/__tests__/buffer-overflow.test.ts +24 -18
  172. package/src/lib/__tests__/capability-span-context.test.ts +505 -0
  173. package/src/lib/__tests__/capacity-tuning.test.ts +5 -3
  174. package/src/lib/__tests__/eager-columns.test.ts +345 -0
  175. package/src/lib/__tests__/enum-metadata-aot.test.ts +153 -0
  176. package/src/lib/__tests__/enum-metadata.test.ts +177 -0
  177. package/src/lib/__tests__/fixtures/empty-vocabulary-runtime.fixture.ts +15 -0
  178. package/src/lib/__tests__/flushScheduler.test.ts +5 -1
  179. package/src/lib/__tests__/identity-view.property.test.ts +179 -0
  180. package/src/lib/__tests__/library.test.ts +19 -235
  181. package/src/lib/__tests__/message-buffer-families.test.ts +878 -0
  182. package/src/lib/__tests__/nested-library-tasks.test.ts +14 -15
  183. package/src/lib/__tests__/op-metadata.test.ts +1 -1
  184. package/src/lib/__tests__/op-type-safety.test.ts +5 -16
  185. package/src/lib/__tests__/physical-layout-plan.test.ts +333 -0
  186. package/src/lib/__tests__/remap-descriptor-integration.test.ts +381 -0
  187. package/src/lib/__tests__/root-context-construction.test.ts +414 -0
  188. package/src/lib/__tests__/span-auto-runtime.test.ts +386 -0
  189. package/src/lib/__tests__/span-buffer-segment-stats.test.ts +221 -0
  190. package/src/lib/__tests__/span-lifecycle.test.ts +53 -25
  191. package/src/lib/__tests__/spanbuffer-materializer-parity.test.ts +266 -0
  192. package/src/lib/__tests__/sync-async-trace.test.ts +24 -23
  193. package/src/lib/__tests__/tag-writer-aot.test.ts +214 -0
  194. package/src/lib/__tests__/test-helpers.ts +67 -12
  195. package/src/lib/__tests__/threadId.test.ts +62 -0
  196. package/src/lib/__tests__/timestamp.test.ts +221 -93
  197. package/src/lib/__tests__/traceTopology.property.test.ts +383 -0
  198. package/src/lib/__tests__/type-narrowing.test.ts +63 -0
  199. package/src/lib/__tests__/vocabulary-dense-decoding.test.ts +405 -0
  200. package/src/lib/__tests__/vocabularyRegistry.test.ts +313 -0
  201. package/src/lib/arrow/lease.ts +43 -0
  202. package/src/lib/arrow/utils.ts +10 -14
  203. package/src/lib/arrow/vocabularyDictionary.ts +123 -0
  204. package/src/lib/bufferStrategy.ts +9 -1
  205. package/src/lib/cloudflare/traceRows.ts +3 -2
  206. package/src/lib/codegen/__tests__/evaluatorGenerator.test.ts +21 -141
  207. package/src/lib/codegen/__tests__/fixedPositionWriter-parity.test.ts +273 -0
  208. package/src/lib/codegen/__tests__/fixedPositionWriter-uint64.test.ts +61 -34
  209. package/src/lib/codegen/__tests__/nullBitmap.test.ts +33 -37
  210. package/src/lib/codegen/__tests__/spanLoggerGenerator.test.ts +34 -18
  211. package/src/lib/codegen/__tests__/spanLoggerMaterializerParity.test.ts +503 -0
  212. package/src/lib/codegen/evaluatorGenerator.ts +18 -13
  213. package/src/lib/codegen/fixedPositionWriterGenerator.ts +329 -245
  214. package/src/lib/codegen/spanLoggerClosureMaterializer.ts +598 -0
  215. package/src/lib/codegen/spanLoggerGenerator.ts +241 -177
  216. package/src/lib/convertToArrow.ts +598 -311
  217. package/src/lib/defineOpContext.ts +1 -1
  218. package/src/lib/enumMetadata.ts +121 -0
  219. package/src/lib/flushScheduler.ts +11 -5
  220. package/src/lib/library.ts +37 -595
  221. package/src/lib/logBinding.ts +21 -27
  222. package/src/lib/op.ts +75 -18
  223. package/src/lib/opContext/__tests__/retry.test.ts +27 -21
  224. package/src/lib/opContext/createOpGroup.ts +33 -31
  225. package/src/lib/opContext/defineOp.ts +88 -11
  226. package/src/lib/opContext/opGroupTypes.ts +2 -2
  227. package/src/lib/opContext/opTypes.ts +1 -1
  228. package/src/lib/opContext/spanContextTypes.ts +122 -24
  229. package/src/lib/opContext/types.ts +7 -3
  230. package/src/lib/physicalLayoutPlan.ts +598 -0
  231. package/src/lib/resolveMessage.ts +67 -0
  232. package/src/lib/result.ts +20 -33
  233. package/src/lib/runtimeHint.ts +98 -0
  234. package/src/lib/schema/__tests__/featureFlags.test.ts +2 -1
  235. package/src/lib/schema/builder.ts +1 -1
  236. package/src/lib/schema/typeGuards.ts +7 -151
  237. package/src/lib/schema/types.ts +1 -1
  238. package/src/lib/span-buffer/aot/v1.ts +26 -0
  239. package/src/lib/spanBuffer.ts +658 -92
  240. package/src/lib/spanBufferStats.ts +1 -1
  241. package/src/lib/spanContext.ts +1350 -861
  242. package/src/lib/sqlite/sqlite-common.ts +20 -31
  243. package/src/lib/testing/__tests__/topology-traversal.test.ts +174 -0
  244. package/src/lib/testing/__tests__/vitest-harness.test.ts +2 -1
  245. package/src/lib/testing/bun-harness.ts +94 -43
  246. package/src/lib/testing/extractFacts.ts +64 -78
  247. package/src/lib/testing/facts.ts +1 -1
  248. package/src/lib/testing/queryable-span.ts +23 -30
  249. package/src/lib/testing/span-query.ts +16 -20
  250. package/src/lib/testing/stdio-replay.ts +25 -8
  251. package/src/lib/testing/vitest-harness.ts +1 -1
  252. package/src/lib/threadId.ts +6 -3
  253. package/src/lib/traceContext.ts +1 -6
  254. package/src/lib/traceRoot.es.ts +77 -42
  255. package/src/lib/traceRoot.node.ts +75 -46
  256. package/src/lib/traceRoot.ts +41 -14
  257. package/src/lib/traceTopology.ts +279 -0
  258. package/src/lib/tracer.ts +109 -98
  259. package/src/lib/tracers/ArrayQueueTracer.ts +1 -1
  260. package/src/lib/tracers/StdioTracer.ts +15 -13
  261. package/src/lib/tracers/TestTracer.ts +6 -8
  262. package/src/lib/tracers/__tests__/ArrayQueueTracer.test.ts +15 -10
  263. package/src/lib/tracers/__tests__/CompositeTracer.test.ts +150 -0
  264. package/src/lib/tracers/__tests__/TestTracer.test.ts +23 -13
  265. package/src/lib/types.ts +64 -33
  266. package/src/lib/vocabularyRegistry.ts +439 -0
  267. package/src/lib/wasm/WasmBufferStrategy.ts +56 -48
  268. package/src/lib/wasm/__tests__/WasmBufferStrategy.test.ts +215 -11
  269. package/src/lib/wasm/__tests__/allocatorFreeBlockClamp.test.ts +86 -0
  270. package/src/lib/wasm/__tests__/wasm-identity-view.test.ts +311 -0
  271. package/src/lib/wasm/__tests__/wasm-integration.test.ts +417 -44
  272. package/src/lib/wasm/__tests__/wasm-message-buffer-families.test.ts +588 -0
  273. package/src/lib/wasm/__tests__/wasmAllocator.test.ts +100 -668
  274. package/src/lib/wasm/__tests__/wasmSpanBuffer.test.ts +419 -63
  275. package/src/lib/wasm/__tests__/wasmTraceRoot.test.ts +24 -29
  276. package/src/lib/wasm/__tests__/wasmTracer.test.ts.disabled +2 -2
  277. package/src/lib/wasm/wasmAllocator.ts +131 -96
  278. package/src/lib/wasm/wasmPhysicalLayout.ts +309 -0
  279. package/src/lib/wasm/wasmSpanBuffer.ts +597 -341
  280. package/src/lib/wasm/wasmTraceRoot.ts +140 -78
  281. package/src/vocabulary/register/v1.ts +1 -0
  282. package/src/wasm.ts +19 -0
  283. package/src/lib/__tests__/remapped-buffer-view-integration.test.ts +0 -319
  284. package/src/lib/__tests__/remapped-buffer-view.test.ts +0 -635
  285. package/src/lib/wasm/allocator.zig +0 -948
@@ -0,0 +1,345 @@
1
+ import { describe, expect, it } from 'bun:test';
2
+ import fc from 'fast-check';
3
+ import { convertSpanTreeToArrowTable } from '../convertToArrow.js';
4
+ import { defineOpContext } from '../defineOpContext.js';
5
+ import { JsBufferStrategy } from '../JsBufferStrategy.js';
6
+ import { createRemapDescriptor } from '../library.js';
7
+ import type { OpContext } from '../opContext/types.js';
8
+ import { type EagerColumnDescriptor, getPhysicalLayoutPlan, resolveEagerColumns } from '../physicalLayoutPlan.js';
9
+ import {
10
+ RUNTIME_HINT_ANALYZED_VALID,
11
+ RUNTIME_HINT_LOG,
12
+ RUNTIME_HINT_RESULT,
13
+ RUNTIME_HINT_TAG,
14
+ runtimeHintInitialCapacity,
15
+ } from '../runtimeHint.js';
16
+ import { S } from '../schema/builder.js';
17
+ import { defineLogSchema } from '../schema/defineLogSchema.js';
18
+ import { LogSchema } from '../schema/LogSchema.js';
19
+ import { createChildSpanBuffer, createOverflowBuffer, createSpanBuffer, getSpanBufferClass } from '../spanBuffer.js';
20
+ import { createSpanContextClass } from '../spanContext.js';
21
+ import { TestTracer } from '../tracers/TestTracer.js';
22
+ import type { AnySpanBuffer } from '../types.js';
23
+ import { createTestOpMetadata, createTestTraceRoot, createTestTracerOptions } from './test-helpers.js';
24
+
25
+ const HINT = RUNTIME_HINT_ANALYZED_VALID | RUNTIME_HINT_LOG | RUNTIME_HINT_RESULT | 8;
26
+
27
+ function descriptorBytes(descriptor: EagerColumnDescriptor): Uint8Array {
28
+ const bytes = new Uint8Array(descriptor.words.length * Uint32Array.BYTES_PER_ELEMENT);
29
+ const view = new DataView(bytes.buffer);
30
+ for (let index = 0; index < descriptor.words.length; index++) {
31
+ view.setUint32(index * Uint32Array.BYTES_PER_ELEMENT, descriptor.words[index], true);
32
+ }
33
+ return bytes;
34
+ }
35
+
36
+ function expectCompilerEagerStorage(buffer: AnySpanBuffer, name: string): void {
37
+ expect(Object.hasOwn(buffer, `_${name}_values`)).toBe(true);
38
+ expect(Object.hasOwn(buffer, `_${name}_nulls`)).toBe(true);
39
+ expect(Reflect.get(buffer, `_${name}_values`)).toBeDefined();
40
+ expect(Reflect.get(buffer, `_${name}_nulls`)).toBeInstanceOf(Uint8Array);
41
+ expect(Object.hasOwn(buffer, `${name}_values`)).toBe(false);
42
+ expect(Object.hasOwn(buffer, `${name}_nulls`)).toBe(false);
43
+ }
44
+
45
+ function expectLazyStorage(buffer: AnySpanBuffer, name: string): void {
46
+ expect(Reflect.get(buffer, `_${name}_values`)).toBeUndefined();
47
+ expect(Reflect.get(buffer, `_${name}_nulls`)).toBeUndefined();
48
+ expect(buffer.getColumnIfAllocated(name)).toBeUndefined();
49
+ expect(buffer.getNullsIfAllocated(name)).toBeUndefined();
50
+ }
51
+
52
+ describe('compiler-proven eager user columns', () => {
53
+ it('encodes exact schema-order descriptor words and bytes beyond 32 fields', () => {
54
+ const schema = new LogSchema({
55
+ f00: S.number(),
56
+ f01: S.number(),
57
+ f02: S.number(),
58
+ f03: S.number(),
59
+ f04: S.number(),
60
+ f05: S.number(),
61
+ f06: S.number(),
62
+ f07: S.number(),
63
+ f08: S.number(),
64
+ f09: S.number(),
65
+ f10: S.number(),
66
+ f11: S.number(),
67
+ f12: S.number(),
68
+ f13: S.number(),
69
+ f14: S.number(),
70
+ f15: S.number(),
71
+ f16: S.number(),
72
+ f17: S.number(),
73
+ f18: S.number(),
74
+ f19: S.number(),
75
+ f20: S.number(),
76
+ f21: S.number(),
77
+ f22: S.number(),
78
+ f23: S.number(),
79
+ f24: S.number(),
80
+ f25: S.number(),
81
+ f26: S.number(),
82
+ f27: S.number(),
83
+ f28: S.number(),
84
+ f29: S.number(),
85
+ f30: S.number(),
86
+ f31: S.number(),
87
+ f32: S.number(),
88
+ f33: S.number(),
89
+ f34: S.number(),
90
+ f35: S.number(),
91
+ f36: S.number(),
92
+ f37: S.number(),
93
+ f38: S.number(),
94
+ f39: S.number(),
95
+ });
96
+
97
+ const descriptor = resolveEagerColumns(schema, ['f39', 'f00', 'f32', 'f00']);
98
+
99
+ expect(descriptor.names).toEqual(['f00', 'f32', 'f39']);
100
+ expect(descriptor.words).toEqual([0x00000001, 0x00000081]);
101
+ expect(descriptor.key).toBe('0000000100000081');
102
+ expect([...descriptorBytes(descriptor)]).toEqual([1, 0, 0, 0, 129, 0, 0, 0]);
103
+ expect(Object.isFrozen(descriptor)).toBe(true);
104
+ expect(Object.isFrozen(descriptor.names)).toBe(true);
105
+ expect(Object.isFrozen(descriptor.words)).toBe(true);
106
+ expect(() => resolveEagerColumns(schema, ['missing'])).toThrow('Unknown eager column: missing');
107
+
108
+ fc.assert(
109
+ fc.property(
110
+ fc.uniqueArray(fc.integer({ min: 0, max: 39 }), { maxLength: 40 }),
111
+ fc.boolean(),
112
+ (ordinals, reverse) => {
113
+ const requested = ordinals.map((ordinal) => `f${ordinal.toString().padStart(2, '0')}`);
114
+ if (reverse) requested.reverse();
115
+ const resolved = resolveEagerColumns(schema, requested);
116
+ const expectedWords = [0, 0];
117
+ for (const ordinal of ordinals) {
118
+ expectedWords[ordinal >>> 5] = (expectedWords[ordinal >>> 5] | (1 << (ordinal & 31))) >>> 0;
119
+ }
120
+ while (expectedWords.length > 0 && expectedWords[expectedWords.length - 1] === 0) expectedWords.pop();
121
+ expect(resolved.words).toEqual(expectedWords);
122
+ expect(resolved.names).toEqual(schema._columnNames.filter((name) => requested.includes(name)));
123
+ expect(descriptorBytes(resolved).byteLength).toBe(resolved.words.length * 4);
124
+ },
125
+ ),
126
+ { numRuns: 80 },
127
+ );
128
+ });
129
+
130
+ it('selects one immutable plan and preallocates only proven nullable lanes across root, child, and overflow', () => {
131
+ const schema = defineLogSchema({
132
+ provenNumber: S.number(),
133
+ lazyString: S.category(),
134
+ provenString: S.category(),
135
+ lazyBoolean: S.boolean(),
136
+ });
137
+ const baseClass = getSpanBufferClass(schema);
138
+ const logBinding = { logSchema: schema, remapDescriptor: undefined };
139
+ const SpanContextClass = createSpanContextClass<OpContext<typeof schema>>(
140
+ schema,
141
+ logBinding,
142
+ RUNTIME_HINT_LOG | RUNTIME_HINT_RESULT,
143
+ );
144
+ const remap = createRemapDescriptor(schema, { local_number: 'provenNumber' });
145
+ const plan = getPhysicalLayoutPlan(baseClass, HINT, SpanContextClass, undefined, 'js-heap', '', [
146
+ 'provenString',
147
+ 'provenNumber',
148
+ 'provenString',
149
+ ]);
150
+ const identical = getPhysicalLayoutPlan(baseClass, HINT, SpanContextClass, undefined, 'js-heap', '', [
151
+ 'provenNumber',
152
+ 'provenString',
153
+ ]);
154
+ const lazyPlan = getPhysicalLayoutPlan(baseClass, HINT, SpanContextClass, undefined, 'js-heap');
155
+ const remapped = getPhysicalLayoutPlan(baseClass, HINT, SpanContextClass, remap, 'js-heap', '', [
156
+ 'provenNumber',
157
+ 'provenString',
158
+ ]);
159
+
160
+ expect(identical).toBe(plan);
161
+ expect(lazyPlan).not.toBe(plan);
162
+ expect(remapped.eagerColumns).toBe(plan.eagerColumns);
163
+ expect(plan.eagerColumns.names).toEqual(['provenNumber', 'provenString']);
164
+ expect(plan.SpanBufferClass.eagerColumns).toBe(plan.eagerColumns);
165
+ expect(plan.wasmLayout.eagerColumns).toBe(plan.eagerColumns);
166
+
167
+ plan.SpanBufferClass.stats.capacity = 8;
168
+ const metadata = createTestOpMetadata();
169
+ const root = createSpanBuffer(schema, createTestTraceRoot('eager-root'), metadata, 8, plan.SpanBufferClass);
170
+ const child = createChildSpanBuffer(root, plan.SpanBufferClass, metadata, metadata, 8);
171
+ const overflow = createOverflowBuffer(child);
172
+
173
+ for (const buffer of [root, child, overflow]) {
174
+ expectCompilerEagerStorage(buffer, 'provenNumber');
175
+ expectCompilerEagerStorage(buffer, 'provenString');
176
+ expectLazyStorage(buffer, 'lazyString');
177
+ expectLazyStorage(buffer, 'lazyBoolean');
178
+ expect(buffer.constructor).toBe(plan.SpanBufferClass);
179
+ }
180
+ expect(child._parent).toBe(root);
181
+ expect(overflow._parent).toBe(root);
182
+ expect(overflow._identity).toBe(child._identity);
183
+ expect(child._identity).not.toBe(root._identity);
184
+
185
+ root.provenNumber(0, 42);
186
+ root.provenNumber(1, null);
187
+ child.provenString(2, 'child');
188
+ overflow.provenNumber(3, null);
189
+ root.lazyString(4, 'materialized lazily');
190
+
191
+ expect(root.getColumnIfAllocated('provenNumber')?.[0]).toBe(42);
192
+ const rootNumberNulls = root.getNullsIfAllocated('provenNumber');
193
+ if (!rootNumberNulls) throw new Error('expected provenNumber null bitmap');
194
+ expect(rootNumberNulls[0] & 0b00000001).toBe(1);
195
+ expect(rootNumberNulls[0] & 0b00000010).toBe(0);
196
+ expect(child.getColumnIfAllocated('provenString')?.[2]).toBe('child');
197
+ const childStringNulls = child.getNullsIfAllocated('provenString');
198
+ if (!childStringNulls) throw new Error('expected provenString null bitmap');
199
+ expect(childStringNulls[0] & 0b00000100).toBe(0b00000100);
200
+ const overflowNumberNulls = overflow.getNullsIfAllocated('provenNumber');
201
+ if (!overflowNumberNulls) throw new Error('expected overflow provenNumber null bitmap');
202
+ expect(overflowNumberNulls[0] & 0b00001000).toBe(0);
203
+ expect(root.getColumnIfAllocated('lazyString')?.[4]).toBe('materialized lazily');
204
+ expect(root.getColumnIfAllocated('lazyBoolean')).toBeUndefined();
205
+ });
206
+
207
+ it('uses exact analyzed capacities 2 through 7 with eager lanes while invalid and zero hints stay adaptive', async () => {
208
+ const schema = defineLogSchema({ proven: S.category(), lazy: S.category() });
209
+ const opContext = defineOpContext({ logSchema: schema });
210
+ const tracer = new TestTracer(opContext, createTestTracerOptions());
211
+
212
+ for (const capacity of [2, 3, 4, 5, 6, 7]) {
213
+ const observed: AnySpanBuffer[] = [];
214
+ const exactOp = opContext.defineOp(
215
+ `exact-${capacity}`,
216
+ (ctx) => {
217
+ observed.push(ctx.buffer);
218
+ for (let index = 0; index < 8; index++) {
219
+ ctx.log.debug(`capacity-${capacity}-${index}`).proven(`capacity-${capacity}-${index}`);
220
+ }
221
+ return ctx.ok(capacity);
222
+ },
223
+ undefined,
224
+ {
225
+ runtimeHint:
226
+ RUNTIME_HINT_ANALYZED_VALID | RUNTIME_HINT_LOG | RUNTIME_HINT_RESULT | RUNTIME_HINT_TAG | capacity,
227
+ eagerColumns: ['proven'],
228
+ },
229
+ );
230
+ const parentOp = opContext.defineOp(`parent-${capacity}`, async (ctx) => {
231
+ await ctx.span(`child-${capacity}`, exactOp);
232
+ return ctx.ok(capacity);
233
+ });
234
+
235
+ await tracer.trace(`root-${capacity}`, exactOp);
236
+ await tracer.trace(`parent-root-${capacity}`, parentOp);
237
+
238
+ expect(runtimeHintInitialCapacity(exactOp.callsitePlan.runtimeHint)).toBe(capacity);
239
+ expect(observed.map((buffer) => buffer._capacity)).toEqual([capacity, capacity]);
240
+ expect(observed).toHaveLength(2);
241
+ for (const buffer of observed) {
242
+ expect(buffer.constructor).toBe(exactOp.callsitePlan.SpanBufferClass);
243
+ const overflow = buffer._overflow;
244
+ if (!overflow) throw new Error(`Expected exact capacity ${capacity} to overflow`);
245
+ expect(overflow.constructor).toBe(exactOp.callsitePlan.SpanBufferClass);
246
+ expect(overflow._identity).toBe(buffer._identity);
247
+
248
+ const table = convertSpanTreeToArrowTable(buffer);
249
+ const proven = table.getChild('proven');
250
+ if (!proven) throw new Error('Expected proven Arrow column');
251
+ expect(
252
+ Array.from({ length: table.numRows }, (_, row) => proven.get(row)).filter(
253
+ (value): value is string => typeof value === 'string',
254
+ ),
255
+ ).toEqual(Array.from({ length: 8 }, (_, index) => `capacity-${capacity}-${index}`));
256
+ expectCompilerEagerStorage(buffer, 'proven');
257
+ expectLazyStorage(buffer, 'lazy');
258
+ }
259
+ }
260
+
261
+ const adaptiveBuffers: AnySpanBuffer[] = [];
262
+ const adaptiveOp = opContext.defineOp('adaptive-zero', (ctx) => {
263
+ adaptiveBuffers.push(ctx.buffer);
264
+ return ctx.ok('adaptive');
265
+ });
266
+ const invalidOneOp = opContext.defineOp(
267
+ 'adaptive-invalid-one',
268
+ (ctx) => {
269
+ adaptiveBuffers.push(ctx.buffer);
270
+ return ctx.ok('adaptive');
271
+ },
272
+ undefined,
273
+ { runtimeHint: RUNTIME_HINT_ANALYZED_VALID | RUNTIME_HINT_RESULT | 1 },
274
+ );
275
+ const adaptiveStats = adaptiveOp.callsitePlan.SpanBufferClass.stats;
276
+ const invalidStats = invalidOneOp.callsitePlan.SpanBufferClass.stats;
277
+ const originalAdaptiveCapacity = adaptiveStats.capacity;
278
+ const originalInvalidCapacity = invalidStats.capacity;
279
+ try {
280
+ adaptiveStats.capacity = 5;
281
+ await tracer.trace('adaptive-minimum', adaptiveOp);
282
+ invalidStats.capacity = 13;
283
+ await tracer.trace('adaptive-stats', invalidOneOp);
284
+ } finally {
285
+ adaptiveStats.capacity = originalAdaptiveCapacity;
286
+ invalidStats.capacity = originalInvalidCapacity;
287
+ }
288
+
289
+ expect(runtimeHintInitialCapacity(0)).toBeUndefined();
290
+ expect(runtimeHintInitialCapacity(RUNTIME_HINT_ANALYZED_VALID)).toBeUndefined();
291
+ expect(runtimeHintInitialCapacity(RUNTIME_HINT_ANALYZED_VALID | 1)).toBeUndefined();
292
+ expect(adaptiveBuffers.map((buffer) => buffer._capacity)).toEqual([8, 13]);
293
+ for (const buffer of adaptiveBuffers) expectLazyStorage(buffer, 'proven');
294
+ });
295
+
296
+ it('rejects schema-incompatible planned classes before root or child allocation', () => {
297
+ const incompatibleContext = defineOpContext({ logSchema: defineLogSchema({ left: S.number() }) });
298
+ const compatibleContext = defineOpContext({ logSchema: defineLogSchema({ right: S.number() }) });
299
+ const incompatibleOp = incompatibleContext.defineOp('incompatible-plan', (ctx) => ctx.ok(null));
300
+ const compatibleOp = compatibleContext.defineOp('compatible-plan', (ctx) => ctx.ok(null));
301
+ const schema = compatibleOp.callsitePlan.schema;
302
+ const traceRoot = createTestTraceRoot('schema-mismatch');
303
+ const parent = createSpanBuffer(
304
+ schema,
305
+ traceRoot,
306
+ compatibleOp.metadata,
307
+ 8,
308
+ compatibleOp.callsitePlan.SpanBufferClass,
309
+ );
310
+ const incompatibleStats = incompatibleOp.callsitePlan.SpanBufferClass.stats;
311
+ const spansCreated = incompatibleStats.spansCreated;
312
+
313
+ expect(() => createSpanBuffer(schema, traceRoot, incompatibleOp.metadata)).toThrow(
314
+ 'Planned SpanBuffer class does not match schema',
315
+ );
316
+ const strategy = new JsBufferStrategy<typeof schema>();
317
+ expect(() =>
318
+ strategy.createChildSpanBuffer(parent, compatibleOp.metadata, incompatibleOp.metadata, undefined, schema),
319
+ ).toThrow('Planned SpanBuffer class does not match schema');
320
+ expect(incompatibleStats.spansCreated).toBe(spansCreated);
321
+ });
322
+
323
+ it('keeps plugin-off metadata all-lazy', () => {
324
+ const schema = defineLogSchema({ field: S.category() });
325
+ const SpanBufferClass = getSpanBufferClass(schema);
326
+ const SpanContextClass = createSpanContextClass<OpContext<typeof schema>>(
327
+ schema,
328
+ { logSchema: schema, remapDescriptor: undefined },
329
+ RUNTIME_HINT_LOG | RUNTIME_HINT_RESULT,
330
+ );
331
+ const plan = getPhysicalLayoutPlan(SpanBufferClass, HINT, SpanContextClass);
332
+ const buffer = createSpanBuffer(
333
+ schema,
334
+ createTestTraceRoot('plugin-off'),
335
+ createTestOpMetadata(),
336
+ 8,
337
+ plan.SpanBufferClass,
338
+ );
339
+
340
+ expect(plan.eagerColumns.names).toEqual([]);
341
+ expect(plan.eagerColumns.words).toEqual([]);
342
+ expect(plan.eagerColumns.key).toBe('');
343
+ expectLazyStorage(buffer, 'field');
344
+ });
345
+ });
@@ -0,0 +1,153 @@
1
+ import { describe, expect, it } from 'bun:test';
2
+ import './test-helpers.js';
3
+ import { resolveEnumLookupDescriptor } from '../enumMetadata.js';
4
+ import { createRemapDescriptor } from '../library.js';
5
+ import type { OpContext } from '../opContext/types.js';
6
+ import { getPhysicalLayoutPlan } from '../physicalLayoutPlan.js';
7
+ import {
8
+ RUNTIME_HINT_ANALYZED_VALID,
9
+ RUNTIME_HINT_LOG,
10
+ RUNTIME_HINT_RESULT,
11
+ RUNTIME_HINT_TAG,
12
+ } from '../runtimeHint.js';
13
+ import { S } from '../schema/builder.js';
14
+ import { defineLogSchema } from '../schema/defineLogSchema.js';
15
+ import { getSpanBufferClass } from '../spanBuffer.js';
16
+ import { createSpanContextClass } from '../spanContext.js';
17
+
18
+ const ENUM_CAPABILITIES = RUNTIME_HINT_TAG | RUNTIME_HINT_LOG | RUNTIME_HINT_RESULT;
19
+ const ENUM_CALLSITE_HINT = RUNTIME_HINT_ANALYZED_VALID | ENUM_CAPABILITIES | 4;
20
+
21
+ function countFunctionConstructorCalls(run: () => void): number {
22
+ const originalDescriptor = Object.getOwnPropertyDescriptor(globalThis, 'Function');
23
+ if (!originalDescriptor) throw new Error('Expected global Function descriptor');
24
+
25
+ const originalFunction = globalThis.Function;
26
+ let calls = 0;
27
+ const functionProbe = new Proxy(originalFunction, {
28
+ apply(target, thisArgument, argumentList) {
29
+ calls++;
30
+ return Reflect.apply(target, thisArgument, argumentList);
31
+ },
32
+ construct(target, argumentList, newTarget) {
33
+ calls++;
34
+ return Reflect.construct(target, argumentList, newTarget);
35
+ },
36
+ });
37
+
38
+ Object.defineProperty(globalThis, 'Function', {
39
+ configurable: originalDescriptor.configurable,
40
+ enumerable: originalDescriptor.enumerable,
41
+ value: functionProbe,
42
+ writable: originalDescriptor.writable,
43
+ });
44
+ try {
45
+ run();
46
+ } finally {
47
+ Object.defineProperty(globalThis, 'Function', originalDescriptor);
48
+ }
49
+ return calls;
50
+ }
51
+
52
+ describe('AOT-safe enum metadata', () => {
53
+ it('encodes declaration order and every storage-width boundary without compiling a Function', () => {
54
+ const functionConstructorCalls = countFunctionConstructorCalls(() => {
55
+ const orderedValues = ['ZERO', '__proto__', 'constructor', 'toString', 'OMEGA'];
56
+ const orderedSchema = defineLogSchema({ state: S.enum(orderedValues) });
57
+ const ordered = resolveEnumLookupDescriptor(orderedSchema).byField.state;
58
+
59
+ expect(ordered.values).toEqual(orderedValues);
60
+ expect(orderedValues.map((value) => ordered.encode(value))).toEqual([0, 1, 2, 3, 4]);
61
+ expect([ordered.encode('MISSING'), ordered.encode('valueOf'), ordered.encode('')]).toEqual([0, 0, 0]);
62
+
63
+ const boundaries = [
64
+ { count: 1, indexArrayConstructor: Uint8Array },
65
+ { count: 256, indexArrayConstructor: Uint8Array },
66
+ { count: 257, indexArrayConstructor: Uint16Array },
67
+ { count: 65_536, indexArrayConstructor: Uint16Array },
68
+ { count: 65_537, indexArrayConstructor: Uint32Array },
69
+ ];
70
+ for (const { count, indexArrayConstructor } of boundaries) {
71
+ const values = Array.from({ length: count }, (_, index) => `VALUE_${index}`);
72
+ const schema = defineLogSchema({ boundary: S.enum(values) });
73
+ const descriptor = resolveEnumLookupDescriptor(schema).byField.boundary;
74
+ const midpoint = Math.floor((count - 1) / 2);
75
+
76
+ expect(descriptor.indexArrayConstructor).toBe(indexArrayConstructor);
77
+ expect(descriptor.values).toEqual(values);
78
+ expect([
79
+ descriptor.encode('VALUE_0'),
80
+ descriptor.encode(`VALUE_${midpoint}`),
81
+ descriptor.encode(`VALUE_${count - 1}`),
82
+ descriptor.encode(`VALUE_${count}`),
83
+ ]).toEqual([0, midpoint, count - 1, 0]);
84
+ }
85
+ });
86
+
87
+ expect(functionConstructorCalls).toBe(0);
88
+ });
89
+
90
+ it('maps non-string values to zero without coercion or Function compilation', () => {
91
+ const functionConstructorCalls = countFunctionConstructorCalls(() => {
92
+ const schema = defineLogSchema({
93
+ value: S.enum(['ZERO', '1', '[object Object]', 'Symbol(value)']),
94
+ });
95
+ const encode = resolveEnumLookupDescriptor(schema).byField.value.encode;
96
+ const invalidValues = [1, {}, Symbol('value')];
97
+
98
+ expect(invalidValues.map((value) => Reflect.apply(encode, undefined, [value]))).toEqual([0, 0, 0]);
99
+ });
100
+
101
+ expect(functionConstructorCalls).toBe(0);
102
+ });
103
+
104
+ it('reuses schema descriptors and field cores without compiling a Function', () => {
105
+ const functionConstructorCalls = countFunctionConstructorCalls(() => {
106
+ const sharedField = S.enum(['QUEUED', 'RUNNING', 'DONE']);
107
+ const primarySchema = defineLogSchema({ state: sharedField });
108
+ const renamedSchema = defineLogSchema({ renamedState: sharedField });
109
+
110
+ const primaryLookup = resolveEnumLookupDescriptor(primarySchema);
111
+ const primaryDescriptor = primaryLookup.byField.state;
112
+ const renamedDescriptor = resolveEnumLookupDescriptor(renamedSchema).byField.renamedState;
113
+
114
+ expect(resolveEnumLookupDescriptor(primarySchema)).toBe(primaryLookup);
115
+ expect(resolveEnumLookupDescriptor(primarySchema).byField.state).toBe(primaryDescriptor);
116
+ expect(renamedDescriptor).not.toBe(primaryDescriptor);
117
+ expect(renamedDescriptor.fieldName).toBe('renamedState');
118
+ expect(renamedDescriptor.values).toBe(primaryDescriptor.values);
119
+ expect(renamedDescriptor.encode).toBe(primaryDescriptor.encode);
120
+ expect(renamedDescriptor.encode('RUNNING')).toBe(1);
121
+ expect(renamedDescriptor.encode('UNKNOWN')).toBe(0);
122
+ });
123
+
124
+ expect(functionConstructorCalls).toBe(0);
125
+ });
126
+
127
+ it('shares the schema descriptor with remapped physical plans without compiling a Function', () => {
128
+ const schema = defineLogSchema({ operation: S.enum(['READ', 'WRITE']) });
129
+ const functionConstructorCalls = countFunctionConstructorCalls(() => {
130
+ const firstLookup = resolveEnumLookupDescriptor(schema);
131
+ expect(firstLookup.byField.operation.encode('WRITE')).toBe(1);
132
+ expect(firstLookup.byField.operation.encode('INVALID')).toBe(0);
133
+ });
134
+
135
+ expect(functionConstructorCalls).toBe(0);
136
+
137
+ const lookup = resolveEnumLookupDescriptor(schema);
138
+ const remap = createRemapDescriptor(schema, { library_operation: 'operation' });
139
+ const SpanBufferClass = getSpanBufferClass(schema);
140
+ const SpanContextClass = createSpanContextClass<OpContext<typeof schema>>(
141
+ schema,
142
+ { logSchema: schema, remapDescriptor: undefined },
143
+ ENUM_CAPABILITIES,
144
+ );
145
+
146
+ const basePlan = getPhysicalLayoutPlan(SpanBufferClass, ENUM_CALLSITE_HINT, SpanContextClass);
147
+ const remappedPlan = getPhysicalLayoutPlan(SpanBufferClass, ENUM_CALLSITE_HINT, SpanContextClass, remap);
148
+
149
+ expect(basePlan.enumLookup).toBe(lookup);
150
+ expect(remappedPlan.enumLookup).toBe(lookup);
151
+ expect(remappedPlan.enumLookup.byField.operation).toBe(lookup.byField.operation);
152
+ });
153
+ });
@@ -0,0 +1,177 @@
1
+ import { describe, expect, it } from 'bun:test';
2
+ import './test-helpers.js';
3
+ import { convertSpanTreeToArrowTable } from '../convertToArrow.js';
4
+ import { defineOpContext } from '../defineOpContext.js';
5
+ import { resolveEnumLookupDescriptor } from '../enumMetadata.js';
6
+ import { createRemapDescriptor } from '../library.js';
7
+ import type { OpContext } from '../opContext/types.js';
8
+ import { getPhysicalLayoutPlan } from '../physicalLayoutPlan.js';
9
+ import {
10
+ RUNTIME_HINT_ANALYZED_VALID,
11
+ RUNTIME_HINT_LOG,
12
+ RUNTIME_HINT_RESULT,
13
+ RUNTIME_HINT_SPAN,
14
+ RUNTIME_HINT_TAG,
15
+ } from '../runtimeHint.js';
16
+ import { S } from '../schema/builder.js';
17
+ import { defineLogSchema } from '../schema/defineLogSchema.js';
18
+ import { getSpanBufferClass } from '../spanBuffer.js';
19
+ import { createSpanContextClass } from '../spanContext.js';
20
+ import { TestTracer } from '../tracers/TestTracer.js';
21
+ import { iterateSpanTree } from '../traceTopology.js';
22
+ import { requireColumn } from './arrow-test-helpers.js';
23
+ import { createTestTracerOptions } from './test-helpers.js';
24
+
25
+ function makeValues(count: number): string[] {
26
+ return Array.from({ length: count }, (_, index) => `VALUE_${index}`);
27
+ }
28
+
29
+ function invokeEnumWriter(writer: object, value: string): void {
30
+ const method = Reflect.get(writer, 'operation');
31
+ if (typeof method !== 'function') throw new TypeError('Expected an operation enum writer');
32
+ Reflect.apply(method, writer, [value]);
33
+ }
34
+
35
+ const CHILD_HINT = RUNTIME_HINT_ANALYZED_VALID | RUNTIME_HINT_TAG | RUNTIME_HINT_LOG | RUNTIME_HINT_RESULT | 4;
36
+ const PARENT_HINT = RUNTIME_HINT_ANALYZED_VALID | RUNTIME_HINT_SPAN | RUNTIME_HINT_RESULT | 4;
37
+
38
+ describe('startup-hoisted enum lookup metadata', () => {
39
+ it.each([
40
+ [255, Uint8Array],
41
+ [256, Uint8Array],
42
+ [65_535, Uint16Array],
43
+ [65_536, Uint16Array],
44
+ [65_537, Uint32Array],
45
+ ])('preserves exact index width and boundary ordinal for %d values', (count, expectedConstructor) => {
46
+ const values = makeValues(count);
47
+ const schema = defineLogSchema({ boundary: S.enum(values) });
48
+ const descriptor = resolveEnumLookupDescriptor(schema).byField.boundary;
49
+
50
+ expect(descriptor.indexArrayConstructor).toBe(expectedConstructor);
51
+ expect(descriptor.values).toEqual(values);
52
+ expect(Object.isFrozen(descriptor.values)).toBe(true);
53
+ expect(descriptor.encode(values[0])).toBe(0);
54
+ expect(descriptor.encode(values[count - 1])).toBe(count - 1);
55
+ expect(descriptor.encode('NOT_IN_THE_ENUM')).toBe(0);
56
+ });
57
+
58
+ it('resolves once and reuses one descriptor through repeated trace, span, tag, log, result, overflow, and Arrow paths', async () => {
59
+ let valueMetadataReads = 0;
60
+ const values = new Proxy(['READ', 'WRITE'], {
61
+ get(target, property, receiver) {
62
+ if (property === Symbol.iterator || (typeof property === 'string' && /^\d+$/.test(property))) {
63
+ valueMetadataReads++;
64
+ }
65
+ return Reflect.get(target, property, receiver);
66
+ },
67
+ });
68
+ const schema = defineLogSchema({ operation: S.enum(values) });
69
+ const context = defineOpContext({ logSchema: schema });
70
+
71
+ valueMetadataReads = 0;
72
+ const child = context.defineOp(
73
+ 'enum-child',
74
+ (ctx) => {
75
+ ctx.tag.operation('WRITE');
76
+ for (let index = 0; index < 10; index++) {
77
+ ctx.log.info(`enum-${index}`).operation(index % 2 === 0 ? 'READ' : 'WRITE');
78
+ }
79
+ return ctx.ok('done').with({ operation: 'WRITE' });
80
+ },
81
+ undefined,
82
+ { runtimeHint: CHILD_HINT },
83
+ );
84
+ const firstResolutionReads = valueMetadataReads;
85
+ const lookup = child.callsitePlan.enumLookup;
86
+ expect(firstResolutionReads).toBeGreaterThan(0);
87
+ expect(resolveEnumLookupDescriptor(child.callsitePlan.schema)).toBe(lookup);
88
+ const encode = lookup.byField.operation.encode;
89
+
90
+ valueMetadataReads = 0;
91
+ const parent = context.defineOp(
92
+ 'enum-parent',
93
+ async (ctx) => {
94
+ for (let index = 0; index < 3; index++) await ctx.span(`child-${index}`, child);
95
+ return ctx.ok('done');
96
+ },
97
+ undefined,
98
+ { runtimeHint: PARENT_HINT },
99
+ );
100
+ expect(parent.callsitePlan.enumLookup).toBe(lookup);
101
+
102
+ const tracer = new TestTracer(context, createTestTracerOptions());
103
+ for (let index = 0; index < 4; index++) await tracer.trace(`trace-${index}`, parent);
104
+
105
+ expect(child.callsitePlan.enumLookup).toBe(lookup);
106
+ expect(parent.callsitePlan.enumLookup).toBe(lookup);
107
+ expect(valueMetadataReads).toBe(0);
108
+
109
+ let overflowCount = 0;
110
+ for (const root of tracer.rootBuffers) {
111
+ for (const segment of iterateSpanTree(root)) {
112
+ if (segment._overflow !== undefined) overflowCount++;
113
+ }
114
+ const table = convertSpanTreeToArrowTable(root);
115
+ const operations = requireColumn(table, 'operation');
116
+ expect(Array.from({ length: operations.length }, (_, row) => operations.get(row)).filter(Boolean)).toContain(
117
+ 'WRITE',
118
+ );
119
+ }
120
+ expect(overflowCount).toBeGreaterThan(0);
121
+ expect(resolveEnumLookupDescriptor(child.callsitePlan.schema)).toBe(lookup);
122
+ expect(child.callsitePlan.enumLookup.byField.operation.encode).toBe(encode);
123
+ });
124
+
125
+ it('keeps invalid-value fallback and dictionary decoding exact for tag, log, and result writers', async () => {
126
+ const schema = defineLogSchema({ operation: S.enum(['READ', 'WRITE']) });
127
+ const context = defineOpContext({ logSchema: schema });
128
+ const op = context.defineOp('invalid-enum', (ctx) => {
129
+ invokeEnumWriter(ctx.tag, 'INVALID');
130
+ invokeEnumWriter(ctx.log.info('invalid-log'), 'INVALID');
131
+ return ctx.ok('done').with({ operation: JSON.parse('"INVALID"') });
132
+ });
133
+ const tracer = new TestTracer(context, createTestTracerOptions());
134
+
135
+ await tracer.trace('invalid-enum-root', op);
136
+
137
+ const root = tracer.rootBuffers[0];
138
+ if (!root) throw new Error('Expected invalid enum trace root');
139
+ const raw = root.operation_values;
140
+ expect([raw[0], raw[1], raw[2]]).toEqual([0, 0, 0]);
141
+ const decoded = requireColumn(convertSpanTreeToArrowTable(root), 'operation');
142
+ expect([decoded.get(0), decoded.get(1), decoded.get(2)]).toEqual(['READ', 'READ', 'READ']);
143
+ expect(op.callsitePlan.enumLookup.byField.operation.encode('INVALID')).toBe(0);
144
+ });
145
+
146
+ it('separates extended schemas while remapped plans reuse the extended schema descriptor', () => {
147
+ const base = defineLogSchema({ operation: S.enum(['READ', 'WRITE']) });
148
+ const extended = base.extend({ phase: S.enum(['QUEUED', 'RUNNING', 'DONE']) });
149
+ const baseLookup = resolveEnumLookupDescriptor(base);
150
+ const extendedLookup = resolveEnumLookupDescriptor(extended);
151
+
152
+ expect(baseLookup.ordered.map((descriptor) => descriptor.fieldName)).toEqual(['operation']);
153
+ expect(extendedLookup.ordered.map((descriptor) => descriptor.fieldName)).toEqual(['operation', 'phase']);
154
+ expect(extendedLookup).not.toBe(baseLookup);
155
+ expect(resolveEnumLookupDescriptor(base)).toBe(baseLookup);
156
+ expect(resolveEnumLookupDescriptor(extended)).toBe(extendedLookup);
157
+
158
+ const remap = createRemapDescriptor(extended, {
159
+ library_operation: 'operation',
160
+ library_phase: 'phase',
161
+ });
162
+ const SpanBufferClass = getSpanBufferClass(extended);
163
+ const SpanContextClass = createSpanContextClass<OpContext<typeof extended>>(
164
+ extended,
165
+ { logSchema: extended, remapDescriptor: undefined },
166
+ RUNTIME_HINT_TAG | RUNTIME_HINT_LOG | RUNTIME_HINT_RESULT,
167
+ );
168
+ const basePlan = getPhysicalLayoutPlan(SpanBufferClass, CHILD_HINT, SpanContextClass);
169
+ const remappedPlan = getPhysicalLayoutPlan(SpanBufferClass, CHILD_HINT, SpanContextClass, remap);
170
+
171
+ expect(basePlan.enumLookup).toBe(extendedLookup);
172
+ expect(remappedPlan.enumLookup).toBe(extendedLookup);
173
+ expect(remappedPlan.remapDescriptor).toBe(remap);
174
+ expect(remappedPlan.enumLookup.byField.operation.values).toEqual(['READ', 'WRITE']);
175
+ expect(remappedPlan.enumLookup.byField.phase.values).toEqual(['QUEUED', 'RUNNING', 'DONE']);
176
+ });
177
+ });