@smoothbricks/lmao 0.2.2 → 0.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (285) hide show
  1. package/README.md +206 -206
  2. package/dist/allocator.wasm +0 -0
  3. package/dist/index.d.ts +1 -0
  4. package/dist/index.d.ts.map +1 -1
  5. package/dist/index.js +1 -0
  6. package/dist/lib/JsBufferStrategy.d.ts +4 -3
  7. package/dist/lib/JsBufferStrategy.d.ts.map +1 -1
  8. package/dist/lib/JsBufferStrategy.js +18 -11
  9. package/dist/lib/arrow/lease.d.ts +10 -0
  10. package/dist/lib/arrow/lease.d.ts.map +1 -0
  11. package/dist/lib/arrow/lease.js +33 -0
  12. package/dist/lib/arrow/utils.d.ts +3 -4
  13. package/dist/lib/arrow/utils.d.ts.map +1 -1
  14. package/dist/lib/arrow/utils.js +12 -13
  15. package/dist/lib/arrow/vocabularyDictionary.d.ts +21 -0
  16. package/dist/lib/arrow/vocabularyDictionary.d.ts.map +1 -0
  17. package/dist/lib/arrow/vocabularyDictionary.js +95 -0
  18. package/dist/lib/bufferStrategy.d.ts +3 -2
  19. package/dist/lib/bufferStrategy.d.ts.map +1 -1
  20. package/dist/lib/cloudflare/traceRows.d.ts.map +1 -1
  21. package/dist/lib/cloudflare/traceRows.js +3 -2
  22. package/dist/lib/codegen/evaluatorGenerator.d.ts.map +1 -1
  23. package/dist/lib/codegen/evaluatorGenerator.js +12 -11
  24. package/dist/lib/codegen/fixedPositionWriterGenerator.d.ts +44 -82
  25. package/dist/lib/codegen/fixedPositionWriterGenerator.d.ts.map +1 -1
  26. package/dist/lib/codegen/fixedPositionWriterGenerator.js +223 -162
  27. package/dist/lib/codegen/spanLoggerClosureMaterializer.d.ts +63 -0
  28. package/dist/lib/codegen/spanLoggerClosureMaterializer.d.ts.map +1 -0
  29. package/dist/lib/codegen/spanLoggerClosureMaterializer.js +447 -0
  30. package/dist/lib/codegen/spanLoggerGenerator.d.ts +36 -19
  31. package/dist/lib/codegen/spanLoggerGenerator.d.ts.map +1 -1
  32. package/dist/lib/codegen/spanLoggerGenerator.js +164 -158
  33. package/dist/lib/convertToArrow.d.ts +7 -2
  34. package/dist/lib/convertToArrow.d.ts.map +1 -1
  35. package/dist/lib/convertToArrow.js +499 -285
  36. package/dist/lib/defineOpContext.js +1 -1
  37. package/dist/lib/enumMetadata.d.ts +19 -0
  38. package/dist/lib/enumMetadata.d.ts.map +1 -0
  39. package/dist/lib/enumMetadata.js +86 -0
  40. package/dist/lib/flushScheduler.d.ts.map +1 -1
  41. package/dist/lib/flushScheduler.js +13 -4
  42. package/dist/lib/library.d.ts +16 -98
  43. package/dist/lib/library.d.ts.map +1 -1
  44. package/dist/lib/library.js +32 -525
  45. package/dist/lib/logBinding.d.ts +21 -27
  46. package/dist/lib/logBinding.d.ts.map +1 -1
  47. package/dist/lib/op.d.ts +30 -30
  48. package/dist/lib/op.d.ts.map +1 -1
  49. package/dist/lib/op.js +15 -22
  50. package/dist/lib/opContext/createOpGroup.d.ts +1 -1
  51. package/dist/lib/opContext/createOpGroup.d.ts.map +1 -1
  52. package/dist/lib/opContext/createOpGroup.js +22 -29
  53. package/dist/lib/opContext/defineOp.d.ts +4 -4
  54. package/dist/lib/opContext/defineOp.d.ts.map +1 -1
  55. package/dist/lib/opContext/defineOp.js +53 -10
  56. package/dist/lib/opContext/opGroupTypes.d.ts +2 -2
  57. package/dist/lib/opContext/opTypes.d.ts +1 -1
  58. package/dist/lib/opContext/opTypes.d.ts.map +1 -1
  59. package/dist/lib/opContext/spanContextTypes.d.ts +33 -18
  60. package/dist/lib/opContext/spanContextTypes.d.ts.map +1 -1
  61. package/dist/lib/opContext/types.d.ts +4 -4
  62. package/dist/lib/opContext/types.d.ts.map +1 -1
  63. package/dist/lib/physicalLayoutPlan.d.ts +89 -0
  64. package/dist/lib/physicalLayoutPlan.d.ts.map +1 -0
  65. package/dist/lib/physicalLayoutPlan.js +442 -0
  66. package/dist/lib/resolveMessage.d.ts +9 -0
  67. package/dist/lib/resolveMessage.d.ts.map +1 -0
  68. package/dist/lib/resolveMessage.js +63 -0
  69. package/dist/lib/result.d.ts +5 -7
  70. package/dist/lib/result.d.ts.map +1 -1
  71. package/dist/lib/result.js +19 -26
  72. package/dist/lib/runtimeHint.d.ts +35 -0
  73. package/dist/lib/runtimeHint.d.ts.map +1 -0
  74. package/dist/lib/runtimeHint.js +79 -0
  75. package/dist/lib/schema/typeGuards.d.ts +7 -47
  76. package/dist/lib/schema/typeGuards.d.ts.map +1 -1
  77. package/dist/lib/schema/typeGuards.js +7 -123
  78. package/dist/lib/schema/types.d.ts +1 -1
  79. package/dist/lib/schema/types.d.ts.map +1 -1
  80. package/dist/lib/span-buffer/aot/v1.d.ts +2 -0
  81. package/dist/lib/span-buffer/aot/v1.d.ts.map +1 -0
  82. package/dist/lib/span-buffer/aot/v1.js +24 -0
  83. package/dist/lib/spanBuffer.d.ts +33 -4
  84. package/dist/lib/spanBuffer.d.ts.map +1 -1
  85. package/dist/lib/spanBuffer.js +583 -91
  86. package/dist/lib/spanBufferStats.d.ts +1 -1
  87. package/dist/lib/spanBufferStats.d.ts.map +1 -1
  88. package/dist/lib/spanContext.d.ts +27 -51
  89. package/dist/lib/spanContext.d.ts.map +1 -1
  90. package/dist/lib/spanContext.js +822 -344
  91. package/dist/lib/sqlite/sqlite-common.d.ts.map +1 -1
  92. package/dist/lib/sqlite/sqlite-common.js +20 -21
  93. package/dist/lib/testing/bun-harness.d.ts +8 -3
  94. package/dist/lib/testing/bun-harness.d.ts.map +1 -1
  95. package/dist/lib/testing/bun-harness.js +30 -18
  96. package/dist/lib/testing/extractFacts.d.ts +1 -1
  97. package/dist/lib/testing/extractFacts.d.ts.map +1 -1
  98. package/dist/lib/testing/extractFacts.js +55 -63
  99. package/dist/lib/testing/facts.d.ts +1 -1
  100. package/dist/lib/testing/facts.d.ts.map +1 -1
  101. package/dist/lib/testing/queryable-span.d.ts +1 -1
  102. package/dist/lib/testing/queryable-span.d.ts.map +1 -1
  103. package/dist/lib/testing/queryable-span.js +26 -28
  104. package/dist/lib/testing/span-query.d.ts.map +1 -1
  105. package/dist/lib/testing/span-query.js +19 -22
  106. package/dist/lib/testing/stdio-replay.d.ts.map +1 -1
  107. package/dist/lib/testing/stdio-replay.js +19 -8
  108. package/dist/lib/threadId.d.ts.map +1 -1
  109. package/dist/lib/threadId.js +6 -3
  110. package/dist/lib/traceContext.d.ts.map +1 -1
  111. package/dist/lib/traceContext.js +1 -4
  112. package/dist/lib/traceRoot.d.ts +30 -8
  113. package/dist/lib/traceRoot.d.ts.map +1 -1
  114. package/dist/lib/traceRoot.es.d.ts +17 -15
  115. package/dist/lib/traceRoot.es.d.ts.map +1 -1
  116. package/dist/lib/traceRoot.es.js +68 -34
  117. package/dist/lib/traceRoot.js +11 -0
  118. package/dist/lib/traceRoot.node.d.ts +17 -19
  119. package/dist/lib/traceRoot.node.d.ts.map +1 -1
  120. package/dist/lib/traceRoot.node.js +66 -38
  121. package/dist/lib/traceTopology.d.ts +42 -0
  122. package/dist/lib/traceTopology.d.ts.map +1 -0
  123. package/dist/lib/traceTopology.js +260 -0
  124. package/dist/lib/tracer.d.ts +9 -5
  125. package/dist/lib/tracer.d.ts.map +1 -1
  126. package/dist/lib/tracer.js +65 -71
  127. package/dist/lib/tracers/ArrayQueueTracer.d.ts +1 -1
  128. package/dist/lib/tracers/ArrayQueueTracer.js +1 -1
  129. package/dist/lib/tracers/StdioTracer.d.ts.map +1 -1
  130. package/dist/lib/tracers/StdioTracer.js +15 -13
  131. package/dist/lib/tracers/TestTracer.d.ts +4 -6
  132. package/dist/lib/tracers/TestTracer.d.ts.map +1 -1
  133. package/dist/lib/tracers/TestTracer.js +6 -8
  134. package/dist/lib/types.d.ts +49 -32
  135. package/dist/lib/types.d.ts.map +1 -1
  136. package/dist/lib/vocabularyRegistry.d.ts +25 -0
  137. package/dist/lib/vocabularyRegistry.d.ts.map +1 -0
  138. package/dist/lib/vocabularyRegistry.js +372 -0
  139. package/dist/lib/wasm/WasmBufferStrategy.d.ts +6 -10
  140. package/dist/lib/wasm/WasmBufferStrategy.d.ts.map +1 -1
  141. package/dist/lib/wasm/WasmBufferStrategy.js +44 -37
  142. package/dist/lib/wasm/wasmAllocator.d.ts +27 -22
  143. package/dist/lib/wasm/wasmAllocator.d.ts.map +1 -1
  144. package/dist/lib/wasm/wasmAllocator.js +66 -44
  145. package/dist/lib/wasm/wasmPhysicalLayout.d.ts +67 -0
  146. package/dist/lib/wasm/wasmPhysicalLayout.d.ts.map +1 -0
  147. package/dist/lib/wasm/wasmPhysicalLayout.js +179 -0
  148. package/dist/lib/wasm/wasmSpanBuffer.d.ts +79 -31
  149. package/dist/lib/wasm/wasmSpanBuffer.d.ts.map +1 -1
  150. package/dist/lib/wasm/wasmSpanBuffer.js +442 -272
  151. package/dist/lib/wasm/wasmTraceRoot.d.ts +24 -22
  152. package/dist/lib/wasm/wasmTraceRoot.d.ts.map +1 -1
  153. package/dist/lib/wasm/wasmTraceRoot.js +110 -71
  154. package/dist/vocabulary/register/v1.d.ts +2 -0
  155. package/dist/vocabulary/register/v1.d.ts.map +1 -0
  156. package/dist/vocabulary/register/v1.js +1 -0
  157. package/dist/wasm.d.ts +5 -0
  158. package/dist/wasm.d.ts.map +1 -0
  159. package/dist/wasm.js +4 -0
  160. package/package.json +54 -6
  161. package/src/index.ts +1 -0
  162. package/src/lib/JsBufferStrategy.ts +27 -9
  163. package/src/lib/__tests__/arrow-builder-integration/arrow-conversion.test.ts +335 -37
  164. package/src/lib/__tests__/arrow-builder-integration/binary-arrow-conversion.test.ts +97 -26
  165. package/src/lib/__tests__/arrow-builder-integration/binary-comparison.test.ts +75 -15
  166. package/src/lib/__tests__/arrow-builder-integration/binary-format-compliance.test.ts +30 -6
  167. package/src/lib/__tests__/arrow-builder-integration/buffer-chaining.test.ts +18 -21
  168. package/src/lib/__tests__/arrow-builder-integration/foundation.test.ts +5 -2
  169. package/src/lib/__tests__/arrow-builder-integration/integration.test.ts +25 -7
  170. package/src/lib/__tests__/arrow-builder-integration/lazy-columns.test.ts +3 -2
  171. package/src/lib/__tests__/buffer-overflow.test.ts +24 -18
  172. package/src/lib/__tests__/capability-span-context.test.ts +505 -0
  173. package/src/lib/__tests__/capacity-tuning.test.ts +5 -3
  174. package/src/lib/__tests__/eager-columns.test.ts +345 -0
  175. package/src/lib/__tests__/enum-metadata-aot.test.ts +153 -0
  176. package/src/lib/__tests__/enum-metadata.test.ts +177 -0
  177. package/src/lib/__tests__/fixtures/empty-vocabulary-runtime.fixture.ts +15 -0
  178. package/src/lib/__tests__/flushScheduler.test.ts +5 -1
  179. package/src/lib/__tests__/identity-view.property.test.ts +179 -0
  180. package/src/lib/__tests__/library.test.ts +19 -235
  181. package/src/lib/__tests__/message-buffer-families.test.ts +878 -0
  182. package/src/lib/__tests__/nested-library-tasks.test.ts +14 -15
  183. package/src/lib/__tests__/op-metadata.test.ts +1 -1
  184. package/src/lib/__tests__/op-type-safety.test.ts +5 -16
  185. package/src/lib/__tests__/physical-layout-plan.test.ts +333 -0
  186. package/src/lib/__tests__/remap-descriptor-integration.test.ts +381 -0
  187. package/src/lib/__tests__/root-context-construction.test.ts +414 -0
  188. package/src/lib/__tests__/span-auto-runtime.test.ts +386 -0
  189. package/src/lib/__tests__/span-buffer-segment-stats.test.ts +221 -0
  190. package/src/lib/__tests__/span-lifecycle.test.ts +53 -25
  191. package/src/lib/__tests__/spanbuffer-materializer-parity.test.ts +266 -0
  192. package/src/lib/__tests__/sync-async-trace.test.ts +24 -23
  193. package/src/lib/__tests__/tag-writer-aot.test.ts +214 -0
  194. package/src/lib/__tests__/test-helpers.ts +67 -12
  195. package/src/lib/__tests__/threadId.test.ts +62 -0
  196. package/src/lib/__tests__/timestamp.test.ts +221 -93
  197. package/src/lib/__tests__/traceTopology.property.test.ts +383 -0
  198. package/src/lib/__tests__/type-narrowing.test.ts +63 -0
  199. package/src/lib/__tests__/vocabulary-dense-decoding.test.ts +405 -0
  200. package/src/lib/__tests__/vocabularyRegistry.test.ts +313 -0
  201. package/src/lib/arrow/lease.ts +43 -0
  202. package/src/lib/arrow/utils.ts +10 -14
  203. package/src/lib/arrow/vocabularyDictionary.ts +123 -0
  204. package/src/lib/bufferStrategy.ts +9 -1
  205. package/src/lib/cloudflare/traceRows.ts +3 -2
  206. package/src/lib/codegen/__tests__/evaluatorGenerator.test.ts +21 -141
  207. package/src/lib/codegen/__tests__/fixedPositionWriter-parity.test.ts +273 -0
  208. package/src/lib/codegen/__tests__/fixedPositionWriter-uint64.test.ts +61 -34
  209. package/src/lib/codegen/__tests__/nullBitmap.test.ts +33 -37
  210. package/src/lib/codegen/__tests__/spanLoggerGenerator.test.ts +34 -18
  211. package/src/lib/codegen/__tests__/spanLoggerMaterializerParity.test.ts +503 -0
  212. package/src/lib/codegen/evaluatorGenerator.ts +18 -13
  213. package/src/lib/codegen/fixedPositionWriterGenerator.ts +329 -245
  214. package/src/lib/codegen/spanLoggerClosureMaterializer.ts +598 -0
  215. package/src/lib/codegen/spanLoggerGenerator.ts +241 -177
  216. package/src/lib/convertToArrow.ts +598 -311
  217. package/src/lib/defineOpContext.ts +1 -1
  218. package/src/lib/enumMetadata.ts +121 -0
  219. package/src/lib/flushScheduler.ts +11 -5
  220. package/src/lib/library.ts +37 -595
  221. package/src/lib/logBinding.ts +21 -27
  222. package/src/lib/op.ts +75 -18
  223. package/src/lib/opContext/__tests__/retry.test.ts +27 -21
  224. package/src/lib/opContext/createOpGroup.ts +33 -31
  225. package/src/lib/opContext/defineOp.ts +88 -11
  226. package/src/lib/opContext/opGroupTypes.ts +2 -2
  227. package/src/lib/opContext/opTypes.ts +1 -1
  228. package/src/lib/opContext/spanContextTypes.ts +122 -24
  229. package/src/lib/opContext/types.ts +7 -3
  230. package/src/lib/physicalLayoutPlan.ts +598 -0
  231. package/src/lib/resolveMessage.ts +67 -0
  232. package/src/lib/result.ts +20 -33
  233. package/src/lib/runtimeHint.ts +98 -0
  234. package/src/lib/schema/__tests__/featureFlags.test.ts +2 -1
  235. package/src/lib/schema/builder.ts +1 -1
  236. package/src/lib/schema/typeGuards.ts +7 -151
  237. package/src/lib/schema/types.ts +1 -1
  238. package/src/lib/span-buffer/aot/v1.ts +26 -0
  239. package/src/lib/spanBuffer.ts +658 -92
  240. package/src/lib/spanBufferStats.ts +1 -1
  241. package/src/lib/spanContext.ts +1350 -861
  242. package/src/lib/sqlite/sqlite-common.ts +20 -31
  243. package/src/lib/testing/__tests__/topology-traversal.test.ts +174 -0
  244. package/src/lib/testing/__tests__/vitest-harness.test.ts +2 -1
  245. package/src/lib/testing/bun-harness.ts +94 -43
  246. package/src/lib/testing/extractFacts.ts +64 -78
  247. package/src/lib/testing/facts.ts +1 -1
  248. package/src/lib/testing/queryable-span.ts +23 -30
  249. package/src/lib/testing/span-query.ts +16 -20
  250. package/src/lib/testing/stdio-replay.ts +25 -8
  251. package/src/lib/testing/vitest-harness.ts +1 -1
  252. package/src/lib/threadId.ts +6 -3
  253. package/src/lib/traceContext.ts +1 -6
  254. package/src/lib/traceRoot.es.ts +77 -42
  255. package/src/lib/traceRoot.node.ts +75 -46
  256. package/src/lib/traceRoot.ts +41 -14
  257. package/src/lib/traceTopology.ts +279 -0
  258. package/src/lib/tracer.ts +109 -98
  259. package/src/lib/tracers/ArrayQueueTracer.ts +1 -1
  260. package/src/lib/tracers/StdioTracer.ts +15 -13
  261. package/src/lib/tracers/TestTracer.ts +6 -8
  262. package/src/lib/tracers/__tests__/ArrayQueueTracer.test.ts +15 -10
  263. package/src/lib/tracers/__tests__/CompositeTracer.test.ts +150 -0
  264. package/src/lib/tracers/__tests__/TestTracer.test.ts +23 -13
  265. package/src/lib/types.ts +64 -33
  266. package/src/lib/vocabularyRegistry.ts +439 -0
  267. package/src/lib/wasm/WasmBufferStrategy.ts +56 -48
  268. package/src/lib/wasm/__tests__/WasmBufferStrategy.test.ts +215 -11
  269. package/src/lib/wasm/__tests__/allocatorFreeBlockClamp.test.ts +86 -0
  270. package/src/lib/wasm/__tests__/wasm-identity-view.test.ts +311 -0
  271. package/src/lib/wasm/__tests__/wasm-integration.test.ts +417 -44
  272. package/src/lib/wasm/__tests__/wasm-message-buffer-families.test.ts +588 -0
  273. package/src/lib/wasm/__tests__/wasmAllocator.test.ts +100 -668
  274. package/src/lib/wasm/__tests__/wasmSpanBuffer.test.ts +419 -63
  275. package/src/lib/wasm/__tests__/wasmTraceRoot.test.ts +24 -29
  276. package/src/lib/wasm/__tests__/wasmTracer.test.ts.disabled +2 -2
  277. package/src/lib/wasm/wasmAllocator.ts +131 -96
  278. package/src/lib/wasm/wasmPhysicalLayout.ts +309 -0
  279. package/src/lib/wasm/wasmSpanBuffer.ts +597 -341
  280. package/src/lib/wasm/wasmTraceRoot.ts +140 -78
  281. package/src/vocabulary/register/v1.ts +1 -0
  282. package/src/wasm.ts +19 -0
  283. package/src/lib/__tests__/remapped-buffer-view-integration.test.ts +0 -319
  284. package/src/lib/__tests__/remapped-buffer-view.test.ts +0 -635
  285. package/src/lib/wasm/allocator.zig +0 -948
