@smoothbricks/lmao 0.1.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 (447) hide show
  1. package/README.md +269 -0
  2. package/dist/allocator.wasm +0 -0
  3. package/dist/bun/preload.d.ts +11 -0
  4. package/dist/bun/preload.d.ts.map +1 -0
  5. package/dist/bun/preload.js +10 -0
  6. package/dist/bun/trace-preload.d.ts +8 -0
  7. package/dist/bun/trace-preload.d.ts.map +1 -0
  8. package/dist/bun/trace-preload.js +12 -0
  9. package/dist/es.d.ts +8 -0
  10. package/dist/es.d.ts.map +1 -0
  11. package/dist/es.js +9 -0
  12. package/dist/index.d.ts +41 -0
  13. package/dist/index.d.ts.map +1 -0
  14. package/dist/index.js +84 -0
  15. package/dist/lib/JsBufferStrategy.d.ts +49 -0
  16. package/dist/lib/JsBufferStrategy.d.ts.map +1 -0
  17. package/dist/lib/JsBufferStrategy.js +60 -0
  18. package/dist/lib/archive/chunkCompaction.d.ts +15 -0
  19. package/dist/lib/archive/chunkCompaction.d.ts.map +1 -0
  20. package/dist/lib/archive/chunkCompaction.js +24 -0
  21. package/dist/lib/archive/chunkEnvelope.d.ts +21 -0
  22. package/dist/lib/archive/chunkEnvelope.d.ts.map +1 -0
  23. package/dist/lib/archive/chunkEnvelope.js +55 -0
  24. package/dist/lib/archive/chunkRouting.d.ts +10 -0
  25. package/dist/lib/archive/chunkRouting.d.ts.map +1 -0
  26. package/dist/lib/archive/chunkRouting.js +47 -0
  27. package/dist/lib/archive/chunkStats.d.ts +12 -0
  28. package/dist/lib/archive/chunkStats.d.ts.map +1 -0
  29. package/dist/lib/archive/chunkStats.js +10 -0
  30. package/dist/lib/archive/index.d.ts +5 -0
  31. package/dist/lib/archive/index.d.ts.map +1 -0
  32. package/dist/lib/archive/index.js +4 -0
  33. package/dist/lib/arrow/capacityStats.d.ts +9 -0
  34. package/dist/lib/arrow/capacityStats.d.ts.map +1 -0
  35. package/dist/lib/arrow/capacityStats.js +231 -0
  36. package/dist/lib/arrow/dictionaries.d.ts +27 -0
  37. package/dist/lib/arrow/dictionaries.d.ts.map +1 -0
  38. package/dist/lib/arrow/dictionaries.js +135 -0
  39. package/dist/lib/arrow/flechette.d.ts +40 -0
  40. package/dist/lib/arrow/flechette.d.ts.map +1 -0
  41. package/dist/lib/arrow/flechette.js +67 -0
  42. package/dist/lib/arrow/utils.d.ts +38 -0
  43. package/dist/lib/arrow/utils.d.ts.map +1 -0
  44. package/dist/lib/arrow/utils.js +112 -0
  45. package/dist/lib/buffer/Nanoseconds.d.ts +109 -0
  46. package/dist/lib/buffer/Nanoseconds.d.ts.map +1 -0
  47. package/dist/lib/buffer/Nanoseconds.js +86 -0
  48. package/dist/lib/bufferStrategy.d.ts +95 -0
  49. package/dist/lib/bufferStrategy.d.ts.map +1 -0
  50. package/dist/lib/bufferStrategy.js +1 -0
  51. package/dist/lib/capacityTuning.d.ts +50 -0
  52. package/dist/lib/capacityTuning.d.ts.map +1 -0
  53. package/dist/lib/capacityTuning.js +65 -0
  54. package/dist/lib/codegen/evaluatorGenerator.d.ts +35 -0
  55. package/dist/lib/codegen/evaluatorGenerator.d.ts.map +1 -0
  56. package/dist/lib/codegen/evaluatorGenerator.js +199 -0
  57. package/dist/lib/codegen/fixedPositionWriterGenerator.d.ts +139 -0
  58. package/dist/lib/codegen/fixedPositionWriterGenerator.d.ts.map +1 -0
  59. package/dist/lib/codegen/fixedPositionWriterGenerator.js +289 -0
  60. package/dist/lib/codegen/spanLoggerGenerator.d.ts +115 -0
  61. package/dist/lib/codegen/spanLoggerGenerator.d.ts.map +1 -0
  62. package/dist/lib/codegen/spanLoggerGenerator.js +575 -0
  63. package/dist/lib/convertToArrow.d.ts +40 -0
  64. package/dist/lib/convertToArrow.d.ts.map +1 -0
  65. package/dist/lib/convertToArrow.js +1467 -0
  66. package/dist/lib/defineOpContext.d.ts +96 -0
  67. package/dist/lib/defineOpContext.d.ts.map +1 -0
  68. package/dist/lib/defineOpContext.js +227 -0
  69. package/dist/lib/errors/Blocked.d.ts +113 -0
  70. package/dist/lib/errors/Blocked.d.ts.map +1 -0
  71. package/dist/lib/errors/Blocked.js +68 -0
  72. package/dist/lib/errors/CodeError.d.ts +101 -0
  73. package/dist/lib/errors/CodeError.d.ts.map +1 -0
  74. package/dist/lib/errors/CodeError.js +42 -0
  75. package/dist/lib/errors/RetriesExhausted.d.ts +67 -0
  76. package/dist/lib/errors/RetriesExhausted.d.ts.map +1 -0
  77. package/dist/lib/errors/RetriesExhausted.js +31 -0
  78. package/dist/lib/errors/Transient.d.ts +115 -0
  79. package/dist/lib/errors/Transient.d.ts.map +1 -0
  80. package/dist/lib/errors/Transient.js +54 -0
  81. package/dist/lib/errors/index.d.ts +14 -0
  82. package/dist/lib/errors/index.d.ts.map +1 -0
  83. package/dist/lib/errors/index.js +16 -0
  84. package/dist/lib/errors/retry-policy.d.ts +60 -0
  85. package/dist/lib/errors/retry-policy.d.ts.map +1 -0
  86. package/dist/lib/errors/retry-policy.js +54 -0
  87. package/dist/lib/flushScheduler.d.ts +162 -0
  88. package/dist/lib/flushScheduler.d.ts.map +1 -0
  89. package/dist/lib/flushScheduler.js +388 -0
  90. package/dist/lib/library.d.ts +149 -0
  91. package/dist/lib/library.d.ts.map +1 -0
  92. package/dist/lib/library.js +588 -0
  93. package/dist/lib/logApi.d.ts +61 -0
  94. package/dist/lib/logApi.d.ts.map +1 -0
  95. package/dist/lib/logApi.js +12 -0
  96. package/dist/lib/logBinding.d.ts +63 -0
  97. package/dist/lib/logBinding.d.ts.map +1 -0
  98. package/dist/lib/logBinding.js +1 -0
  99. package/dist/lib/op.d.ts +105 -0
  100. package/dist/lib/op.d.ts.map +1 -0
  101. package/dist/lib/op.js +59 -0
  102. package/dist/lib/opContext/contextTypes.d.ts +88 -0
  103. package/dist/lib/opContext/contextTypes.d.ts.map +1 -0
  104. package/dist/lib/opContext/contextTypes.js +34 -0
  105. package/dist/lib/opContext/createOpGroup.d.ts +57 -0
  106. package/dist/lib/opContext/createOpGroup.d.ts.map +1 -0
  107. package/dist/lib/opContext/createOpGroup.js +341 -0
  108. package/dist/lib/opContext/defineOp.d.ts +83 -0
  109. package/dist/lib/opContext/defineOp.d.ts.map +1 -0
  110. package/dist/lib/opContext/defineOp.js +197 -0
  111. package/dist/lib/opContext/featureFlagTypes.d.ts +58 -0
  112. package/dist/lib/opContext/featureFlagTypes.d.ts.map +1 -0
  113. package/dist/lib/opContext/featureFlagTypes.js +1 -0
  114. package/dist/lib/opContext/index.d.ts +13 -0
  115. package/dist/lib/opContext/index.d.ts.map +1 -0
  116. package/dist/lib/opContext/index.js +15 -0
  117. package/dist/lib/opContext/opGroupTypes.d.ts +250 -0
  118. package/dist/lib/opContext/opGroupTypes.d.ts.map +1 -0
  119. package/dist/lib/opContext/opGroupTypes.js +31 -0
  120. package/dist/lib/opContext/opTypes.d.ts +48 -0
  121. package/dist/lib/opContext/opTypes.d.ts.map +1 -0
  122. package/dist/lib/opContext/opTypes.js +7 -0
  123. package/dist/lib/opContext/spanContextTypes.d.ts +347 -0
  124. package/dist/lib/opContext/spanContextTypes.d.ts.map +1 -0
  125. package/dist/lib/opContext/spanContextTypes.js +1 -0
  126. package/dist/lib/opContext/types.d.ts +297 -0
  127. package/dist/lib/opContext/types.d.ts.map +1 -0
  128. package/dist/lib/opContext/types.js +16 -0
  129. package/dist/lib/result.d.ts +308 -0
  130. package/dist/lib/result.d.ts.map +1 -0
  131. package/dist/lib/result.js +359 -0
  132. package/dist/lib/schema/LogSchema.d.ts +62 -0
  133. package/dist/lib/schema/LogSchema.d.ts.map +1 -0
  134. package/dist/lib/schema/LogSchema.js +134 -0
  135. package/dist/lib/schema/builder.d.ts +12 -0
  136. package/dist/lib/schema/builder.d.ts.map +1 -0
  137. package/dist/lib/schema/builder.js +169 -0
  138. package/dist/lib/schema/defineFeatureFlags.d.ts +60 -0
  139. package/dist/lib/schema/defineFeatureFlags.d.ts.map +1 -0
  140. package/dist/lib/schema/defineFeatureFlags.js +29 -0
  141. package/dist/lib/schema/defineLogSchema.d.ts +87 -0
  142. package/dist/lib/schema/defineLogSchema.d.ts.map +1 -0
  143. package/dist/lib/schema/defineLogSchema.js +171 -0
  144. package/dist/lib/schema/evaluator.d.ts +267 -0
  145. package/dist/lib/schema/evaluator.d.ts.map +1 -0
  146. package/dist/lib/schema/evaluator.js +169 -0
  147. package/dist/lib/schema/extend.d.ts +14 -0
  148. package/dist/lib/schema/extend.d.ts.map +1 -0
  149. package/dist/lib/schema/extend.js +51 -0
  150. package/dist/lib/schema/systemSchema.d.ts +112 -0
  151. package/dist/lib/schema/systemSchema.d.ts.map +1 -0
  152. package/dist/lib/schema/systemSchema.js +260 -0
  153. package/dist/lib/schema/typeGuards.d.ts +52 -0
  154. package/dist/lib/schema/typeGuards.d.ts.map +1 -0
  155. package/dist/lib/schema/typeGuards.js +127 -0
  156. package/dist/lib/schema/types.d.ts +156 -0
  157. package/dist/lib/schema/types.d.ts.map +1 -0
  158. package/dist/lib/schema/types.js +4 -0
  159. package/dist/lib/spanBuffer.d.ts +133 -0
  160. package/dist/lib/spanBuffer.d.ts.map +1 -0
  161. package/dist/lib/spanBuffer.js +442 -0
  162. package/dist/lib/spanBufferHelpers.d.ts +53 -0
  163. package/dist/lib/spanBufferHelpers.d.ts.map +1 -0
  164. package/dist/lib/spanBufferHelpers.js +65 -0
  165. package/dist/lib/spanBufferStats.d.ts +38 -0
  166. package/dist/lib/spanBufferStats.d.ts.map +1 -0
  167. package/dist/lib/spanBufferStats.js +1 -0
  168. package/dist/lib/spanContext.d.ts +141 -0
  169. package/dist/lib/spanContext.d.ts.map +1 -0
  170. package/dist/lib/spanContext.js +763 -0
  171. package/dist/lib/sqlite/index.d.ts +14 -0
  172. package/dist/lib/sqlite/index.d.ts.map +1 -0
  173. package/dist/lib/sqlite/index.js +12 -0
  174. package/dist/lib/sqlite/sqlite-async-writer.d.ts +24 -0
  175. package/dist/lib/sqlite/sqlite-async-writer.d.ts.map +1 -0
  176. package/dist/lib/sqlite/sqlite-async-writer.js +65 -0
  177. package/dist/lib/sqlite/sqlite-common.d.ts +37 -0
  178. package/dist/lib/sqlite/sqlite-common.d.ts.map +1 -0
  179. package/dist/lib/sqlite/sqlite-common.js +207 -0
  180. package/dist/lib/sqlite/sqlite-d1.d.ts +25 -0
  181. package/dist/lib/sqlite/sqlite-d1.d.ts.map +1 -0
  182. package/dist/lib/sqlite/sqlite-d1.js +67 -0
  183. package/dist/lib/sqlite/sqlite-db.d.ts +29 -0
  184. package/dist/lib/sqlite/sqlite-db.d.ts.map +1 -0
  185. package/dist/lib/sqlite/sqlite-db.js +1 -0
  186. package/dist/lib/sqlite/sqlite-node.d.ts +16 -0
  187. package/dist/lib/sqlite/sqlite-node.d.ts.map +1 -0
  188. package/dist/lib/sqlite/sqlite-node.js +68 -0
  189. package/dist/lib/sqlite/sqlite-writer.d.ts +39 -0
  190. package/dist/lib/sqlite/sqlite-writer.d.ts.map +1 -0
  191. package/dist/lib/sqlite/sqlite-writer.js +78 -0
  192. package/dist/lib/testing/bun-harness.d.ts +150 -0
  193. package/dist/lib/testing/bun-harness.d.ts.map +1 -0
  194. package/dist/lib/testing/bun-harness.js +729 -0
  195. package/dist/lib/testing/extractFacts.d.ts +73 -0
  196. package/dist/lib/testing/extractFacts.d.ts.map +1 -0
  197. package/dist/lib/testing/extractFacts.js +237 -0
  198. package/dist/lib/testing/facts.d.ts +235 -0
  199. package/dist/lib/testing/facts.d.ts.map +1 -0
  200. package/dist/lib/testing/facts.js +209 -0
  201. package/dist/lib/testing/index.d.ts +58 -0
  202. package/dist/lib/testing/index.d.ts.map +1 -0
  203. package/dist/lib/testing/index.js +74 -0
  204. package/dist/lib/testing/queryable-span.d.ts +39 -0
  205. package/dist/lib/testing/queryable-span.d.ts.map +1 -0
  206. package/dist/lib/testing/queryable-span.js +58 -0
  207. package/dist/lib/testing/span-query.d.ts +21 -0
  208. package/dist/lib/testing/span-query.d.ts.map +1 -0
  209. package/dist/lib/testing/span-query.js +47 -0
  210. package/dist/lib/testing/stdio-replay.d.ts +4 -0
  211. package/dist/lib/testing/stdio-replay.d.ts.map +1 -0
  212. package/dist/lib/testing/stdio-replay.js +42 -0
  213. package/dist/lib/testing/trace-query.d.ts +59 -0
  214. package/dist/lib/testing/trace-query.d.ts.map +1 -0
  215. package/dist/lib/testing/trace-query.js +462 -0
  216. package/dist/lib/testing/vitest-harness.d.ts +135 -0
  217. package/dist/lib/testing/vitest-harness.d.ts.map +1 -0
  218. package/dist/lib/testing/vitest-harness.js +486 -0
  219. package/dist/lib/threadId.d.ts +58 -0
  220. package/dist/lib/threadId.d.ts.map +1 -0
  221. package/dist/lib/threadId.js +123 -0
  222. package/dist/lib/traceContext.d.ts +116 -0
  223. package/dist/lib/traceContext.d.ts.map +1 -0
  224. package/dist/lib/traceContext.js +37 -0
  225. package/dist/lib/traceId.d.ts +78 -0
  226. package/dist/lib/traceId.d.ts.map +1 -0
  227. package/dist/lib/traceId.js +87 -0
  228. package/dist/lib/traceRoot.d.ts +133 -0
  229. package/dist/lib/traceRoot.d.ts.map +1 -0
  230. package/dist/lib/traceRoot.es.d.ts +75 -0
  231. package/dist/lib/traceRoot.es.d.ts.map +1 -0
  232. package/dist/lib/traceRoot.es.js +129 -0
  233. package/dist/lib/traceRoot.js +19 -0
  234. package/dist/lib/traceRoot.node.d.ts +77 -0
  235. package/dist/lib/traceRoot.node.d.ts.map +1 -0
  236. package/dist/lib/traceRoot.node.js +133 -0
  237. package/dist/lib/traceRoot.universal.d.ts +3 -0
  238. package/dist/lib/traceRoot.universal.d.ts.map +1 -0
  239. package/dist/lib/traceRoot.universal.js +17 -0
  240. package/dist/lib/tracer.d.ts +296 -0
  241. package/dist/lib/tracer.d.ts.map +1 -0
  242. package/dist/lib/tracer.js +346 -0
  243. package/dist/lib/tracers/ArrayQueueTracer.d.ts +56 -0
  244. package/dist/lib/tracers/ArrayQueueTracer.d.ts.map +1 -0
  245. package/dist/lib/tracers/ArrayQueueTracer.js +67 -0
  246. package/dist/lib/tracers/CompositeTracer.d.ts +24 -0
  247. package/dist/lib/tracers/CompositeTracer.d.ts.map +1 -0
  248. package/dist/lib/tracers/CompositeTracer.js +57 -0
  249. package/dist/lib/tracers/NoOpTracer.d.ts +58 -0
  250. package/dist/lib/tracers/NoOpTracer.d.ts.map +1 -0
  251. package/dist/lib/tracers/NoOpTracer.js +41 -0
  252. package/dist/lib/tracers/SQLiteTracer.d.ts +42 -0
  253. package/dist/lib/tracers/SQLiteTracer.d.ts.map +1 -0
  254. package/dist/lib/tracers/SQLiteTracer.js +84 -0
  255. package/dist/lib/tracers/StdioTracer.d.ts +83 -0
  256. package/dist/lib/tracers/StdioTracer.d.ts.map +1 -0
  257. package/dist/lib/tracers/StdioTracer.js +270 -0
  258. package/dist/lib/tracers/TestTracer.d.ts +95 -0
  259. package/dist/lib/tracers/TestTracer.d.ts.map +1 -0
  260. package/dist/lib/tracers/TestTracer.js +87 -0
  261. package/dist/lib/tracers/index.d.ts +12 -0
  262. package/dist/lib/tracers/index.d.ts.map +1 -0
  263. package/dist/lib/tracers/index.js +11 -0
  264. package/dist/lib/types.d.ts +366 -0
  265. package/dist/lib/types.d.ts.map +1 -0
  266. package/dist/lib/types.js +1 -0
  267. package/dist/lib/utf8Cache.d.ts +111 -0
  268. package/dist/lib/utf8Cache.d.ts.map +1 -0
  269. package/dist/lib/utf8Cache.js +177 -0
  270. package/dist/lib/wasm/WasmBufferStrategy.d.ts +88 -0
  271. package/dist/lib/wasm/WasmBufferStrategy.d.ts.map +1 -0
  272. package/dist/lib/wasm/WasmBufferStrategy.js +138 -0
  273. package/dist/lib/wasm/wasmAllocator.d.ts +109 -0
  274. package/dist/lib/wasm/wasmAllocator.d.ts.map +1 -0
  275. package/dist/lib/wasm/wasmAllocator.js +227 -0
  276. package/dist/lib/wasm/wasmSpanBuffer.d.ts +123 -0
  277. package/dist/lib/wasm/wasmSpanBuffer.d.ts.map +1 -0
  278. package/dist/lib/wasm/wasmSpanBuffer.js +772 -0
  279. package/dist/lib/wasm/wasmTraceRoot.d.ts +167 -0
  280. package/dist/lib/wasm/wasmTraceRoot.d.ts.map +1 -0
  281. package/dist/lib/wasm/wasmTraceRoot.js +264 -0
  282. package/dist/node.d.ts +7 -0
  283. package/dist/node.d.ts.map +1 -0
  284. package/dist/node.js +8 -0
  285. package/dist/testing.d.ts +18 -0
  286. package/dist/testing.d.ts.map +1 -0
  287. package/dist/testing.js +17 -0
  288. package/package.json +193 -0
  289. package/src/bun/preload.ts +11 -0
  290. package/src/bun/trace-preload.ts +14 -0
  291. package/src/es.ts +11 -0
  292. package/src/index.ts +194 -0
  293. package/src/lib/JsBufferStrategy.ts +102 -0
  294. package/src/lib/__tests__/arrow-builder-integration/arrow-conversion.test.ts +485 -0
  295. package/src/lib/__tests__/arrow-builder-integration/binary-arrow-conversion.test.ts +390 -0
  296. package/src/lib/__tests__/arrow-builder-integration/binary-comparison.test.ts +647 -0
  297. package/src/lib/__tests__/arrow-builder-integration/binary-format-compliance.test.ts +292 -0
  298. package/src/lib/__tests__/arrow-builder-integration/buffer-chaining.test.ts +354 -0
  299. package/src/lib/__tests__/arrow-builder-integration/foundation.test.ts +127 -0
  300. package/src/lib/__tests__/arrow-builder-integration/integration.test.ts +392 -0
  301. package/src/lib/__tests__/arrow-builder-integration/lazy-columns.test.ts +389 -0
  302. package/src/lib/__tests__/arrow-conversion-algorithms.test.ts +54 -0
  303. package/src/lib/__tests__/arrow-test-helpers.ts +71 -0
  304. package/src/lib/__tests__/binary-e2e.test.ts +203 -0
  305. package/src/lib/__tests__/buffer-overflow-op-api.test.ts +608 -0
  306. package/src/lib/__tests__/buffer-overflow.test.ts +612 -0
  307. package/src/lib/__tests__/capacity-tuning.test.ts +544 -0
  308. package/src/lib/__tests__/effective-schema.test.ts +439 -0
  309. package/src/lib/__tests__/flechette.test.ts +42 -0
  310. package/src/lib/__tests__/flushScheduler.test.ts +537 -0
  311. package/src/lib/__tests__/integration.test.ts +600 -0
  312. package/src/lib/__tests__/library.test.ts +934 -0
  313. package/src/lib/__tests__/nested-library-tasks.test.ts +849 -0
  314. package/src/lib/__tests__/op-context-binding.test.ts +135 -0
  315. package/src/lib/__tests__/op-metadata.test.ts +198 -0
  316. package/src/lib/__tests__/op-type-safety.test.ts +71 -0
  317. package/src/lib/__tests__/package-exports.test.ts +57 -0
  318. package/src/lib/__tests__/remapped-buffer-view-integration.test.ts +319 -0
  319. package/src/lib/__tests__/remapped-buffer-view.test.ts +635 -0
  320. package/src/lib/__tests__/scope-attributes.test.ts +348 -0
  321. package/src/lib/__tests__/span-lifecycle.test.ts +680 -0
  322. package/src/lib/__tests__/sync-async-trace.test.ts +435 -0
  323. package/src/lib/__tests__/test-helpers.ts +318 -0
  324. package/src/lib/__tests__/timestamp.test.ts +259 -0
  325. package/src/lib/__tests__/trace-archive-primitives.test.ts +98 -0
  326. package/src/lib/__tests__/traceContext-type-enforcement.test.ts +271 -0
  327. package/src/lib/__tests__/tracer.test.ts +211 -0
  328. package/src/lib/__tests__/type-narrowing.test.ts +137 -0
  329. package/src/lib/archive/chunkCompaction.ts +44 -0
  330. package/src/lib/archive/chunkEnvelope.ts +79 -0
  331. package/src/lib/archive/chunkRouting.ts +70 -0
  332. package/src/lib/archive/chunkStats.ts +25 -0
  333. package/src/lib/archive/index.ts +4 -0
  334. package/src/lib/arrow/capacityStats.ts +340 -0
  335. package/src/lib/arrow/dictionaries.ts +159 -0
  336. package/src/lib/arrow/flechette.ts +114 -0
  337. package/src/lib/arrow/utils.ts +126 -0
  338. package/src/lib/buffer/Nanoseconds.ts +131 -0
  339. package/src/lib/bufferStrategy.ts +106 -0
  340. package/src/lib/capacityTuning.ts +102 -0
  341. package/src/lib/codegen/__tests__/__snapshots__/evaluatorGenerator.test.ts.snap +160 -0
  342. package/src/lib/codegen/__tests__/__snapshots__/scopeGenerator.test.ts.snap +196 -0
  343. package/src/lib/codegen/__tests__/__snapshots__/spanLoggerGenerator.test.ts.snap +1965 -0
  344. package/src/lib/codegen/__tests__/evaluatorGenerator.test.ts +516 -0
  345. package/src/lib/codegen/__tests__/nullBitmap.test.ts +207 -0
  346. package/src/lib/codegen/__tests__/spanLoggerGenerator.test.ts +237 -0
  347. package/src/lib/codegen/evaluatorGenerator.ts +309 -0
  348. package/src/lib/codegen/fixedPositionWriterGenerator.ts +444 -0
  349. package/src/lib/codegen/spanLoggerGenerator.ts +715 -0
  350. package/src/lib/convertToArrow.ts +1805 -0
  351. package/src/lib/defineOpContext.ts +398 -0
  352. package/src/lib/errors/Blocked.ts +165 -0
  353. package/src/lib/errors/CodeError.ts +132 -0
  354. package/src/lib/errors/RetriesExhausted.ts +78 -0
  355. package/src/lib/errors/Transient.ts +168 -0
  356. package/src/lib/errors/__tests__/Blocked-config.test.ts +152 -0
  357. package/src/lib/errors/__tests__/Transient.test.ts +129 -0
  358. package/src/lib/errors/__tests__/retry-policy.test.ts +85 -0
  359. package/src/lib/errors/index.ts +24 -0
  360. package/src/lib/errors/retry-policy.ts +87 -0
  361. package/src/lib/flushScheduler.ts +509 -0
  362. package/src/lib/library.ts +682 -0
  363. package/src/lib/logApi.ts +70 -0
  364. package/src/lib/logBinding.ts +67 -0
  365. package/src/lib/op.ts +103 -0
  366. package/src/lib/opContext/__tests__/retry.test.ts +764 -0
  367. package/src/lib/opContext/contextTypes.ts +130 -0
  368. package/src/lib/opContext/createOpGroup.ts +456 -0
  369. package/src/lib/opContext/defineOp.ts +281 -0
  370. package/src/lib/opContext/featureFlagTypes.ts +54 -0
  371. package/src/lib/opContext/index.ts +16 -0
  372. package/src/lib/opContext/opGroupTypes.ts +342 -0
  373. package/src/lib/opContext/opTypes.ts +80 -0
  374. package/src/lib/opContext/spanContextTypes.ts +398 -0
  375. package/src/lib/opContext/types.ts +403 -0
  376. package/src/lib/result.ts +550 -0
  377. package/src/lib/schema/LogSchema.ts +153 -0
  378. package/src/lib/schema/__tests__/defineLogSchema.test.ts +342 -0
  379. package/src/lib/schema/__tests__/featureFlags.test.ts +443 -0
  380. package/src/lib/schema/builder.ts +201 -0
  381. package/src/lib/schema/defineFeatureFlags.ts +91 -0
  382. package/src/lib/schema/defineLogSchema.ts +291 -0
  383. package/src/lib/schema/evaluator.ts +404 -0
  384. package/src/lib/schema/extend.ts +69 -0
  385. package/src/lib/schema/systemSchema.ts +339 -0
  386. package/src/lib/schema/typeGuards.ts +163 -0
  387. package/src/lib/schema/types.ts +265 -0
  388. package/src/lib/spanBuffer.ts +571 -0
  389. package/src/lib/spanBufferHelpers.ts +73 -0
  390. package/src/lib/spanBufferStats.ts +40 -0
  391. package/src/lib/spanContext.ts +1566 -0
  392. package/src/lib/sqlite/__tests__/sqlite-common.test.ts +89 -0
  393. package/src/lib/sqlite/index.ts +26 -0
  394. package/src/lib/sqlite/sqlite-async-writer.ts +101 -0
  395. package/src/lib/sqlite/sqlite-common.ts +309 -0
  396. package/src/lib/sqlite/sqlite-d1.ts +104 -0
  397. package/src/lib/sqlite/sqlite-db.ts +32 -0
  398. package/src/lib/sqlite/sqlite-node.ts +83 -0
  399. package/src/lib/sqlite/sqlite-writer.ts +128 -0
  400. package/src/lib/testing/TRACE-TESTING.md +100 -0
  401. package/src/lib/testing/__tests__/bun-harness.test.ts +108 -0
  402. package/src/lib/testing/__tests__/extractFacts.test.ts +279 -0
  403. package/src/lib/testing/__tests__/facts.test.ts +324 -0
  404. package/src/lib/testing/__tests__/vitest-harness.test.ts +32 -0
  405. package/src/lib/testing/bun-harness.ts +990 -0
  406. package/src/lib/testing/extractFacts.ts +362 -0
  407. package/src/lib/testing/facts.ts +470 -0
  408. package/src/lib/testing/index.ts +141 -0
  409. package/src/lib/testing/queryable-span.ts +108 -0
  410. package/src/lib/testing/span-query.ts +65 -0
  411. package/src/lib/testing/stdio-replay.ts +50 -0
  412. package/src/lib/testing/trace-query.ts +329 -0
  413. package/src/lib/testing/vitest-harness.ts +691 -0
  414. package/src/lib/threadId.ts +135 -0
  415. package/src/lib/traceContext.ts +182 -0
  416. package/src/lib/traceId.ts +142 -0
  417. package/src/lib/traceRoot.es.ts +157 -0
  418. package/src/lib/traceRoot.node.ts +170 -0
  419. package/src/lib/traceRoot.ts +154 -0
  420. package/src/lib/traceRoot.universal.ts +22 -0
  421. package/src/lib/tracer.ts +896 -0
  422. package/src/lib/tracers/ArrayQueueTracer.ts +76 -0
  423. package/src/lib/tracers/CompositeTracer.ts +76 -0
  424. package/src/lib/tracers/NoOpTracer.ts +73 -0
  425. package/src/lib/tracers/SQLiteTracer.ts +115 -0
  426. package/src/lib/tracers/StdioTracer.ts +380 -0
  427. package/src/lib/tracers/TestTracer.ts +146 -0
  428. package/src/lib/tracers/__tests__/ArrayQueueTracer.test.ts +184 -0
  429. package/src/lib/tracers/__tests__/NoOpTracer.test.ts +204 -0
  430. package/src/lib/tracers/__tests__/StdioTracer.test.ts +313 -0
  431. package/src/lib/tracers/__tests__/TestTracer.test.ts +200 -0
  432. package/src/lib/tracers/index.ts +12 -0
  433. package/src/lib/types.ts +498 -0
  434. package/src/lib/utf8Cache.ts +201 -0
  435. package/src/lib/wasm/WasmBufferStrategy.ts +219 -0
  436. package/src/lib/wasm/__tests__/WasmBufferStrategy.test.ts +256 -0
  437. package/src/lib/wasm/__tests__/wasm-integration.test.ts +476 -0
  438. package/src/lib/wasm/__tests__/wasmAllocator.test.ts +699 -0
  439. package/src/lib/wasm/__tests__/wasmSpanBuffer.test.ts +449 -0
  440. package/src/lib/wasm/__tests__/wasmTraceRoot.test.ts +390 -0
  441. package/src/lib/wasm/__tests__/wasmTracer.test.ts.disabled +444 -0
  442. package/src/lib/wasm/allocator.zig +945 -0
  443. package/src/lib/wasm/wasmAllocator.ts +469 -0
  444. package/src/lib/wasm/wasmSpanBuffer.ts +1014 -0
  445. package/src/lib/wasm/wasmTraceRoot.ts +315 -0
  446. package/src/node.ts +10 -0
  447. package/src/testing.ts +18 -0
