@smoothbricks/lmao 0.2.2 → 0.2.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (276) hide show
  1. package/dist/allocator.wasm +0 -0
  2. package/dist/index.d.ts +1 -0
  3. package/dist/index.d.ts.map +1 -1
  4. package/dist/index.js +1 -0
  5. package/dist/lib/JsBufferStrategy.d.ts +4 -3
  6. package/dist/lib/JsBufferStrategy.d.ts.map +1 -1
  7. package/dist/lib/JsBufferStrategy.js +18 -11
  8. package/dist/lib/arrow/lease.d.ts +10 -0
  9. package/dist/lib/arrow/lease.d.ts.map +1 -0
  10. package/dist/lib/arrow/lease.js +33 -0
  11. package/dist/lib/arrow/utils.d.ts +3 -4
  12. package/dist/lib/arrow/utils.d.ts.map +1 -1
  13. package/dist/lib/arrow/utils.js +12 -13
  14. package/dist/lib/arrow/vocabularyDictionary.d.ts +21 -0
  15. package/dist/lib/arrow/vocabularyDictionary.d.ts.map +1 -0
  16. package/dist/lib/arrow/vocabularyDictionary.js +95 -0
  17. package/dist/lib/bufferStrategy.d.ts +3 -2
  18. package/dist/lib/bufferStrategy.d.ts.map +1 -1
  19. package/dist/lib/cloudflare/traceRows.d.ts.map +1 -1
  20. package/dist/lib/cloudflare/traceRows.js +3 -2
  21. package/dist/lib/codegen/evaluatorGenerator.d.ts.map +1 -1
  22. package/dist/lib/codegen/evaluatorGenerator.js +12 -11
  23. package/dist/lib/codegen/fixedPositionWriterGenerator.d.ts +44 -82
  24. package/dist/lib/codegen/fixedPositionWriterGenerator.d.ts.map +1 -1
  25. package/dist/lib/codegen/fixedPositionWriterGenerator.js +223 -162
  26. package/dist/lib/codegen/spanLoggerClosureMaterializer.d.ts +63 -0
  27. package/dist/lib/codegen/spanLoggerClosureMaterializer.d.ts.map +1 -0
  28. package/dist/lib/codegen/spanLoggerClosureMaterializer.js +447 -0
  29. package/dist/lib/codegen/spanLoggerGenerator.d.ts +36 -19
  30. package/dist/lib/codegen/spanLoggerGenerator.d.ts.map +1 -1
  31. package/dist/lib/codegen/spanLoggerGenerator.js +164 -158
  32. package/dist/lib/convertToArrow.d.ts +7 -2
  33. package/dist/lib/convertToArrow.d.ts.map +1 -1
  34. package/dist/lib/convertToArrow.js +499 -285
  35. package/dist/lib/defineOpContext.js +1 -1
  36. package/dist/lib/enumMetadata.d.ts +19 -0
  37. package/dist/lib/enumMetadata.d.ts.map +1 -0
  38. package/dist/lib/enumMetadata.js +86 -0
  39. package/dist/lib/flushScheduler.d.ts.map +1 -1
  40. package/dist/lib/flushScheduler.js +13 -4
  41. package/dist/lib/library.d.ts +16 -98
  42. package/dist/lib/library.d.ts.map +1 -1
  43. package/dist/lib/library.js +32 -525
  44. package/dist/lib/logBinding.d.ts +21 -27
  45. package/dist/lib/logBinding.d.ts.map +1 -1
  46. package/dist/lib/op.d.ts +30 -30
  47. package/dist/lib/op.d.ts.map +1 -1
  48. package/dist/lib/op.js +15 -22
  49. package/dist/lib/opContext/createOpGroup.d.ts +1 -1
  50. package/dist/lib/opContext/createOpGroup.d.ts.map +1 -1
  51. package/dist/lib/opContext/createOpGroup.js +22 -29
  52. package/dist/lib/opContext/defineOp.d.ts +4 -4
  53. package/dist/lib/opContext/defineOp.d.ts.map +1 -1
  54. package/dist/lib/opContext/defineOp.js +53 -10
  55. package/dist/lib/opContext/opGroupTypes.d.ts +2 -2
  56. package/dist/lib/opContext/opTypes.d.ts +1 -1
  57. package/dist/lib/opContext/opTypes.d.ts.map +1 -1
  58. package/dist/lib/opContext/spanContextTypes.d.ts +29 -14
  59. package/dist/lib/opContext/spanContextTypes.d.ts.map +1 -1
  60. package/dist/lib/opContext/types.d.ts +4 -4
  61. package/dist/lib/opContext/types.d.ts.map +1 -1
  62. package/dist/lib/physicalLayoutPlan.d.ts +89 -0
  63. package/dist/lib/physicalLayoutPlan.d.ts.map +1 -0
  64. package/dist/lib/physicalLayoutPlan.js +442 -0
  65. package/dist/lib/resolveMessage.d.ts +9 -0
  66. package/dist/lib/resolveMessage.d.ts.map +1 -0
  67. package/dist/lib/resolveMessage.js +63 -0
  68. package/dist/lib/result.d.ts +5 -7
  69. package/dist/lib/result.d.ts.map +1 -1
  70. package/dist/lib/result.js +19 -26
  71. package/dist/lib/runtimeHint.d.ts +35 -0
  72. package/dist/lib/runtimeHint.d.ts.map +1 -0
  73. package/dist/lib/runtimeHint.js +79 -0
  74. package/dist/lib/schema/types.d.ts +1 -1
  75. package/dist/lib/schema/types.d.ts.map +1 -1
  76. package/dist/lib/span-buffer/aot/v1.d.ts +2 -0
  77. package/dist/lib/span-buffer/aot/v1.d.ts.map +1 -0
  78. package/dist/lib/span-buffer/aot/v1.js +24 -0
  79. package/dist/lib/spanBuffer.d.ts +33 -4
  80. package/dist/lib/spanBuffer.d.ts.map +1 -1
  81. package/dist/lib/spanBuffer.js +583 -91
  82. package/dist/lib/spanBufferStats.d.ts +1 -1
  83. package/dist/lib/spanBufferStats.d.ts.map +1 -1
  84. package/dist/lib/spanContext.d.ts +27 -51
  85. package/dist/lib/spanContext.d.ts.map +1 -1
  86. package/dist/lib/spanContext.js +822 -344
  87. package/dist/lib/sqlite/sqlite-common.d.ts.map +1 -1
  88. package/dist/lib/sqlite/sqlite-common.js +20 -21
  89. package/dist/lib/testing/bun-harness.d.ts +8 -3
  90. package/dist/lib/testing/bun-harness.d.ts.map +1 -1
  91. package/dist/lib/testing/bun-harness.js +30 -18
  92. package/dist/lib/testing/extractFacts.d.ts +1 -1
  93. package/dist/lib/testing/extractFacts.d.ts.map +1 -1
  94. package/dist/lib/testing/extractFacts.js +55 -63
  95. package/dist/lib/testing/facts.d.ts +1 -1
  96. package/dist/lib/testing/facts.d.ts.map +1 -1
  97. package/dist/lib/testing/queryable-span.d.ts +1 -1
  98. package/dist/lib/testing/queryable-span.d.ts.map +1 -1
  99. package/dist/lib/testing/queryable-span.js +26 -28
  100. package/dist/lib/testing/span-query.d.ts.map +1 -1
  101. package/dist/lib/testing/span-query.js +19 -22
  102. package/dist/lib/testing/stdio-replay.d.ts.map +1 -1
  103. package/dist/lib/testing/stdio-replay.js +19 -8
  104. package/dist/lib/threadId.d.ts.map +1 -1
  105. package/dist/lib/threadId.js +6 -3
  106. package/dist/lib/traceRoot.d.ts +30 -8
  107. package/dist/lib/traceRoot.d.ts.map +1 -1
  108. package/dist/lib/traceRoot.es.d.ts +17 -15
  109. package/dist/lib/traceRoot.es.d.ts.map +1 -1
  110. package/dist/lib/traceRoot.es.js +68 -34
  111. package/dist/lib/traceRoot.js +11 -0
  112. package/dist/lib/traceRoot.node.d.ts +17 -19
  113. package/dist/lib/traceRoot.node.d.ts.map +1 -1
  114. package/dist/lib/traceRoot.node.js +66 -38
  115. package/dist/lib/traceTopology.d.ts +42 -0
  116. package/dist/lib/traceTopology.d.ts.map +1 -0
  117. package/dist/lib/traceTopology.js +260 -0
  118. package/dist/lib/tracer.d.ts +9 -5
  119. package/dist/lib/tracer.d.ts.map +1 -1
  120. package/dist/lib/tracer.js +65 -71
  121. package/dist/lib/tracers/ArrayQueueTracer.d.ts +1 -1
  122. package/dist/lib/tracers/ArrayQueueTracer.js +1 -1
  123. package/dist/lib/tracers/StdioTracer.d.ts.map +1 -1
  124. package/dist/lib/tracers/StdioTracer.js +15 -13
  125. package/dist/lib/tracers/TestTracer.d.ts +4 -6
  126. package/dist/lib/tracers/TestTracer.d.ts.map +1 -1
  127. package/dist/lib/tracers/TestTracer.js +6 -8
  128. package/dist/lib/types.d.ts +49 -32
  129. package/dist/lib/types.d.ts.map +1 -1
  130. package/dist/lib/vocabularyRegistry.d.ts +25 -0
  131. package/dist/lib/vocabularyRegistry.d.ts.map +1 -0
  132. package/dist/lib/vocabularyRegistry.js +372 -0
  133. package/dist/lib/wasm/WasmBufferStrategy.d.ts +6 -10
  134. package/dist/lib/wasm/WasmBufferStrategy.d.ts.map +1 -1
  135. package/dist/lib/wasm/WasmBufferStrategy.js +44 -37
  136. package/dist/lib/wasm/wasmAllocator.d.ts +27 -22
  137. package/dist/lib/wasm/wasmAllocator.d.ts.map +1 -1
  138. package/dist/lib/wasm/wasmAllocator.js +66 -44
  139. package/dist/lib/wasm/wasmPhysicalLayout.d.ts +67 -0
  140. package/dist/lib/wasm/wasmPhysicalLayout.d.ts.map +1 -0
  141. package/dist/lib/wasm/wasmPhysicalLayout.js +179 -0
  142. package/dist/lib/wasm/wasmSpanBuffer.d.ts +79 -31
  143. package/dist/lib/wasm/wasmSpanBuffer.d.ts.map +1 -1
  144. package/dist/lib/wasm/wasmSpanBuffer.js +442 -272
  145. package/dist/lib/wasm/wasmTraceRoot.d.ts +24 -22
  146. package/dist/lib/wasm/wasmTraceRoot.d.ts.map +1 -1
  147. package/dist/lib/wasm/wasmTraceRoot.js +110 -71
  148. package/dist/vocabulary/register/v1.d.ts +2 -0
  149. package/dist/vocabulary/register/v1.d.ts.map +1 -0
  150. package/dist/vocabulary/register/v1.js +1 -0
  151. package/dist/wasm.d.ts +5 -0
  152. package/dist/wasm.d.ts.map +1 -0
  153. package/dist/wasm.js +4 -0
  154. package/package.json +54 -6
  155. package/src/index.ts +1 -0
  156. package/src/lib/JsBufferStrategy.ts +27 -9
  157. package/src/lib/__tests__/arrow-builder-integration/arrow-conversion.test.ts +335 -37
  158. package/src/lib/__tests__/arrow-builder-integration/binary-arrow-conversion.test.ts +97 -26
  159. package/src/lib/__tests__/arrow-builder-integration/binary-comparison.test.ts +75 -15
  160. package/src/lib/__tests__/arrow-builder-integration/binary-format-compliance.test.ts +30 -6
  161. package/src/lib/__tests__/arrow-builder-integration/buffer-chaining.test.ts +18 -21
  162. package/src/lib/__tests__/arrow-builder-integration/foundation.test.ts +5 -2
  163. package/src/lib/__tests__/arrow-builder-integration/integration.test.ts +25 -7
  164. package/src/lib/__tests__/arrow-builder-integration/lazy-columns.test.ts +3 -2
  165. package/src/lib/__tests__/buffer-overflow.test.ts +24 -18
  166. package/src/lib/__tests__/capability-span-context.test.ts +505 -0
  167. package/src/lib/__tests__/capacity-tuning.test.ts +5 -3
  168. package/src/lib/__tests__/eager-columns.test.ts +345 -0
  169. package/src/lib/__tests__/enum-metadata-aot.test.ts +153 -0
  170. package/src/lib/__tests__/enum-metadata.test.ts +177 -0
  171. package/src/lib/__tests__/fixtures/empty-vocabulary-runtime.fixture.ts +15 -0
  172. package/src/lib/__tests__/flushScheduler.test.ts +5 -1
  173. package/src/lib/__tests__/identity-view.property.test.ts +179 -0
  174. package/src/lib/__tests__/library.test.ts +19 -235
  175. package/src/lib/__tests__/message-buffer-families.test.ts +878 -0
  176. package/src/lib/__tests__/nested-library-tasks.test.ts +14 -15
  177. package/src/lib/__tests__/op-metadata.test.ts +1 -1
  178. package/src/lib/__tests__/op-type-safety.test.ts +5 -16
  179. package/src/lib/__tests__/physical-layout-plan.test.ts +333 -0
  180. package/src/lib/__tests__/remap-descriptor-integration.test.ts +381 -0
  181. package/src/lib/__tests__/root-context-construction.test.ts +414 -0
  182. package/src/lib/__tests__/span-auto-runtime.test.ts +386 -0
  183. package/src/lib/__tests__/span-buffer-segment-stats.test.ts +221 -0
  184. package/src/lib/__tests__/span-lifecycle.test.ts +53 -25
  185. package/src/lib/__tests__/spanbuffer-materializer-parity.test.ts +266 -0
  186. package/src/lib/__tests__/sync-async-trace.test.ts +24 -23
  187. package/src/lib/__tests__/tag-writer-aot.test.ts +214 -0
  188. package/src/lib/__tests__/test-helpers.ts +67 -12
  189. package/src/lib/__tests__/threadId.test.ts +62 -0
  190. package/src/lib/__tests__/timestamp.test.ts +221 -93
  191. package/src/lib/__tests__/traceTopology.property.test.ts +383 -0
  192. package/src/lib/__tests__/type-narrowing.test.ts +63 -0
  193. package/src/lib/__tests__/vocabulary-dense-decoding.test.ts +405 -0
  194. package/src/lib/__tests__/vocabularyRegistry.test.ts +313 -0
  195. package/src/lib/arrow/lease.ts +43 -0
  196. package/src/lib/arrow/utils.ts +10 -14
  197. package/src/lib/arrow/vocabularyDictionary.ts +123 -0
  198. package/src/lib/bufferStrategy.ts +9 -1
  199. package/src/lib/cloudflare/traceRows.ts +3 -2
  200. package/src/lib/codegen/__tests__/evaluatorGenerator.test.ts +21 -141
  201. package/src/lib/codegen/__tests__/fixedPositionWriter-parity.test.ts +273 -0
  202. package/src/lib/codegen/__tests__/fixedPositionWriter-uint64.test.ts +61 -34
  203. package/src/lib/codegen/__tests__/nullBitmap.test.ts +33 -37
  204. package/src/lib/codegen/__tests__/spanLoggerGenerator.test.ts +34 -18
  205. package/src/lib/codegen/__tests__/spanLoggerMaterializerParity.test.ts +503 -0
  206. package/src/lib/codegen/evaluatorGenerator.ts +18 -13
  207. package/src/lib/codegen/fixedPositionWriterGenerator.ts +329 -245
  208. package/src/lib/codegen/spanLoggerClosureMaterializer.ts +598 -0
  209. package/src/lib/codegen/spanLoggerGenerator.ts +241 -177
  210. package/src/lib/convertToArrow.ts +598 -311
  211. package/src/lib/defineOpContext.ts +1 -1
  212. package/src/lib/enumMetadata.ts +121 -0
  213. package/src/lib/flushScheduler.ts +11 -5
  214. package/src/lib/library.ts +37 -595
  215. package/src/lib/logBinding.ts +21 -27
  216. package/src/lib/op.ts +75 -18
  217. package/src/lib/opContext/__tests__/retry.test.ts +27 -21
  218. package/src/lib/opContext/createOpGroup.ts +33 -31
  219. package/src/lib/opContext/defineOp.ts +88 -11
  220. package/src/lib/opContext/opGroupTypes.ts +2 -2
  221. package/src/lib/opContext/opTypes.ts +1 -1
  222. package/src/lib/opContext/spanContextTypes.ts +98 -17
  223. package/src/lib/opContext/types.ts +7 -3
  224. package/src/lib/physicalLayoutPlan.ts +598 -0
  225. package/src/lib/resolveMessage.ts +67 -0
  226. package/src/lib/result.ts +20 -33
  227. package/src/lib/runtimeHint.ts +98 -0
  228. package/src/lib/schema/__tests__/featureFlags.test.ts +2 -1
  229. package/src/lib/schema/builder.ts +1 -1
  230. package/src/lib/schema/types.ts +1 -1
  231. package/src/lib/span-buffer/aot/v1.ts +26 -0
  232. package/src/lib/spanBuffer.ts +658 -92
  233. package/src/lib/spanBufferStats.ts +1 -1
  234. package/src/lib/spanContext.ts +1350 -861
  235. package/src/lib/sqlite/sqlite-common.ts +20 -31
  236. package/src/lib/testing/__tests__/topology-traversal.test.ts +174 -0
  237. package/src/lib/testing/__tests__/vitest-harness.test.ts +2 -1
  238. package/src/lib/testing/bun-harness.ts +94 -43
  239. package/src/lib/testing/extractFacts.ts +64 -78
  240. package/src/lib/testing/facts.ts +1 -1
  241. package/src/lib/testing/queryable-span.ts +23 -30
  242. package/src/lib/testing/span-query.ts +16 -20
  243. package/src/lib/testing/stdio-replay.ts +25 -8
  244. package/src/lib/testing/vitest-harness.ts +1 -1
  245. package/src/lib/threadId.ts +6 -3
  246. package/src/lib/traceRoot.es.ts +77 -42
  247. package/src/lib/traceRoot.node.ts +75 -46
  248. package/src/lib/traceRoot.ts +41 -14
  249. package/src/lib/traceTopology.ts +279 -0
  250. package/src/lib/tracer.ts +109 -98
  251. package/src/lib/tracers/ArrayQueueTracer.ts +1 -1
  252. package/src/lib/tracers/StdioTracer.ts +15 -13
  253. package/src/lib/tracers/TestTracer.ts +6 -8
  254. package/src/lib/tracers/__tests__/ArrayQueueTracer.test.ts +15 -10
  255. package/src/lib/tracers/__tests__/TestTracer.test.ts +23 -13
  256. package/src/lib/types.ts +64 -33
  257. package/src/lib/vocabularyRegistry.ts +439 -0
  258. package/src/lib/wasm/WasmBufferStrategy.ts +56 -48
  259. package/src/lib/wasm/__tests__/WasmBufferStrategy.test.ts +215 -11
  260. package/src/lib/wasm/__tests__/allocatorFreeBlockClamp.test.ts +86 -0
  261. package/src/lib/wasm/__tests__/wasm-identity-view.test.ts +311 -0
  262. package/src/lib/wasm/__tests__/wasm-integration.test.ts +417 -44
  263. package/src/lib/wasm/__tests__/wasm-message-buffer-families.test.ts +588 -0
  264. package/src/lib/wasm/__tests__/wasmAllocator.test.ts +100 -668
  265. package/src/lib/wasm/__tests__/wasmSpanBuffer.test.ts +419 -63
  266. package/src/lib/wasm/__tests__/wasmTraceRoot.test.ts +24 -29
  267. package/src/lib/wasm/__tests__/wasmTracer.test.ts.disabled +2 -2
  268. package/src/lib/wasm/wasmAllocator.ts +131 -96
  269. package/src/lib/wasm/wasmPhysicalLayout.ts +309 -0
  270. package/src/lib/wasm/wasmSpanBuffer.ts +597 -341
  271. package/src/lib/wasm/wasmTraceRoot.ts +140 -78
  272. package/src/vocabulary/register/v1.ts +1 -0
  273. package/src/wasm.ts +19 -0
  274. package/src/lib/__tests__/remapped-buffer-view-integration.test.ts +0 -319
  275. package/src/lib/__tests__/remapped-buffer-view.test.ts +0 -635
  276. package/src/lib/wasm/allocator.zig +0 -948
