@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
@@ -0,0 +1,386 @@
1
+ import { describe, expect, it } from 'bun:test';
2
+ import { convertSpanTreeToArrowTable } from '../convertToArrow.js';
3
+ import { defineOpContext } from '../defineOpContext.js';
4
+ import { Transient } from '../errors/Transient.js';
5
+ import type { OpContext, OpContextOf, SpanContext } from '../opContext/types.js';
6
+ import { resolveEntryType, resolveMessage } from '../resolveMessage.js';
7
+ import {
8
+ RUNTIME_HINT_ANALYZED_VALID,
9
+ RUNTIME_HINT_FF,
10
+ RUNTIME_HINT_LOG,
11
+ RUNTIME_HINT_RESERVED_MASK,
12
+ RUNTIME_HINT_RESULT,
13
+ } from '../runtimeHint.js';
14
+ import { S } from '../schema/builder.js';
15
+ import { defineFeatureFlags } from '../schema/defineFeatureFlags.js';
16
+ import { defineLogSchema } from '../schema/defineLogSchema.js';
17
+ import { InMemoryFlagEvaluator, type SpanContextWithoutFf } from '../schema/evaluator.js';
18
+ import {
19
+ ENTRY_TYPE_INFO,
20
+ ENTRY_TYPE_SPAN_EXCEPTION,
21
+ ENTRY_TYPE_SPAN_OK,
22
+ ENTRY_TYPE_SPAN_RETRY,
23
+ } from '../schema/systemSchema.js';
24
+ import { TestTracer } from '../tracers/TestTracer.js';
25
+ import { iterateSpanChildren } from '../traceTopology.js';
26
+ import { createTestTracerOptions } from './test-helpers.js';
27
+
28
+ const schema = defineLogSchema({ marker: S.category() });
29
+ const opContext = defineOpContext({ logSchema: schema, ctx: { inheritedMarker: 'parent' } });
30
+ const analyzedResult = RUNTIME_HINT_ANALYZED_VALID | RUNTIME_HINT_RESULT | 2;
31
+
32
+ function childOf(tracer: TestTracer<typeof opContext>) {
33
+ const [child] = iterateSpanChildren(tracer.rootBuffers[0]);
34
+ if (!child) throw new Error('expected one child span buffer');
35
+ return child;
36
+ }
37
+
38
+ class GetterCountingPromise<T> implements Promise<T> {
39
+ readonly [Symbol.toStringTag] = 'Promise';
40
+
41
+ constructor(
42
+ private readonly value: T,
43
+ private readonly onThenAccess: () => void,
44
+ ) {}
45
+
46
+ // biome-ignore lint/suspicious/noThenProperty: intentional thenable fixture for await scheduling tests
47
+ get then(): Promise<T>['then'] {
48
+ this.onThenAccess();
49
+ return <TResult1 = T, TResult2 = never>(
50
+ onfulfilled?: ((value: T) => TResult1 | PromiseLike<TResult1>) | null,
51
+ onrejected?: ((reason: unknown) => TResult2 | PromiseLike<TResult2>) | null,
52
+ ) => Promise.resolve(this.value).then(onfulfilled, onrejected);
53
+ }
54
+
55
+ catch<TResult = never>(
56
+ onrejected?: ((reason: unknown) => TResult | PromiseLike<TResult>) | null,
57
+ ): Promise<T | TResult> {
58
+ return Promise.resolve(this.value).catch(onrejected);
59
+ }
60
+
61
+ finally(onfinally?: (() => void) | null): Promise<T> {
62
+ return Promise.resolve(this.value).finally(onfinally);
63
+ }
64
+ }
65
+
66
+ describe('spanAuto synchronous and fallback execution', () => {
67
+ it('returns a synchronous Result and fires exact hooks around one terminal row', async () => {
68
+ const op = opContext.defineOp('sync', (ctx) => ctx.ok('done'), undefined, {
69
+ runtimeHint: analyzedResult,
70
+ });
71
+ const plan = op.callsitePlan;
72
+ const tracer = new TestTracer(opContext, createTestTracerOptions());
73
+ const hooks: string[] = [];
74
+ tracer.onSpanStart = (buffer) => hooks.push(`start:${resolveMessage(buffer, 0)}`);
75
+ tracer.onSpanEnd = (buffer) => hooks.push(`end:${resolveMessage(buffer, 0)}`);
76
+
77
+ await tracer.trace('root', async (ctx) => {
78
+ const result = ctx.spanAuto0(41, 'sync-child', op);
79
+ expect(result).not.toBeInstanceOf(Promise);
80
+ if (result instanceof Promise) throw new Error('sync spanAuto unexpectedly returned a Promise');
81
+ expect(result.success).toBe(true);
82
+ expect(hooks).toEqual(['start:sync-child', 'end:sync-child']);
83
+ return ctx.ok(null);
84
+ });
85
+
86
+ const child = childOf(tracer);
87
+ expect(child._writeIndex).toBe(2);
88
+ expect(resolveEntryType(child, 1)).toBe(ENTRY_TYPE_SPAN_OK);
89
+ expect(child._overflow).toBeUndefined();
90
+ expect(hooks).toEqual(['start:sync-child', 'end:sync-child']);
91
+ expect(op.callsitePlan).toBe(plan);
92
+ expect(child).toBeInstanceOf(plan.SpanBufferClass);
93
+ });
94
+
95
+ it('propagates a synchronous onSpanEnd failure without a second hook or exception rewrite', async () => {
96
+ const op = opContext.defineOp('hook-throw', (ctx) => ctx.ok('done'), undefined, {
97
+ runtimeHint: analyzedResult,
98
+ });
99
+ const tracer = new TestTracer(opContext, createTestTracerOptions());
100
+ const hookFailure = new Error('hook exploded');
101
+ let starts = 0;
102
+ let ends = 0;
103
+ tracer.onSpanStart = () => starts++;
104
+ tracer.onSpanEnd = () => {
105
+ ends++;
106
+ throw hookFailure;
107
+ };
108
+
109
+ await tracer.trace('root', async (ctx) => {
110
+ expect(() => ctx.spanAuto0(42, 'hook-throw', op)).toThrow('hook exploded');
111
+ return ctx.ok(null);
112
+ });
113
+
114
+ const child = childOf(tracer);
115
+ expect(starts).toBe(1);
116
+ expect(ends).toBe(1);
117
+ expect(child._writeIndex).toBe(2);
118
+ expect(resolveEntryType(child, 1)).toBe(ENTRY_TYPE_SPAN_OK);
119
+ expect(resolveMessage(child, 1)).not.toBe('hook exploded');
120
+ });
121
+
122
+ it('falls back to a Promise for a thenable and still writes one terminal row', async () => {
123
+ const op = opContext.defineOp('async', async (ctx) => ctx.ok('later'), undefined, {
124
+ runtimeHint: analyzedResult,
125
+ });
126
+ const plan = op.callsitePlan;
127
+ const tracer = new TestTracer(opContext, createTestTracerOptions());
128
+
129
+ await tracer.trace('root', async (ctx) => {
130
+ const pending = ctx.spanAuto0(5, 'async-child', op);
131
+ expect(pending).toBeInstanceOf(Promise);
132
+ const result = await pending;
133
+ expect(result.success).toBe(true);
134
+ return ctx.ok(null);
135
+ });
136
+
137
+ const child = childOf(tracer);
138
+ expect(child._writeIndex).toBe(2);
139
+ expect(resolveEntryType(child, 1)).toBe(ENTRY_TYPE_SPAN_OK);
140
+ expect(op.callsitePlan).toBe(plan);
141
+ expect(child).toBeInstanceOf(plan.SpanBufferClass);
142
+ });
143
+
144
+ it('observes a custom Promise then getter exactly once', async () => {
145
+ let thenAccesses = 0;
146
+ const op = opContext.defineOp(
147
+ 'custom-then',
148
+ (ctx) =>
149
+ new GetterCountingPromise(ctx.ok('custom'), () => {
150
+ thenAccesses++;
151
+ }),
152
+ undefined,
153
+ { runtimeHint: analyzedResult },
154
+ );
155
+ const tracer = new TestTracer(opContext, createTestTracerOptions());
156
+
157
+ await tracer.trace('root', async (ctx) => {
158
+ const result = await ctx.spanAuto0(6, 'custom-then', op);
159
+ expect(result.success).toBe(true);
160
+ return ctx.ok(null);
161
+ });
162
+
163
+ expect(thenAccesses).toBe(1);
164
+ expect(resolveEntryType(childOf(tracer), 1)).toBe(ENTRY_TYPE_SPAN_OK);
165
+ });
166
+
167
+ it('falls back to retry when the first synchronous Result is transient', async () => {
168
+ const RETRY = Transient<Record<string, never>>('RETRY', {
169
+ backoff: 'fixed',
170
+ baseDelayMs: 0,
171
+ jitter: false,
172
+ maxAttempts: 2,
173
+ });
174
+ let attempts = 0;
175
+ const op = opContext.defineOp(
176
+ 'retry',
177
+ (ctx) => {
178
+ attempts++;
179
+ return attempts === 1 ? ctx.err(RETRY({})) : ctx.ok('recovered');
180
+ },
181
+ undefined,
182
+ { runtimeHint: RUNTIME_HINT_ANALYZED_VALID | RUNTIME_HINT_RESULT | 3 },
183
+ );
184
+ const plan = op.callsitePlan;
185
+ const tracer = new TestTracer(opContext, createTestTracerOptions());
186
+
187
+ await tracer.trace('root', async (ctx) => {
188
+ const pending = ctx.spanAuto0(9, 'retry-child', op);
189
+ expect(pending).toBeInstanceOf(Promise);
190
+ const result = await pending;
191
+ expect(result.success).toBe(true);
192
+ return ctx.ok(null);
193
+ });
194
+
195
+ const child = childOf(tracer);
196
+ expect(attempts).toBe(2);
197
+ expect(resolveEntryType(child, 1)).toBe(ENTRY_TYPE_SPAN_OK);
198
+ expect(resolveEntryType(child, 2)).toBe(ENTRY_TYPE_SPAN_RETRY);
199
+ expect(child._writeIndex).toBe(3);
200
+ expect(op.callsitePlan).toBe(plan);
201
+ expect(child).toBeInstanceOf(plan.SpanBufferClass);
202
+ });
203
+
204
+ it('records a rejected Promise as an exception and ends hooks exactly once', async () => {
205
+ const failure = new Error('async exploded');
206
+ const op = opContext.defineOp('reject', () => Promise.reject(failure), undefined, {
207
+ runtimeHint: analyzedResult,
208
+ });
209
+ const tracer = new TestTracer(opContext, createTestTracerOptions());
210
+ let starts = 0;
211
+ let ends = 0;
212
+ tracer.onSpanStart = () => starts++;
213
+ tracer.onSpanEnd = () => ends++;
214
+
215
+ await tracer.trace('root', async (ctx) => {
216
+ const pending = ctx.spanAuto0(7, 'rejected-child', op);
217
+ expect(pending).toBeInstanceOf(Promise);
218
+ await expect(pending).rejects.toBe(failure);
219
+ return ctx.ok(null);
220
+ });
221
+
222
+ const child = childOf(tracer);
223
+ expect(starts).toBe(1);
224
+ expect(ends).toBe(1);
225
+ expect(child._writeIndex).toBe(2);
226
+ expect(resolveEntryType(child, 1)).toBe(ENTRY_TYPE_SPAN_EXCEPTION);
227
+ expect(resolveMessage(child, 1)).toBe('async exploded');
228
+ });
229
+ });
230
+
231
+ describe('runtime hint specialization', () => {
232
+ it('uses full setup for zero and invalid hints', async () => {
233
+ for (const { name, hint } of [
234
+ { name: 'zero', hint: 0 },
235
+ { name: 'reserved', hint: RUNTIME_HINT_ANALYZED_VALID | RUNTIME_HINT_RESERVED_MASK },
236
+ ]) {
237
+ let childContext: SpanContext<OpContextOf<typeof opContext>> | undefined;
238
+ const op = opContext.defineOp(
239
+ name,
240
+ (ctx) => {
241
+ childContext = ctx;
242
+ return ctx.ok(name);
243
+ },
244
+ undefined,
245
+ { runtimeHint: hint },
246
+ );
247
+ const tracer = new TestTracer(opContext, createTestTracerOptions());
248
+ await tracer.trace('root', async (ctx) => {
249
+ await ctx.spanAuto0(1, name, op);
250
+ return ctx.ok(null);
251
+ });
252
+ if (!childContext) throw new Error(`missing ${name} child context`);
253
+ for (const capability of ['tag', 'log', 'ff', 'span', 'ok', 'err', 'setScope', 'deps']) {
254
+ expect(Object.hasOwn(childContext, capability)).toBe(true);
255
+ }
256
+ }
257
+ });
258
+
259
+ it('omits unused capabilities while terminal writes stay on the child buffer', async () => {
260
+ let childContext: SpanContext<OpContextOf<typeof opContext>> | undefined;
261
+ const op = opContext.defineOp(
262
+ 'result-only',
263
+ (ctx) => {
264
+ childContext = ctx;
265
+ return ctx.ok('child');
266
+ },
267
+ undefined,
268
+ { runtimeHint: analyzedResult },
269
+ );
270
+ const tracer = new TestTracer(opContext, createTestTracerOptions());
271
+
272
+ await tracer.trace('root', async (ctx) => {
273
+ const result = ctx.spanAuto0(1, 'result-only', op);
274
+ if (result instanceof Promise) throw new Error('result-only spanAuto unexpectedly returned a Promise');
275
+ expect(result.success).toBe(true);
276
+ return ctx.ok('parent');
277
+ });
278
+
279
+ if (!childContext) throw new Error('missing captured child context');
280
+ expect(Object.hasOwn(childContext, 'ok')).toBe(true);
281
+ expect(Object.hasOwn(childContext, 'tag')).toBe(false);
282
+ expect(Object.hasOwn(childContext, 'log')).toBe(false);
283
+ expect(resolveEntryType(tracer.rootBuffers[0], 1)).toBe(ENTRY_TYPE_SPAN_OK);
284
+ expect(resolveEntryType(childOf(tracer), 1)).toBe(ENTRY_TYPE_SPAN_OK);
285
+ });
286
+
287
+ it('keeps invalid capacity adaptive while exact capacity preserves overflow rows', async () => {
288
+ const minimumOp = opContext.defineOp('minimum', (ctx) => ctx.ok(null), undefined, {
289
+ runtimeHint: RUNTIME_HINT_ANALYZED_VALID | RUNTIME_HINT_RESULT | 1,
290
+ });
291
+ const loggingOp = opContext.defineOp(
292
+ 'overflow',
293
+ (ctx) => {
294
+ ctx.log.info('first');
295
+ ctx.log.info('second');
296
+ return ctx.ok(null);
297
+ },
298
+ undefined,
299
+ { runtimeHint: RUNTIME_HINT_ANALYZED_VALID | RUNTIME_HINT_LOG | RUNTIME_HINT_RESULT | 3 },
300
+ );
301
+ const tracer = new TestTracer(opContext, createTestTracerOptions());
302
+
303
+ await tracer.trace('root', async (ctx) => {
304
+ ctx.spanAuto0(1, 'minimum', minimumOp);
305
+ ctx.spanAuto0(2, 'overflow', loggingOp);
306
+ return ctx.ok(null);
307
+ });
308
+
309
+ const [minimum, overflow] = iterateSpanChildren(tracer.rootBuffers[0]);
310
+ if (!minimum || !overflow) throw new Error('expected minimum and overflow child spans');
311
+ expect(minimum._capacity).toBe(8);
312
+ expect(minimum._overflow).toBeUndefined();
313
+ expect(overflow._capacity).toBe(3);
314
+ expect(resolveEntryType(overflow, 2)).toBe(ENTRY_TYPE_INFO);
315
+ expect(overflow._overflow ? resolveEntryType(overflow._overflow, 0) : undefined).toBe(ENTRY_TYPE_INFO);
316
+ expect(overflow._overflow ? resolveMessage(overflow._overflow, 0) : undefined).toBe('second');
317
+ expect(convertSpanTreeToArrowTable(tracer.rootBuffers[0]).numRows).toBe(8);
318
+ });
319
+
320
+ it('maps defineOps hints by key and preserves them through prefixed and mapped clones', () => {
321
+ const existing = opContext.defineOp('existing', (ctx) => ctx.ok(null), undefined, {
322
+ runtimeHint: analyzedResult | 7,
323
+ });
324
+ const group = opContext.defineOps(
325
+ {
326
+ existing,
327
+ raw: (ctx) => ctx.ok(null),
328
+ },
329
+ { raw: { runtimeHint: analyzedResult | 5 } },
330
+ );
331
+
332
+ expect(group.existing).toBe(existing);
333
+ expect(group.existing.callsitePlan.runtimeHint).toBe(analyzedResult | 7);
334
+ expect(group.raw.callsitePlan.runtimeHint).toBe(analyzedResult | 5);
335
+ expect(group.prefix('lib').existing.callsitePlan.runtimeHint).toBe(analyzedResult | 7);
336
+ expect(group.mapColumns({ marker: 'renamedMarker' }).raw.callsitePlan.runtimeHint).toBe(analyzedResult | 5);
337
+
338
+ const overridden = opContext.defineOps({ existing }, { existing: { runtimeHint: analyzedResult | 9 } });
339
+ expect(overridden.existing).not.toBe(existing);
340
+ expect(overridden.existing.callsitePlan.runtimeHint).toBe(analyzedResult | 9);
341
+ });
342
+ });
343
+
344
+ describe('feature evaluator child context identity', () => {
345
+ const flags = defineFeatureFlags({ enabled: S.boolean().default(false).sync() });
346
+ const featureContext = defineOpContext({
347
+ logSchema: defineLogSchema({}),
348
+ flags: flags.schema,
349
+ ctx: { inheritedMarker: 'from-parent' },
350
+ });
351
+ type FeatureOpContext = OpContextOf<typeof featureContext>;
352
+
353
+ class CapturingEvaluator extends InMemoryFlagEvaluator {
354
+ readonly contexts: SpanContextWithoutFf<OpContext>[] = [];
355
+
356
+ override getSync<K extends string>(ctx: SpanContextWithoutFf<OpContext>, flag: K) {
357
+ this.contexts.push(ctx);
358
+ return super.getSync(ctx, flag);
359
+ }
360
+ }
361
+
362
+ it('binds the evaluator to the direct inherited child rather than a copied context', async () => {
363
+ const evaluator = new CapturingEvaluator(flags.schema, { enabled: true });
364
+ let childContext: SpanContext<FeatureOpContext> | undefined;
365
+ const op = featureContext.defineOp(
366
+ 'flagged',
367
+ (ctx) => {
368
+ childContext = ctx;
369
+ expect(ctx.ff.enabled?.value).toBe(true);
370
+ return ctx.ok(null);
371
+ },
372
+ undefined,
373
+ { runtimeHint: RUNTIME_HINT_ANALYZED_VALID | RUNTIME_HINT_FF | RUNTIME_HINT_RESULT | 3 },
374
+ );
375
+ const tracer = new TestTracer(featureContext, { ...createTestTracerOptions(), flagEvaluator: evaluator });
376
+
377
+ await tracer.trace('root', async (ctx) => {
378
+ ctx.spanAuto0(1, 'flagged', op);
379
+ return ctx.ok(null);
380
+ });
381
+
382
+ if (!childContext) throw new Error('missing captured feature context');
383
+ expect(evaluator.contexts).toEqual([childContext]);
384
+ expect(childContext.inheritedMarker).toBe('from-parent');
385
+ });
386
+ });
@@ -0,0 +1,221 @@
1
+ import { beforeEach, describe, expect, it } from 'bun:test';
2
+ import fc from 'fast-check';
3
+ import { defineOpContext } from '../defineOpContext.js';
4
+ import { S } from '../schema/builder.js';
5
+ import { LogSchema } from '../schema/LogSchema.js';
6
+ import { getSpanBufferClass } from '../spanBuffer.js';
7
+ import { TestTracer } from '../tracers/TestTracer.js';
8
+ import type { AnySpanBuffer } from '../types.js';
9
+
10
+ import { createTestTracerOptions } from './test-helpers.js';
11
+
12
+ const opContext = defineOpContext({
13
+ logSchema: new LogSchema({
14
+ testField: S.category(),
15
+ }),
16
+ });
17
+
18
+ function requireBuffer(buffer: AnySpanBuffer | undefined, label: string): AnySpanBuffer {
19
+ if (!buffer) {
20
+ throw new Error(`Expected ${label} buffer`);
21
+ }
22
+ return buffer;
23
+ }
24
+
25
+ function writeIndices(first: AnySpanBuffer): number[] {
26
+ const indices: number[] = [];
27
+ let segment: AnySpanBuffer | undefined = first;
28
+ while (segment) {
29
+ indices.push(segment._writeIndex);
30
+ segment = segment._overflow;
31
+ }
32
+ return indices;
33
+ }
34
+
35
+ function accountedRows(first: AnySpanBuffer): number {
36
+ let rows = Math.max(0, first._writeIndex - 2);
37
+ let segment = first._overflow;
38
+ while (segment) {
39
+ rows += segment._writeIndex;
40
+ segment = segment._overflow;
41
+ }
42
+ return rows;
43
+ }
44
+
45
+ function fullSegmentRows(capacities: readonly number[], segmentIndex: number): number {
46
+ return capacities[segmentIndex] - (segmentIndex === 0 ? 2 : 0);
47
+ }
48
+
49
+ function totalRowsFor(capacities: readonly number[], tailRows: number): number {
50
+ let rows = tailRows;
51
+ for (let segmentIndex = 0; segmentIndex < capacities.length - 1; segmentIndex++) {
52
+ rows += fullSegmentRows(capacities, segmentIndex);
53
+ }
54
+ return rows;
55
+ }
56
+
57
+ describe('SpanBuffer physical-segment statistics accounting', () => {
58
+ const SpanBufferClass = getSpanBufferClass(opContext.logBinding.logSchema);
59
+ const stats = SpanBufferClass.stats;
60
+
61
+ beforeEach(() => {
62
+ stats.capacity = 8;
63
+ stats.totalWrites = 0;
64
+ stats.spansCreated = 0;
65
+ });
66
+
67
+ it('accounts full overflow segments immediately and the final segment exactly once on completion', () => {
68
+ const tracer = new TestTracer(opContext, createTestTracerOptions());
69
+
70
+ tracer.trace('three-segment-root', (ctx) => {
71
+ for (let row = 0; row < 20; row++) {
72
+ ctx.log.info(`row ${row}`);
73
+ }
74
+
75
+ // The first two physical segments are sealed; the six-row tail is still active.
76
+ expect(stats.totalWrites).toBe(14);
77
+ return ctx.ok(null);
78
+ });
79
+
80
+ const root = requireBuffer(tracer.rootBuffers[0], 'root');
81
+ expect(writeIndices(root)).toEqual([8, 8, 6]);
82
+ expect(accountedRows(root)).toBe(20);
83
+ expect(stats.totalWrites).toBe(accountedRows(root));
84
+ });
85
+
86
+ it('snapshots the just-sealed segment before overflow-triggered reset and tuning', () => {
87
+ const tracer = new TestTracer(opContext, createTestTracerOptions());
88
+
89
+ // Put the next overflow above the tuning threshold. The six newly sealed rows
90
+ // must be visible to the snapshot before tuning resets the counters.
91
+ stats.spansCreated = 10;
92
+ stats.totalWrites = 100;
93
+
94
+ tracer.trace('snapshot-order', (ctx) => {
95
+ for (let row = 0; row < 7; row++) {
96
+ ctx.log.info(`row ${row}`);
97
+ }
98
+ return ctx.ok(null);
99
+ });
100
+
101
+ expect(tracer.statsSnapshots.map((snapshot) => snapshot.totalWrites)).toEqual([106]);
102
+ // Tuning reset the sealed prefix; terminal completion then accounts the one-row tail.
103
+ expect(stats.totalWrites).toBe(1);
104
+ });
105
+
106
+ it('accounts root and child overflow chains without lifecycle rows or overlap double-counting', async () => {
107
+ const tracer = new TestTracer(opContext, createTestTracerOptions());
108
+ let root: AnySpanBuffer | undefined;
109
+ let child: AnySpanBuffer | undefined;
110
+
111
+ await tracer.trace('root', async (ctx) => {
112
+ root = ctx.buffer;
113
+ for (let row = 0; row < 7; row++) {
114
+ ctx.log.info(`root-before-child ${row}`);
115
+ }
116
+ expect(stats.totalWrites).toBe(6);
117
+
118
+ await ctx.span('child', (childCtx) => {
119
+ child = childCtx.buffer;
120
+ for (let row = 0; row < 15; row++) {
121
+ childCtx.log.info(`child ${row}`);
122
+ }
123
+ return childCtx.ok(null);
124
+ });
125
+
126
+ // The root's active one-row tail is not complete yet; the child chain is.
127
+ expect(stats.totalWrites).toBe(21);
128
+
129
+ for (let row = 0; row < 6; row++) {
130
+ ctx.log.info(`root-after-child ${row}`);
131
+ }
132
+ return ctx.ok(null);
133
+ });
134
+
135
+ const completedRoot = requireBuffer(root, 'root');
136
+ const completedChild = requireBuffer(child, 'child');
137
+ expect(writeIndices(completedRoot)).toEqual([8, 7]);
138
+ expect(writeIndices(completedChild)).toEqual([8, 8, 1]);
139
+
140
+ const expectedRows = accountedRows(completedRoot) + accountedRows(completedChild);
141
+ expect(expectedRows).toBe(28);
142
+ expect(stats.totalWrites).toBe(expectedRows);
143
+ });
144
+
145
+ it('accounts randomized writes across no-overflow, overflow, child, and mixed-capacity segments', async () => {
146
+ await fc.assert(
147
+ fc.asyncProperty(
148
+ fc.oneof(
149
+ fc.tuple(fc.integer({ min: 1, max: 4 }).map((units) => units * 8)),
150
+ fc.uniqueArray(
151
+ fc.integer({ min: 1, max: 4 }).map((units) => units * 8),
152
+ {
153
+ minLength: 2,
154
+ maxLength: 4,
155
+ },
156
+ ),
157
+ ),
158
+ fc.boolean(),
159
+ fc.integer({ min: 0, max: 31 }),
160
+ async (capacities, useChild, tailSeed) => {
161
+ const lastCapacity = capacities[capacities.length - 1];
162
+ if (lastCapacity === undefined) {
163
+ throw new Error('Expected at least one generated capacity');
164
+ }
165
+ const tailRows = capacities.length === 1 ? tailSeed % (lastCapacity - 1) : 1 + (tailSeed % lastCapacity);
166
+ const rowsToWrite = totalRowsFor(capacities, tailRows);
167
+
168
+ stats.capacity = capacities[0];
169
+ stats.totalWrites = 0;
170
+ stats.spansCreated = 0;
171
+ const tracer = new TestTracer(opContext, createTestTracerOptions());
172
+ let completed: AnySpanBuffer | undefined;
173
+
174
+ await tracer.trace('property-root', async (ctx) => {
175
+ const writeRows = (spanCtx: typeof ctx) => {
176
+ completed = spanCtx.buffer;
177
+ let row = 0;
178
+ for (let segmentIndex = 0; segmentIndex < capacities.length; segmentIndex++) {
179
+ if (segmentIndex > 0) {
180
+ stats.capacity = capacities[segmentIndex];
181
+ }
182
+ const rowsInSegment =
183
+ segmentIndex === capacities.length - 1 ? tailRows : fullSegmentRows(capacities, segmentIndex);
184
+ for (let segmentRow = 0; segmentRow < rowsInSegment; segmentRow++) {
185
+ spanCtx.log.info(`row ${row++}`);
186
+ }
187
+ }
188
+ };
189
+
190
+ if (useChild) {
191
+ await ctx.span('property-child', (childCtx) => {
192
+ writeRows(childCtx);
193
+ return childCtx.ok(null);
194
+ });
195
+ } else {
196
+ writeRows(ctx);
197
+ }
198
+ return ctx.ok(null);
199
+ });
200
+
201
+ const first = requireBuffer(completed, useChild ? 'child' : 'root');
202
+ const expectedIndices = capacities.map((capacity, segmentIndex) =>
203
+ segmentIndex === capacities.length - 1 ? tailRows + (segmentIndex === 0 ? 2 : 0) : capacity,
204
+ );
205
+ expect(writeIndices(first)).toEqual(expectedIndices);
206
+ expect(accountedRows(first)).toBe(rowsToWrite);
207
+ expect(stats.totalWrites).toBe(rowsToWrite);
208
+
209
+ const sealedPrefixTotals: number[] = [];
210
+ let sealedRows = 0;
211
+ for (let segmentIndex = 0; segmentIndex < capacities.length - 1; segmentIndex++) {
212
+ sealedRows += fullSegmentRows(capacities, segmentIndex);
213
+ sealedPrefixTotals.push(sealedRows);
214
+ }
215
+ expect(tracer.statsSnapshots.map((snapshot) => snapshot.totalWrites)).toEqual(sealedPrefixTotals);
216
+ },
217
+ ),
218
+ { numRuns: 80 },
219
+ );
220
+ });
221
+ });