@@ -0,0 +1,1805 @@
1
+ /**
2
+ * Zero-copy conversion from SpanBuffer to Flechette tables
3
+ *
4
+ * Per specs/lmao/01f_arrow_table_structure.md:
5
+ * - Enum columns: Dictionary with compile-time values
6
+ * - Category columns: Dictionary with runtime-built values
7
+ * - Text columns: Plain strings without dictionary
8
+ * - Zero-copy wrap TypedArrays as Arrow vectors
9
+ */
10
+
11
+ import {
12
+ clearBitRange,
13
+ compareStrings,
14
+ createSortedDictionary,
15
+ DictionaryBuilder,
16
+ type FinalizedDictionary,
17
+ getMaskTransform,
18
+ type PreEncodedEntry,
19
+ sortInPlace,
20
+ } from '@smoothbricks/arrow-builder';
21
+ import {
22
+ batchType,
23
+ binary,
24
+ bool,
25
+ Column,
26
+ type DataType,
27
+ dictionary,
28
+ float64,
29
+ type IntType,
30
+ int8,
31
+ type Table,
32
+ TimeUnit,
33
+ tableFromColumns,
34
+ timestamp,
35
+ uint8,
36
+ uint16,
37
+ uint32,
38
+ uint64,
39
+ utf8,
40
+ } from '@uwdata/flechette';
41
+ import { type CapacityStatsEntry, createCapacityStatsTable } from './arrow/capacityStats.js';
42
+ import { buildSortedCategoryDictionary, buildTextDictionary } from './arrow/dictionaries.js';
43
+ import {
44
+ type DictionaryColumnData,
45
+ type GenericColumnData,
46
+ getArrowIndexArrayConstructorOr,
47
+ getArrowIndexTypeOr,
48
+ makeArrowColumn,
49
+ type Utf8ColumnData,
50
+ } from './arrow/flechette.js';
51
+ import {
52
+ calculateUtf8Offsets,
53
+ concatenateFloat64Arrays,
54
+ concatenateNullBitmaps,
55
+ concatenateUint8Arrays,
56
+ encodeUtf8Strings,
57
+ getArrowFieldName,
58
+ walkSpanTree,
59
+ } from './arrow/utils.js';
60
+ import { ENTRY_TYPE_NAMES, SYSTEM_SCHEMA_FIELD_NAMES } from './schema/systemSchema.js';
61
+ import { getBinaryEncoder, getEnumUtf8, getEnumValues, getSchemaType } from './schema/typeGuards.js';
62
+ import type { LogSchema } from './schema/types.js';
63
+ import type { AnySpanBuffer, OpMetadata } from './types.js';
64
+ import { globalUtf8Cache } from './utf8Cache.js';
65
+
66
+ const DictBuilder = DictionaryBuilder;
67
+ const F64Array = Float64Array;
68
+
69
+ type BuiltArrowColumn = { column: Column<unknown>; nullCount: number };
70
+ type BuiltMetadataColumns = { fields: string[]; vectors: Column<unknown>[] };
71
+
72
+ export type SystemColumnBuilder = (
73
+ buffer: AnySpanBuffer,
74
+ buffers: AnySpanBuffer[],
75
+ totalRows: number,
76
+ ) => BuiltMetadataColumns;
77
+
78
+ const EMPTY_VALIDITY = new Uint8Array(0);
79
+ const BINARY_TYPE = binary();
80
+ type ArrowColumnDataBase<TType, TValues = unknown> = {
81
+ type: TType;
82
+ length: number;
83
+ nullCount: number;
84
+ data?: TValues;
85
+ nullBitmap?: Uint8Array;
86
+ };
87
+ type DictionaryColumnBuildData = ArrowColumnDataBase<ReturnType<typeof dictionary>> & {
88
+ dictionary: Column<unknown>;
89
+ };
90
+ type Utf8ColumnBuildData = ArrowColumnDataBase<ReturnType<typeof utf8>, Uint8Array> & {
91
+ valueOffsets: Int32Array;
92
+ };
93
+ type GenericColumnBuildData<TType extends DataType = DataType, TValues = unknown> = ArrowColumnDataBase<
94
+ TType,
95
+ TValues
96
+ > & {
97
+ dictionary?: never;
98
+ valueOffsets?: never;
99
+ };
100
+
101
+ function buildData<T extends Record<string, unknown>>(data: T): T {
102
+ return data;
103
+ }
104
+
105
+ function buildColumn(data: DictionaryColumnBuildData): Column<unknown>;
106
+ function buildColumn(data: Utf8ColumnBuildData): Column<unknown>;
107
+ function buildColumn(data: GenericColumnBuildData): Column<unknown>;
108
+ function buildColumn(data: DictionaryColumnBuildData | Utf8ColumnBuildData | GenericColumnBuildData): Column<unknown> {
109
+ if (isDictionaryColumnBuildData(data)) {
110
+ const dictionaryData: DictionaryColumnData = {
111
+ type: data.type,
112
+ length: data.length,
113
+ nullCount: data.nullCount,
114
+ values: data.data,
115
+ validity: data.nullBitmap,
116
+ dictionary: data.dictionary,
117
+ };
118
+ return makeArrowColumn(dictionaryData);
119
+ }
120
+ if (isUtf8ColumnBuildData(data)) {
121
+ const utf8Data: Utf8ColumnData = {
122
+ type: data.type,
123
+ length: data.length,
124
+ nullCount: data.nullCount,
125
+ values: data.data,
126
+ validity: data.nullBitmap,
127
+ offsets: data.valueOffsets,
128
+ };
129
+ return makeArrowColumn(utf8Data);
130
+ }
131
+ const genericData: GenericColumnData<DataType> = {
132
+ type: data.type,
133
+ length: data.length,
134
+ nullCount: data.nullCount,
135
+ values: data.data,
136
+ validity: data.nullBitmap,
137
+ };
138
+ return makeArrowColumn(genericData);
139
+ }
140
+
141
+ function isDictionaryColumnBuildData(
142
+ data: DictionaryColumnBuildData | Utf8ColumnBuildData | GenericColumnBuildData,
143
+ ): data is DictionaryColumnBuildData {
144
+ return 'dictionary' in data && data.dictionary instanceof Column;
145
+ }
146
+
147
+ function isUtf8ColumnBuildData(
148
+ data: DictionaryColumnBuildData | Utf8ColumnBuildData | GenericColumnBuildData,
149
+ ): data is Utf8ColumnBuildData {
150
+ return 'valueOffsets' in data && data.valueOffsets instanceof Int32Array;
151
+ }
152
+
153
+ function buildTable(fields: string[], vectors: Column<unknown>[]): Table {
154
+ if (fields.length === 0) return tableFromColumns({});
155
+ const columns: Record<string, Column<unknown>> = {};
156
+ for (let index = 0; index < fields.length; index++) {
157
+ columns[fields[index]] = vectors[index];
158
+ }
159
+ return tableFromColumns(columns);
160
+ }
161
+
162
+ function appendTables(base: Table, extra: Table): Table {
163
+ const columns: Record<string, Column<unknown>> = {};
164
+ for (let i = 0; i < base.numCols; i++) {
165
+ const name = base.names[i];
166
+ const left = base.getChildAt(i);
167
+ const right = extra.getChild(name);
168
+ if (!left || !right) {
169
+ throw new Error(`Cannot append tables: missing column '${name}'`);
170
+ }
171
+ columns[name] = new Column([...left.data, ...right.data]);
172
+ }
173
+ return tableFromColumns(columns);
174
+ }
175
+
176
+ function getAllocatedStringColumn(buffer: AnySpanBuffer, columnName: string): string[] | undefined {
177
+ const column = buffer.getColumnIfAllocated(columnName);
178
+ if (!Array.isArray(column)) {
179
+ return undefined;
180
+ }
181
+ return isStringArray(column) ? column : undefined;
182
+ }
183
+
184
+ function getAllocatedBinaryColumn(buffer: AnySpanBuffer, columnName: string): unknown[] | undefined {
185
+ const column = buffer.getColumnIfAllocated(columnName);
186
+ return Array.isArray(column) ? column : undefined;
187
+ }
188
+
189
+ function getStringScopeValue(buffer: AnySpanBuffer, fieldName: string): string | undefined {
190
+ const value = buffer._scopeValues?.[fieldName];
191
+ return typeof value === 'string' ? value : undefined;
192
+ }
193
+
194
+ function getNumberScopeValue(buffer: AnySpanBuffer, fieldName: string): number | undefined {
195
+ const value = buffer._scopeValues?.[fieldName];
196
+ return typeof value === 'number' ? value : undefined;
197
+ }
198
+
199
+ function getBooleanScopeValue(buffer: AnySpanBuffer, fieldName: string): boolean | undefined {
200
+ const value = buffer._scopeValues?.[fieldName];
201
+ return typeof value === 'boolean' ? value : undefined;
202
+ }
203
+
204
+ function requireUint8Array(value: unknown, message: string): Uint8Array {
205
+ if (!(value instanceof Uint8Array)) {
206
+ throw new Error(message);
207
+ }
208
+ return value;
209
+ }
210
+
211
+ function isStringArray(value: unknown): value is string[] {
212
+ return Array.isArray(value) && value.every((entry) => typeof entry === 'string' || entry == null);
213
+ }
214
+
215
+ function indexTypeForCount(count: number): IntType {
216
+ if (count <= 255) return uint8();
217
+ if (count <= 65535) return uint16();
218
+ return uint32();
219
+ }
220
+
221
+ // ═══════════════════════════════════════════════════════════════════════════════
222
+ // Shared metadata column helpers (used by both buildMetadataColumnsWithFields
223
+ // and convertBuffersWithSharedDicts to avoid duplication)
224
+ // ═══════════════════════════════════════════════════════════════════════════════
225
+
226
+ /**
227
+ * Build non-dictionary identity columns: timestamp, thread_id, span_id, parent_thread_id, parent_span_id.
228
+ * These are identical in both single-batch and shared-dict paths.
229
+ *
230
+ * Callers are responsible for inserting trace_id (dictionary column) at the correct position
231
+ * and entry_type (dictionary column) after these columns.
232
+ */
233
+ function buildNonDictIdentityColumns(
234
+ buffers: AnySpanBuffer[],
235
+ totalRows: number,
236
+ fields: string[],
237
+ vectors: Column<unknown>[],
238
+ ): void {
239
+ // thread_id (BigUint64Array, constant per buffer)
240
+ const threadIds = new BigUint64Array(totalRows);
241
+ let offset = 0;
242
+ for (const buf of buffers) {
243
+ threadIds.fill(buf.thread_id, offset, offset + buf._writeIndex);
244
+ offset += buf._writeIndex;
245
+ }
246
+ fields.push('thread_id');
247
+ vectors.push(buildColumn(buildData({ type: uint64(), offset: 0, length: totalRows, nullCount: 0, data: threadIds })));
248
+
249
+ // span_id (Uint32Array, constant per buffer)
250
+ const spanIds = new Uint32Array(totalRows);
251
+ offset = 0;
252
+ for (const buf of buffers) {
253
+ spanIds.fill(buf.span_id, offset, offset + buf._writeIndex);
254
+ offset += buf._writeIndex;
255
+ }
256
+ fields.push('span_id');
257
+ vectors.push(buildColumn(buildData({ type: uint32(), offset: 0, length: totalRows, nullCount: 0, data: spanIds })));
258
+
259
+ // parent_thread_id (nullable BigUint64Array)
260
+ const parentThreadIds = new BigUint64Array(totalRows);
261
+ const parentThreadIdNulls = new Uint8Array(Math.ceil(totalRows / 8));
262
+ parentThreadIdNulls.fill(0xff);
263
+ let parentThreadIdNullCount = 0;
264
+ offset = 0;
265
+ for (const buf of buffers) {
266
+ if (buf._parent) {
267
+ parentThreadIds.fill(buf.parent_thread_id, offset, offset + buf._writeIndex);
268
+ } else {
269
+ clearBitRange(parentThreadIdNulls, offset, buf._writeIndex);
270
+ parentThreadIdNullCount += buf._writeIndex;
271
+ }
272
+ offset += buf._writeIndex;
273
+ }
274
+ fields.push('parent_thread_id');
275
+ vectors.push(
276
+ buildColumn(
277
+ buildData({
278
+ type: uint64(),
279
+ offset: 0,
280
+ length: totalRows,
281
+ nullCount: parentThreadIdNullCount,
282
+ data: parentThreadIds,
283
+ nullBitmap: parentThreadIdNullCount > 0 ? parentThreadIdNulls : undefined,
284
+ }),
285
+ ),
286
+ );
287
+
288
+ // parent_span_id (nullable Uint32Array)
289
+ const parentSpanIds = new Uint32Array(totalRows);
290
+ const parentSpanIdNulls = new Uint8Array(Math.ceil(totalRows / 8));
291
+ parentSpanIdNulls.fill(0xff);
292
+ let parentSpanIdNullCount = 0;
293
+ offset = 0;
294
+ for (const buf of buffers) {
295
+ if (buf._hasParent) {
296
+ parentSpanIds.fill(buf.parent_span_id, offset, offset + buf._writeIndex);
297
+ } else {
298
+ clearBitRange(parentSpanIdNulls, offset, buf._writeIndex);
299
+ parentSpanIdNullCount += buf._writeIndex;
300
+ }
301
+ offset += buf._writeIndex;
302
+ }
303
+ fields.push('parent_span_id');
304
+ vectors.push(
305
+ buildColumn(
306
+ buildData({
307
+ type: uint32(),
308
+ offset: 0,
309
+ length: totalRows,
310
+ nullCount: parentSpanIdNullCount,
311
+ data: parentSpanIds,
312
+ nullBitmap: parentSpanIdNullCount > 0 ? parentSpanIdNulls : undefined,
313
+ }),
314
+ ),
315
+ );
316
+ }
317
+
318
+ /**
319
+ * Build timestamp column from buffers.
320
+ */
321
+ function buildTimestampColumn(
322
+ buffers: AnySpanBuffer[],
323
+ totalRows: number,
324
+ fields: string[],
325
+ vectors: Column<unknown>[],
326
+ ): void {
327
+ const allTimestamps = new BigInt64Array(totalRows);
328
+ let offset = 0;
329
+ for (const buf of buffers) {
330
+ if (!buf.timestamp) {
331
+ throw new Error(`Buffer missing timestamps property (_writeIndex: ${buf._writeIndex})`);
332
+ }
333
+ allTimestamps.set(buf.timestamp.subarray(0, buf._writeIndex), offset);
334
+ offset += buf._writeIndex;
335
+ }
336
+ fields.push('timestamp');
337
+ vectors.push(
338
+ buildColumn(
339
+ buildData({
340
+ type: timestamp(TimeUnit.NANOSECOND),
341
+ offset: 0,
342
+ length: totalRows,
343
+ nullCount: 0,
344
+ data: allTimestamps,
345
+ }),
346
+ ),
347
+ );
348
+ }
349
+
350
+ /**
351
+ * Build entry_type dictionary column.
352
+ */
353
+ function buildEntryTypeColumn(
354
+ buffers: AnySpanBuffer[],
355
+ totalRows: number,
356
+ fields: string[],
357
+ vectors: Column<unknown>[],
358
+ dictType?: ReturnType<typeof dictionary>,
359
+ ): void {
360
+ const entryTypeIndices = new Int8Array(totalRows);
361
+ let offset = 0;
362
+ for (const buf of buffers) {
363
+ if (!buf.entry_type) {
364
+ throw new Error(`Buffer missing operations property (_writeIndex: ${buf._writeIndex})`);
365
+ }
366
+ entryTypeIndices.set(buf.entry_type.subarray(0, buf._writeIndex), offset);
367
+ offset += buf._writeIndex;
368
+ }
369
+ const entryTypeDictData = buildData({
370
+ type: utf8(),
371
+ offset: 0,
372
+ length: ENTRY_TYPE_NAMES.length,
373
+ nullCount: 0,
374
+ valueOffsets: calculateUtf8Offsets(ENTRY_TYPE_NAMES),
375
+ data: encodeUtf8Strings(ENTRY_TYPE_NAMES),
376
+ });
377
+ fields.push('entry_type');
378
+ vectors.push(
379
+ buildColumn(
380
+ buildData({
381
+ type: dictType ?? dictionary(utf8(), int8()),
382
+ offset: 0,
383
+ length: totalRows,
384
+ nullCount: 0,
385
+ data: entryTypeIndices,
386
+ dictionary: buildColumn(entryTypeDictData),
387
+ }),
388
+ ),
389
+ );
390
+ }
391
+
392
+ /**
393
+ * Build a per-buffer metadata dictionary column (package_name, package_file, git_sha).
394
+ * Row 0 uses callsiteMetadata, rows 1+ use opMetadata (per context flow spec).
395
+ */
396
+ function buildPerBufferDictColumn(
397
+ buffers: AnySpanBuffer[],
398
+ totalRows: number,
399
+ fieldName: string,
400
+ metadataGetter: (m: OpMetadata) => string,
401
+ dictType: ReturnType<typeof dictionary>,
402
+ dictValues: string[],
403
+ dictMap: Map<string, number>,
404
+ dictUtf8Offsets: Int32Array,
405
+ dictUtf8Data: Uint8Array,
406
+ fields: string[],
407
+ vectors: Column<unknown>[],
408
+ ): void {
409
+ const uniqueCount = dictValues.length;
410
+ const IndexArrayCtor = uniqueCount <= 255 ? Uint8Array : uniqueCount <= 65535 ? Uint16Array : Uint32Array;
411
+ const indices = new IndexArrayCtor(totalRows);
412
+ let offset = 0;
413
+ for (const buf of buffers) {
414
+ const callsiteValue = metadataGetter(buf._callsiteMetadata ?? buf._opMetadata);
415
+ const callsiteIdx = dictMap.get(callsiteValue) ?? 0;
416
+ const opIdx = dictMap.get(metadataGetter(buf._opMetadata)) ?? 0;
417
+ indices[offset] = callsiteIdx;
418
+ if (buf._writeIndex > 1) {
419
+ indices.fill(opIdx, offset + 1, offset + buf._writeIndex);
420
+ }
421
+ offset += buf._writeIndex;
422
+ }
423
+ fields.push(fieldName);
424
+ vectors.push(
425
+ buildColumn(
426
+ buildData({
427
+ type: dictType,
428
+ offset: 0,
429
+ length: totalRows,
430
+ nullCount: 0,
431
+ data: indices,
432
+ dictionary: buildColumn(
433
+ buildData({
434
+ type: utf8(),
435
+ offset: 0,
436
+ length: dictValues.length,
437
+ nullCount: 0,
438
+ valueOffsets: dictUtf8Offsets,
439
+ data: dictUtf8Data,
440
+ }),
441
+ ),
442
+ }),
443
+ ),
444
+ );
445
+ }
446
+
447
+ /**
448
+ * Build a binary Arrow column from SpanBuffer data.
449
+ * Shared by both convertBuffersToTable and convertBuffersWithSharedDicts.
450
+ *
451
+ * Binary columns store arbitrary bytes (raw Uint8Array or encoder-encoded objects).
452
+ * At flush time, the encoder (if present) transforms stored values into Uint8Array.
453
+ */
454
+ function buildBinaryColumnFromBuffers(
455
+ buffers: AnySpanBuffer[],
456
+ columnName: string,
457
+ totalRows: number,
458
+ fieldSchema: unknown,
459
+ ): BuiltArrowColumn {
460
+ const encoder = getBinaryEncoder(fieldSchema);
461
+
462
+ // Collect all values from buffers, encode each if encoder is present
463
+ const allValues: (Uint8Array | null)[] = [];
464
+ for (const buf of buffers) {
465
+ const col = getAllocatedBinaryColumn(buf, columnName);
466
+ const srcNulls = buf.getNullsIfAllocated(columnName);
467
+ for (let i = 0; i < buf._writeIndex; i++) {
468
+ if (srcNulls) {
469
+ const isValid = (srcNulls[i >>> 3] & (1 << (i & 7))) !== 0;
470
+ if (isValid && col) {
471
+ const raw = col[i];
472
+ allValues.push(
473
+ encoder
474
+ ? encoder.encode(raw)
475
+ : requireUint8Array(raw, `Binary column '${columnName}' must store Uint8Array values`),
476
+ );
477
+ } else {
478
+ allValues.push(null);
479
+ }
480
+ } else if (col) {
481
+ // No null bitmap -- column allocated but check for undefined/null values
482
+ const raw = col[i];
483
+ if (raw != null) {
484
+ allValues.push(
485
+ encoder
486
+ ? encoder.encode(raw)
487
+ : requireUint8Array(raw, `Binary column '${columnName}' must store Uint8Array values`),
488
+ );
489
+ } else {
490
+ allValues.push(null);
491
+ }
492
+ } else {
493
+ allValues.push(null);
494
+ }
495
+ }
496
+ }
497
+
498
+ // Build offsets and concatenated data buffer
499
+ const offsets = new Int32Array(totalRows + 1);
500
+ let dataLength = 0;
501
+ for (let i = 0; i < allValues.length; i++) {
502
+ offsets[i] = dataLength;
503
+ const value = allValues[i];
504
+ if (value !== null) {
505
+ dataLength += value.length;
506
+ }
507
+ }
508
+ offsets[allValues.length] = dataLength;
509
+
510
+ const data = new Uint8Array(dataLength);
511
+ let dataOffset = 0;
512
+ let nullCount = 0;
513
+ const nullBitmapSize = Math.ceil(totalRows / 8);
514
+ const nullBitmap = new Uint8Array(nullBitmapSize);
515
+ for (let i = 0; i < allValues.length; i++) {
516
+ const val = allValues[i];
517
+ if (val !== null) {
518
+ data.set(val, dataOffset);
519
+ dataOffset += val.length;
520
+ // Mark valid in null bitmap
521
+ nullBitmap[i >>> 3] |= 1 << (i & 7);
522
+ } else {
523
+ nullCount++;
524
+ }
525
+ }
526
+
527
+ // Build Arrow Binary column via flechette
528
+ const batch = new (batchType(BINARY_TYPE))({
529
+ type: BINARY_TYPE,
530
+ length: totalRows,
531
+ nullCount,
532
+ validity: nullCount > 0 ? nullBitmap : EMPTY_VALIDITY,
533
+ offsets,
534
+ values: data,
535
+ });
536
+ return { column: new Column([batch]), nullCount };
537
+ }
538
+
539
+ /**
540
+ * Convert SpanBuffer (and its overflow chain) to an Arrow table.
541
+ *
542
+ * **Dictionary handling**: Each table batch has its own dictionary data.
543
+ * Dictionaries are built from the data in this batch only and are not shared
544
+ * with other batches, even when combined into a Table.
545
+ */
546
+ export function convertToTable(buffer: AnySpanBuffer, systemColumnBuilder?: SystemColumnBuilder): Table {
547
+ const buffers: AnySpanBuffer[] = [];
548
+ let currentBuffer: AnySpanBuffer | undefined = buffer;
549
+
550
+ while (currentBuffer) {
551
+ buffers.push(currentBuffer);
552
+ currentBuffer = currentBuffer._overflow;
553
+ }
554
+
555
+ return convertBuffersToTable(buffers, systemColumnBuilder);
556
+ }
557
+
558
+ /**
559
+ * Convert multiple buffers to a single table.
560
+ *
561
+ * **Dictionary handling**: Each table batch has its own dictionary data.
562
+ * Dictionaries are built from the data in this batch only and are not shared
563
+ * with other batches, even when combined into a Table.
564
+ */
565
+ function convertBuffersToTable(buffers: AnySpanBuffer[], systemColumnBuilder?: SystemColumnBuilder): Table {
566
+ if (buffers.length === 0) return tableFromColumns({});
567
+
568
+ const totalRows = buffers.reduce((sum, buf) => sum + buf._writeIndex, 0);
569
+ if (totalRows === 0) return tableFromColumns({});
570
+
571
+ const schema: LogSchema = buffers[0]._logSchema;
572
+
573
+ // Build vectors first, then derive schema from them
574
+ // This ensures Field types and vector data types are identical (Arrow IPC requirement)
575
+ const fields: string[] = [];
576
+ const vectors: Column<unknown>[] = [];
577
+
578
+ if (systemColumnBuilder) {
579
+ const systemColumns = systemColumnBuilder(buffers[0], buffers, totalRows);
580
+ fields.push(...systemColumns.fields);
581
+ vectors.push(...systemColumns.vectors);
582
+ } else {
583
+ // Build metadata columns - returns both fields and vectors with matching types
584
+ const metadataResult = buildMetadataColumnsWithFields(buffers, totalRows);
585
+ fields.push(...metadataResult.fields);
586
+ vectors.push(...metadataResult.vectors);
587
+
588
+ // System attribute column: message (eager category)
589
+ const maskTransform = undefined; // message has no masking
590
+ let {
591
+ dictionary: dictValues,
592
+ indices,
593
+ arrowIndexType,
594
+ nullBitmap,
595
+ } = buildSortedCategoryDictionary(buffers, 'message', maskTransform);
596
+ // Ensure dictionary has at least one entry (empty string) if no messages were written
597
+ if (dictValues.length === 0) {
598
+ dictValues = [''];
599
+ }
600
+ // Ensure nullBitmap is all 1s (all valid) for eager column
601
+ if (!nullBitmap) {
602
+ const bitmapBytes = Math.ceil(totalRows / 8);
603
+ nullBitmap = new Uint8Array(bitmapBytes);
604
+ nullBitmap.fill(0xff);
605
+ }
606
+
607
+ // CRITICAL: Create ONE Dictionary type instance for field and batch data
608
+ // Arrow Schema validates that fields with same dictionary ID have identical type references
609
+ const messageDictType = dictionary(utf8(), arrowIndexType);
610
+ fields.push('message');
611
+
612
+ const { data: messageUtf8Data, offsets: messageUtf8Offsets } = globalUtf8Cache.encodeMany(dictValues);
613
+
614
+ const messageDictData = buildData({
615
+ type: utf8(),
616
+ offset: 0,
617
+ length: dictValues.length,
618
+ nullCount: 0,
619
+ valueOffsets: messageUtf8Offsets,
620
+ data: messageUtf8Data,
621
+ });
622
+
623
+ const messageData = buildData({
624
+ type: messageDictType,
625
+ offset: 0,
626
+ length: totalRows,
627
+ nullCount: 0, // message is eager, never null
628
+ data: indices,
629
+ nullBitmap, // Always include nullBitmap for message (even though nullCount is 0)
630
+ dictionary: buildColumn(messageDictData),
631
+ });
632
+
633
+ vectors.push(buildColumn(messageData));
634
+ }
635
+
636
+ // Build user attribute vectors
637
+ // Skip system schema fields - they are handled separately as system columns
638
+ // (message is handled above, line/error_code/exception_stack/ff_value/uint64_value below)
639
+ const userSchemaFields = schema._columns.filter(([fieldName]) => !SYSTEM_SCHEMA_FIELD_NAMES.has(fieldName));
640
+ for (const [fieldName, fieldSchema] of userSchemaFields) {
641
+ const lmaoType = getSchemaType(fieldSchema);
642
+ const arrowFieldName = getArrowFieldName(fieldName);
643
+ const columnName = fieldName; // User columns have no prefix
644
+
645
+ if (lmaoType === 'enum') {
646
+ const enumValues = getEnumValues(fieldSchema) || [];
647
+ const enumUtf8 = getEnumUtf8(fieldSchema);
648
+ // Get constructors from schema metadata
649
+ const indexArrayCtor = getArrowIndexArrayConstructorOr(fieldSchema);
650
+ const arrowIndexType = getArrowIndexTypeOr(fieldSchema, uint8());
651
+ // Collect value arrays - need to handle different TypedArray types
652
+ const valueArrays: (Uint8Array | Uint16Array | Uint32Array)[] = [];
653
+ for (const buf of buffers) {
654
+ const column = buf.getColumnIfAllocated(columnName);
655
+ if (column && column instanceof indexArrayCtor) {
656
+ valueArrays.push(column.subarray(0, buf._writeIndex));
657
+ } else {
658
+ valueArrays.push(new indexArrayCtor(buf._writeIndex));
659
+ }
660
+ }
661
+
662
+ // Concatenate arrays based on type
663
+ let allIndices: Uint8Array | Uint16Array | Uint32Array;
664
+ if (indexArrayCtor === Uint8Array) {
665
+ const uint8ValueArrays: Uint8Array[] = [];
666
+ for (const array of valueArrays) {
667
+ if (!(array instanceof Uint8Array)) {
668
+ throw new Error(`Enum column '${columnName}' expected Uint8Array chunks`);
669
+ }
670
+ uint8ValueArrays.push(array);
671
+ }
672
+ allIndices = concatenateUint8Arrays(uint8ValueArrays);
673
+ } else {
674
+ const totalLength = valueArrays.reduce((sum, arr) => sum + arr.length, 0);
675
+ allIndices = new indexArrayCtor(totalLength);
676
+ let offset = 0;
677
+ for (const arr of valueArrays) {
678
+ allIndices.set(arr, offset);
679
+ offset += arr.length;
680
+ }
681
+ }
682
+
683
+ const { nullBitmap, nullCount } = concatenateNullBitmaps(buffers, columnName);
684
+
685
+ // CRITICAL: Create ONE Dictionary type instance for field and batch data
686
+ const enumDictType = dictionary(utf8(), arrowIndexType);
687
+ fields.push(arrowFieldName);
688
+
689
+ const enumDictData = buildData({
690
+ type: utf8(),
691
+ offset: 0,
692
+ length: enumValues.length,
693
+ nullCount: 0,
694
+ valueOffsets: enumUtf8?.offsets ?? calculateUtf8Offsets(enumValues),
695
+ data: enumUtf8?.concatenated ?? encodeUtf8Strings(enumValues),
696
+ });
697
+
698
+ const enumData = buildData({
699
+ type: enumDictType,
700
+ offset: 0,
701
+ length: totalRows,
702
+ nullCount,
703
+ data: allIndices,
704
+ nullBitmap,
705
+ dictionary: buildColumn(enumDictData),
706
+ });
707
+
708
+ vectors.push(buildColumn(enumData));
709
+ } else if (lmaoType === 'category') {
710
+ // Get mask transform from schema metadata (if present)
711
+ const maskTransform = getMaskTransform(fieldSchema);
712
+ const {
713
+ dictionary: dictValues,
714
+ indices,
715
+ arrowIndexType,
716
+ nullBitmap,
717
+ nullCount,
718
+ } = buildSortedCategoryDictionary(buffers, columnName, maskTransform);
719
+
720
+ // CRITICAL: Create ONE Dictionary type instance for field and batch data
721
+ const categoryDictType = dictionary(utf8(), arrowIndexType);
722
+ fields.push(arrowFieldName);
723
+
724
+ const { data: categoryUtf8Data, offsets: categoryUtf8Offsets } = globalUtf8Cache.encodeMany(dictValues);
725
+
726
+ const categoryDictData = buildData({
727
+ type: utf8(),
728
+ offset: 0,
729
+ length: dictValues.length,
730
+ nullCount: 0,
731
+ valueOffsets: categoryUtf8Offsets,
732
+ data: categoryUtf8Data,
733
+ });
734
+
735
+ const categoryData = buildData({
736
+ type: categoryDictType,
737
+ offset: 0,
738
+ length: totalRows,
739
+ nullCount,
740
+ data: indices,
741
+ nullBitmap,
742
+ dictionary: buildColumn(categoryDictData),
743
+ });
744
+
745
+ vectors.push(buildColumn(categoryData));
746
+ } else if (lmaoType === 'text') {
747
+ // Get mask transform from schema metadata (if present)
748
+ const maskTransform = getMaskTransform(fieldSchema);
749
+ const result = buildTextDictionary(buffers, columnName, maskTransform);
750
+ if (result) {
751
+ const { dictionary: dictValues, indices, arrowIndexType, nullBitmap, nullCount } = result;
752
+
753
+ // CRITICAL: Create ONE Dictionary type instance for field and batch data
754
+ const textDictType = dictionary(utf8(), arrowIndexType);
755
+ fields.push(arrowFieldName);
756
+
757
+ const { data: textUtf8Data, offsets: textUtf8Offsets } = globalUtf8Cache.encodeMany(dictValues);
758
+
759
+ const textDictData = buildData({
760
+ type: utf8(),
761
+ offset: 0,
762
+ length: dictValues.length,
763
+ nullCount: 0,
764
+ valueOffsets: textUtf8Offsets,
765
+ data: textUtf8Data,
766
+ });
767
+
768
+ const textData = buildData({
769
+ type: textDictType,
770
+ offset: 0,
771
+ length: totalRows,
772
+ nullCount,
773
+ data: indices,
774
+ nullBitmap,
775
+ dictionary: buildColumn(textDictData),
776
+ });
777
+
778
+ vectors.push(buildColumn(textData));
779
+ }
780
+ } else if (lmaoType === 'number') {
781
+ const valueArrays: Float64Array[] = [];
782
+
783
+ for (const buf of buffers) {
784
+ const column = buf.getColumnIfAllocated(columnName);
785
+ if (column && column instanceof Float64Array) {
786
+ valueArrays.push(column.subarray(0, buf._writeIndex));
787
+ } else {
788
+ valueArrays.push(new F64Array(buf._writeIndex));
789
+ }
790
+ }
791
+
792
+ const allValues = concatenateFloat64Arrays(valueArrays);
793
+ const { nullBitmap, nullCount } = concatenateNullBitmaps(buffers, columnName);
794
+
795
+ const numberType = float64();
796
+ fields.push(arrowFieldName);
797
+
798
+ const numberData = buildData({
799
+ type: numberType,
800
+ offset: 0,
801
+ length: totalRows,
802
+ nullCount,
803
+ data: allValues,
804
+ nullBitmap,
805
+ });
806
+
807
+ vectors.push(buildColumn(numberData));
808
+ } else if (lmaoType === 'boolean') {
809
+ const valueArrays: Uint8Array[] = [];
810
+
811
+ for (const buf of buffers) {
812
+ const column = buf.getColumnIfAllocated(columnName);
813
+ if (column && column instanceof Uint8Array) {
814
+ const requiredBytes = Math.ceil(buf._writeIndex / 8);
815
+ valueArrays.push(column.subarray(0, requiredBytes));
816
+ } else {
817
+ const requiredBytes = Math.ceil(buf._writeIndex / 8);
818
+ valueArrays.push(new Uint8Array(requiredBytes));
819
+ }
820
+ }
821
+
822
+ const allValues = concatenateUint8Arrays(valueArrays);
823
+ const { nullBitmap, nullCount } = concatenateNullBitmaps(buffers, columnName);
824
+
825
+ const boolType = bool();
826
+ fields.push(arrowFieldName);
827
+
828
+ const boolData = buildData({
829
+ type: boolType,
830
+ offset: 0,
831
+ length: totalRows,
832
+ nullCount,
833
+ data: allValues,
834
+ nullBitmap,
835
+ });
836
+
837
+ vectors.push(buildColumn(boolData));
838
+ } else if (lmaoType === 'binary') {
839
+ // Binary columns: raw Uint8Array or encoder-wrapped values (e.g. msgpack)
840
+ const { column } = buildBinaryColumnFromBuffers(buffers, columnName, totalRows, fieldSchema);
841
+ fields.push(arrowFieldName);
842
+ vectors.push(column);
843
+ }
844
+ }
845
+
846
+ return buildTable(fields, vectors);
847
+ }
848
+
849
+ /**
850
+ * Build metadata columns with matching Field definitions.
851
+ * Returns both fields and vectors with identical Dictionary type instances.
852
+ * This is required for Arrow IPC serialization - Schema validates that
853
+ * fields with same dictionary ID have identical type object references.
854
+ */
855
+ function buildMetadataColumnsWithFields(buffers: AnySpanBuffer[], totalRows: number): BuiltMetadataColumns {
856
+ const fields: string[] = [];
857
+ const vectors: Column<unknown>[] = [];
858
+
859
+ // Column order: timestamp, trace_id, thread_id, span_id, parent_thread_id, parent_span_id,
860
+ // entry_type, package_name, package_file, git_sha
861
+
862
+ // 1. Timestamp
863
+ buildTimestampColumn(buffers, totalRows, fields, vectors);
864
+
865
+ // 2. Trace ID dictionary (built inline from buffers)
866
+ const traceIdSet = new Set<string>();
867
+ for (const buf of buffers) traceIdSet.add(buf.trace_id);
868
+ const traceIdArray = Array.from(traceIdSet);
869
+ const traceIdMap = new Map(traceIdArray.map((id, idx) => [id, idx]));
870
+ const traceIdArrowIndexType = indexTypeForCount(traceIdArray.length);
871
+ const TraceIdIndexArrayCtor =
872
+ traceIdArray.length <= 255 ? Uint8Array : traceIdArray.length <= 65535 ? Uint16Array : Uint32Array;
873
+
874
+ const traceIdIndices = new TraceIdIndexArrayCtor(totalRows);
875
+ let rowOffset = 0;
876
+ for (const buf of buffers) {
877
+ const traceIdIndex = traceIdMap.get(buf.trace_id);
878
+ if (traceIdIndex === undefined) {
879
+ throw new Error(`TraceId index not found for: ${buf.trace_id}`);
880
+ }
881
+ traceIdIndices.fill(traceIdIndex, rowOffset, rowOffset + buf._writeIndex);
882
+ rowOffset += buf._writeIndex;
883
+ }
884
+ const traceIdDictType = dictionary(utf8(), traceIdArrowIndexType);
885
+ fields.push('trace_id');
886
+ vectors.push(
887
+ buildColumn(
888
+ buildData({
889
+ type: traceIdDictType,
890
+ offset: 0,
891
+ length: totalRows,
892
+ nullCount: 0,
893
+ data: traceIdIndices,
894
+ dictionary: buildColumn(
895
+ buildData({
896
+ type: utf8(),
897
+ offset: 0,
898
+ length: traceIdArray.length,
899
+ nullCount: 0,
900
+ valueOffsets: calculateUtf8Offsets(traceIdArray),
901
+ data: encodeUtf8Strings(traceIdArray),
902
+ }),
903
+ ),
904
+ }),
905
+ ),
906
+ );
907
+
908
+ // 3-6. thread_id, span_id, parent_thread_id, parent_span_id
909
+ buildNonDictIdentityColumns(buffers, totalRows, fields, vectors);
910
+
911
+ // 7. Entry type
912
+ buildEntryTypeColumn(buffers, totalRows, fields, vectors);
913
+
914
+ // 8-10. Per-buffer metadata columns (package_name, package_file, git_sha)
915
+ for (const [fieldName, metadataGetter] of [
916
+ ['package_name', (m: OpMetadata) => m.package_name],
917
+ ['package_file', (m: OpMetadata) => m.package_file],
918
+ ['git_sha', (m: OpMetadata) => m.git_sha],
919
+ ] as [string, (m: OpMetadata) => string][]) {
920
+ const valueSet = new Set<string>();
921
+ for (const buf of buffers) {
922
+ valueSet.add(metadataGetter(buf._opMetadata));
923
+ if (buf._callsiteMetadata) valueSet.add(metadataGetter(buf._callsiteMetadata));
924
+ }
925
+ const dictValues = Array.from(valueSet);
926
+ const dictMap = new Map(dictValues.map((v, i) => [v, i]));
927
+ const arrowIndexType = indexTypeForCount(dictValues.length);
928
+ const dictType = dictionary(utf8(), arrowIndexType);
929
+
930
+ buildPerBufferDictColumn(
931
+ buffers,
932
+ totalRows,
933
+ fieldName,
934
+ metadataGetter,
935
+ dictType,
936
+ dictValues,
937
+ dictMap,
938
+ calculateUtf8Offsets(dictValues),
939
+ encodeUtf8Strings(dictValues),
940
+ fields,
941
+ vectors,
942
+ );
943
+ }
944
+
945
+ return { fields, vectors };
946
+ }
947
+
948
+ /**
949
+ * Convert SpanBuffer (and its overflow chain) to Arrow Table
950
+ */
951
+ export function convertToArrowTable<_T extends LogSchema = LogSchema>(
952
+ buffer: AnySpanBuffer,
953
+ systemColumnBuilder?: SystemColumnBuilder,
954
+ ): Table {
955
+ const batch = convertToTable(buffer, systemColumnBuilder);
956
+ if (batch.numRows === 0) return tableFromColumns({});
957
+ return batch;
958
+ }
959
+
960
+ // ═══════════════════════════════════════════════════════════════════════════════
961
+ // Tree Conversion with Shared Dictionaries
962
+ // ═══════════════════════════════════════════════════════════════════════════════
963
+
964
+ /**
965
+ * Convert SpanBuffer tree to Arrow Table
966
+ *
967
+ * Two-pass conversion with shared dictionaries:
968
+ * - Pass 1: Walk tree, build dictionaries (collect unique strings, cache UTF-8)
969
+ * - Pass 2: Walk tree, convert each buffer to a table using shared dictionaries
970
+ */
971
+ export function convertSpanTreeToArrowTable(
972
+ rootBuffer: AnySpanBuffer,
973
+ _systemColumnBuilder?: SystemColumnBuilder,
974
+ modulesToLogStats?: CapacityStatsEntry[],
975
+ periodStartNs?: bigint,
976
+ ): Table {
977
+ // ═══════════════════════════════════════════════════════════════════════════
978
+ // PASS 0: Collect ALL unique schema fields from ALL buffers in the tree
979
+ // Per specs/lmao/01k_tree_walker_and_arrow_conversion.md - child spans may have different schemas
980
+ // (e.g., library prefixed schemas like db_query, http_status, etc.)
981
+ // ═══════════════════════════════════════════════════════════════════════════
982
+ const mergedSchemaFields = new Map<string, unknown>();
983
+
984
+ walkSpanTree(rootBuffer, (buffer) => {
985
+ // Use buffer._columns directly - works for both SpanBuffer and RemappedBufferView
986
+ const fields = buffer._columns;
987
+ for (const [fieldName, fieldSchema] of fields) {
988
+ // Skip system schema fields - they are handled separately as system columns
989
+ // (message, line, error_code, exception_stack, ff_value, uint64_value)
990
+ if (SYSTEM_SCHEMA_FIELD_NAMES.has(fieldName)) {
991
+ continue;
992
+ }
993
+ // Only add if not already present (first buffer with this field wins)
994
+ if (!mergedSchemaFields.has(fieldName)) {
995
+ mergedSchemaFields.set(fieldName, fieldSchema);
996
+ }
997
+ }
998
+ });
999
+
1000
+ // Convert merged schema fields to array format for iteration
1001
+ const schemaFields: Array<[string, unknown]> = Array.from(mergedSchemaFields.entries());
1002
+
1003
+ // Create merged schema object from collected fields (for capacity stats and other uses)
1004
+ const mergedSchema: Record<string, unknown> = Object.fromEntries(mergedSchemaFields);
1005
+
1006
+ // ═══════════════════════════════════════════════════════════════════════════
1007
+ // PASS 1: Build dictionaries using DictionaryBuilder from arrow-builder
1008
+ // ═══════════════════════════════════════════════════════════════════════════
1009
+ const categoryBuilders = new Map<string, DictionaryBuilder>();
1010
+ const textBuilders = new Map<string, DictionaryBuilder>();
1011
+ // Store mask transforms and original->masked mappings per column
1012
+ const categoryMaskTransforms = new Map<string, ((value: string) => string) | undefined>();
1013
+ const textMaskTransforms = new Map<string, ((value: string) => string) | undefined>();
1014
+ const categoryOriginalToMasked = new Map<string, Map<string, string>>();
1015
+ const textOriginalToMasked = new Map<string, Map<string, string>>();
1016
+
1017
+ // Use cached schemaFields to filter out methods (extend, validate, parse, safeParse)
1018
+ for (const [fieldName, fieldSchema] of schemaFields) {
1019
+ const lmaoType = getSchemaType(fieldSchema);
1020
+ if (lmaoType === 'category') {
1021
+ categoryBuilders.set(fieldName, new DictBuilder(globalUtf8Cache));
1022
+ categoryMaskTransforms.set(fieldName, getMaskTransform(fieldSchema));
1023
+ categoryOriginalToMasked.set(fieldName, new Map());
1024
+ }
1025
+ if (lmaoType === 'text') {
1026
+ textBuilders.set(fieldName, new DictBuilder(globalUtf8Cache));
1027
+ textMaskTransforms.set(fieldName, getMaskTransform(fieldSchema));
1028
+ textOriginalToMasked.set(fieldName, new Map());
1029
+ }
1030
+ }
1031
+
1032
+ // System attribute column: message (eager category, not in user schema)
1033
+ // message is always present (eager allocation), so we need to handle it explicitly
1034
+ categoryBuilders.set('message', new DictBuilder(globalUtf8Cache));
1035
+ categoryMaskTransforms.set('message', undefined); // message has no masking
1036
+ categoryOriginalToMasked.set('message', new Map());
1037
+
1038
+ // Metadata column dictionaries
1039
+ // For traceId (metadata), use DictionaryBuilder (values not pre-encoded)
1040
+ const traceIdBuilder = new DictBuilder(globalUtf8Cache);
1041
+
1042
+ // For package, modulePath, gitSha - collect pre-encoded entries for direct dictionary creation
1043
+ // Use a Set to deduplicate by OpMetadata identity (same module = same entries)
1044
+ const uniqueModules = new Set<OpMetadata>();
1045
+
1046
+ let spanRows = 0;
1047
+ walkSpanTree(rootBuffer, (buffer) => {
1048
+ spanRows += buffer._writeIndex;
1049
+ traceIdBuilder.add(buffer.trace_id);
1050
+
1051
+ // Collect unique OpMetadata (for package_name, package_file, git_sha)
1052
+ // Include both buffer._opMetadata (for rows 1+) and callsiteMetadata (for row 0) if present
1053
+ uniqueModules.add(buffer._opMetadata);
1054
+ if (buffer._callsiteMetadata) {
1055
+ uniqueModules.add(buffer._callsiteMetadata);
1056
+ }
1057
+
1058
+ for (const [fieldName, builder] of categoryBuilders) {
1059
+ const col = getAllocatedStringColumn(buffer, fieldName);
1060
+ const maskTransform = categoryMaskTransforms.get(fieldName);
1061
+ const originalToMasked = categoryOriginalToMasked.get(fieldName);
1062
+ if (!originalToMasked) {
1063
+ throw new Error(`Category originalToMasked map not found for field: ${fieldName}`);
1064
+ }
1065
+ if (col) {
1066
+ for (let i = 0; i < buffer._writeIndex; i++) {
1067
+ const originalValue = col[i];
1068
+ if (originalValue != null) {
1069
+ let maskedValue = originalToMasked.get(originalValue);
1070
+ if (maskedValue === undefined) {
1071
+ maskedValue = maskTransform ? maskTransform(originalValue) : originalValue;
1072
+ originalToMasked.set(originalValue, maskedValue);
1073
+ }
1074
+ builder.add(maskedValue);
1075
+ }
1076
+ }
1077
+ }
1078
+ // Also add scope values to dictionary (they may not be in columns)
1079
+ // Per specs/lmao/01i_span_scope_attributes.md: scope values fill NULL cells at Arrow conversion
1080
+ const scopeValue = getStringScopeValue(buffer, fieldName);
1081
+ if (scopeValue !== undefined) {
1082
+ let maskedScopeValue = originalToMasked.get(scopeValue);
1083
+ if (maskedScopeValue === undefined) {
1084
+ maskedScopeValue = maskTransform ? maskTransform(scopeValue) : scopeValue;
1085
+ originalToMasked.set(scopeValue, maskedScopeValue);
1086
+ }
1087
+ builder.add(maskedScopeValue);
1088
+ }
1089
+ }
1090
+
1091
+ for (const [fieldName, builder] of textBuilders) {
1092
+ const col = getAllocatedStringColumn(buffer, fieldName);
1093
+ const maskTransform = textMaskTransforms.get(fieldName);
1094
+ const originalToMasked = textOriginalToMasked.get(fieldName);
1095
+ if (!originalToMasked) {
1096
+ throw new Error(`Text originalToMasked map not found for field: ${fieldName}`);
1097
+ }
1098
+ if (col) {
1099
+ for (let i = 0; i < buffer._writeIndex; i++) {
1100
+ const originalValue = col[i];
1101
+ if (originalValue != null) {
1102
+ let maskedValue = originalToMasked.get(originalValue);
1103
+ if (maskedValue === undefined) {
1104
+ maskedValue = maskTransform ? maskTransform(originalValue) : originalValue;
1105
+ originalToMasked.set(originalValue, maskedValue);
1106
+ }
1107
+ builder.add(maskedValue);
1108
+ }
1109
+ }
1110
+ }
1111
+ // Also add scope values to dictionary (they may not be in columns)
1112
+ // Per specs/lmao/01i_span_scope_attributes.md: scope values fill NULL cells at Arrow conversion
1113
+ const scopeValue = getStringScopeValue(buffer, fieldName);
1114
+ if (scopeValue !== undefined) {
1115
+ let maskedScopeValue = originalToMasked.get(scopeValue);
1116
+ if (maskedScopeValue === undefined) {
1117
+ maskedScopeValue = maskTransform ? maskTransform(scopeValue) : scopeValue;
1118
+ originalToMasked.set(scopeValue, maskedScopeValue);
1119
+ }
1120
+ builder.add(maskedScopeValue);
1121
+ }
1122
+ // Scope values are now pre-filled in columns at allocation time
1123
+ // (via lazy getters and _prefillScopedAttributes on overflow),
1124
+ // so they'll be added to dictionary via the column iteration above
1125
+ }
1126
+ });
1127
+
1128
+ if (spanRows === 0 && (!modulesToLogStats || modulesToLogStats.length === 0)) {
1129
+ return tableFromColumns({});
1130
+ }
1131
+
1132
+ // Finalize dictionaries
1133
+ const traceIdDict = traceIdBuilder.finalize(false);
1134
+
1135
+ // Build dictionaries from OpMetadata entries
1136
+ // Use pre-encoded entries from OpMetadata (interned at Op definition time)
1137
+ const moduleCount = uniqueModules.size;
1138
+ const packageEntries: PreEncodedEntry[] = new Array(moduleCount);
1139
+ const packagePathEntries: PreEncodedEntry[] = new Array(moduleCount);
1140
+ const gitShaEntries: PreEncodedEntry[] = new Array(moduleCount);
1141
+ let idx = 0;
1142
+ for (const m of uniqueModules) {
1143
+ packageEntries[idx] = m.package_name_entry;
1144
+ packagePathEntries[idx] = m.package_file_entry;
1145
+ gitShaEntries[idx] = m.git_sha_entry;
1146
+ idx++;
1147
+ }
1148
+
1149
+ const cmp = (a: PreEncodedEntry, b: PreEncodedEntry) => compareStrings(a.str, b.str);
1150
+ const packageDict = createSortedDictionary(sortInPlace(packageEntries, cmp));
1151
+ const packagePathDict = createSortedDictionary(sortInPlace(packagePathEntries, cmp));
1152
+ const gitShaDict = createSortedDictionary(sortInPlace(gitShaEntries, cmp));
1153
+ const categoryDicts = new Map<string, FinalizedDictionary>();
1154
+ const textDicts = new Map<string, FinalizedDictionary>();
1155
+
1156
+ for (const [name, builder] of categoryBuilders) {
1157
+ categoryDicts.set(name, builder.finalize(true)); // sorted for binary search
1158
+ }
1159
+ for (const [name, builder] of textBuilders) {
1160
+ textDicts.set(name, builder.finalize(false)); // not sorted
1161
+ }
1162
+
1163
+ // Get messageDict from categoryDicts (message is handled as a category column)
1164
+ // If no messages were written, create an empty dictionary
1165
+ let messageDict = categoryDicts.get('message');
1166
+ if (!messageDict) {
1167
+ const messageBuilder = new DictBuilder(globalUtf8Cache);
1168
+ messageBuilder.add(''); // Single empty string entry
1169
+ messageDict = messageBuilder.finalize(true); // sorted
1170
+ }
1171
+
1172
+ // ═══════════════════════════════════════════════════════════════════════════
1173
+ // PASS 2: Collect all buffers with non-zero rows
1174
+ // ═══════════════════════════════════════════════════════════════════════════
1175
+ const allBuffers: AnySpanBuffer[] = [];
1176
+
1177
+ walkSpanTree(rootBuffer, (buffer) => {
1178
+ if (buffer._writeIndex > 0) {
1179
+ allBuffers.push(buffer);
1180
+ }
1181
+ });
1182
+
1183
+ // Build a single table from all buffers
1184
+ const batch =
1185
+ allBuffers.length > 0
1186
+ ? convertBuffersWithSharedDicts(
1187
+ allBuffers,
1188
+ traceIdDict,
1189
+ packageDict,
1190
+ packagePathDict,
1191
+ gitShaDict,
1192
+ categoryDicts,
1193
+ textDicts,
1194
+ schemaFields,
1195
+ categoryOriginalToMasked,
1196
+ textOriginalToMasked,
1197
+ )
1198
+ : undefined;
1199
+
1200
+ // Create capacity stats table if needed
1201
+ if (modulesToLogStats && modulesToLogStats.length > 0) {
1202
+ // periodStartNs must be provided by the caller (from FlushScheduler's tracked period start)
1203
+ // If not provided, use 0n as a fallback (indicates period tracking not enabled)
1204
+ const effectivePeriodStartNs = periodStartNs ?? 0n;
1205
+ const capacityStatsBatch = createCapacityStatsTable(modulesToLogStats, effectivePeriodStartNs, mergedSchema);
1206
+ if (batch) {
1207
+ return appendTables(batch, capacityStatsBatch);
1208
+ }
1209
+ return capacityStatsBatch;
1210
+ }
1211
+
1212
+ if (batch) {
1213
+ return batch;
1214
+ }
1215
+ return tableFromColumns({});
1216
+ }
1217
+
1218
+ /**
1219
+ * Convert multiple buffers to a single table using pre-built dictionaries.
1220
+ *
1221
+ * **Dictionary handling**: Each table batch has its own dictionary data.
1222
+ * While dictionaries are built in a first pass from all buffers, they are used to create
1223
+ * a single table. The resulting batches contain their own dictionary data and
1224
+ * are not shared with other batches, even when combined into a Table.
1225
+ */
1226
+ function convertBuffersWithSharedDicts(
1227
+ buffers: AnySpanBuffer[],
1228
+ traceIdDict: FinalizedDictionary,
1229
+ packageDict: FinalizedDictionary,
1230
+ packagePathDict: FinalizedDictionary,
1231
+ gitShaDict: FinalizedDictionary,
1232
+ categoryDicts: Map<string, FinalizedDictionary>,
1233
+ textDicts: Map<string, FinalizedDictionary>,
1234
+ schemaFields: Array<[string, unknown]>,
1235
+ categoryOriginalToMasked: Map<string, Map<string, string>>,
1236
+ textOriginalToMasked: Map<string, Map<string, string>>,
1237
+ ): Table {
1238
+ const totalRows = buffers.reduce((sum, buf) => sum + buf._writeIndex, 0);
1239
+ const fields: string[] = [];
1240
+ const vectors: Column<unknown>[] = [];
1241
+
1242
+ // Dictionary types with explicit IDs for IPC serialization
1243
+ const traceIdType = dictionary(utf8(), traceIdDict.arrowIndexType, false, 0);
1244
+ const entryTypeDictType = dictionary(utf8(), int8(), false, 1);
1245
+ const packageType = dictionary(utf8(), packageDict.arrowIndexType, false, 2);
1246
+ const packagePathType = dictionary(utf8(), packagePathDict.arrowIndexType, false, 3);
1247
+ const gitShaType = dictionary(utf8(), gitShaDict.arrowIndexType, false, 4);
1248
+
1249
+ // Column order: timestamp, trace_id, thread_id, span_id, parent_thread_id, parent_span_id,
1250
+ // entry_type, package_name, package_file, git_sha
1251
+
1252
+ // 1. Timestamp
1253
+ buildTimestampColumn(buffers, totalRows, fields, vectors);
1254
+
1255
+ // 2. Trace ID (using shared pre-built dictionary)
1256
+ const traceIdIndices = new traceIdDict.indexArrayCtor(totalRows);
1257
+ let offset = 0;
1258
+ for (const buf of buffers) {
1259
+ const idx = traceIdDict.indexMap.get(buf.trace_id) ?? 0;
1260
+ traceIdIndices.fill(idx, offset, offset + buf._writeIndex);
1261
+ offset += buf._writeIndex;
1262
+ }
1263
+ fields.push('trace_id');
1264
+ vectors.push(
1265
+ buildColumn(
1266
+ buildData({
1267
+ type: traceIdType,
1268
+ offset: 0,
1269
+ length: totalRows,
1270
+ nullCount: 0,
1271
+ data: traceIdIndices,
1272
+ dictionary: buildColumn(
1273
+ buildData({
1274
+ type: utf8(),
1275
+ offset: 0,
1276
+ length: traceIdDict.indexMap.size,
1277
+ nullCount: 0,
1278
+ valueOffsets: traceIdDict.offsets,
1279
+ data: traceIdDict.data,
1280
+ }),
1281
+ ),
1282
+ }),
1283
+ ),
1284
+ );
1285
+
1286
+ // 3-6. thread_id, span_id, parent_thread_id, parent_span_id
1287
+ buildNonDictIdentityColumns(buffers, totalRows, fields, vectors);
1288
+
1289
+ // 7. Entry type (with explicit dictionary ID for IPC)
1290
+ buildEntryTypeColumn(buffers, totalRows, fields, vectors, entryTypeDictType);
1291
+
1292
+ // 8-10. Per-buffer metadata columns using shared pre-built dictionaries
1293
+ // Per specs/lmao/01c_context_flow_and_op_wrappers.md:
1294
+ // - Row 0 (span-start): uses callsiteModule for gitSha/packageName/packagePath
1295
+ // - Rows 1+ (logs, span-end): uses task._module
1296
+ for (const [fieldName, metadataGetter, dictType, dict] of [
1297
+ ['package_name', (m: OpMetadata) => m.package_name, packageType, packageDict],
1298
+ ['package_file', (m: OpMetadata) => m.package_file, packagePathType, packagePathDict],
1299
+ ['git_sha', (m: OpMetadata) => m.git_sha, gitShaType, gitShaDict],
1300
+ ] as [string, (m: OpMetadata) => string, ReturnType<typeof dictionary>, FinalizedDictionary][]) {
1301
+ const dictValues = Array.from(dict.indexMap.keys());
1302
+ buildPerBufferDictColumn(
1303
+ buffers,
1304
+ totalRows,
1305
+ fieldName,
1306
+ metadataGetter,
1307
+ dictType,
1308
+ dictValues,
1309
+ dict.indexMap,
1310
+ dict.offsets,
1311
+ dict.data,
1312
+ fields,
1313
+ vectors,
1314
+ );
1315
+ }
1316
+
1317
+ // ═══════════════════════════════════════════════════════════════════════════
1318
+ // System attribute and user attribute columns - concatenate from all buffers
1319
+ // ═══════════════════════════════════════════════════════════════════════════
1320
+
1321
+ // System attribute column: message (at fixed position 10, before user attributes)
1322
+ const messageDict = categoryDicts.get('message');
1323
+ const messageOriginalToMasked = categoryOriginalToMasked.get('message');
1324
+ if (!messageDict || !messageOriginalToMasked) {
1325
+ throw new Error('Message dictionary or mapping not found');
1326
+ }
1327
+ const messageFieldType = dictionary(utf8(), messageDict.arrowIndexType, false, 5);
1328
+ const messageIndices = new messageDict.indexArrayCtor(totalRows);
1329
+ const messageNullBitmap = new Uint8Array(Math.ceil(totalRows / 8));
1330
+ let messageRowOffset = 0;
1331
+ for (const buf of buffers) {
1332
+ const col = getAllocatedStringColumn(buf, 'message');
1333
+ if (col) {
1334
+ for (let i = 0; i < buf._writeIndex; i++) {
1335
+ const v = col[i];
1336
+ const rowIdx = messageRowOffset + i;
1337
+ if (v != null) {
1338
+ const maskedValue = messageOriginalToMasked.get(v) ?? v;
1339
+ messageIndices[rowIdx] = messageDict.indexMap.get(maskedValue) ?? 0;
1340
+ messageNullBitmap[rowIdx >>> 3] |= 1 << (rowIdx & 7);
1341
+ }
1342
+ }
1343
+ }
1344
+ messageRowOffset += buf._writeIndex;
1345
+ }
1346
+ vectors.push(
1347
+ buildColumn(
1348
+ buildData({
1349
+ type: messageFieldType,
1350
+ offset: 0,
1351
+ length: totalRows,
1352
+ nullCount: 0, // message is eager, never null
1353
+ data: messageIndices,
1354
+ nullBitmap: messageNullBitmap,
1355
+ dictionary: buildColumn(
1356
+ buildData({
1357
+ type: utf8(),
1358
+ offset: 0,
1359
+ length: messageDict.indexMap.size,
1360
+ nullCount: 0,
1361
+ valueOffsets: messageDict.offsets,
1362
+ data: messageDict.data,
1363
+ }),
1364
+ ),
1365
+ }),
1366
+ ),
1367
+ );
1368
+ fields.push('message');
1369
+
1370
+ // System attribute column: uint64_value (for buffer metrics - op durations, counts, etc.)
1371
+ // For span data, this is all null - only buffer metrics use it
1372
+ const uint64ValueNullBitmap = new Uint8Array(Math.ceil(totalRows / 8));
1373
+ // All zeros = all null (Arrow null bitmap: 1 = valid, 0 = null)
1374
+ vectors.push(
1375
+ buildColumn(
1376
+ buildData({
1377
+ type: uint64(),
1378
+ offset: 0,
1379
+ length: totalRows,
1380
+ nullCount: totalRows,
1381
+ data: new BigUint64Array(totalRows),
1382
+ nullBitmap: uint64ValueNullBitmap,
1383
+ }),
1384
+ ),
1385
+ );
1386
+ fields.push('uint64_value');
1387
+
1388
+ for (const [fieldName, fieldSchema] of schemaFields) {
1389
+ const lmaoType = getSchemaType(fieldSchema);
1390
+ const columnName = fieldName; // User columns have no prefix
1391
+ const arrowFieldName = getArrowFieldName(fieldName);
1392
+
1393
+ if (lmaoType === 'category') {
1394
+ const dict = categoryDicts.get(fieldName);
1395
+ const originalToMasked = categoryOriginalToMasked.get(fieldName);
1396
+ if (!dict || !originalToMasked) {
1397
+ throw new Error(`Category dictionary or mapping not found for field: ${fieldName}`);
1398
+ }
1399
+ const indices = new dict.indexArrayCtor(totalRows);
1400
+ const nullBitmap = new Uint8Array(Math.ceil(totalRows / 8));
1401
+ let nullCount = 0;
1402
+ let rowOffset = 0;
1403
+
1404
+ // Per specs/lmao/01i_span_scope_attributes.md: scope values fill NULL cells at Arrow conversion
1405
+ for (const buf of buffers) {
1406
+ const col = getAllocatedStringColumn(buf, columnName);
1407
+ // Check if this buffer has a scope value for this field
1408
+ const scopeValue = getStringScopeValue(buf, fieldName);
1409
+ let scopeEncodedValue: number | undefined;
1410
+ if (scopeValue !== undefined) {
1411
+ const maskedScopeValue = originalToMasked.get(scopeValue) ?? scopeValue;
1412
+ scopeEncodedValue = dict.indexMap.get(maskedScopeValue);
1413
+ }
1414
+
1415
+ if (col) {
1416
+ for (let i = 0; i < buf._writeIndex; i++) {
1417
+ const v = col[i];
1418
+ const rowIdx = rowOffset + i;
1419
+ if (v != null) {
1420
+ // Direct write wins
1421
+ const maskedValue = originalToMasked.get(v) ?? v;
1422
+ indices[rowIdx] = dict.indexMap.get(maskedValue) ?? 0;
1423
+ nullBitmap[rowIdx >>> 3] |= 1 << (rowIdx & 7);
1424
+ } else if (scopeEncodedValue !== undefined) {
1425
+ // No direct write, but have scope value - use it
1426
+ indices[rowIdx] = scopeEncodedValue;
1427
+ nullBitmap[rowIdx >>> 3] |= 1 << (rowIdx & 7);
1428
+ } else {
1429
+ nullCount++;
1430
+ }
1431
+ }
1432
+ } else if (scopeEncodedValue !== undefined) {
1433
+ // Column not allocated but we have scope - fill all rows with scope value
1434
+ for (let i = 0; i < buf._writeIndex; i++) {
1435
+ const rowIdx = rowOffset + i;
1436
+ indices[rowIdx] = scopeEncodedValue;
1437
+ nullBitmap[rowIdx >>> 3] |= 1 << (rowIdx & 7);
1438
+ }
1439
+ } else {
1440
+ // No column and no scope - all nulls
1441
+ nullCount += buf._writeIndex;
1442
+ }
1443
+ rowOffset += buf._writeIndex;
1444
+ }
1445
+
1446
+ vectors.push(
1447
+ buildColumn(
1448
+ buildData({
1449
+ type: dictionary(utf8(), dict.arrowIndexType),
1450
+ offset: 0,
1451
+ length: totalRows,
1452
+ nullCount,
1453
+ data: indices,
1454
+ nullBitmap: nullCount > 0 ? nullBitmap : undefined,
1455
+ dictionary: buildColumn(
1456
+ buildData({
1457
+ type: utf8(),
1458
+ offset: 0,
1459
+ length: dict.indexMap.size,
1460
+ nullCount: 0,
1461
+ valueOffsets: dict.offsets,
1462
+ data: dict.data,
1463
+ }),
1464
+ ),
1465
+ }),
1466
+ ),
1467
+ );
1468
+ fields.push(arrowFieldName);
1469
+ } else if (lmaoType === 'text') {
1470
+ const dict = textDicts.get(fieldName);
1471
+ const originalToMasked = textOriginalToMasked.get(fieldName);
1472
+ if (!dict || !originalToMasked) {
1473
+ throw new Error(`Text dictionary or mapping not found for field: ${fieldName}`);
1474
+ }
1475
+ const indices = new dict.indexArrayCtor(totalRows);
1476
+ const nullBitmap = new Uint8Array(Math.ceil(totalRows / 8));
1477
+ let nullCount = 0;
1478
+ let rowOffset = 0;
1479
+
1480
+ for (const buf of buffers) {
1481
+ const col = getAllocatedStringColumn(buf, columnName);
1482
+ // Check if this buffer has a scope value for this field
1483
+ const scopeValue = getStringScopeValue(buf, fieldName);
1484
+ let scopeEncodedValue: number | undefined;
1485
+ if (scopeValue !== undefined) {
1486
+ const maskedScopeValue = originalToMasked.get(scopeValue) ?? scopeValue;
1487
+ scopeEncodedValue = dict.indexMap.get(maskedScopeValue);
1488
+ }
1489
+
1490
+ if (col) {
1491
+ for (let i = 0; i < buf._writeIndex; i++) {
1492
+ const v = col[i];
1493
+ const rowIdx = rowOffset + i;
1494
+ if (v != null) {
1495
+ // Direct write wins - look up the masked value
1496
+ const maskedValue = originalToMasked.get(v) ?? v;
1497
+ indices[rowIdx] = dict.indexMap.get(maskedValue) ?? 0;
1498
+ nullBitmap[rowIdx >>> 3] |= 1 << (rowIdx & 7);
1499
+ } else if (scopeEncodedValue !== undefined) {
1500
+ // No direct write, but have scope value - use it
1501
+ indices[rowIdx] = scopeEncodedValue;
1502
+ nullBitmap[rowIdx >>> 3] |= 1 << (rowIdx & 7);
1503
+ } else {
1504
+ nullCount++;
1505
+ }
1506
+ }
1507
+ } else if (scopeEncodedValue !== undefined) {
1508
+ // Column not allocated but we have scope - fill all rows with scope value
1509
+ for (let i = 0; i < buf._writeIndex; i++) {
1510
+ const rowIdx = rowOffset + i;
1511
+ indices[rowIdx] = scopeEncodedValue;
1512
+ nullBitmap[rowIdx >>> 3] |= 1 << (rowIdx & 7);
1513
+ }
1514
+ } else {
1515
+ // No column and no scope - all nulls
1516
+ nullCount += buf._writeIndex;
1517
+ }
1518
+ rowOffset += buf._writeIndex;
1519
+ }
1520
+
1521
+ vectors.push(
1522
+ buildColumn(
1523
+ buildData({
1524
+ type: dictionary(utf8(), dict.arrowIndexType),
1525
+ offset: 0,
1526
+ length: totalRows,
1527
+ nullCount,
1528
+ data: indices,
1529
+ nullBitmap: nullCount > 0 ? nullBitmap : undefined,
1530
+ dictionary: buildColumn(
1531
+ buildData({
1532
+ type: utf8(),
1533
+ offset: 0,
1534
+ length: dict.indexMap.size,
1535
+ nullCount: 0,
1536
+ valueOffsets: dict.offsets,
1537
+ data: dict.data,
1538
+ }),
1539
+ ),
1540
+ }),
1541
+ ),
1542
+ );
1543
+ fields.push(arrowFieldName);
1544
+ } else if (lmaoType === 'number') {
1545
+ const allValues = new F64Array(totalRows);
1546
+ const nullBitmap = new Uint8Array(Math.ceil(totalRows / 8));
1547
+ // Start with all nulls (bits cleared) - we'll set bits as we find values
1548
+ let nullCount = 0;
1549
+ let rowOffset = 0;
1550
+
1551
+ for (const buf of buffers) {
1552
+ const col = buf.getColumnIfAllocated(columnName);
1553
+ const srcNulls = buf.getNullsIfAllocated(columnName);
1554
+ // Check if this buffer has a scope value for this field
1555
+ const scopeValue = getNumberScopeValue(buf, fieldName);
1556
+
1557
+ if (col instanceof Float64Array) {
1558
+ allValues.set(col.subarray(0, buf._writeIndex), rowOffset);
1559
+ if (srcNulls) {
1560
+ // Process each row - direct write wins, then scope, then null
1561
+ for (let i = 0; i < buf._writeIndex; i++) {
1562
+ const srcByte = i >>> 3;
1563
+ const srcBit = i & 7;
1564
+ const isValid = (srcNulls[srcByte] & (1 << srcBit)) !== 0;
1565
+ const rowIdx = rowOffset + i;
1566
+ if (isValid) {
1567
+ // Direct write - mark as valid
1568
+ nullBitmap[rowIdx >>> 3] |= 1 << (rowIdx & 7);
1569
+ } else if (scopeValue !== undefined) {
1570
+ // No direct write, but have scope value - use it
1571
+ allValues[rowIdx] = scopeValue;
1572
+ nullBitmap[rowIdx >>> 3] |= 1 << (rowIdx & 7);
1573
+ } else {
1574
+ // No direct write and no scope - null
1575
+ nullCount++;
1576
+ }
1577
+ }
1578
+ } else {
1579
+ // No null bitmap means column was allocated but no writes - check scope
1580
+ for (let i = 0; i < buf._writeIndex; i++) {
1581
+ const rowIdx = rowOffset + i;
1582
+ if (scopeValue !== undefined) {
1583
+ allValues[rowIdx] = scopeValue;
1584
+ nullBitmap[rowIdx >>> 3] |= 1 << (rowIdx & 7);
1585
+ } else {
1586
+ nullCount++;
1587
+ }
1588
+ }
1589
+ }
1590
+ } else if (scopeValue !== undefined) {
1591
+ // Column not allocated but we have scope - fill all rows with scope value
1592
+ for (let i = 0; i < buf._writeIndex; i++) {
1593
+ const rowIdx = rowOffset + i;
1594
+ allValues[rowIdx] = scopeValue;
1595
+ nullBitmap[rowIdx >>> 3] |= 1 << (rowIdx & 7);
1596
+ }
1597
+ } else {
1598
+ // Column doesn't exist for this buffer and no scope - all nulls
1599
+ nullCount += buf._writeIndex;
1600
+ }
1601
+ rowOffset += buf._writeIndex;
1602
+ }
1603
+
1604
+ vectors.push(
1605
+ buildColumn(
1606
+ buildData({
1607
+ type: float64(),
1608
+ offset: 0,
1609
+ length: totalRows,
1610
+ nullCount,
1611
+ data: allValues,
1612
+ nullBitmap: nullCount > 0 ? nullBitmap : undefined,
1613
+ }),
1614
+ ),
1615
+ );
1616
+ fields.push(arrowFieldName);
1617
+ } else if (lmaoType === 'boolean') {
1618
+ const requiredBytes = Math.ceil(totalRows / 8);
1619
+ const allValues = new Uint8Array(requiredBytes);
1620
+ const nullBitmap = new Uint8Array(requiredBytes);
1621
+ // Start with all nulls (bits cleared) - we'll set bits as we find values
1622
+ let nullCount = 0;
1623
+ let rowOffset = 0;
1624
+
1625
+ for (const buf of buffers) {
1626
+ const col = buf.getColumnIfAllocated(columnName);
1627
+ const srcNulls = buf.getNullsIfAllocated(columnName);
1628
+ // Check if this buffer has a scope value for this field
1629
+ const scopeValue = getBooleanScopeValue(buf, fieldName);
1630
+
1631
+ if (col instanceof Uint8Array) {
1632
+ // Copy boolean values bit by bit - can't avoid loop for bit-level operations
1633
+ for (let i = 0; i < buf._writeIndex; i++) {
1634
+ const srcByte = i >>> 3;
1635
+ const srcBit = i & 7;
1636
+ const rowIdx = rowOffset + i;
1637
+
1638
+ if (srcNulls) {
1639
+ const isValid = (srcNulls[srcByte] & (1 << srcBit)) !== 0;
1640
+ if (isValid) {
1641
+ // Direct write - copy the value
1642
+ const value = (col[srcByte] & (1 << srcBit)) !== 0;
1643
+ if (value) {
1644
+ allValues[rowIdx >>> 3] |= 1 << (rowIdx & 7);
1645
+ }
1646
+ nullBitmap[rowIdx >>> 3] |= 1 << (rowIdx & 7);
1647
+ } else if (scopeValue !== undefined) {
1648
+ // No direct write, but have scope value - use it
1649
+ if (scopeValue) {
1650
+ allValues[rowIdx >>> 3] |= 1 << (rowIdx & 7);
1651
+ }
1652
+ nullBitmap[rowIdx >>> 3] |= 1 << (rowIdx & 7);
1653
+ } else {
1654
+ // No direct write and no scope - null
1655
+ nullCount++;
1656
+ }
1657
+ } else {
1658
+ // No null bitmap - check scope
1659
+ if (scopeValue !== undefined) {
1660
+ if (scopeValue) {
1661
+ allValues[rowIdx >>> 3] |= 1 << (rowIdx & 7);
1662
+ }
1663
+ nullBitmap[rowIdx >>> 3] |= 1 << (rowIdx & 7);
1664
+ } else {
1665
+ nullCount++;
1666
+ }
1667
+ }
1668
+ }
1669
+ } else if (scopeValue !== undefined) {
1670
+ // Column not allocated but we have scope - fill all rows with scope value
1671
+ for (let i = 0; i < buf._writeIndex; i++) {
1672
+ const rowIdx = rowOffset + i;
1673
+ if (scopeValue) {
1674
+ allValues[rowIdx >>> 3] |= 1 << (rowIdx & 7);
1675
+ }
1676
+ nullBitmap[rowIdx >>> 3] |= 1 << (rowIdx & 7);
1677
+ }
1678
+ } else {
1679
+ // Column doesn't exist for this buffer and no scope - all nulls
1680
+ nullCount += buf._writeIndex;
1681
+ }
1682
+ rowOffset += buf._writeIndex;
1683
+ }
1684
+
1685
+ vectors.push(
1686
+ buildColumn(
1687
+ buildData({
1688
+ type: bool(),
1689
+ offset: 0,
1690
+ length: totalRows,
1691
+ nullCount,
1692
+ data: allValues,
1693
+ nullBitmap: nullCount > 0 ? nullBitmap : undefined,
1694
+ }),
1695
+ ),
1696
+ );
1697
+ fields.push(arrowFieldName);
1698
+ } else if (lmaoType === 'enum') {
1699
+ const enumValues = getEnumValues(fieldSchema) || [];
1700
+ const enumUtf8 = getEnumUtf8(fieldSchema);
1701
+ // Get constructors from schema metadata
1702
+ const indexArrayCtor = getArrowIndexArrayConstructorOr(fieldSchema);
1703
+ const arrowIndexType = getArrowIndexTypeOr(fieldSchema, uint8());
1704
+ const allIndices = new indexArrayCtor(totalRows);
1705
+ const nullBitmap = new Uint8Array(Math.ceil(totalRows / 8));
1706
+ // Start with all nulls (bits cleared) - we'll set bits as we find values
1707
+ let nullCount = 0;
1708
+ let rowOffset = 0;
1709
+
1710
+ // Build enum value to index mapping for scope lookup
1711
+ const enumValueToIndex = new Map<string, number>();
1712
+ for (let i = 0; i < enumValues.length; i++) {
1713
+ enumValueToIndex.set(enumValues[i], i);
1714
+ }
1715
+
1716
+ for (const buf of buffers) {
1717
+ const col = buf.getColumnIfAllocated(columnName);
1718
+ const srcNulls = buf.getNullsIfAllocated(columnName);
1719
+ // Check if this buffer has a scope value for this field
1720
+ const scopeValue = getStringScopeValue(buf, fieldName);
1721
+ let scopeEncodedValue: number | undefined;
1722
+ if (scopeValue !== undefined) {
1723
+ scopeEncodedValue = enumValueToIndex.get(scopeValue);
1724
+ }
1725
+
1726
+ if (col instanceof indexArrayCtor) {
1727
+ allIndices.set(col.subarray(0, buf._writeIndex), rowOffset);
1728
+ if (srcNulls) {
1729
+ for (let i = 0; i < buf._writeIndex; i++) {
1730
+ const srcByte = i >>> 3;
1731
+ const srcBit = i & 7;
1732
+ const isValid = (srcNulls[srcByte] & (1 << srcBit)) !== 0;
1733
+ const rowIdx = rowOffset + i;
1734
+ if (isValid) {
1735
+ // Direct write - mark as valid
1736
+ nullBitmap[rowIdx >>> 3] |= 1 << (rowIdx & 7);
1737
+ } else if (scopeEncodedValue !== undefined) {
1738
+ // No direct write, but have scope value - use it
1739
+ allIndices[rowIdx] = scopeEncodedValue;
1740
+ nullBitmap[rowIdx >>> 3] |= 1 << (rowIdx & 7);
1741
+ } else {
1742
+ // No direct write and no scope - null
1743
+ nullCount++;
1744
+ }
1745
+ }
1746
+ } else {
1747
+ // No null bitmap - check scope for each row
1748
+ for (let i = 0; i < buf._writeIndex; i++) {
1749
+ const rowIdx = rowOffset + i;
1750
+ if (scopeEncodedValue !== undefined) {
1751
+ allIndices[rowIdx] = scopeEncodedValue;
1752
+ nullBitmap[rowIdx >>> 3] |= 1 << (rowIdx & 7);
1753
+ } else {
1754
+ nullCount++;
1755
+ }
1756
+ }
1757
+ }
1758
+ } else if (scopeEncodedValue !== undefined) {
1759
+ // Column not allocated but we have scope - fill all rows with scope value
1760
+ for (let i = 0; i < buf._writeIndex; i++) {
1761
+ const rowIdx = rowOffset + i;
1762
+ allIndices[rowIdx] = scopeEncodedValue;
1763
+ nullBitmap[rowIdx >>> 3] |= 1 << (rowIdx & 7);
1764
+ }
1765
+ } else {
1766
+ // Column doesn't exist for this buffer and no scope - all nulls
1767
+ nullCount += buf._writeIndex;
1768
+ }
1769
+ rowOffset += buf._writeIndex;
1770
+ }
1771
+
1772
+ const enumDictData = buildData({
1773
+ type: utf8(),
1774
+ offset: 0,
1775
+ length: enumValues.length,
1776
+ nullCount: 0,
1777
+ valueOffsets: enumUtf8?.offsets ?? calculateUtf8Offsets(enumValues),
1778
+ data: enumUtf8?.concatenated ?? encodeUtf8Strings(enumValues),
1779
+ });
1780
+
1781
+ vectors.push(
1782
+ buildColumn(
1783
+ buildData({
1784
+ type: dictionary(utf8(), arrowIndexType),
1785
+ offset: 0,
1786
+ length: totalRows,
1787
+ nullCount,
1788
+ data: allIndices,
1789
+ nullBitmap: nullCount > 0 ? nullBitmap : undefined,
1790
+ dictionary: buildColumn(enumDictData),
1791
+ }),
1792
+ ),
1793
+ );
1794
+ fields.push(arrowFieldName);
1795
+ } else if (lmaoType === 'binary') {
1796
+ // Binary columns: raw Uint8Array or encoder-wrapped values (e.g. msgpack)
1797
+ // Binary columns don't use dictionaries and are not scope-fillable
1798
+ const { column } = buildBinaryColumnFromBuffers(buffers, columnName, totalRows, fieldSchema);
1799
+ fields.push(arrowFieldName);
1800
+ vectors.push(column);
1801
+ }
1802
+ }
1803
+
1804
+ return buildTable(fields, vectors);
1805
+ }