@@ -7,14 +7,19 @@
7
7
  * - Text columns: Plain strings without dictionary
8
8
  * - Zero-copy wrap TypedArrays as Arrow vectors
9
9
  */
10
- import { clearBitRange, compareStrings, createSortedDictionary, DictionaryBuilder, getMaskTransform, sortInPlace, } from '@smoothbricks/arrow-builder';
10
+ import { clearBitRange, compareStrings, countNulls, createSortedDictionary, DictionaryBuilder, getMaskTransform, sortInPlace, } from '@smoothbricks/arrow-builder';
11
11
  import { batchType, binary, bool, Column, dictionary, float64, int8, TimeUnit, tableFromColumns, timestamp, uint8, uint16, uint32, uint64, utf8, } from '@uwdata/flechette';
12
12
  import { createCapacityStatsTable } from './arrow/capacityStats.js';
13
13
  import { buildSortedCategoryDictionary, buildTextDictionary } from './arrow/dictionaries.js';
14
- import { getArrowIndexArrayConstructorOr, getArrowIndexTypeOr, makeArrowColumn, } from './arrow/flechette.js';
15
- import { calculateUtf8Offsets, concatenateFloat64Arrays, concatenateNullBitmaps, concatenateUint8Arrays, encodeUtf8Strings, getArrowFieldName, walkSpanTree, } from './arrow/utils.js';
16
- import { ENTRY_TYPE_NAMES, SYSTEM_SCHEMA_FIELD_NAMES } from './schema/systemSchema.js';
17
- import { getBinaryEncoder, getEnumUtf8, getEnumValues, getSchemaType } from './schema/typeGuards.js';
14
+ import { makeArrowColumn, } from './arrow/flechette.js';
15
+ import { createArrowLease } from './arrow/lease.js';
16
+ import { calculateUtf8Offsets, combineColumnChunks, concatenateFloat64Arrays, concatenateNullBitmaps, concatenateUint8Arrays, encodeUtf8Strings, getArrowFieldName, } from './arrow/utils.js';
17
+ import { appendVocabularyDictionarySuffix, getVocabularyDictionaryPrefix, PREBUILT_ENTRY_TYPE_DICTIONARY, } from './arrow/vocabularyDictionary.js';
18
+ import { resolveEnumLookupDescriptor } from './enumMetadata.js';
19
+ import { resolveEntryType } from './resolveMessage.js';
20
+ import { ENTRY_TYPE_ERROR, ENTRY_TYPE_SPAN_START, ENTRY_TYPE_TRACE, SYSTEM_SCHEMA_FIELD_NAMES, } from './schema/systemSchema.js';
21
+ import { getBinaryEncoder, getEnumUtf8, getSchemaType } from './schema/typeGuards.js';
22
+ import { walkSpanTree } from './traceTopology.js';
18
23
  import { globalUtf8Cache } from './utf8Cache.js';