@@ -0,0 +1,503 @@
1
+ /**
2
+ * Materializer parity for generated SpanLogger classes.
3
+ *
4
+ * Drives identical logging scripts through the compiled (string codegen) and
5
+ * closure-composed (no-eval) SpanLogger classes against real SpanBuffers and
6
+ * byte-compares the resulting buffer contents and writer state, including
7
+ * entry_type/timestamp lanes and message storage for every layout family and
8
+ * physical message layout the generator distinguishes.
9
+ *
10
+ * The closure mode is forced via setMaterializerModeOverride('closure') and
11
+ * restored in afterEach.
12
+ */
13
+
14
+ import { afterEach, describe, expect, it } from 'bun:test';
15
+ import { createHash } from 'node:crypto';
16
+ import { setMaterializerModeOverride } from '@smoothbricks/arrow-builder';
17
+ import { createTestTraceRoot } from '../../__tests__/test-helpers.js';
18
+ import { defineOpContext } from '../../defineOpContext.js';
19
+ import type { OpMetadata } from '../../opContext/opTypes.js';
20
+ import type { OpContext } from '../../opContext/types.js';
21
+ import type { CallsitePlan } from '../../physicalLayoutPlan.js';
22
+ import {
23
+ type MessageLayoutFamily,
24
+ type MessagePhysicalLayout,
25
+ RUNTIME_HINT_ANALYZED_VALID,
26
+ RUNTIME_HINT_LOG,
27
+ RUNTIME_HINT_MESSAGE_LAYOUT_DYNAMIC_ONLY,
28
+ RUNTIME_HINT_MESSAGE_LAYOUT_MIXED,
29
+ RUNTIME_HINT_MESSAGE_LAYOUT_STATIC_ONLY,
30
+ RUNTIME_HINT_MESSAGE_PHYSICAL_PACKED,
31
+ RUNTIME_HINT_MESSAGE_PHYSICAL_SPECIALIZED,
32
+ RUNTIME_HINT_RESULT,
33
+ } from '../../runtimeHint.js';
34
+ import { S } from '../../schema/builder.js';
35
+ import { defineLogSchema } from '../../schema/defineLogSchema.js';
36
+ import type { LogSchema } from '../../schema/types.js';
37
+ import { createSpanBuffer } from '../../spanBuffer.js';
38
+ import type { AnySpanBuffer } from '../../types.js';
39
+ import { registerVocabularyFragment, type VocabularyFragment } from '../../vocabularyRegistry.js';
40
+ import type { WriterState } from '../fixedPositionWriterGenerator.js';
41
+ import { createSpanLoggerClass } from '../spanLoggerGenerator.js';
42
+
43
+ // =============================================================================
44
+ // Vocabulary fixture (same fragment scheme as message-buffer-families.test.ts)
45
+ // =============================================================================
46
+
47
+ const encoder = new TextEncoder();
48
+
49
+ function encodeRecord(text: string): Uint8Array {
50
+ const textBytes = encoder.encode(text);
51
+ const record = new Uint8Array(4 + textBytes.length + 2);
52
+ new DataView(record.buffer).setUint32(0, textBytes.length, true);
53
+ record.set(textBytes, 4);
54
+ return record;
55
+ }
56
+
57
+ function fragmentHash(fragment: Omit<VocabularyFragment, 'contentHash'>): string {
58
+ const algorithm = encoder.encode(fragment.idAlgorithm);
59
+ const byteLength =
60
+ 1 +
61
+ 2 +
62
+ algorithm.length +
63
+ 4 +
64
+ fragment.ids.length * 4 +
65
+ 4 +
66
+ fragment.kindTags.length +
67
+ 4 +
68
+ fragment.utf8.length +
69
+ 4 +
70
+ fragment.offsets.length * 4;
71
+ const bytes = new Uint8Array(byteLength);
72
+ const view = new DataView(bytes.buffer);
73
+ let offset = 0;
74
+ bytes[offset++] = fragment.schemaVersion;
75
+ view.setUint16(offset, algorithm.length, true);
76
+ offset += 2;
77
+ bytes.set(algorithm, offset);
78
+ offset += algorithm.length;
79
+ view.setUint32(offset, fragment.ids.length, true);
80
+ offset += 4;
81
+ for (const id of fragment.ids) {
82
+ view.setUint32(offset, id, true);
83
+ offset += 4;
84
+ }
85
+ view.setUint32(offset, fragment.kindTags.length, true);
86
+ offset += 4;
87
+ bytes.set(fragment.kindTags, offset);
88
+ offset += fragment.kindTags.length;
89
+ view.setUint32(offset, fragment.utf8.length, true);
90
+ offset += 4;
91
+ bytes.set(fragment.utf8, offset);
92
+ offset += fragment.utf8.length;
93
+ view.setUint32(offset, fragment.offsets.length, true);
94
+ offset += 4;
95
+ for (const boundary of fragment.offsets) {
96
+ view.setInt32(offset, boundary, true);
97
+ offset += 4;
98
+ }
99
+ return createHash('sha256').update(bytes).digest('hex');
100
+ }
101
+
102
+ function makeFragment(text: string): VocabularyFragment {
103
+ const record = encodeRecord(text);
104
+ const digest = createHash('sha256').update(Uint8Array.of(1)).update(record).digest();
105
+ const fragment: Omit<VocabularyFragment, 'contentHash'> = {
106
+ schemaVersion: 1,
107
+ idAlgorithm: 'sha256-24-v1',
108
+ ids: new Uint32Array([(digest[0] << 16) | (digest[1] << 8) | digest[2]]),
109
+ kindTags: new Uint8Array([1]),
110
+ utf8: record,
111
+ offsets: new Int32Array([0, record.length]),
112
+ };
113
+ return { ...fragment, contentHash: fragmentHash(fragment) };
114
+ }
115
+
116
+ const PARITY_BINDING = registerVocabularyFragment(makeFragment('parity local vocabulary literal'));
117
+ const DENSE_INDEX = PARITY_BINDING[0];
118
+
119
+ // =============================================================================
120
+ // Schemas, op contexts, and layout-specific callsite plans
121
+ // =============================================================================
122
+
123
+ const CAPACITY = 64;
124
+
125
+ const schema = defineLogSchema({
126
+ userId: S.category(),
127
+ count: S.number(),
128
+ enabled: S.boolean(),
129
+ note: S.text(),
130
+ level: S.enum(['low', 'mid', 'high']),
131
+ });
132
+ const context = defineOpContext({ logSchema: schema });
133
+ const runtimeSchema = context.logBinding.logSchema;
134
+
135
+ const emptySchema = defineLogSchema({});
136
+ const emptyContext = defineOpContext({ logSchema: emptySchema });
137
+ const emptyRuntimeSchema = emptyContext.logBinding.logSchema;
138
+
139
+ const FAMILY_BITS: Record<MessageLayoutFamily, number> = {
140
+ mixed: RUNTIME_HINT_MESSAGE_LAYOUT_MIXED,
141
+ 'static-only': RUNTIME_HINT_MESSAGE_LAYOUT_STATIC_ONLY,
142
+ 'dynamic-only': RUNTIME_HINT_MESSAGE_LAYOUT_DYNAMIC_ONLY,
143
+ };
144
+ const PHYSICAL_BITS: Record<MessagePhysicalLayout, number> = {
145
+ current: 0,
146
+ specialized: RUNTIME_HINT_MESSAGE_PHYSICAL_SPECIALIZED,
147
+ packed: RUNTIME_HINT_MESSAGE_PHYSICAL_PACKED,
148
+ };
149
+
150
+ interface ParityCase {
151
+ readonly name: string;
152
+ readonly family: MessageLayoutFamily;
153
+ readonly physical: MessagePhysicalLayout;
154
+ readonly dictionary: boolean;
155
+ }
156
+
157
+ const cases: readonly ParityCase[] = [
158
+ { name: 'mixed/current', family: 'mixed', physical: 'current', dictionary: true },
159
+ { name: 'mixed/current (raw fallback, empty dictionary)', family: 'mixed', physical: 'current', dictionary: false },
160
+ { name: 'mixed/specialized', family: 'mixed', physical: 'specialized', dictionary: true },
161
+ { name: 'mixed/packed', family: 'mixed', physical: 'packed', dictionary: true },
162
+ { name: 'static-only/current', family: 'static-only', physical: 'current', dictionary: true },
163
+ { name: 'static-only/specialized', family: 'static-only', physical: 'specialized', dictionary: true },
164
+ { name: 'static-only/packed', family: 'static-only', physical: 'packed', dictionary: true },
165
+ { name: 'dynamic-only/current', family: 'dynamic-only', physical: 'current', dictionary: false },
166
+ { name: 'dynamic-only/packed', family: 'dynamic-only', physical: 'packed', dictionary: false },
167
+ ];
168
+
169
+ function parityOpOptions(parityCase: ParityCase): { runtimeHint: number; localMessageDictionary?: number[] } {
170
+ return {
171
+ runtimeHint:
172
+ RUNTIME_HINT_ANALYZED_VALID |
173
+ FAMILY_BITS[parityCase.family] |
174
+ PHYSICAL_BITS[parityCase.physical] |
175
+ RUNTIME_HINT_LOG |
176
+ RUNTIME_HINT_RESULT |
177
+ CAPACITY,
178
+ ...(parityCase.dictionary ? { localMessageDictionary: [DENSE_INDEX] } : {}),
179
+ };
180
+ }
181
+
182
+ const opsByCase = new Map(
183
+ cases.map((parityCase) => [
184
+ parityCase.name,
185
+ context.defineOp(`parity-${parityCase.name}`, (op) => op.ok(null), undefined, parityOpOptions(parityCase)),
186
+ ]),
187
+ );
188
+
189
+ // =============================================================================
190
+ // Scenario driver
191
+ // =============================================================================
192
+
193
+ /** Structural view of the generated logger surface exercised by this suite. */
194
+ interface ParityLogger {
195
+ [key: string]: unknown;
196
+ info(message: string, fields?: Record<string, unknown>): ParityLogger;
197
+ debug(message: string): ParityLogger;
198
+ warn(message: string, fields?: Record<string, unknown>): ParityLogger;
199
+ error(message: string, fields?: Record<string, unknown>): ParityLogger;
200
+ trace(message: string): ParityLogger;
201
+ _infoTemplate(vocabularyIndex: number): ParityLogger;
202
+ _debugTemplate(vocabularyIndex: number): ParityLogger;
203
+ _warnTemplate(vocabularyIndex: number): ParityLogger;
204
+ _errorTemplate(vocabularyIndex: number): ParityLogger;
205
+ _traceTemplate(vocabularyIndex: number): ParityLogger;
206
+ with(attributes: Record<string, unknown>): ParityLogger;
207
+ ffAccess(flagName: string, value: unknown): void;
208
+ ffUsage(flagName: string, context?: Record<string, unknown>): ParityLogger;
209
+ line(lineNumber: number): ParityLogger;
210
+ error_code(code: string): ParityLogger;
211
+ exception_stack(stack: string): ParityLogger;
212
+ ff_value(value: string): ParityLogger;
213
+ uint64_value(value: bigint): ParityLogger;
214
+ userId(value: string): ParityLogger;
215
+ count(value: number): ParityLogger;
216
+ enabled(value: boolean): ParityLogger;
217
+ note(value: string): ParityLogger;
218
+ level(value: string): ParityLogger;
219
+ readonly scope: Record<string, unknown> | undefined;
220
+ _setScope(attributes: Record<string, unknown>): void;
221
+ _prefillScopedAttributesOn(buffer: AnySpanBuffer): void;
222
+ }
223
+
224
+ type MaterializerChoice = 'compiled' | 'closure';
225
+
226
+ /** Runtime objects flow untyped out of Reflect/constructors; every real one is an object. */
227
+ function isWriterStateValue(value: unknown): value is WriterState {
228
+ return typeof value === 'object' && value !== null;
229
+ }
230
+
231
+ function isParityLogger(value: unknown): value is ParityLogger {
232
+ return typeof value === 'object' && value !== null;
233
+ }
234
+
235
+ function toParityLogger(value: unknown): ParityLogger {
236
+ if (isParityLogger(value)) return value;
237
+ throw new TypeError('Expected a SpanLogger instance');
238
+ }
239
+
240
+ /** The op surface the scenarios consume (metadata + resolved callsite plan). */
241
+ interface ParityOpHandle<T extends LogSchema, Ctx extends OpContext<T>> {
242
+ readonly metadata: OpMetadata;
243
+ readonly callsitePlan: CallsitePlan<T, Ctx>;
244
+ }
245
+
246
+ interface ScenarioSetup {
247
+ readonly buffer: AnySpanBuffer;
248
+ readonly state: WriterState;
249
+ readonly logger: ParityLogger;
250
+ }
251
+
252
+ function setUpScenario<T extends LogSchema, Ctx extends OpContext<T>>(
253
+ op: ParityOpHandle<T, Ctx>,
254
+ rtSchema: T,
255
+ parityCase: Pick<ParityCase, 'family' | 'physical'>,
256
+ mode: MaterializerChoice,
257
+ eagerColumns: readonly string[] = [],
258
+ ): ScenarioSetup {
259
+ op.callsitePlan.SpanBufferClass.stats.capacity = CAPACITY;
260
+ const buffer = createSpanBuffer(
261
+ rtSchema,
262
+ createTestTraceRoot('parity-trace'),
263
+ op.metadata,
264
+ CAPACITY,
265
+ op.callsitePlan.SpanBufferClass,
266
+ );
267
+ const spanContext = new op.callsitePlan.SpanContextClass(buffer, rtSchema, op.callsitePlan);
268
+ op.callsitePlan.appenders.writeSpanStart(buffer, parityCase.family === 'static-only' ? DENSE_INDEX : 'parity root');
269
+ const state = Reflect.get(spanContext._spanLogger, '_state');
270
+ if (!isWriterStateValue(state)) throw new TypeError('Expected writer state on the span logger');
271
+
272
+ if (mode === 'closure') setMaterializerModeOverride('closure');
273
+ try {
274
+ const LoggerClass = createSpanLoggerClass(rtSchema, parityCase.family, parityCase.physical, eagerColumns);
275
+ return { buffer, state, logger: toParityLogger(new LoggerClass(state)) };
276
+ } finally {
277
+ setMaterializerModeOverride(undefined);
278
+ }
279
+ }
280
+
281
+ /** Identical logging script for both materializers (schema with fields). */
282
+ function driveFieldSchema(setup: ScenarioSetup, parityCase: Pick<ParityCase, 'family' | 'physical'>): void {
283
+ const { logger, buffer } = setup;
284
+ const { family, physical } = parityCase;
285
+
286
+ if (family === 'static-only') {
287
+ const staticThrow = 'Dynamic log write reached a static-only callsite plan';
288
+ expect(() => logger.info('nope')).toThrow(staticThrow);
289
+ expect(() => logger.debug('nope')).toThrow(staticThrow);
290
+ expect(() => logger.warn('nope')).toThrow(staticThrow);
291
+ expect(() => logger.error('nope')).toThrow(staticThrow);
292
+ expect(() => logger.trace('nope')).toThrow(staticThrow);
293
+ expect(() => logger.ffAccess('flag-a', true)).toThrow('Feature flag write reached a static-only callsite plan');
294
+ expect(() => logger.ffUsage('flag-b')).toThrow('Feature flag write reached a static-only callsite plan');
295
+ } else {
296
+ logger.info('parity {{braced}} info').line(11).error_code('E-1');
297
+ logger.debug('parity debug');
298
+ logger.warn('parity warn', { userId: 'warn-user', count: 7 });
299
+ logger.error('parity error', { enabled: true });
300
+ logger.trace('parity trace');
301
+ logger.info('fields', { note: 'field-note' });
302
+ logger.exception_stack('stack-1');
303
+ logger.ff_value('ff-raw');
304
+ logger.uint64_value(12345678901234567890n);
305
+ logger.with({ userId: 'with-user', level: 'high', nullish: null, missing: undefined });
306
+ logger.ffAccess('flag-a', true);
307
+ logger.ffAccess('flag-null', null);
308
+ logger.ffUsage('flag-b', { count: 3 });
309
+ logger.ffUsage('flag-c');
310
+ }
311
+
312
+ if (family !== 'dynamic-only') {
313
+ logger._infoTemplate(DENSE_INDEX).userId('tpl-user').count(42).enabled(true).note('tpl-note').level('mid');
314
+ logger._debugTemplate(DENSE_INDEX);
315
+ logger._warnTemplate(DENSE_INDEX).line(77);
316
+ logger._errorTemplate(DENSE_INDEX);
317
+ logger._traceTemplate(DENSE_INDEX);
318
+ if (physical === 'packed') {
319
+ expect(() => logger._infoTemplate(0x00ffffff)).toThrow('Packed message dense index exceeds 0xFFFFFE');
320
+ }
321
+ } else {
322
+ const dynamicThrow = 'Static log write reached a dynamic-only callsite plan';
323
+ expect(() => logger._infoTemplate(DENSE_INDEX)).toThrow(dynamicThrow);
324
+ expect(() => logger._warnTemplate(DENSE_INDEX)).toThrow(dynamicThrow);
325
+ }
326
+
327
+ logger._setScope({ userId: 'scope-user', count: 5, enabled: false, note: 'scope-note', level: 'low' });
328
+ logger._setScope({ note: null, count: 6 });
329
+ expect(logger.scope).toEqual({ userId: 'scope-user', count: 6, enabled: false, level: 'low' });
330
+ logger._prefillScopedAttributesOn(buffer);
331
+ }
332
+
333
+ // =============================================================================
334
+ // Buffer snapshots (timestamps normalized to written/unwritten flags)
335
+ // =============================================================================
336
+
337
+ /** Lanes exposed via prototype getters that an own-property walk would miss. */
338
+ const EXPLICIT_LANES = ['timestamp', 'entry_type', '_rowHeaders', '_messageIds', '_logHeaders', 'message_values'];
339
+ /** Own properties whose bytes embed per-buffer random identity (span/trace ids). */
340
+ const RANDOM_IDENTITY_KEYS = new Set(['_system', '_identity']);
341
+
342
+ function isArrayLikeView(value: ArrayBufferView): value is ArrayBufferView & ArrayLike<unknown> {
343
+ return typeof Reflect.get(value, 'length') === 'number';
344
+ }
345
+
346
+ function snapshotValue(value: unknown): unknown {
347
+ if (ArrayBuffer.isView(value) && !(value instanceof DataView) && isArrayLikeView(value)) {
348
+ return Array.from(value);
349
+ }
350
+ if (Array.isArray(value)) return value.slice();
351
+ const type = typeof value;
352
+ if (value === null || type === 'number' || type === 'string' || type === 'boolean' || type === 'bigint') {
353
+ return value;
354
+ }
355
+ return undefined;
356
+ }
357
+
358
+ function isDynamicRecord(value: object): value is Record<string, unknown> {
359
+ return value !== null;
360
+ }
361
+
362
+ function snapshotBuffer(buffer: AnySpanBuffer): Record<string, unknown> {
363
+ if (!isDynamicRecord(buffer)) throw new TypeError('Expected a buffer object');
364
+ const dyn = buffer;
365
+
366
+ // Timestamps are wall-clock: assert per-row written-ness, then zero them so
367
+ // every remaining lane byte-compares exactly.
368
+ const timestamps = buffer.timestamp;
369
+ const timestampWritten: boolean[] = [];
370
+ for (let i = 0; i < timestamps.length; i++) {
371
+ timestampWritten.push(timestamps[i] !== 0n);
372
+ timestamps[i] = 0n;
373
+ }
374
+
375
+ const snap: Record<string, unknown> = { timestampWritten };
376
+ for (const key of EXPLICIT_LANES) {
377
+ snap[key] = snapshotValue(dyn[key]);
378
+ }
379
+ for (const key of Object.keys(dyn).sort()) {
380
+ if (RANDOM_IDENTITY_KEYS.has(key)) continue;
381
+ if (key === '_scopeValues') {
382
+ const scope = dyn[key];
383
+ snap[key] = scope && typeof scope === 'object' ? { ...scope } : scope;
384
+ continue;
385
+ }
386
+ const copied = snapshotValue(dyn[key]);
387
+ if (copied !== undefined) snap[key] = copied;
388
+ }
389
+ return snap;
390
+ }
391
+
392
+ function runFieldSchemaScenario(parityCase: ParityCase, mode: MaterializerChoice): Record<string, unknown> {
393
+ const op = opsByCase.get(parityCase.name);
394
+ if (op === undefined) throw new Error(`No op defined for parity case ${parityCase.name}`);
395
+ const setup = setUpScenario(op, runtimeSchema, parityCase, mode);
396
+ driveFieldSchema(setup, parityCase);
397
+ expect(setup.buffer._overflow).toBeUndefined();
398
+ expect(setup.state._buffer).toBe(setup.buffer);
399
+ // Every script appends rows beyond span-start/end, so the snapshot must
400
+ // capture written timestamp lanes — guards against a vacuous comparison.
401
+ expect(setup.buffer._writeIndex).toBeGreaterThan(2);
402
+ const snapshot = snapshotBuffer(setup.buffer);
403
+ const timestampWritten = snapshot.timestampWritten;
404
+ expect(Array.isArray(timestampWritten) && timestampWritten.includes(true)).toBe(true);
405
+ return snapshot;
406
+ }
407
+
408
+ // =============================================================================
409
+ // Suite
410
+ // =============================================================================
411
+
412
+ describe('SpanLogger materializer parity (compiled vs closure)', () => {
413
+ afterEach(() => {
414
+ setMaterializerModeOverride(undefined);
415
+ });
416
+
417
+ for (const parityCase of cases) {
418
+ it(`produces byte-identical buffers for ${parityCase.name}`, () => {
419
+ const compiled = runFieldSchemaScenario(parityCase, 'compiled');
420
+ const closure = runFieldSchemaScenario(parityCase, 'closure');
421
+ expect(closure).toEqual(compiled);
422
+ });
423
+ }
424
+
425
+ it('produces byte-identical buffers with preallocated (eager) columns', () => {
426
+ const parityCase = cases[0];
427
+ const op = opsByCase.get(parityCase.name);
428
+ if (op === undefined) throw new Error('Missing mixed/current op');
429
+ const eager = ['userId', 'count'] as const;
430
+
431
+ const run = (mode: MaterializerChoice): Record<string, unknown> => {
432
+ const setup = setUpScenario(op, runtimeSchema, parityCase, mode, eager);
433
+ // Warm the raw lanes through a lazy-mode logger so the preallocated
434
+ // setters (which write `_userId_values`/`_count_values` directly) hit
435
+ // allocated storage in both runs.
436
+ const warm = toParityLogger(new (createSpanLoggerClass(runtimeSchema))(setup.state));
437
+ warm.userId('warm-user').count(1);
438
+ driveFieldSchema(setup, parityCase);
439
+ return snapshotBuffer(setup.buffer);
440
+ };
441
+
442
+ const compiled = run('compiled');
443
+ const closure = run('closure');
444
+ expect(closure).toEqual(compiled);
445
+ });
446
+
447
+ it('produces byte-identical buffers for the empty schema', () => {
448
+ const parityCase: ParityCase = { name: 'empty', family: 'mixed', physical: 'current', dictionary: false };
449
+ const op = emptyContext.defineOp('parity-empty', (opCtx) => opCtx.ok(null), undefined, parityOpOptions(parityCase));
450
+
451
+ const run = (mode: MaterializerChoice): Record<string, unknown> => {
452
+ const setup = setUpScenario(op, emptyRuntimeSchema, parityCase, mode);
453
+ const { logger, buffer } = setup;
454
+ logger.info('empty {{schema}} info').line(3);
455
+ logger.debug('empty debug');
456
+ logger.warn('empty warn');
457
+ logger.error('empty error').error_code('E-9').exception_stack('stack-9');
458
+ logger.trace('empty trace');
459
+ logger._infoTemplate(DENSE_INDEX);
460
+ logger.ffAccess('flag-a', 42);
461
+ logger.ffUsage('flag-b');
462
+ logger.ff_value('ff-raw').uint64_value(7n);
463
+ logger.with({ unknownField: 'ignored' });
464
+ logger._setScope({ foo: 'bar' });
465
+ logger._prefillScopedAttributesOn(buffer);
466
+ return snapshotBuffer(buffer);
467
+ };
468
+
469
+ const compiled = run('compiled');
470
+ const closure = run('closure');
471
+ expect(closure).toEqual(compiled);
472
+ });
473
+
474
+ it('matches the compiled prototype member contract and instance shape', () => {
475
+ const CompiledClass = createSpanLoggerClass(runtimeSchema, 'mixed', 'current');
476
+ setMaterializerModeOverride('closure');
477
+ const ClosureClass = createSpanLoggerClass(runtimeSchema, 'mixed', 'current');
478
+ setMaterializerModeOverride(undefined);
479
+
480
+ expect(ClosureClass).not.toBe(CompiledClass);
481
+ expect(new Set(Reflect.ownKeys(ClosureClass.prototype))).toEqual(new Set(Reflect.ownKeys(CompiledClass.prototype)));
482
+
483
+ for (const key of Reflect.ownKeys(CompiledClass.prototype)) {
484
+ if (key === 'constructor') continue;
485
+ const compiledDescriptor = Object.getOwnPropertyDescriptor(CompiledClass.prototype, key);
486
+ const closureDescriptor = Object.getOwnPropertyDescriptor(ClosureClass.prototype, key);
487
+ if (compiledDescriptor === undefined || closureDescriptor === undefined) {
488
+ throw new Error(`Missing prototype member ${String(key)}`);
489
+ }
490
+ expect(closureDescriptor.enumerable).toBe(compiledDescriptor.enumerable);
491
+ expect(closureDescriptor.configurable).toBe(compiledDescriptor.configurable);
492
+ expect(closureDescriptor.writable).toBe(compiledDescriptor.writable);
493
+ expect(typeof closureDescriptor.value).toBe(typeof compiledDescriptor.value);
494
+ expect(typeof closureDescriptor.get).toBe(typeof compiledDescriptor.get);
495
+ expect(typeof closureDescriptor.set).toBe(typeof compiledDescriptor.set);
496
+ }
497
+
498
+ const op = opsByCase.get(cases[0].name);
499
+ if (op === undefined) throw new Error('Missing mixed/current op');
500
+ const setup = setUpScenario(op, runtimeSchema, cases[0], 'closure');
501
+ expect(Reflect.ownKeys(setup.logger as object)).toEqual(['_state']);
502
+ });
503
+ });
@@ -8,6 +8,7 @@
8
8
 
9
9
  import { isRecord } from '@smoothbricks/validation';
10
10
  import type { OpContext } from '../opContext/types.js';
11
+ import { resolveEntryType, resolveMessage } from '../resolveMessage.js';
11
12
  import type { FeatureFlagSchema } from '../schema/defineFeatureFlags.js';
12
13
  import type {
13
14
  FlagEvaluator,
@@ -47,7 +48,7 @@ type WorkerSafeEvaluatorInstance = WorkerSafeGeneratedEvaluator<OpContext>;
47
48
 
48
49
  type EvaluatorRuntimeContext<Ctx extends OpContext> = SpanContextWithoutFf<Ctx> & {
49
50
  _buffer: AnySpanBuffer;
50
- log: SpanLoggerInternal<Ctx['logSchema']>;
51
+ _spanLogger: SpanLoggerInternal<Ctx['logSchema']>;
51
52
  };
52
53
 
53
54
  /**
@@ -73,22 +74,26 @@ function isInternalSpanLogger<T extends OpContext['logSchema']>(value: unknown):
73
74
  return isRecord(value) && 'ffAccess' in value && 'ffUsage' in value;
74
75
  }
75
76
 
77
+ function isEvaluatorRuntimeContext<Ctx extends OpContext>(
78
+ value: SpanContextWithoutFf<Ctx>,
79
+ ): value is EvaluatorRuntimeContext<Ctx> {
80
+ return (
81
+ isRecord(value) &&
82
+ '_buffer' in value &&
83
+ '_spanLogger' in value &&
84
+ isAnySpanBuffer(value._buffer) &&
85
+ isInternalSpanLogger<Ctx['logSchema']>(value._spanLogger)
86
+ );
87
+ }
88
+
76
89
  function getEvaluatorRuntimeContext<Ctx extends OpContext>(
77
90
  ctx: SpanContextWithoutFf<Ctx>,
78
91
  ): EvaluatorRuntimeContext<Ctx> {
79
- if (!isRecord(ctx) || !('_buffer' in ctx) || !('log' in ctx)) {
80
- throw new Error('Feature-flag evaluator requires an internal span context');
81
- }
82
-
83
- if (!isAnySpanBuffer(ctx._buffer) || !isInternalSpanLogger<Ctx['logSchema']>(ctx.log)) {
92
+ if (!isEvaluatorRuntimeContext(ctx)) {
84
93
  throw new Error('Feature-flag evaluator requires runtime span buffer and logger internals');
85
94
  }
86
95
 
87
- return {
88
- ...ctx,
89
- _buffer: ctx._buffer,
90
- log: ctx.log,
91
- };
96
+ return ctx;
92
97
  }
93
98
 
94
99
  function createFlagGetter(flagName: string) {
@@ -183,7 +188,7 @@ class WorkerSafeGeneratedEvaluator<Ctx extends OpContext> implements GeneratedEv
183
188
  while (buf) {
184
189
  const limit = buf._writeIndex;
185
190
  for (let i = limit - 1; i >= 0; i--) {
186
- if (buf.entry_type[i] === this.#entryTypeFfAccess && buf.message_values && buf.message_values[i] === flagName) {
191
+ if (resolveEntryType(buf, i) === this.#entryTypeFfAccess && resolveMessage(buf, i) === flagName) {
187
192
  return true;
188
193
  }
189
194
  }
@@ -262,7 +267,7 @@ class WorkerSafeGeneratedEvaluator<Ctx extends OpContext> implements GeneratedEv
262
267
  }
263
268
 
264
269
  private getLogger(): SpanLoggerInternal<Ctx['logSchema']> {
265
- return this.#spanContext.log;
270
+ return this.#spanContext._spanLogger;
266
271
  }
267
272
 
268
273
  private logUsage(flagName: string, context?: FlagTrackContext) {