19
24
  const DictBuilder = DictionaryBuilder;
20
25
  const F64Array = Float64Array;
@@ -83,15 +88,20 @@ function appendTables(base, extra) {
83
88
  }
84
89
  return tableFromColumns(columns);
85
90
  }
86
- function getAllocatedStringColumn(buffer, columnName) {
87
- const column = buffer.getColumnIfAllocated(columnName);
91
+ function resolveColumnName(buffer, outputName, remaps) {
92
+ return remaps.get(buffer)?.sourceNames[outputName] ?? outputName;
93
+ }
94
+ function getAllocatedStringColumn(buffer, columnName, remaps) {
95
+ const sourceName = remaps ? resolveColumnName(buffer, columnName, remaps) : columnName;
96
+ const column = buffer.getColumnIfAllocated(sourceName);
88
97
  if (!Array.isArray(column)) {
89
98
  return undefined;
90
99
  }
91
100
  return isStringArray(column) ? column : undefined;
92
101
  }
93
- function getAllocatedBinaryColumn(buffer, columnName) {
94
- const column = buffer.getColumnIfAllocated(columnName);
102
+ function getAllocatedBinaryColumn(buffer, columnName, remaps) {
103
+ const sourceName = remaps ? resolveColumnName(buffer, columnName, remaps) : columnName;
104
+ const column = buffer.getColumnIfAllocated(sourceName);
95
105
  return Array.isArray(column) ? column : undefined;
96
106
  }
97
107
  //#region smoo/lmao!n/scope-attributes.arrow-fill
@@ -99,16 +109,19 @@ function getAllocatedBinaryColumn(buffer, columnName) {
99
109
  // Arrow conversion. These typed readers pull from the buffer's immutable _scopeValues; the
100
110
  // per-column fill loops below call them so a row with a direct write keeps its value and scope
101
111
  // only fills the gaps.
102
- function getStringScopeValue(buffer, fieldName) {
103
- const value = buffer._scopeValues?.[fieldName];
112
+ function getStringScopeValue(buffer, fieldName, remaps) {
113
+ const sourceName = remaps ? resolveColumnName(buffer, fieldName, remaps) : fieldName;
114
+ const value = buffer._scopeValues?.[sourceName];
104
115
  return typeof value === 'string' ? value : undefined;
105
116
  }
106
- function getNumberScopeValue(buffer, fieldName) {
107
- const value = buffer._scopeValues?.[fieldName];
117
+ function getNumberScopeValue(buffer, fieldName, remaps) {
118
+ const sourceName = remaps ? resolveColumnName(buffer, fieldName, remaps) : fieldName;
119
+ const value = buffer._scopeValues?.[sourceName];
108
120
  return typeof value === 'number' ? value : undefined;
109
121
  }
110
- function getBooleanScopeValue(buffer, fieldName) {
111
- const value = buffer._scopeValues?.[fieldName];
122
+ function getBooleanScopeValue(buffer, fieldName, remaps) {
123
+ const sourceName = remaps ? resolveColumnName(buffer, fieldName, remaps) : fieldName;
124
+ const value = buffer._scopeValues?.[sourceName];
112
125
  return typeof value === 'boolean' ? value : undefined;
113
126
  }
114
127
  //#endregion smoo/lmao!n/scope-attributes.arrow-fill
@@ -122,12 +135,166 @@ function isStringArray(value) {
122
135
  return Array.isArray(value) && value.every((entry) => typeof entry === 'string' || entry == null);
123
136
  }
124
137
  function indexTypeForCount(count) {
125
- if (count <= 255)
138
+ if (count <= 0x100)
126
139
  return uint8();
127
- if (count <= 65535)
140
+ if (count <= 65536)
128
141
  return uint16();
129
142
  return uint32();
130
143
  }
144
+ function assertStaticMessageKind(buffer, row, entryType, denseIndex) {
145
+ const generation = buffer._vocabularyGeneration;
146
+ if (denseIndex >= generation.ids.length) {
147
+ throw new Error(`Invalid vocabulary dense index ${denseIndex} for generation ${generation.generation}`);
148
+ }
149
+ const expectedKind = entryType === ENTRY_TYPE_SPAN_START ? 2 : entryType >= ENTRY_TYPE_TRACE && entryType <= ENTRY_TYPE_ERROR ? 1 : 0;
150
+ if (expectedKind === 0) {
151
+ throw new Error(`Entry type ${entryType} at row ${row} cannot carry a static vocabulary identity`);
152
+ }
153
+ const actualKind = generation.kindTags[denseIndex];
154
+ if (actualKind !== expectedKind) {
155
+ throw new Error(`Vocabulary kind ${actualKind} at dense index ${denseIndex} does not match entry type ${entryType} at row ${row}`);
156
+ }
157
+ }
158
+ function buildMessageColumn(buffers, totalRows, dictionaryId, materializeEntryTypes = true) {
159
+ let generation = buffers[0]._vocabularyGeneration;
160
+ for (let index = 1; index < buffers.length; index++) {
161
+ const candidate = buffers[index]._vocabularyGeneration;
162
+ if (candidate.generation > generation.generation)
163
+ generation = candidate;
164
+ }
165
+ const prefix = getVocabularyDictionaryPrefix(generation);
166
+ const indices = new Uint32Array(totalRows);
167
+ const entryTypeIndices = materializeEntryTypes ? new Int8Array(totalRows) : undefined;
168
+ const nullBitmap = new Uint8Array(Math.ceil(totalRows / 8));
169
+ let nullCount = 0;
170
+ let rowOffset = 0;
171
+ let suffixValues;
172
+ let suffixIndices;
173
+ for (const buffer of buffers) {
174
+ const plannedStorage = buffer._opMetadata._physicalLayoutPlan?.arrowExposure.messageIdentityStorage;
175
+ const messageIdentityStorage = plannedStorage ??
176
+ (buffer._messagePhysicalLayout === 'current'
177
+ ? 'local-u16'
178
+ : buffer._messagePhysicalLayout === 'specialized'
179
+ ? 'global-u32'
180
+ : 'packed-row-headers');
181
+ for (let row = 0; row < buffer._writeIndex; row++) {
182
+ const outputRow = rowOffset + row;
183
+ const hasDedicatedMessage = (row === 0 && buffer._spanName !== undefined) || (row === 1 && buffer._terminalMessage !== undefined);
184
+ let dictionaryIndex;
185
+ let entryType;
186
+ if (messageIdentityStorage === 'local-u16') {
187
+ entryType = resolveEntryType(buffer, row);
188
+ const localId = buffer._messageIds?.[row];
189
+ const rawMessage = buffer.message_values?.[row];
190
+ if (row === 0 && typeof buffer._spanName === 'number') {
191
+ if ((localId !== undefined && localId > 0) || rawMessage !== undefined) {
192
+ throw new Error(`Local row ${row} has conflicting static message identities`);
193
+ }
194
+ dictionaryIndex = buffer._spanName;
195
+ }
196
+ else if (localId !== undefined && localId > 0) {
197
+ if (hasDedicatedMessage || rawMessage !== undefined) {
198
+ throw new Error(`Local row ${row} has conflicting static and raw message storage`);
199
+ }
200
+ dictionaryIndex = buffer._opMetadata._physicalLayoutPlan?.localMessageDictionary[localId - 1];
201
+ if (dictionaryIndex === undefined)
202
+ throw new Error(`Missing local message dictionary entry ${localId}`);
203
+ }
204
+ }
205
+ else if (messageIdentityStorage === 'global-u32') {
206
+ entryType = resolveEntryType(buffer, row);
207
+ const encodedDenseIndex = buffer._logHeaders?.[row];
208
+ if (encodedDenseIndex !== undefined && encodedDenseIndex > 0) {
209
+ if (hasDedicatedMessage || buffer.message_values?.[row] !== undefined) {
210
+ throw new Error(`Global row ${row} has conflicting static and raw message storage`);
211
+ }
212
+ dictionaryIndex = encodedDenseIndex - 1;
213
+ }
214
+ }
215
+ else {
216
+ const rowHeaders = buffer._rowHeaders;
217
+ if (rowHeaders === undefined)
218
+ throw new TypeError('Packed message layout is missing row headers');
219
+ const header = rowHeaders[row];
220
+ entryType = header & 0xff;
221
+ const encodedDenseIndex = header >>> 8;
222
+ if (encodedDenseIndex !== 0) {
223
+ if (hasDedicatedMessage) {
224
+ throw new Error(`Packed row ${row} has conflicting static and dedicated message storage`);
225
+ }
226
+ dictionaryIndex = encodedDenseIndex - 1;
227
+ }
228
+ }
229
+ const dedicatedDenseIndex = row === 0 && typeof buffer._spanName === 'number' ? buffer._spanName : undefined;
230
+ if (dedicatedDenseIndex !== undefined) {
231
+ if (buffer.message_values?.[row] !== undefined) {
232
+ throw new Error(`Row ${row} has conflicting dedicated static and raw message storage`);
233
+ }
234
+ dictionaryIndex ??= dedicatedDenseIndex;
235
+ }
236
+ if (entryTypeIndices !== undefined)
237
+ entryTypeIndices[outputRow] = entryType;
238
+ if (dictionaryIndex !== undefined) {
239
+ assertStaticMessageKind(buffer, row, entryType, dictionaryIndex);
240
+ }
241
+ else {
242
+ const message = row === 0 && typeof buffer._spanName === 'string'
243
+ ? buffer._spanName
244
+ : row === 1 && buffer._terminalMessage !== undefined
245
+ ? buffer._terminalMessage
246
+ : buffer.message_values?.[row];
247
+ if (message === undefined) {
248
+ nullCount++;
249
+ continue;
250
+ }
251
+ const staticIndex = prefix.valueToDenseIndex.get(message);
252
+ if (staticIndex !== undefined) {
253
+ dictionaryIndex = staticIndex;
254
+ }
255
+ else {
256
+ let dynamicIndex = suffixIndices?.get(message);
257
+ if (dynamicIndex === undefined) {
258
+ suffixValues ??= [];
259
+ suffixIndices ??= new Map();
260
+ dynamicIndex = suffixValues.length;
261
+ suffixValues.push(message);
262
+ suffixIndices.set(message, dynamicIndex);
263
+ }
264
+ dictionaryIndex = prefix.length + dynamicIndex;
265
+ }
266
+ }
267
+ indices[outputRow] = dictionaryIndex;
268
+ nullBitmap[outputRow >>> 3] |= 1 << (outputRow & 7);
269
+ }
270
+ rowOffset += buffer._writeIndex;
271
+ }
272
+ let messageDictionary = prefix.column;
273
+ if (suffixValues !== undefined) {
274
+ const encoded = globalUtf8Cache.encodeMany(suffixValues);
275
+ const suffix = buildColumn(buildData({
276
+ type: utf8(),
277
+ offset: 0,
278
+ length: suffixValues.length,
279
+ nullCount: 0,
280
+ valueOffsets: encoded.offsets,
281
+ data: encoded.data,
282
+ }));
283
+ messageDictionary = appendVocabularyDictionarySuffix(prefix, suffix, suffixValues);
284
+ }
285
+ return {
286
+ column: buildColumn(buildData({
287
+ type: dictionaryId === undefined ? dictionary(utf8(), uint32()) : dictionary(utf8(), uint32(), false, dictionaryId),
288
+ offset: 0,
289
+ length: totalRows,
290
+ nullCount,
291
+ data: indices,
292
+ nullBitmap: nullCount > 0 ? nullBitmap : undefined,
293
+ dictionary: messageDictionary,
294
+ })),
295
+ entryTypeIndices,
296
+ };
297
+ }
131
298
  // ═══════════════════════════════════════════════════════════════════════════════
132
299
  // Shared metadata column helpers (used by both buildMetadataColumnsWithFields
133
300
  // and convertBuffersWithSharedDicts to avoid duplication)
@@ -209,57 +376,73 @@ function buildNonDictIdentityColumns(buffers, totalRows, fields, vectors) {
209
376
  nullBitmap: parentSpanIdNullCount > 0 ? parentSpanIdNulls : undefined,
210
377
  })));
211
378
  }
212
- /**
213
- * Build timestamp column from buffers.
214
- */
215
- function buildTimestampColumn(buffers, totalRows, fields, vectors) {
379
+ function buildTimestampColumn(buffers, totalRows, fields, vectors, borrowChunks = false) {
380
+ const timestampType = timestamp(TimeUnit.NANOSECOND);
381
+ fields.push('timestamp');
382
+ if (borrowChunks) {
383
+ const chunks = buffers.map((buffer) => buildColumn(buildData({
384
+ type: timestampType,
385
+ offset: 0,
386
+ length: buffer._writeIndex,
387
+ nullCount: 0,
388
+ data: buffer.timestamp.subarray(0, buffer._writeIndex),
389
+ })));
390
+ vectors.push(combineColumnChunks(chunks));
391
+ return;
392
+ }
216
393
  const allTimestamps = new BigInt64Array(totalRows);
217
394
  let offset = 0;
218
- for (const buf of buffers) {
219
- if (!buf.timestamp) {
220
- throw new Error(`Buffer missing timestamps property (_writeIndex: ${buf._writeIndex})`);
221
- }
222
- allTimestamps.set(buf.timestamp.subarray(0, buf._writeIndex), offset);
223
- offset += buf._writeIndex;
395
+ for (const buffer of buffers) {
396
+ allTimestamps.set(buffer.timestamp.subarray(0, buffer._writeIndex), offset);
397
+ offset += buffer._writeIndex;
224
398
  }
225
- fields.push('timestamp');
226
- vectors.push(buildColumn(buildData({
227
- type: timestamp(TimeUnit.NANOSECOND),
228
- offset: 0,
229
- length: totalRows,
230
- nullCount: 0,
231
- data: allTimestamps,
232
- })));
399
+ vectors.push(buildColumn(buildData({ type: timestampType, offset: 0, length: totalRows, nullCount: 0, data: allTimestamps })));
233
400
  }
234
- /**
235
- * Build entry_type dictionary column.
236
- */
237
- function buildEntryTypeColumn(buffers, totalRows, fields, vectors, dictType) {
238
- const entryTypeIndices = new Int8Array(totalRows);
239
- let offset = 0;
240
- for (const buf of buffers) {
241
- if (!buf.entry_type) {
242
- throw new Error(`Buffer missing operations property (_writeIndex: ${buf._writeIndex})`);
401
+ function canBorrowEntryTypeChunks(buffers, borrowChunks) {
402
+ return (borrowChunks &&
403
+ buffers.every((buffer) => {
404
+ const declared = buffer._opMetadata._physicalLayoutPlan?.arrowExposure.entryTypeStorage;
405
+ return declared === 'borrowed-u8' || (declared === undefined && buffer.entry_type !== undefined);
406
+ }));
407
+ }
408
+ function buildEntryTypeColumn(buffers, totalRows, fields, vectors, dictType, borrowChunks = false, classifiedEntryTypes) {
409
+ const entryTypeDictionary = PREBUILT_ENTRY_TYPE_DICTIONARY;
410
+ const type = dictType ?? dictionary(utf8(), int8());
411
+ fields.push('entry_type');
412
+ if (canBorrowEntryTypeChunks(buffers, borrowChunks)) {
413
+ const chunks = buffers.map((buffer) => {
414
+ const source = buffer.entry_type;
415
+ if (source === undefined)
416
+ throw new TypeError('Current/specialized buffer is missing entry types');
417
+ const values = new Int8Array(source.buffer, source.byteOffset, buffer._writeIndex);
418
+ return buildColumn(buildData({
419
+ type,
420
+ offset: 0,
421
+ length: buffer._writeIndex,
422
+ nullCount: 0,
423
+ data: values,
424
+ dictionary: entryTypeDictionary,
425
+ }));
426
+ });
427
+ vectors.push(combineColumnChunks(chunks));
428
+ return;
429
+ }
430
+ const entryTypeIndices = classifiedEntryTypes ?? new Int8Array(totalRows);
431
+ if (classifiedEntryTypes === undefined) {
432
+ let offset = 0;
433
+ for (const buffer of buffers) {
434
+ for (let row = 0; row < buffer._writeIndex; row++) {
435
+ entryTypeIndices[offset++] = resolveEntryType(buffer, row);
436
+ }
243
437
  }
244
- entryTypeIndices.set(buf.entry_type.subarray(0, buf._writeIndex), offset);
245
- offset += buf._writeIndex;
246
438
  }
247
- const entryTypeDictData = buildData({
248
- type: utf8(),
249
- offset: 0,
250
- length: ENTRY_TYPE_NAMES.length,
251
- nullCount: 0,
252
- valueOffsets: calculateUtf8Offsets(ENTRY_TYPE_NAMES),
253
- data: encodeUtf8Strings(ENTRY_TYPE_NAMES),
254
- });
255
- fields.push('entry_type');
256
439
  vectors.push(buildColumn(buildData({
257
- type: dictType ?? dictionary(utf8(), int8()),
440
+ type,
258
441
  offset: 0,
259
442
  length: totalRows,
260
443
  nullCount: 0,
261
444
  data: entryTypeIndices,
262
- dictionary: buildColumn(entryTypeDictData),
445
+ dictionary: entryTypeDictionary,
263
446
  })));
264
447
  }
265
448
  /**
@@ -305,13 +488,14 @@ function buildPerBufferDictColumn(buffers, totalRows, fieldName, metadataGetter,
305
488
  * Binary columns store arbitrary bytes (raw Uint8Array or encoder-encoded objects).
306
489
  * At flush time, the encoder (if present) transforms stored values into Uint8Array.
307
490
  */
308
- function buildBinaryColumnFromBuffers(buffers, columnName, totalRows, fieldSchema) {
491
+ function buildBinaryColumnFromBuffers(buffers, columnName, totalRows, fieldSchema, remaps) {
309
492
  const encoder = getBinaryEncoder(fieldSchema);
310
493
  // Collect all values from buffers, encode each if encoder is present
311
494
  const allValues = [];
312
495
  for (const buf of buffers) {
313
- const col = getAllocatedBinaryColumn(buf, columnName);
314
- const srcNulls = buf.getNullsIfAllocated(columnName);
496
+ const col = getAllocatedBinaryColumn(buf, columnName, remaps);
497
+ const sourceName = remaps ? resolveColumnName(buf, columnName, remaps) : columnName;
498
+ const srcNulls = buf.getNullsIfAllocated(sourceName);
315
499
  for (let i = 0; i < buf._writeIndex; i++) {
316
500
  if (srcNulls) {
317
501
  const isValid = (srcNulls[i >>> 3] & (1 << (i & 7))) !== 0;
@@ -388,14 +572,14 @@ function buildBinaryColumnFromBuffers(buffers, columnName, totalRows, fieldSchem
388
572
  * Dictionaries are built from the data in this batch only and are not shared
389
573
  * with other batches, even when combined into a Table.
390
574
  */
391
- export function convertToTable(buffer, systemColumnBuilder) {
575
+ export function convertToTable(buffer, systemColumnBuilder, borrowChunks = false) {
392
576
  const buffers = [];
393
577
  let currentBuffer = buffer;
394
578
  while (currentBuffer) {
395
579
  buffers.push(currentBuffer);
396
580
  currentBuffer = currentBuffer._overflow;
397
581
  }
398
- return convertBuffersToTable(buffers, systemColumnBuilder);
582
+ return convertBuffersToTable(buffers, systemColumnBuilder, borrowChunks);
399
583
  }
400
584
  /**
401
585
  * Convert multiple buffers to a single table.
@@ -404,13 +588,14 @@ export function convertToTable(buffer, systemColumnBuilder) {
404
588
  * Dictionaries are built from the data in this batch only and are not shared
405
589
  * with other batches, even when combined into a Table.
406
590
  */
407
- function convertBuffersToTable(buffers, systemColumnBuilder) {
591
+ function convertBuffersToTable(buffers, systemColumnBuilder, borrowChunks = false) {
408
592
  if (buffers.length === 0)
409
593
  return tableFromColumns({});
410
594
  const totalRows = buffers.reduce((sum, buf) => sum + buf._writeIndex, 0);
411
595
  if (totalRows === 0)
412
596
  return tableFromColumns({});
413
597
  const schema = buffers[0]._logSchema;
598
+ const enumLookup = resolveEnumLookupDescriptor(schema);
414
599
  // Build vectors first, then derive schema from them
415
600
  // This ensures Field types and vector data types are identical (Arrow IPC requirement)
416
601
  const fields = [];
@@ -421,46 +606,16 @@ function convertBuffersToTable(buffers, systemColumnBuilder) {
421
606
  vectors.push(...systemColumns.vectors);
422
607
  }
423
608
  else {
424
- // Build metadata columns - returns both fields and vectors with matching types
425
- const metadataResult = buildMetadataColumnsWithFields(buffers, totalRows);
609
+ const borrowEntryTypes = canBorrowEntryTypeChunks(buffers, borrowChunks);
610
+ const messageResult = buildMessageColumn(buffers, totalRows, undefined, !borrowEntryTypes);
611
+ // Build metadata columns - returns both fields and vectors with matching types.
612
+ const metadataResult = buildMetadataColumnsWithFields(buffers, totalRows, borrowChunks, messageResult.entryTypeIndices);
426
613
  fields.push(...metadataResult.fields);
427
614
  vectors.push(...metadataResult.vectors);
428
- // System attribute column: message (eager category)
429
- const maskTransform = undefined; // message has no masking
430
- let { dictionary: dictValues, indices, arrowIndexType, nullBitmap, } = buildSortedCategoryDictionary(buffers, 'message', maskTransform);
431
- // Ensure dictionary has at least one entry (empty string) if no messages were written
432
- if (dictValues.length === 0) {
433
- dictValues = [''];
434
- }
435
- // Ensure nullBitmap is all 1s (all valid) for eager column
436
- if (!nullBitmap) {
437
- const bitmapBytes = Math.ceil(totalRows / 8);
438
- nullBitmap = new Uint8Array(bitmapBytes);
439
- nullBitmap.fill(0xff);
440
- }
441
- // CRITICAL: Create ONE Dictionary type instance for field and batch data
442
- // Arrow Schema validates that fields with same dictionary ID have identical type references
443
- const messageDictType = dictionary(utf8(), arrowIndexType);
615
+ // Static vocabulary values form an immutable prefix. Only raw messages
616
+ // missing from that prefix are interned into a first-seen suffix.
444
617
  fields.push('message');
445
- const { data: messageUtf8Data, offsets: messageUtf8Offsets } = globalUtf8Cache.encodeMany(dictValues);
446
- const messageDictData = buildData({
447
- type: utf8(),
448
- offset: 0,
449
- length: dictValues.length,
450
- nullCount: 0,
451
- valueOffsets: messageUtf8Offsets,
452
- data: messageUtf8Data,
453
- });
454
- const messageData = buildData({
455
- type: messageDictType,
456
- offset: 0,
457
- length: totalRows,
458
- nullCount: 0, // message is eager, never null
459
- data: indices,
460
- nullBitmap, // Always include nullBitmap for message (even though nullCount is 0)
461
- dictionary: buildColumn(messageDictData),
462
- });
463
- vectors.push(buildColumn(messageData));
618
+ vectors.push(messageResult.column);
464
619
  }
465
620
  // Build user attribute vectors
466
621
  // Skip system schema fields - they are handled separately as system columns
@@ -471,17 +626,19 @@ function convertBuffersToTable(buffers, systemColumnBuilder) {
471
626
  const arrowFieldName = getArrowFieldName(fieldName);
472
627
  const columnName = fieldName; // User columns have no prefix
473
628
  if (lmaoType === 'enum') {
474
- const enumValues = getEnumValues(fieldSchema) || [];
629
+ const descriptor = enumLookup.byField[fieldName];
630
+ if (!descriptor)
631
+ throw new TypeError(`Enum lookup missing for field '${fieldName}'`);
632
+ const enumValues = descriptor.values;
475
633
  const enumUtf8 = getEnumUtf8(fieldSchema);
476
- // Get constructors from schema metadata
477
- const indexArrayCtor = getArrowIndexArrayConstructorOr(fieldSchema);
478
- const arrowIndexType = getArrowIndexTypeOr(fieldSchema, uint8());
634
+ const indexArrayCtor = descriptor.indexArrayConstructor;
635
+ const arrowIndexType = indexTypeForCount(enumValues.length);
479
636
  // Collect value arrays - need to handle different TypedArray types
480
637
  const valueArrays = [];
481
638
  for (const buf of buffers) {
482
639
  const column = buf.getColumnIfAllocated(columnName);
483
640
  if (column && column instanceof indexArrayCtor) {
484
- valueArrays.push(column.subarray(0, buf._writeIndex));
641
+ valueArrays.push(column.subarray(0, buf._writeIndex).slice());
485
642
  }
486
643
  else {
487
644
  valueArrays.push(new indexArrayCtor(buf._writeIndex));
@@ -589,29 +746,46 @@ function convertBuffersToTable(buffers, systemColumnBuilder) {
589
746
  }
590
747
  }
591
748
  else if (lmaoType === 'number') {
592
- const valueArrays = [];
593
- for (const buf of buffers) {
594
- const column = buf.getColumnIfAllocated(columnName);
595
- if (column && column instanceof Float64Array) {
596
- valueArrays.push(column.subarray(0, buf._writeIndex));
597
- }
598
- else {
599
- valueArrays.push(new F64Array(buf._writeIndex));
600
- }
601
- }
602
- const allValues = concatenateFloat64Arrays(valueArrays);
603
- const { nullBitmap, nullCount } = concatenateNullBitmaps(buffers, columnName);
604
749
  const numberType = float64();
605
750
  fields.push(arrowFieldName);
606
- const numberData = buildData({
607
- type: numberType,
608
- offset: 0,
609
- length: totalRows,
610
- nullCount,
611
- data: allValues,
612
- nullBitmap,
613
- });
614
- vectors.push(buildColumn(numberData));
751
+ const borrowed = borrowChunks
752
+ ? buffers.map((buffer) => {
753
+ const column = buffer.getColumnIfAllocated(columnName);
754
+ const validity = buffer.getNullsIfAllocated(columnName);
755
+ if (!(column instanceof Float64Array) || !(validity instanceof Uint8Array))
756
+ return undefined;
757
+ return buildColumn(buildData({
758
+ type: numberType,
759
+ offset: 0,
760
+ length: buffer._writeIndex,
761
+ nullCount: countNulls(validity, buffer._writeIndex),
762
+ data: column.subarray(0, buffer._writeIndex),
763
+ nullBitmap: validity.subarray(0, Math.ceil(buffer._writeIndex / 8)),
764
+ }));
765
+ })
766
+ : undefined;
767
+ if (borrowed?.every((chunk) => chunk !== undefined)) {
768
+ vectors.push(combineColumnChunks(borrowed));
769
+ }
770
+ else {
771
+ const valueArrays = [];
772
+ for (const buffer of buffers) {
773
+ const column = buffer.getColumnIfAllocated(columnName);
774
+ valueArrays.push(column instanceof Float64Array
775
+ ? column.subarray(0, buffer._writeIndex).slice()
776
+ : new F64Array(buffer._writeIndex));
777
+ }
778
+ const allValues = concatenateFloat64Arrays(valueArrays);
779
+ const { nullBitmap, nullCount } = concatenateNullBitmaps(buffers, columnName);
780
+ vectors.push(buildColumn(buildData({
781
+ type: numberType,
782
+ offset: 0,
783
+ length: totalRows,
784
+ nullCount,
785
+ data: allValues,
786
+ nullBitmap,
787
+ })));
788
+ }
615
789
  }
616
790
  else if (lmaoType === 'boolean') {
617
791
  const valueArrays = [];
@@ -619,7 +793,7 @@ function convertBuffersToTable(buffers, systemColumnBuilder) {
619
793
  const column = buf.getColumnIfAllocated(columnName);
620
794
  if (column && column instanceof Uint8Array) {
621
795
  const requiredBytes = Math.ceil(buf._writeIndex / 8);
622
- valueArrays.push(column.subarray(0, requiredBytes));
796
+ valueArrays.push(column.subarray(0, requiredBytes).slice());
623
797
  }
624
798
  else {
625
799
  const requiredBytes = Math.ceil(buf._writeIndex / 8);
@@ -655,13 +829,13 @@ function convertBuffersToTable(buffers, systemColumnBuilder) {
655
829
  * This is required for Arrow IPC serialization - Schema validates that
656
830
  * fields with same dictionary ID have identical type object references.
657
831
  */
658
- function buildMetadataColumnsWithFields(buffers, totalRows) {
832
+ function buildMetadataColumnsWithFields(buffers, totalRows, borrowChunks = false, classifiedEntryTypes) {
659
833
  const fields = [];
660
834
  const vectors = [];
661
835
  // Column order: timestamp, trace_id, thread_id, span_id, parent_thread_id, parent_span_id,
662
836
  // entry_type, package_name, package_file, git_sha
663
837
  // 1. Timestamp
664
- buildTimestampColumn(buffers, totalRows, fields, vectors);
838
+ buildTimestampColumn(buffers, totalRows, fields, vectors, borrowChunks);
665
839
  // 2. Trace ID dictionary (built inline from buffers)
666
840
  const traceIdSet = new Set();
667
841
  for (const buf of buffers)
@@ -700,7 +874,7 @@ function buildMetadataColumnsWithFields(buffers, totalRows) {
700
874
  // 3-6. thread_id, span_id, parent_thread_id, parent_span_id
701
875
  buildNonDictIdentityColumns(buffers, totalRows, fields, vectors);
702
876
  // 7. Entry type
703
- buildEntryTypeColumn(buffers, totalRows, fields, vectors);
877
+ buildEntryTypeColumn(buffers, totalRows, fields, vectors, undefined, borrowChunks, classifiedEntryTypes);
704
878
  // 8-10. Per-buffer metadata columns (package_name, package_file, git_sha)
705
879
  for (const [fieldName, metadataGetter] of [
706
880
  ['package_name', (m) => m.package_name],
@@ -721,6 +895,43 @@ function buildMetadataColumnsWithFields(buffers, totalRows) {
721
895
  }
722
896
  return { fields, vectors };
723
897
  }
898
+ function isEpochPinnableBuffer(buffer) {
899
+ if (!('_allocator' in buffer))
900
+ return false;
901
+ const allocator = Reflect.get(buffer, '_allocator');
902
+ return (typeof allocator === 'object' &&
903
+ allocator !== null &&
904
+ typeof Reflect.get(allocator, 'pinMemoryEpoch') === 'function');
905
+ }
906
+ function createLeasedConversion(root, buffers, build) {
907
+ const topology = root._traceRoot._topology;
908
+ topology.assertLive(root);
909
+ const releaseTopology = topology.acquireLease();
910
+ const generations = new Set();
911
+ const allocators = new Set();
912
+ for (const buffer of buffers) {
913
+ generations.add(buffer._vocabularyGeneration);
914
+ if (isEpochPinnableBuffer(buffer))
915
+ allocators.add(buffer._allocator);
916
+ }
917
+ const epochPins = Array.from(allocators, (allocator) => allocator.pinMemoryEpoch());
918
+ // WASM linear-memory growth cannot preserve borrowed JS TypedArray views, so
919
+ // its leased result uses the existing Arrow-owned copy path. JS heap buffers
920
+ // expose their physical chunks directly under the topology lease.
921
+ const borrowChunks = epochPins.length === 0;
922
+ try {
923
+ const table = build(borrowChunks);
924
+ for (const pin of epochPins)
925
+ pin.release();
926
+ return createArrowLease(table, [...buffers, ...generations], [releaseTopology]);
927
+ }
928
+ catch (error) {
929
+ for (const pin of epochPins)
930
+ pin.release();
931
+ releaseTopology();
932
+ throw error;
933
+ }
934
+ }
724
935
  /**
725
936
  * Convert SpanBuffer (and its overflow chain) to Arrow Table
726
937
  */
@@ -730,6 +941,19 @@ export function convertToArrowTable(buffer, systemColumnBuilder) {
730
941
  return tableFromColumns({});
731
942
  return batch;
732
943
  }
944
+ /** Borrow JS physical Arrow chunks until the returned lease is released. */
945
+ export function convertToLeasedArrowTable(buffer, systemColumnBuilder) {
946
+ const buffers = [];
947
+ let segment = buffer;
948
+ while (segment) {
949
+ buffers.push(segment);
950
+ segment = segment._overflow;
951
+ }
952
+ return createLeasedConversion(buffer, buffers, (borrowChunks) => {
953
+ const table = convertToTable(buffer, systemColumnBuilder, borrowChunks);
954
+ return table.numRows === 0 ? tableFromColumns({}) : table;
955
+ });
956
+ }
733
957
  // ═══════════════════════════════════════════════════════════════════════════════
734
958
  // Tree Conversion with Shared Dictionaries
735
959
  // ═══════════════════════════════════════════════════════════════════════════════
@@ -740,25 +964,35 @@ export function convertToArrowTable(buffer, systemColumnBuilder) {
740
964
  * - Pass 1: Walk tree, build dictionaries (collect unique strings, cache UTF-8)
741
965
  * - Pass 2: Walk tree, convert each buffer to a table using shared dictionaries
742
966
  */
743
- export function convertSpanTreeToArrowTable(rootBuffer, _systemColumnBuilder, modulesToLogStats, periodStartNs) {
967
+ export function convertSpanTreeToArrowTable(rootBuffer, _systemColumnBuilder, modulesToLogStats, periodStartNs, borrowChunks = false) {
744
968
  // ═══════════════════════════════════════════════════════════════════════════
745
969
  // PASS 0: Collect ALL unique schema fields from ALL buffers in the tree
746
970
  // Per specs/lmao/01k_tree_walker_and_arrow_conversion.md - child spans may have different schemas
747
971
  // (e.g., library prefixed schemas like db_query, http_status, etc.)
748
972
  // ═══════════════════════════════════════════════════════════════════════════
749
973
  const mergedSchemaFields = new Map();
750
- walkSpanTree(rootBuffer, (buffer) => {
751
- // Use buffer._columns directly - works for both SpanBuffer and RemappedBufferView
752
- const fields = buffer._columns;
753
- for (const [fieldName, fieldSchema] of fields) {
754
- // Skip system schema fields - they are handled separately as system columns
755
- // (message, line, error_code, exception_stack, ff_value, uint64_value)
756
- if (SYSTEM_SCHEMA_FIELD_NAMES.has(fieldName)) {
757
- continue;
974
+ const remaps = new WeakMap();
975
+ const enumLookupsByField = new Map();
976
+ walkSpanTree(rootBuffer, (buffer, remapDescriptor) => {
977
+ const enumLookup = resolveEnumLookupDescriptor(buffer._logSchema);
978
+ if (remapDescriptor) {
979
+ remaps.set(buffer, remapDescriptor);
980
+ for (const [fieldName, sourceName, fieldSchema] of remapDescriptor.columns) {
981
+ if (!SYSTEM_SCHEMA_FIELD_NAMES.has(fieldName) && !mergedSchemaFields.has(fieldName)) {
982
+ mergedSchemaFields.set(fieldName, fieldSchema);
983
+ const descriptor = enumLookup.byField[sourceName];
984
+ if (descriptor)
985
+ enumLookupsByField.set(fieldName, descriptor);
986
+ }
758
987
  }
759
- // Only add if not already present (first buffer with this field wins)
760
- if (!mergedSchemaFields.has(fieldName)) {
988
+ return;
989
+ }
990
+ for (const [fieldName, fieldSchema] of buffer._columns) {
991
+ if (!SYSTEM_SCHEMA_FIELD_NAMES.has(fieldName) && !mergedSchemaFields.has(fieldName)) {
761
992
  mergedSchemaFields.set(fieldName, fieldSchema);
993
+ const descriptor = enumLookup.byField[fieldName];
994
+ if (descriptor)
995
+ enumLookupsByField.set(fieldName, descriptor);
762
996
  }
763
997
  }
764
998
  });
@@ -790,11 +1024,6 @@ export function convertSpanTreeToArrowTable(rootBuffer, _systemColumnBuilder, mo
790
1024
  textOriginalToMasked.set(fieldName, new Map());
791
1025
  }
792
1026
  }
793
- // System attribute column: message (eager category, not in user schema)
794
- // message is always present (eager allocation), so we need to handle it explicitly
795
- categoryBuilders.set('message', new DictBuilder(globalUtf8Cache));
796
- categoryMaskTransforms.set('message', undefined); // message has no masking
797
- categoryOriginalToMasked.set('message', new Map());
798
1027
  // Metadata column dictionaries
799
1028
  // For traceId (metadata), use DictionaryBuilder (values not pre-encoded)
800
1029
  const traceIdBuilder = new DictBuilder(globalUtf8Cache);
@@ -812,12 +1041,12 @@ export function convertSpanTreeToArrowTable(rootBuffer, _systemColumnBuilder, mo
812
1041
  uniqueModules.add(buffer._callsiteMetadata);
813
1042
  }
814
1043
  for (const [fieldName, builder] of categoryBuilders) {
815
- const col = getAllocatedStringColumn(buffer, fieldName);
816
1044
  const maskTransform = categoryMaskTransforms.get(fieldName);
817
1045
  const originalToMasked = categoryOriginalToMasked.get(fieldName);
818
1046
  if (!originalToMasked) {
819
1047
  throw new Error(`Category originalToMasked map not found for field: ${fieldName}`);
820
1048
  }
1049
+ const col = getAllocatedStringColumn(buffer, fieldName, remaps);
821
1050
  if (col) {
822
1051
  for (let i = 0; i < buffer._writeIndex; i++) {
823
1052
  const originalValue = col[i];
@@ -833,7 +1062,7 @@ export function convertSpanTreeToArrowTable(rootBuffer, _systemColumnBuilder, mo
833
1062
  }
834
1063
  // Also add scope values to dictionary (they may not be in columns)
835
1064
  // Per specs/lmao/01i_span_scope_attributes.md: scope values fill NULL cells at Arrow conversion
836
- const scopeValue = getStringScopeValue(buffer, fieldName);
1065
+ const scopeValue = getStringScopeValue(buffer, fieldName, remaps);
837
1066
  if (scopeValue !== undefined) {
838
1067
  let maskedScopeValue = originalToMasked.get(scopeValue);
839
1068
  if (maskedScopeValue === undefined) {
@@ -844,7 +1073,7 @@ export function convertSpanTreeToArrowTable(rootBuffer, _systemColumnBuilder, mo
844
1073
  }
845
1074
  }
846
1075
  for (const [fieldName, builder] of textBuilders) {
847
- const col = getAllocatedStringColumn(buffer, fieldName);
1076
+ const col = getAllocatedStringColumn(buffer, fieldName, remaps);
848
1077
  const maskTransform = textMaskTransforms.get(fieldName);
849
1078
  const originalToMasked = textOriginalToMasked.get(fieldName);
850
1079
  if (!originalToMasked) {
@@ -865,7 +1094,7 @@ export function convertSpanTreeToArrowTable(rootBuffer, _systemColumnBuilder, mo
865
1094
  }
866
1095
  // Also add scope values to dictionary (they may not be in columns)
867
1096
  // Per specs/lmao/01i_span_scope_attributes.md: scope values fill NULL cells at Arrow conversion
868
- const scopeValue = getStringScopeValue(buffer, fieldName);
1097
+ const scopeValue = getStringScopeValue(buffer, fieldName, remaps);
869
1098
  if (scopeValue !== undefined) {
870
1099
  let maskedScopeValue = originalToMasked.get(scopeValue);
871
1100
  if (maskedScopeValue === undefined) {
@@ -909,14 +1138,6 @@ export function convertSpanTreeToArrowTable(rootBuffer, _systemColumnBuilder, mo
909
1138
  for (const [name, builder] of textBuilders) {
910
1139
  textDicts.set(name, builder.finalize(false)); // not sorted
911
1140
  }
912
- // Get messageDict from categoryDicts (message is handled as a category column)
913
- // If no messages were written, create an empty dictionary
914
- let messageDict = categoryDicts.get('message');
915
- if (!messageDict) {
916
- const messageBuilder = new DictBuilder(globalUtf8Cache);
917
- messageBuilder.add(''); // Single empty string entry
918
- messageDict = messageBuilder.finalize(true); // sorted
919
- }
920
1141
  // ═══════════════════════════════════════════════════════════════════════════
921
1142
  // PASS 2: Collect all buffers with non-zero rows
922
1143
  // ═══════════════════════════════════════════════════════════════════════════
@@ -928,7 +1149,7 @@ export function convertSpanTreeToArrowTable(rootBuffer, _systemColumnBuilder, mo
928
1149
  });
929
1150
  // Build a single table from all buffers
930
1151
  const batch = allBuffers.length > 0
931
- ? convertBuffersWithSharedDicts(allBuffers, traceIdDict, packageDict, packagePathDict, gitShaDict, categoryDicts, textDicts, schemaFields, categoryOriginalToMasked, textOriginalToMasked)
1152
+ ? convertBuffersWithSharedDicts(allBuffers, traceIdDict, packageDict, packagePathDict, gitShaDict, categoryDicts, textDicts, schemaFields, enumLookupsByField, categoryOriginalToMasked, textOriginalToMasked, remaps, borrowChunks)
932
1153
  : undefined;
933
1154
  // Create capacity stats table if needed
934
1155
  if (modulesToLogStats && modulesToLogStats.length > 0) {
@@ -946,6 +1167,15 @@ export function convertSpanTreeToArrowTable(rootBuffer, _systemColumnBuilder, mo
946
1167
  }
947
1168
  return tableFromColumns({});
948
1169
  }
1170
+ /** Convert a logical trace tree while pinning every borrowed physical chunk. */
1171
+ export function convertSpanTreeToLeasedArrowTable(rootBuffer, systemColumnBuilder, modulesToLogStats, periodStartNs) {
1172
+ const buffers = [];
1173
+ walkSpanTree(rootBuffer, (buffer) => {
1174
+ if (buffer._writeIndex > 0)
1175
+ buffers.push(buffer);
1176
+ });
1177
+ return createLeasedConversion(rootBuffer, buffers, (borrowChunks) => convertSpanTreeToArrowTable(rootBuffer, systemColumnBuilder, modulesToLogStats, periodStartNs, borrowChunks));
1178
+ }
949
1179
  /**
950
1180
  * Convert multiple buffers to a single table using pre-built dictionaries.
951
1181
  *
@@ -954,10 +1184,12 @@ export function convertSpanTreeToArrowTable(rootBuffer, _systemColumnBuilder, mo
954
1184
  * a single table. The resulting batches contain their own dictionary data and
955
1185
  * are not shared with other batches, even when combined into a Table.
956
1186
  */
957
- function convertBuffersWithSharedDicts(buffers, traceIdDict, packageDict, packagePathDict, gitShaDict, categoryDicts, textDicts, schemaFields, categoryOriginalToMasked, textOriginalToMasked) {
1187
+ function convertBuffersWithSharedDicts(buffers, traceIdDict, packageDict, packagePathDict, gitShaDict, categoryDicts, textDicts, schemaFields, enumLookupsByField, categoryOriginalToMasked, textOriginalToMasked, remaps, borrowChunks = false) {
958
1188
  const totalRows = buffers.reduce((sum, buf) => sum + buf._writeIndex, 0);
959
1189
  const fields = [];
960
1190
  const vectors = [];
1191
+ const borrowEntryTypes = canBorrowEntryTypeChunks(buffers, borrowChunks);
1192
+ const messageResult = buildMessageColumn(buffers, totalRows, 5, !borrowEntryTypes);
961
1193
  // Dictionary types with explicit IDs for IPC serialization
962
1194
  const traceIdType = dictionary(utf8(), traceIdDict.arrowIndexType, false, 0);
963
1195
  const entryTypeDictType = dictionary(utf8(), int8(), false, 1);
@@ -967,7 +1199,7 @@ function convertBuffersWithSharedDicts(buffers, traceIdDict, packageDict, packag
967
1199
  // Column order: timestamp, trace_id, thread_id, span_id, parent_thread_id, parent_span_id,
968
1200
  // entry_type, package_name, package_file, git_sha
969
1201
  // 1. Timestamp
970
- buildTimestampColumn(buffers, totalRows, fields, vectors);
1202
+ buildTimestampColumn(buffers, totalRows, fields, vectors, borrowChunks);
971
1203
  // 2. Trace ID (using shared pre-built dictionary)
972
1204
  const traceIdIndices = new traceIdDict.indexArrayCtor(totalRows);
973
1205
  let offset = 0;
@@ -995,7 +1227,7 @@ function convertBuffersWithSharedDicts(buffers, traceIdDict, packageDict, packag
995
1227
  // 3-6. thread_id, span_id, parent_thread_id, parent_span_id
996
1228
  buildNonDictIdentityColumns(buffers, totalRows, fields, vectors);
997
1229
  // 7. Entry type (with explicit dictionary ID for IPC)
998
- buildEntryTypeColumn(buffers, totalRows, fields, vectors, entryTypeDictType);
1230
+ buildEntryTypeColumn(buffers, totalRows, fields, vectors, entryTypeDictType, borrowChunks, messageResult.entryTypeIndices);
999
1231
  // 8-10. Per-buffer metadata columns using shared pre-built dictionaries
1000
1232
  // Per specs/lmao/01c_context_flow_and_op_wrappers.md:
1001
1233
  // - Row 0 (span-start): uses callsiteModule for gitSha/packageName/packagePath
@@ -1012,46 +1244,7 @@ function convertBuffersWithSharedDicts(buffers, traceIdDict, packageDict, packag
1012
1244
  // System attribute and user attribute columns - concatenate from all buffers
1013
1245
  // ═══════════════════════════════════════════════════════════════════════════
1014
1246
  // System attribute column: message (at fixed position 10, before user attributes)
1015
- const messageDict = categoryDicts.get('message');
1016
- const messageOriginalToMasked = categoryOriginalToMasked.get('message');
1017
- if (!messageDict || !messageOriginalToMasked) {
1018
- throw new Error('Message dictionary or mapping not found');
1019
- }
1020
- const messageFieldType = dictionary(utf8(), messageDict.arrowIndexType, false, 5);
1021
- const messageIndices = new messageDict.indexArrayCtor(totalRows);
1022
- const messageNullBitmap = new Uint8Array(Math.ceil(totalRows / 8));
1023
- let messageRowOffset = 0;
1024
- for (const buf of buffers) {
1025
- const col = getAllocatedStringColumn(buf, 'message');
1026
- if (col) {
1027
- for (let i = 0; i < buf._writeIndex; i++) {
1028
- const v = col[i];
1029
- const rowIdx = messageRowOffset + i;
1030
- if (v != null) {
1031
- const maskedValue = messageOriginalToMasked.get(v) ?? v;
1032
- messageIndices[rowIdx] = messageDict.indexMap.get(maskedValue) ?? 0;
1033
- messageNullBitmap[rowIdx >>> 3] |= 1 << (rowIdx & 7);
1034
- }
1035
- }
1036
- }
1037
- messageRowOffset += buf._writeIndex;
1038
- }
1039
- vectors.push(buildColumn(buildData({
1040
- type: messageFieldType,
1041
- offset: 0,
1042
- length: totalRows,
1043
- nullCount: 0, // message is eager, never null
1044
- data: messageIndices,
1045
- nullBitmap: messageNullBitmap,
1046
- dictionary: buildColumn(buildData({
1047
- type: utf8(),
1048
- offset: 0,
1049
- length: messageDict.indexMap.size,
1050
- nullCount: 0,
1051
- valueOffsets: messageDict.offsets,
1052
- data: messageDict.data,
1053
- })),
1054
- })));
1247
+ vectors.push(messageResult.column);
1055
1248
  fields.push('message');
1056
1249
  // System attribute column: uint64_value (for buffer metrics - op durations, counts, etc.)
1057
1250
  // For span data, this is all null - only buffer metrics use it
@@ -1082,9 +1275,9 @@ function convertBuffersWithSharedDicts(buffers, traceIdDict, packageDict, packag
1082
1275
  let rowOffset = 0;
1083
1276
  // Per specs/lmao/01i_span_scope_attributes.md: scope values fill NULL cells at Arrow conversion
1084
1277
  for (const buf of buffers) {
1085
- const col = getAllocatedStringColumn(buf, columnName);
1278
+ const col = getAllocatedStringColumn(buf, columnName, remaps);
1086
1279
  // Check if this buffer has a scope value for this field
1087
- const scopeValue = getStringScopeValue(buf, fieldName);
1280
+ const scopeValue = getStringScopeValue(buf, fieldName, remaps);
1088
1281
  let scopeEncodedValue;
1089
1282
  if (scopeValue !== undefined) {
1090
1283
  const maskedScopeValue = originalToMasked.get(scopeValue) ?? scopeValue;
@@ -1153,9 +1346,9 @@ function convertBuffersWithSharedDicts(buffers, traceIdDict, packageDict, packag
1153
1346
  let nullCount = 0;
1154
1347
  let rowOffset = 0;
1155
1348
  for (const buf of buffers) {
1156
- const col = getAllocatedStringColumn(buf, columnName);
1349
+ const col = getAllocatedStringColumn(buf, columnName, remaps);
1157
1350
  // Check if this buffer has a scope value for this field
1158
- const scopeValue = getStringScopeValue(buf, fieldName);
1351
+ const scopeValue = getStringScopeValue(buf, fieldName, remaps);
1159
1352
  let scopeEncodedValue;
1160
1353
  if (scopeValue !== undefined) {
1161
1354
  const maskedScopeValue = originalToMasked.get(scopeValue) ?? scopeValue;
@@ -1214,76 +1407,101 @@ function convertBuffersWithSharedDicts(buffers, traceIdDict, packageDict, packag
1214
1407
  fields.push(arrowFieldName);
1215
1408
  }
1216
1409
  else if (lmaoType === 'number') {
1217
- const allValues = new F64Array(totalRows);
1218
- const nullBitmap = new Uint8Array(Math.ceil(totalRows / 8));
1219
- // Start with all nulls (bits cleared) - we'll set bits as we find values
1220
- let nullCount = 0;
1221
- let rowOffset = 0;
1222
- for (const buf of buffers) {
1223
- const col = buf.getColumnIfAllocated(columnName);
1224
- const srcNulls = buf.getNullsIfAllocated(columnName);
1225
- // Check if this buffer has a scope value for this field
1226
- const scopeValue = getNumberScopeValue(buf, fieldName);
1227
- if (col instanceof Float64Array) {
1228
- allValues.set(col.subarray(0, buf._writeIndex), rowOffset);
1229
- if (srcNulls) {
1230
- // Process each row - direct write wins, then scope, then null
1231
- for (let i = 0; i < buf._writeIndex; i++) {
1232
- const srcByte = i >>> 3;
1233
- const srcBit = i & 7;
1234
- const isValid = (srcNulls[srcByte] & (1 << srcBit)) !== 0;
1235
- const rowIdx = rowOffset + i;
1236
- if (isValid) {
1237
- // Direct write - mark as valid
1238
- nullBitmap[rowIdx >>> 3] |= 1 << (rowIdx & 7);
1239
- }
1240
- else if (scopeValue !== undefined) {
1241
- // No direct write, but have scope value - use it
1242
- allValues[rowIdx] = scopeValue;
1243
- nullBitmap[rowIdx >>> 3] |= 1 << (rowIdx & 7);
1410
+ const borrowed = borrowChunks
1411
+ ? buffers.map((buffer) => {
1412
+ const sourceName = resolveColumnName(buffer, columnName, remaps);
1413
+ if (getNumberScopeValue(buffer, fieldName, remaps) !== undefined)
1414
+ return undefined;
1415
+ const column = buffer.getColumnIfAllocated(sourceName);
1416
+ const validity = buffer.getNullsIfAllocated(sourceName);
1417
+ if (!(column instanceof Float64Array) || !(validity instanceof Uint8Array))
1418
+ return undefined;
1419
+ return buildColumn(buildData({
1420
+ type: float64(),
1421
+ offset: 0,
1422
+ length: buffer._writeIndex,
1423
+ nullCount: countNulls(validity, buffer._writeIndex),
1424
+ data: column.subarray(0, buffer._writeIndex),
1425
+ nullBitmap: validity.subarray(0, Math.ceil(buffer._writeIndex / 8)),
1426
+ }));
1427
+ })
1428
+ : undefined;
1429
+ if (borrowed?.every((chunk) => chunk !== undefined)) {
1430
+ vectors.push(combineColumnChunks(borrowed));
1431
+ }
1432
+ else {
1433
+ const allValues = new F64Array(totalRows);
1434
+ const nullBitmap = new Uint8Array(Math.ceil(totalRows / 8));
1435
+ // Start with all nulls (bits cleared) - we'll set bits as we find values
1436
+ let nullCount = 0;
1437
+ let rowOffset = 0;
1438
+ for (const buf of buffers) {
1439
+ const sourceName = resolveColumnName(buf, columnName, remaps);
1440
+ const col = buf.getColumnIfAllocated(sourceName);
1441
+ const srcNulls = buf.getNullsIfAllocated(sourceName);
1442
+ // Check if this buffer has a scope value for this field
1443
+ const scopeValue = getNumberScopeValue(buf, fieldName, remaps);
1444
+ if (col instanceof Float64Array) {
1445
+ allValues.set(col.subarray(0, buf._writeIndex), rowOffset);
1446
+ if (srcNulls) {
1447
+ // Process each row - direct write wins, then scope, then null
1448
+ for (let i = 0; i < buf._writeIndex; i++) {
1449
+ const srcByte = i >>> 3;
1450
+ const srcBit = i & 7;
1451
+ const isValid = (srcNulls[srcByte] & (1 << srcBit)) !== 0;
1452
+ const rowIdx = rowOffset + i;
1453
+ if (isValid) {
1454
+ // Direct write - mark as valid
1455
+ nullBitmap[rowIdx >>> 3] |= 1 << (rowIdx & 7);
1456
+ }
1457
+ else if (scopeValue !== undefined) {
1458
+ // No direct write, but have scope value - use it
1459
+ allValues[rowIdx] = scopeValue;
1460
+ nullBitmap[rowIdx >>> 3] |= 1 << (rowIdx & 7);
1461
+ }
1462
+ else {
1463
+ // No direct write and no scope - null
1464
+ nullCount++;
1465
+ }
1244
1466
  }
1245
- else {
1246
- // No direct write and no scope - null
1247
- nullCount++;
1467
+ }
1468
+ else {
1469
+ // No null bitmap means column was allocated but no writes - check scope
1470
+ for (let i = 0; i < buf._writeIndex; i++) {
1471
+ const rowIdx = rowOffset + i;
1472
+ if (scopeValue !== undefined) {
1473
+ allValues[rowIdx] = scopeValue;
1474
+ nullBitmap[rowIdx >>> 3] |= 1 << (rowIdx & 7);
1475
+ }
1476
+ else {
1477
+ nullCount++;
1478
+ }
1248
1479
  }
1249
1480
  }
1250
1481
  }
1251
- else {
1252
- // No null bitmap means column was allocated but no writes - check scope
1482
+ else if (scopeValue !== undefined) {
1483
+ // Column not allocated but we have scope - fill all rows with scope value
1253
1484
  for (let i = 0; i < buf._writeIndex; i++) {
1254
1485
  const rowIdx = rowOffset + i;
1255
- if (scopeValue !== undefined) {
1256
- allValues[rowIdx] = scopeValue;
1257
- nullBitmap[rowIdx >>> 3] |= 1 << (rowIdx & 7);
1258
- }
1259
- else {
1260
- nullCount++;
1261
- }
1486
+ allValues[rowIdx] = scopeValue;
1487
+ nullBitmap[rowIdx >>> 3] |= 1 << (rowIdx & 7);
1262
1488
  }
1263
1489
  }
1264
- }
1265
- else if (scopeValue !== undefined) {
1266
- // Column not allocated but we have scope - fill all rows with scope value
1267
- for (let i = 0; i < buf._writeIndex; i++) {
1268
- const rowIdx = rowOffset + i;
1269
- allValues[rowIdx] = scopeValue;
1270
- nullBitmap[rowIdx >>> 3] |= 1 << (rowIdx & 7);
1490
+ else {
1491
+ // Column doesn't exist for this buffer and no scope - all nulls
1492
+ nullCount += buf._writeIndex;
1271
1493
  }
1494
+ rowOffset += buf._writeIndex;
1272
1495
  }
1273
- else {
1274
- // Column doesn't exist for this buffer and no scope - all nulls
1275
- nullCount += buf._writeIndex;
1276
- }
1277
- rowOffset += buf._writeIndex;
1496
+ vectors.push(buildColumn(buildData({
1497
+ type: float64(),
1498
+ offset: 0,
1499
+ length: totalRows,
1500
+ nullCount,
1501
+ data: allValues,
1502
+ nullBitmap: nullCount > 0 ? nullBitmap : undefined,
1503
+ })));
1278
1504
  }
1279
- vectors.push(buildColumn(buildData({
1280
- type: float64(),
1281
- offset: 0,
1282
- length: totalRows,
1283
- nullCount,
1284
- data: allValues,
1285
- nullBitmap: nullCount > 0 ? nullBitmap : undefined,
1286
- })));
1287
1505
  fields.push(arrowFieldName);
1288
1506
  }
1289
1507
  else if (lmaoType === 'boolean') {
@@ -1294,10 +1512,11 @@ function convertBuffersWithSharedDicts(buffers, traceIdDict, packageDict, packag
1294
1512
  let nullCount = 0;
1295
1513
  let rowOffset = 0;
1296
1514
  for (const buf of buffers) {
1297
- const col = buf.getColumnIfAllocated(columnName);
1298
- const srcNulls = buf.getNullsIfAllocated(columnName);
1515
+ const sourceName = resolveColumnName(buf, columnName, remaps);
1516
+ const col = buf.getColumnIfAllocated(sourceName);
1517
+ const srcNulls = buf.getNullsIfAllocated(sourceName);
1299
1518
  // Check if this buffer has a scope value for this field
1300
- const scopeValue = getBooleanScopeValue(buf, fieldName);
1519
+ const scopeValue = getBooleanScopeValue(buf, fieldName, remaps);
1301
1520
  if (col instanceof Uint8Array) {
1302
1521
  // Copy boolean values bit by bit - can't avoid loop for bit-level operations
1303
1522
  for (let i = 0; i < buf._writeIndex; i++) {
@@ -1367,30 +1586,25 @@ function convertBuffersWithSharedDicts(buffers, traceIdDict, packageDict, packag
1367
1586
  fields.push(arrowFieldName);
1368
1587
  }
1369
1588
  else if (lmaoType === 'enum') {
1370
- const enumValues = getEnumValues(fieldSchema) || [];
1589
+ const descriptor = enumLookupsByField.get(fieldName);
1590
+ if (!descriptor)
1591
+ throw new TypeError(`Enum lookup missing for field '${fieldName}'`);
1592
+ const enumValues = descriptor.values;
1371
1593
  const enumUtf8 = getEnumUtf8(fieldSchema);
1372
- // Get constructors from schema metadata
1373
- const indexArrayCtor = getArrowIndexArrayConstructorOr(fieldSchema);
1374
- const arrowIndexType = getArrowIndexTypeOr(fieldSchema, uint8());
1594
+ const indexArrayCtor = descriptor.indexArrayConstructor;
1595
+ const arrowIndexType = indexTypeForCount(enumValues.length);
1375
1596
  const allIndices = new indexArrayCtor(totalRows);
1376
1597
  const nullBitmap = new Uint8Array(Math.ceil(totalRows / 8));
1377
1598
  // Start with all nulls (bits cleared) - we'll set bits as we find values
1378
1599
  let nullCount = 0;
1379
1600
  let rowOffset = 0;
1380
- // Build enum value to index mapping for scope lookup
1381
- const enumValueToIndex = new Map();
1382
- for (let i = 0; i < enumValues.length; i++) {
1383
- enumValueToIndex.set(enumValues[i], i);
1384
- }
1385
1601
  for (const buf of buffers) {
1386
- const col = buf.getColumnIfAllocated(columnName);
1387
- const srcNulls = buf.getNullsIfAllocated(columnName);
1602
+ const sourceName = resolveColumnName(buf, columnName, remaps);
1603
+ const col = buf.getColumnIfAllocated(sourceName);
1604
+ const srcNulls = buf.getNullsIfAllocated(sourceName);
1388
1605
  // Check if this buffer has a scope value for this field
1389
- const scopeValue = getStringScopeValue(buf, fieldName);
1390
- let scopeEncodedValue;
1391
- if (scopeValue !== undefined) {
1392
- scopeEncodedValue = enumValueToIndex.get(scopeValue);
1393
- }
1606
+ const scopeValue = getStringScopeValue(buf, fieldName, remaps);
1607
+ const scopeEncodedValue = scopeValue === undefined ? undefined : descriptor.encode(scopeValue);
1394
1608
  if (col instanceof indexArrayCtor) {
1395
1609
  allIndices.set(col.subarray(0, buf._writeIndex), rowOffset);
1396
1610
  if (srcNulls) {
@@ -1464,7 +1678,7 @@ function convertBuffersWithSharedDicts(buffers, traceIdDict, packageDict, packag
1464
1678
  else if (lmaoType === 'binary') {
1465
1679
  // Binary columns: raw Uint8Array or encoder-wrapped values (e.g. msgpack)
1466
1680
  // Binary columns don't use dictionaries and are not scope-fillable
1467
- const { column } = buildBinaryColumnFromBuffers(buffers, columnName, totalRows, fieldSchema);
1681
+ const { column } = buildBinaryColumnFromBuffers(buffers, columnName, totalRows, fieldSchema, remaps);
1468
1682
  fields.push(arrowFieldName);
1469
1683
  vectors.push(column);
1470
1684
  }