@smoothbricks/lmao 0.1.4 → 0.2.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.
- package/dist/cloudflare.d.ts +15 -0
- package/dist/cloudflare.d.ts.map +1 -0
- package/dist/cloudflare.js +14 -0
- package/dist/lib/cloudflare/classSplit.d.ts +73 -0
- package/dist/lib/cloudflare/classSplit.d.ts.map +1 -0
- package/dist/lib/cloudflare/classSplit.js +82 -0
- package/dist/lib/cloudflare/collectorClient.d.ts +69 -0
- package/dist/lib/cloudflare/collectorClient.d.ts.map +1 -0
- package/dist/lib/cloudflare/collectorClient.js +82 -0
- package/dist/lib/cloudflare/diagnosticDrain.d.ts +78 -0
- package/dist/lib/cloudflare/diagnosticDrain.d.ts.map +1 -0
- package/dist/lib/cloudflare/diagnosticDrain.js +120 -0
- package/dist/lib/cloudflare/traceRows.d.ts +19 -0
- package/dist/lib/cloudflare/traceRows.d.ts.map +1 -0
- package/dist/lib/cloudflare/traceRows.js +51 -0
- package/dist/lib/codegen/evaluatorGenerator.js +1 -1
- package/dist/lib/codegen/fixedPositionWriterGenerator.d.ts +8 -0
- package/dist/lib/codegen/fixedPositionWriterGenerator.d.ts.map +1 -1
- package/dist/lib/codegen/fixedPositionWriterGenerator.js +17 -1
- package/dist/lib/sqlite/sqlite-common.d.ts +2 -0
- package/dist/lib/sqlite/sqlite-common.d.ts.map +1 -1
- package/dist/lib/sqlite/sqlite-common.js +4 -0
- package/dist/lib/traceRoot.d.ts +5 -5
- package/dist/lib/traceRoot.es.js +1 -1
- package/dist/lib/traceRoot.js +1 -1
- package/dist/lib/traceRoot.node.d.ts +2 -2
- package/dist/lib/traceRoot.node.js +3 -3
- package/package.json +7 -1
- package/src/cloudflare.ts +35 -0
- package/src/lib/bufferStrategy.ts +2 -2
- package/src/lib/cloudflare/__tests__/classSplit.test.ts +171 -0
- package/src/lib/cloudflare/__tests__/collectorClient.test.ts +104 -0
- package/src/lib/cloudflare/__tests__/diagnosticDrain.test.ts +165 -0
- package/src/lib/cloudflare/classSplit.ts +139 -0
- package/src/lib/cloudflare/collectorClient.ts +117 -0
- package/src/lib/cloudflare/diagnosticDrain.ts +152 -0
- package/src/lib/cloudflare/traceRows.ts +60 -0
- package/src/lib/codegen/__tests__/fixedPositionWriter-uint64.test.ts +304 -0
- package/src/lib/codegen/evaluatorGenerator.ts +2 -2
- package/src/lib/codegen/fixedPositionWriterGenerator.ts +26 -1
- package/src/lib/schema/defineFeatureFlags.ts +2 -2
- package/src/lib/schema/evaluator.ts +2 -2
- package/src/lib/sqlite/sqlite-common.ts +5 -0
- package/src/lib/testing/__tests__/facts.test.ts +6 -6
- package/src/lib/traceRoot.es.ts +2 -2
- package/src/lib/traceRoot.node.ts +3 -3
- package/src/lib/traceRoot.ts +5 -5
- package/src/lib/tracers/ArrayQueueTracer.ts +2 -2
- package/src/lib/codegen/__tests__/__snapshots__/spanLoggerGenerator.test.ts.snap +0 -1965
|
@@ -48,6 +48,10 @@ export type TagWriter<T extends LogSchema> = {
|
|
|
48
48
|
* Bulk set multiple attributes at once.
|
|
49
49
|
*/
|
|
50
50
|
with(attributes: Partial<InferSchema<T>>): TagWriter<T>;
|
|
51
|
+
/**
|
|
52
|
+
* Set the reserved `uint64_value` column for this span-start row.
|
|
53
|
+
*/
|
|
54
|
+
uint64_value(value: bigint): TagWriter<T>;
|
|
51
55
|
} & {
|
|
52
56
|
[K in keyof InferSchema<T>]: (value: InferSchema<T>[K]) => TagWriter<T>;
|
|
53
57
|
};
|
|
@@ -81,6 +85,10 @@ export type ResultWriter<T extends LogSchema, R = unknown, E = unknown> = {
|
|
|
81
85
|
* Set the result-row source line (row 1).
|
|
82
86
|
*/
|
|
83
87
|
line(lineNumber: number): ResultWriter<T, R, E>;
|
|
88
|
+
/**
|
|
89
|
+
* Set the reserved `uint64_value` column for this result row.
|
|
90
|
+
*/
|
|
91
|
+
uint64_value(value: bigint): ResultWriter<T, R, E>;
|
|
84
92
|
} & {
|
|
85
93
|
[K in keyof InferSchema<T>]: (value: InferSchema<T>[K]) => ResultWriter<T, R, E>;
|
|
86
94
|
};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"fixedPositionWriterGenerator.d.ts","sourceRoot":"","sources":["../../../src/lib/codegen/fixedPositionWriterGenerator.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;GAmBG;AAIH,OAAO,KAAK,EAAE,WAAW,EAAE,SAAS,EAAE,MAAM,oBAAoB,CAAC;AACjE,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,aAAa,CAAC;AAEjD;;GAEG;AACH,MAAM,WAAW,4BAA4B;IAC3C;;;OAGG;IACH,eAAe,CAAC,EAAE,MAAM,CAAC;IAEzB;;OAEG;IACH,OAAO,CAAC,EAAE,MAAM,CAAC;IAEjB;;;;OAIG;IACH,iBAAiB,CAAC,EAAE,MAAM,CAAC;CAC5B;AAED;;GAEG;AACH,MAAM,MAAM,SAAS,CAAC,CAAC,SAAS,SAAS,IAAI;IAC3C;;OAEG;IACH,IAAI,CAAC,UAAU,EAAE,OAAO,CAAC,WAAW,CAAC,CAAC,CAAC,CAAC,GAAG,SAAS,CAAC,CAAC,CAAC,CAAC;
|
|
1
|
+
{"version":3,"file":"fixedPositionWriterGenerator.d.ts","sourceRoot":"","sources":["../../../src/lib/codegen/fixedPositionWriterGenerator.ts"],"names":[],"mappings":"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"}
|
|
@@ -171,12 +171,23 @@ const resultWriterClassCache = new WeakMap();
|
|
|
171
171
|
// ============================================================================
|
|
172
172
|
// TagWriter API
|
|
173
173
|
// ============================================================================
|
|
174
|
+
/**
|
|
175
|
+
* TagWriter extension - adds the reserved system `uint64_value` writer (row 0).
|
|
176
|
+
*/
|
|
177
|
+
const tagWriterExtension = {
|
|
178
|
+
methods: `
|
|
179
|
+
uint64_value(value) {
|
|
180
|
+
this._buffer.uint64_value(this._pos, value);
|
|
181
|
+
return this;
|
|
182
|
+
}
|
|
183
|
+
`,
|
|
184
|
+
};
|
|
174
185
|
/**
|
|
175
186
|
* Generate TagWriter class code for a schema.
|
|
176
187
|
* TagWriter writes to position 0 (span-start row).
|
|
177
188
|
*/
|
|
178
189
|
export function generateTagWriterClass(schema) {
|
|
179
|
-
return generateFixedPositionWriterClass(schema, 0, 'GeneratedTagWriter');
|
|
190
|
+
return generateFixedPositionWriterClass(schema, 0, 'GeneratedTagWriter', tagWriterExtension);
|
|
180
191
|
}
|
|
181
192
|
/**
|
|
182
193
|
* Get or create a cached TagWriter class for a schema.
|
|
@@ -243,6 +254,11 @@ line(lineNumber) {
|
|
|
243
254
|
this._buffer.line(this._pos, lineNumber);
|
|
244
255
|
return this;
|
|
245
256
|
}
|
|
257
|
+
|
|
258
|
+
uint64_value(value) {
|
|
259
|
+
this._buffer.uint64_value(this._pos, value);
|
|
260
|
+
return this;
|
|
261
|
+
}
|
|
246
262
|
`,
|
|
247
263
|
};
|
|
248
264
|
/**
|
|
@@ -33,5 +33,7 @@ export declare function getMissingSchemaColumns(schema: LogSchema, knownColumns:
|
|
|
33
33
|
export declare function getActiveUserFields(schema: LogSchema, knownColumns: ReadonlySet<string>): string[];
|
|
34
34
|
export declare function getInsertStatementCacheKey(activeUserFields: readonly string[]): string;
|
|
35
35
|
export declare function buildInsertSql(activeUserFields: readonly string[]): string;
|
|
36
|
+
/** Read a user schema field for one buffer row, falling back to the buffer's scope values. */
|
|
37
|
+
export declare function readSpanFieldValue(buffer: AnySpanBuffer, fieldName: string, row: number): unknown;
|
|
36
38
|
export declare function buildInsertParams(segment: SpanSegment, row: number, activeUserFields: readonly string[]): unknown[];
|
|
37
39
|
//# sourceMappingURL=sqlite-common.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"sqlite-common.d.ts","sourceRoot":"","sources":["../../../src/lib/sqlite/sqlite-common.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,wBAAwB,CAAC;AAExD,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,aAAa,CAAC;AAEjD,eAAO,MAAM,oBAAoB,sdAchC,CAAC;AAEF,eAAO,MAAM,oBAAoB,6BAA6B,CAAC;AAI/D,MAAM,WAAW,sBAAsB;IACrC,IAAI,EAAE,MAAM,CAAC;IACb,UAAU,EAAE,MAAM,CAAC;CACpB;AAED,MAAM,WAAW,kBAAkB;IACjC,GAAG,EAAE,MAAM,CAAC;IACZ,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,MAAM,CAAC;IACb,OAAO,EAAE,MAAM,CAAC;IAChB,UAAU,EAAE,MAAM,GAAG,IAAI,CAAC;IAC1B,EAAE,EAAE,MAAM,CAAC;CACZ;AAED,MAAM,WAAW,WAAW;IAC1B,MAAM,EAAE,aAAa,CAAC;IACtB,OAAO,EAAE,MAAM,CAAC;IAChB,MAAM,EAAE,MAAM,CAAC;IACf,YAAY,EAAE,MAAM,CAAC;IACrB,SAAS,EAAE,MAAM,CAAC;CACnB;AAqID,wBAAgB,yBAAyB,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAE9D;AAED,wBAAgB,kBAAkB,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAKvD;AAED,wBAAgB,iBAAiB,CAAC,MAAM,EAAE,sBAAsB,GAAG,MAAM,CAExE;AAED,wBAAgB,4BAA4B,CAAC,KAAK,EAAE,OAAO,EAAE,UAAU,EAAE,MAAM,GAAG,OAAO,CAMxF;AAED,wBAAgB,uBAAuB,CACrC,OAAO,EAAE,SAAS,sBAAsB,EAAE,EAC1C,YAAY,EAAE,WAAW,CAAC,MAAM,CAAC,GAChC,sBAAsB,EAAE,CAQ1B;AAED,wBAAgB,iCAAiC,CAAC,OAAO,EAAE,SAAS,kBAAkB,EAAE,GAAG,sBAAsB,EAAE,CAKlH;AAED,wBAAgB,wBAAwB,CACtC,IAAI,EAAE,SAAS,OAAO,EAAE,EACxB,QAAQ,SAA6B,GACpC,kBAAkB,EAAE,CAEtB;AA8BD,wBAAiB,gBAAgB,CAAC,UAAU,EAAE,aAAa,GAAG,SAAS,CAAC,WAAW,CAAC,CAEnF;AAED,wBAAgB,uBAAuB,CACrC,MAAM,EAAE,SAAS,EACjB,YAAY,EAAE,WAAW,CAAC,MAAM,CAAC,GAChC,sBAAsB,EAAE,CAW1B;AAED,wBAAgB,mBAAmB,CAAC,MAAM,EAAE,SAAS,EAAE,YAAY,EAAE,WAAW,CAAC,MAAM,CAAC,GAAG,MAAM,EAAE,CAElG;AAED,wBAAgB,0BAA0B,CAAC,gBAAgB,EAAE,SAAS,MAAM,EAAE,GAAG,MAAM,CAEtF;AAED,wBAAgB,cAAc,CAAC,gBAAgB,EAAE,SAAS,MAAM,EAAE,GAAG,MAAM,CAS1E;AAED,wBAAgB,iBAAiB,CAAC,OAAO,EAAE,WAAW,EAAE,GAAG,EAAE,MAAM,EAAE,gBAAgB,EAAE,SAAS,MAAM,EAAE,GAAG,OAAO,EAAE,CAgBnH"}
|
|
1
|
+
{"version":3,"file":"sqlite-common.d.ts","sourceRoot":"","sources":["../../../src/lib/sqlite/sqlite-common.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,wBAAwB,CAAC;AAExD,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,aAAa,CAAC;AAEjD,eAAO,MAAM,oBAAoB,sdAchC,CAAC;AAEF,eAAO,MAAM,oBAAoB,6BAA6B,CAAC;AAI/D,MAAM,WAAW,sBAAsB;IACrC,IAAI,EAAE,MAAM,CAAC;IACb,UAAU,EAAE,MAAM,CAAC;CACpB;AAED,MAAM,WAAW,kBAAkB;IACjC,GAAG,EAAE,MAAM,CAAC;IACZ,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,MAAM,CAAC;IACb,OAAO,EAAE,MAAM,CAAC;IAChB,UAAU,EAAE,MAAM,GAAG,IAAI,CAAC;IAC1B,EAAE,EAAE,MAAM,CAAC;CACZ;AAED,MAAM,WAAW,WAAW;IAC1B,MAAM,EAAE,aAAa,CAAC;IACtB,OAAO,EAAE,MAAM,CAAC;IAChB,MAAM,EAAE,MAAM,CAAC;IACf,YAAY,EAAE,MAAM,CAAC;IACrB,SAAS,EAAE,MAAM,CAAC;CACnB;AAqID,wBAAgB,yBAAyB,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAE9D;AAED,wBAAgB,kBAAkB,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAKvD;AAED,wBAAgB,iBAAiB,CAAC,MAAM,EAAE,sBAAsB,GAAG,MAAM,CAExE;AAED,wBAAgB,4BAA4B,CAAC,KAAK,EAAE,OAAO,EAAE,UAAU,EAAE,MAAM,GAAG,OAAO,CAMxF;AAED,wBAAgB,uBAAuB,CACrC,OAAO,EAAE,SAAS,sBAAsB,EAAE,EAC1C,YAAY,EAAE,WAAW,CAAC,MAAM,CAAC,GAChC,sBAAsB,EAAE,CAQ1B;AAED,wBAAgB,iCAAiC,CAAC,OAAO,EAAE,SAAS,kBAAkB,EAAE,GAAG,sBAAsB,EAAE,CAKlH;AAED,wBAAgB,wBAAwB,CACtC,IAAI,EAAE,SAAS,OAAO,EAAE,EACxB,QAAQ,SAA6B,GACpC,kBAAkB,EAAE,CAEtB;AA8BD,wBAAiB,gBAAgB,CAAC,UAAU,EAAE,aAAa,GAAG,SAAS,CAAC,WAAW,CAAC,CAEnF;AAED,wBAAgB,uBAAuB,CACrC,MAAM,EAAE,SAAS,EACjB,YAAY,EAAE,WAAW,CAAC,MAAM,CAAC,GAChC,sBAAsB,EAAE,CAW1B;AAED,wBAAgB,mBAAmB,CAAC,MAAM,EAAE,SAAS,EAAE,YAAY,EAAE,WAAW,CAAC,MAAM,CAAC,GAAG,MAAM,EAAE,CAElG;AAED,wBAAgB,0BAA0B,CAAC,gBAAgB,EAAE,SAAS,MAAM,EAAE,GAAG,MAAM,CAEtF;AAED,wBAAgB,cAAc,CAAC,gBAAgB,EAAE,SAAS,MAAM,EAAE,GAAG,MAAM,CAS1E;AAED,8FAA8F;AAC9F,wBAAgB,kBAAkB,CAAC,MAAM,EAAE,aAAa,EAAE,SAAS,EAAE,MAAM,EAAE,GAAG,EAAE,MAAM,GAAG,OAAO,CAEjG;AAED,wBAAgB,iBAAiB,CAAC,OAAO,EAAE,WAAW,EAAE,GAAG,EAAE,MAAM,EAAE,gBAAgB,EAAE,SAAS,MAAM,EAAE,GAAG,OAAO,EAAE,CAgBnH"}
|
|
@@ -194,6 +194,10 @@ export function buildInsertSql(activeUserFields) {
|
|
|
194
194
|
return `INSERT INTO spans (trace_id, span_id, parent_span_id, row_index, entry_type, timestamp_ns, message${userColsSql})
|
|
195
195
|
VALUES (?, ?, ?, ?, ?, ?, ?${userPlaceholders})`;
|
|
196
196
|
}
|
|
197
|
+
/** Read a user schema field for one buffer row, falling back to the buffer's scope values. */
|
|
198
|
+
export function readSpanFieldValue(buffer, fieldName, row) {
|
|
199
|
+
return readUserValue(buffer, fieldName, row) ?? readScopeValue(buffer, fieldName);
|
|
200
|
+
}
|
|
197
201
|
export function buildInsertParams(segment, row, activeUserFields) {
|
|
198
202
|
const { buffer, traceId, spanId, parentSpanId, rowOffset } = segment;
|
|
199
203
|
const entryType = buffer.entry_type[row];
|
package/dist/lib/traceRoot.d.ts
CHANGED
|
@@ -8,7 +8,7 @@
|
|
|
8
8
|
*
|
|
9
9
|
* Platform-specific implementations:
|
|
10
10
|
* - TraceRoot.es.ts: Browser - uses performance.now() for timing
|
|
11
|
-
* - TraceRoot.node.ts: Node.js -
|
|
11
|
+
* - TraceRoot.node.ts: Node.js - process.hrtime.bigint(), pure-JS writes to SpanBuffer state
|
|
12
12
|
*
|
|
13
13
|
* @module traceRoot
|
|
14
14
|
*/
|
|
@@ -47,7 +47,7 @@ export interface TracerLifecycleHooks {
|
|
|
47
47
|
*
|
|
48
48
|
* Total: 17 + trace_id.length bytes
|
|
49
49
|
*
|
|
50
|
-
* This layout enables
|
|
50
|
+
* This layout enables the WASM path to read anchors directly without BigInt extraction.
|
|
51
51
|
*/
|
|
52
52
|
export declare const TRACE_ROOT_ANCHOR_EPOCH_OFFSET = 0;
|
|
53
53
|
export declare const TRACE_ROOT_ANCHOR_PERF_OFFSET = 8;
|
|
@@ -58,7 +58,7 @@ export declare const TRACE_ROOT_TRACE_ID_OFFSET = 17;
|
|
|
58
58
|
*
|
|
59
59
|
* Each implementation handles timestamp calculation and span writes differently:
|
|
60
60
|
* - Browser: Pure JS with performance.now()
|
|
61
|
-
* - Node.js: NAPI addon
|
|
61
|
+
* - Node.js: process.hrtime.bigint() (pure JS; a NAPI addon was benchmarked slower than WASM and removed)
|
|
62
62
|
*/
|
|
63
63
|
/**
|
|
64
64
|
* Factory function type for creating platform-specific TraceRoot instances.
|
|
@@ -70,7 +70,7 @@ export type TraceRootFactory = (trace_id: string, tracer: TracerLifecycleHooks)
|
|
|
70
70
|
export interface ITraceRoot {
|
|
71
71
|
/**
|
|
72
72
|
* Raw backing buffer containing anchor timestamps and trace_id.
|
|
73
|
-
*
|
|
73
|
+
* The WASM path can read this directly without BigInt extraction.
|
|
74
74
|
*/
|
|
75
75
|
readonly _system: ArrayBuffer;
|
|
76
76
|
/**
|
|
@@ -89,7 +89,7 @@ export interface ITraceRoot {
|
|
|
89
89
|
/**
|
|
90
90
|
* High-resolution timer anchor when trace was created.
|
|
91
91
|
* Browser: performance.now() value (number)
|
|
92
|
-
* Node.js: stored as number
|
|
92
|
+
* Node.js: stored as number in the shared layout (WASM-readable), but represents hrtime
|
|
93
93
|
*/
|
|
94
94
|
readonly anchorPerfNow: number;
|
|
95
95
|
/**
|
package/dist/lib/traceRoot.es.js
CHANGED
package/dist/lib/traceRoot.js
CHANGED
|
@@ -12,7 +12,7 @@ export { createTraceId, extractSpanIdentity, generateTraceId, isValidTraceId, MA
|
|
|
12
12
|
*
|
|
13
13
|
* Total: 17 + trace_id.length bytes
|
|
14
14
|
*
|
|
15
|
-
* This layout enables
|
|
15
|
+
* This layout enables the WASM path to read anchors directly without BigInt extraction.
|
|
16
16
|
*/
|
|
17
17
|
//#region smoo/lmao!n/trace-root-timestamps #contract
|
|
18
18
|
export const TRACE_ROOT_ANCHOR_EPOCH_OFFSET = 0;
|
|
@@ -30,8 +30,8 @@ export declare class TraceRoot implements ITraceRoot {
|
|
|
30
30
|
private readonly _epochView;
|
|
31
31
|
private readonly _perfView;
|
|
32
32
|
/**
|
|
33
|
-
* Cached anchor hrtime as BigInt
|
|
34
|
-
*
|
|
33
|
+
* Cached anchor hrtime as the exact BigInt, so the JS timestamp delta stays
|
|
34
|
+
* correct after the f64 `anchorPerfNow` loses integer precision (~104 days uptime).
|
|
35
35
|
*/
|
|
36
36
|
private readonly _anchorHrtimeBigInt;
|
|
37
37
|
constructor(trace_id: TraceId, anchorEpochNanos: bigint, anchorPerfNow: number, anchorHrtimeBigInt: bigint, tracer: TracerLifecycleHooks);
|
|
@@ -36,8 +36,8 @@ export class TraceRoot {
|
|
|
36
36
|
_epochView;
|
|
37
37
|
_perfView;
|
|
38
38
|
/**
|
|
39
|
-
* Cached anchor hrtime as BigInt
|
|
40
|
-
*
|
|
39
|
+
* Cached anchor hrtime as the exact BigInt, so the JS timestamp delta stays
|
|
40
|
+
* correct after the f64 `anchorPerfNow` loses integer precision (~104 days uptime).
|
|
41
41
|
*/
|
|
42
42
|
_anchorHrtimeBigInt;
|
|
43
43
|
constructor(trace_id, anchorEpochNanos, anchorPerfNow, anchorHrtimeBigInt, tracer) {
|
|
@@ -128,7 +128,7 @@ export class TraceRoot {
|
|
|
128
128
|
export function createTraceRoot(trace_id, tracer) {
|
|
129
129
|
const anchorEpochNanos = BigInt(Date.now()) * 1000000n;
|
|
130
130
|
const anchorHrtimeBigInt = process.hrtime.bigint();
|
|
131
|
-
//
|
|
131
|
+
// Also store as f64 in the shared _system layout (WASM reads it without BigInt extraction)
|
|
132
132
|
const anchorPerfNow = Number(anchorHrtimeBigInt);
|
|
133
133
|
return new TraceRoot(createTraceId(trace_id), anchorEpochNanos, anchorPerfNow, anchorHrtimeBigInt, tracer);
|
|
134
134
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@smoothbricks/lmao",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.2.0",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"sideEffects": false,
|
|
6
6
|
"main": "./dist/index.js",
|
|
@@ -26,6 +26,12 @@
|
|
|
26
26
|
"import": "./dist/es.js",
|
|
27
27
|
"default": "./dist/es.js"
|
|
28
28
|
},
|
|
29
|
+
"./cloudflare": {
|
|
30
|
+
"types": "./dist/cloudflare.d.ts",
|
|
31
|
+
"development": "./src/cloudflare.ts",
|
|
32
|
+
"import": "./dist/cloudflare.js",
|
|
33
|
+
"default": "./dist/cloudflare.js"
|
|
34
|
+
},
|
|
29
35
|
"./tracers": {
|
|
30
36
|
"types": "./dist/lib/tracers/index.d.ts",
|
|
31
37
|
"development": "./src/lib/tracers/index.ts",
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Cloudflare trace-sink adapters (specs/lmao/01u_cloudflare_trace_segments.md).
|
|
3
|
+
*
|
|
4
|
+
* Two delivery lanes over the base Tracer lifecycle, no Cloudflare types required — every
|
|
5
|
+
* platform binding (Pipelines stream send, Queues producer, collector shard) is injected
|
|
6
|
+
* structurally:
|
|
7
|
+
* - billing-grade: CollectorClient (awaited within the request; ack = DO storage commit)
|
|
8
|
+
* - diagnostic: DiagnosticDrainTracer + transports (waitUntil, fire-and-forget)
|
|
9
|
+
* - both: ClassSplitTracer routes rows per delivery class via an injected classifier
|
|
10
|
+
*/
|
|
11
|
+
|
|
12
|
+
export {
|
|
13
|
+
type ClassSplitOptions,
|
|
14
|
+
ClassSplitTracer,
|
|
15
|
+
type DeliveryClass,
|
|
16
|
+
type DeliveryClassifier,
|
|
17
|
+
} from './lib/cloudflare/classSplit.js';
|
|
18
|
+
export {
|
|
19
|
+
COLLECTOR_ACK,
|
|
20
|
+
type CollectorAck,
|
|
21
|
+
type CollectorClient,
|
|
22
|
+
type CollectorSendFailure,
|
|
23
|
+
FakeCollectorClient,
|
|
24
|
+
} from './lib/cloudflare/collectorClient.js';
|
|
25
|
+
export {
|
|
26
|
+
type DiagnosticDrainOptions,
|
|
27
|
+
DiagnosticDrainTracer,
|
|
28
|
+
type DiagnosticTransport,
|
|
29
|
+
type PipelinesStreamSend,
|
|
30
|
+
PipelinesStreamTransport,
|
|
31
|
+
type QueuesFallbackOptions,
|
|
32
|
+
QueuesFallbackTransport,
|
|
33
|
+
type TraceChunkQueueMessage,
|
|
34
|
+
} from './lib/cloudflare/diagnosticDrain.js';
|
|
35
|
+
export { spanBufferToTraceRows, type TraceRow, type TraceRowValue } from './lib/cloudflare/traceRows.js';
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
//#region
|
|
1
|
+
//#region smoo/lmao!n/buffer-strategy
|
|
2
2
|
/**
|
|
3
3
|
* BufferStrategy - Abstraction for SpanBuffer memory management and Arrow conversion.
|
|
4
4
|
*
|
|
@@ -105,4 +105,4 @@ export interface BufferStrategy<T extends LogSchema = LogSchema> {
|
|
|
105
105
|
*/
|
|
106
106
|
releaseBuffer(buffer: AnySpanBuffer): void;
|
|
107
107
|
}
|
|
108
|
-
//#endregion
|
|
108
|
+
//#endregion smoo/lmao!n/buffer-strategy
|
|
@@ -0,0 +1,171 @@
|
|
|
1
|
+
// Configure Node.js timestamp implementation - MUST be first import
|
|
2
|
+
import '../../__tests__/test-helpers.js';
|
|
3
|
+
|
|
4
|
+
import { describe, expect, it } from 'bun:test';
|
|
5
|
+
import { createTestTracerOptions } from '../../__tests__/test-helpers.js';
|
|
6
|
+
import { defineLogSchema, defineOpContext, S } from '../../defineOpContext.js';
|
|
7
|
+
import { Err, Ok } from '../../result.js';
|
|
8
|
+
import { ClassSplitTracer, type DeliveryClassifier } from '../classSplit.js';
|
|
9
|
+
import { type CollectorSendFailure, FakeCollectorClient } from '../collectorClient.js';
|
|
10
|
+
import type { TraceRow } from '../traceRows.js';
|
|
11
|
+
|
|
12
|
+
const testSchema = defineLogSchema({
|
|
13
|
+
lane: S.category(),
|
|
14
|
+
});
|
|
15
|
+
|
|
16
|
+
const opContext = defineOpContext({ logSchema: testSchema });
|
|
17
|
+
const { defineOp } = opContext;
|
|
18
|
+
|
|
19
|
+
// Injected classifier seam — here: a row is billing-grade iff it carries the billing lane tag.
|
|
20
|
+
const classifyByLane: DeliveryClassifier = (row) => (row.lane === 'billing' ? 'billing-grade' : 'diagnostic');
|
|
21
|
+
|
|
22
|
+
const billableOp = defineOp('billable', (ctx) => {
|
|
23
|
+
ctx.tag.lane('billing');
|
|
24
|
+
return ctx.ok('charged');
|
|
25
|
+
});
|
|
26
|
+
|
|
27
|
+
const diagnosticOp = defineOp('diagnostic', (ctx) => ctx.ok('observed'));
|
|
28
|
+
|
|
29
|
+
interface Harness {
|
|
30
|
+
tracer: ClassSplitTracer<typeof opContext>;
|
|
31
|
+
collector: FakeCollectorClient;
|
|
32
|
+
diagnosticBatches: (readonly TraceRow[])[];
|
|
33
|
+
diagnosticErrors: unknown[];
|
|
34
|
+
lateFailures: CollectorSendFailure[];
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
function makeHarness(options?: { diagnosticsFail?: boolean }): Harness {
|
|
38
|
+
const collector = new FakeCollectorClient();
|
|
39
|
+
const diagnosticBatches: (readonly TraceRow[])[] = [];
|
|
40
|
+
const diagnosticErrors: unknown[] = [];
|
|
41
|
+
const lateFailures: CollectorSendFailure[] = [];
|
|
42
|
+
const tracer = new ClassSplitTracer(opContext, {
|
|
43
|
+
...createTestTracerOptions(),
|
|
44
|
+
collector,
|
|
45
|
+
diagnostics: {
|
|
46
|
+
send: async (rows) => {
|
|
47
|
+
if (options?.diagnosticsFail) throw new Error('diagnostic lane down');
|
|
48
|
+
diagnosticBatches.push(rows);
|
|
49
|
+
},
|
|
50
|
+
},
|
|
51
|
+
classify: classifyByLane,
|
|
52
|
+
onDiagnosticSendError: (error) => {
|
|
53
|
+
diagnosticErrors.push(error);
|
|
54
|
+
},
|
|
55
|
+
onLateBillableSendFailure: (failure) => {
|
|
56
|
+
lateFailures.push(failure);
|
|
57
|
+
},
|
|
58
|
+
});
|
|
59
|
+
return { tracer, collector, diagnosticBatches, diagnosticErrors, lateFailures };
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
function tick(): Promise<void> {
|
|
63
|
+
return new Promise((resolve) => setTimeout(resolve, 0));
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
describe('ClassSplitTracer', () => {
|
|
67
|
+
describe('settleBillable (awaited within the request)', () => {
|
|
68
|
+
it('returns Ok(null) when the request produced no billing-grade rows', async () => {
|
|
69
|
+
const { tracer, collector, diagnosticBatches } = makeHarness();
|
|
70
|
+
await tracer.trace('req', diagnosticOp);
|
|
71
|
+
|
|
72
|
+
const settled = await tracer.settleBillable();
|
|
73
|
+
expect(settled).toBeInstanceOf(Ok);
|
|
74
|
+
if (settled instanceof Ok) expect(settled.value).toBeNull();
|
|
75
|
+
expect(collector.acked).toHaveLength(0);
|
|
76
|
+
// Diagnostic rows are staged, not sent yet — that is flush()'s lazy job.
|
|
77
|
+
expect(diagnosticBatches).toHaveLength(0);
|
|
78
|
+
});
|
|
79
|
+
|
|
80
|
+
it('sends billing-grade rows to the collector and resolves only on its ack', async () => {
|
|
81
|
+
const { tracer, collector } = makeHarness();
|
|
82
|
+
collector.holdSends();
|
|
83
|
+
await tracer.trace('req', billableOp);
|
|
84
|
+
|
|
85
|
+
let settledFlag = false;
|
|
86
|
+
const pending = tracer.settleBillable().then((result) => {
|
|
87
|
+
settledFlag = true;
|
|
88
|
+
return result;
|
|
89
|
+
});
|
|
90
|
+
|
|
91
|
+
await tick();
|
|
92
|
+
// Send-before-response: the request must still be blocked here.
|
|
93
|
+
expect(settledFlag).toBe(false);
|
|
94
|
+
expect(collector.pendingCount).toBe(1);
|
|
95
|
+
|
|
96
|
+
collector.ackNext();
|
|
97
|
+
const settled = await pending;
|
|
98
|
+
expect(settled).toBeInstanceOf(Ok);
|
|
99
|
+
expect(collector.acked).toHaveLength(1);
|
|
100
|
+
expect(collector.acked[0].every((row) => row.lane === 'billing')).toBe(true);
|
|
101
|
+
// No billing-grade rows may remain buffered in the isolate.
|
|
102
|
+
expect(tracer.queue).toHaveLength(0);
|
|
103
|
+
});
|
|
104
|
+
|
|
105
|
+
it('returns Err when the collector is unavailable — the billable request must fail', async () => {
|
|
106
|
+
const { tracer, collector } = makeHarness();
|
|
107
|
+
collector.failNext({ code: 'collector-unavailable', message: 'shard down' });
|
|
108
|
+
await tracer.trace('req', billableOp);
|
|
109
|
+
|
|
110
|
+
const settled = await tracer.settleBillable();
|
|
111
|
+
expect(settled).toBeInstanceOf(Err);
|
|
112
|
+
if (settled instanceof Err) expect(settled.error.code).toBe('collector-unavailable');
|
|
113
|
+
});
|
|
114
|
+
|
|
115
|
+
it('routes rows of one trace to both lanes per the injected classifier', async () => {
|
|
116
|
+
const { tracer, collector, diagnosticBatches } = makeHarness();
|
|
117
|
+
await tracer.trace('req', billableOp);
|
|
118
|
+
await tracer.trace('req', diagnosticOp);
|
|
119
|
+
|
|
120
|
+
const settled = await tracer.settleBillable();
|
|
121
|
+
expect(settled).toBeInstanceOf(Ok);
|
|
122
|
+
expect(collector.acked).toHaveLength(1);
|
|
123
|
+
|
|
124
|
+
await tracer.flush();
|
|
125
|
+
expect(diagnosticBatches).toHaveLength(1);
|
|
126
|
+
const diagnosticRows = diagnosticBatches[0];
|
|
127
|
+
expect(diagnosticRows.length).toBeGreaterThan(0);
|
|
128
|
+
expect(diagnosticRows.every((row) => row.lane !== 'billing')).toBe(true);
|
|
129
|
+
});
|
|
130
|
+
});
|
|
131
|
+
|
|
132
|
+
describe('flush (lazy, waitUntil-schedulable)', () => {
|
|
133
|
+
it('sends staged + newly drained diagnostic rows and never rejects on transport failure', async () => {
|
|
134
|
+
const { tracer, diagnosticErrors } = makeHarness({ diagnosticsFail: true });
|
|
135
|
+
await tracer.trace('req', diagnosticOp);
|
|
136
|
+
|
|
137
|
+
await tracer.flush();
|
|
138
|
+
expect(diagnosticErrors).toHaveLength(1);
|
|
139
|
+
expect(diagnosticErrors[0]).toBeInstanceOf(Error);
|
|
140
|
+
});
|
|
141
|
+
|
|
142
|
+
it('still delivers billing rows found at flush time (settle skipped), awaited', async () => {
|
|
143
|
+
const { tracer, collector, lateFailures } = makeHarness();
|
|
144
|
+
await tracer.trace('req', billableOp);
|
|
145
|
+
|
|
146
|
+
await tracer.flush();
|
|
147
|
+
expect(collector.acked).toHaveLength(1);
|
|
148
|
+
expect(lateFailures).toHaveLength(0);
|
|
149
|
+
});
|
|
150
|
+
|
|
151
|
+
it('escalates late billable send failures via onLateBillableSendFailure without rejecting', async () => {
|
|
152
|
+
const { tracer, collector, lateFailures } = makeHarness();
|
|
153
|
+
collector.failNext({ code: 'collector-rejected', message: 'commit failed' });
|
|
154
|
+
await tracer.trace('req', billableOp);
|
|
155
|
+
|
|
156
|
+
await tracer.flush();
|
|
157
|
+
expect(lateFailures).toHaveLength(1);
|
|
158
|
+
expect(lateFailures[0].code).toBe('collector-rejected');
|
|
159
|
+
});
|
|
160
|
+
|
|
161
|
+
it('does not resend diagnostic rows on a second flush', async () => {
|
|
162
|
+
const { tracer, diagnosticBatches } = makeHarness();
|
|
163
|
+
await tracer.trace('req', diagnosticOp);
|
|
164
|
+
|
|
165
|
+
await tracer.settleBillable();
|
|
166
|
+
await tracer.flush();
|
|
167
|
+
await tracer.flush();
|
|
168
|
+
expect(diagnosticBatches).toHaveLength(1);
|
|
169
|
+
});
|
|
170
|
+
});
|
|
171
|
+
});
|
|
@@ -0,0 +1,104 @@
|
|
|
1
|
+
// Configure Node.js timestamp implementation - MUST be first import
|
|
2
|
+
import '../../__tests__/test-helpers.js';
|
|
3
|
+
|
|
4
|
+
import { describe, expect, it } from 'bun:test';
|
|
5
|
+
import { Err, Ok } from '../../result.js';
|
|
6
|
+
import { COLLECTOR_ACK, FakeCollectorClient } from '../collectorClient.js';
|
|
7
|
+
import type { TraceRow } from '../traceRows.js';
|
|
8
|
+
|
|
9
|
+
const rows: TraceRow[] = [
|
|
10
|
+
{
|
|
11
|
+
trace_id: 't1',
|
|
12
|
+
span_id: 1,
|
|
13
|
+
parent_span_id: 0,
|
|
14
|
+
row_index: 0,
|
|
15
|
+
entry_type: 1,
|
|
16
|
+
timestamp_ns: 1_000_000,
|
|
17
|
+
message: 'op',
|
|
18
|
+
},
|
|
19
|
+
];
|
|
20
|
+
|
|
21
|
+
function tick(): Promise<void> {
|
|
22
|
+
return new Promise((resolve) => setTimeout(resolve, 0));
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
describe('CollectorClient contract (via FakeCollectorClient)', () => {
|
|
26
|
+
describe('ack semantics', () => {
|
|
27
|
+
it('resolves Ok with the durability ack and records the batch', async () => {
|
|
28
|
+
const client = new FakeCollectorClient();
|
|
29
|
+
const result = await client.send(rows);
|
|
30
|
+
|
|
31
|
+
expect(result).toBeInstanceOf(Ok);
|
|
32
|
+
if (result instanceof Ok) expect(result.value).toBe(COLLECTOR_ACK);
|
|
33
|
+
expect(client.acked).toEqual([rows]);
|
|
34
|
+
});
|
|
35
|
+
|
|
36
|
+
it('send resolves ONLY when the collector acks (durability point)', async () => {
|
|
37
|
+
const client = new FakeCollectorClient();
|
|
38
|
+
client.holdSends();
|
|
39
|
+
|
|
40
|
+
let settled = false;
|
|
41
|
+
const pending = client.send(rows).then((result) => {
|
|
42
|
+
settled = true;
|
|
43
|
+
return result;
|
|
44
|
+
});
|
|
45
|
+
|
|
46
|
+
await tick();
|
|
47
|
+
// No ack yet — the send-before-response contract means the caller is still blocked here.
|
|
48
|
+
expect(settled).toBe(false);
|
|
49
|
+
expect(client.pendingCount).toBe(1);
|
|
50
|
+
expect(client.acked).toHaveLength(0);
|
|
51
|
+
|
|
52
|
+
client.ackNext();
|
|
53
|
+
const result = await pending;
|
|
54
|
+
expect(settled).toBe(true);
|
|
55
|
+
expect(result).toBeInstanceOf(Ok);
|
|
56
|
+
expect(client.acked).toEqual([rows]);
|
|
57
|
+
});
|
|
58
|
+
|
|
59
|
+
it('supports injected ack latency', async () => {
|
|
60
|
+
const client = new FakeCollectorClient();
|
|
61
|
+
client.setLatency(20);
|
|
62
|
+
|
|
63
|
+
const start = Date.now();
|
|
64
|
+
const result = await client.send(rows);
|
|
65
|
+
expect(result).toBeInstanceOf(Ok);
|
|
66
|
+
expect(Date.now() - start).toBeGreaterThanOrEqual(15);
|
|
67
|
+
});
|
|
68
|
+
});
|
|
69
|
+
|
|
70
|
+
describe('failure semantics', () => {
|
|
71
|
+
it('returns Err (never throws) on injected failure — the billable request must fail', async () => {
|
|
72
|
+
const client = new FakeCollectorClient();
|
|
73
|
+
client.failNext({ code: 'collector-unavailable', message: 'shard down' });
|
|
74
|
+
|
|
75
|
+
const result = await client.send(rows);
|
|
76
|
+
expect(result).toBeInstanceOf(Err);
|
|
77
|
+
if (result instanceof Err) expect(result.error.code).toBe('collector-unavailable');
|
|
78
|
+
// A failed send is NOT durable — nothing may be recorded as acked.
|
|
79
|
+
expect(client.acked).toHaveLength(0);
|
|
80
|
+
});
|
|
81
|
+
|
|
82
|
+
it('held sends can be rejected with an operational failure', async () => {
|
|
83
|
+
const client = new FakeCollectorClient();
|
|
84
|
+
client.holdSends();
|
|
85
|
+
|
|
86
|
+
const pending = client.send(rows);
|
|
87
|
+
client.rejectNext({ code: 'collector-rejected', message: 'storage commit failed' });
|
|
88
|
+
|
|
89
|
+
const result = await pending;
|
|
90
|
+
expect(result).toBeInstanceOf(Err);
|
|
91
|
+
if (result instanceof Err) expect(result.error.code).toBe('collector-rejected');
|
|
92
|
+
expect(client.acked).toHaveLength(0);
|
|
93
|
+
});
|
|
94
|
+
|
|
95
|
+
it('recovers after a failure: subsequent sends ack normally', async () => {
|
|
96
|
+
const client = new FakeCollectorClient();
|
|
97
|
+
client.failNext({ code: 'collector-unavailable', message: 'blip' });
|
|
98
|
+
|
|
99
|
+
expect(await client.send(rows)).toBeInstanceOf(Err);
|
|
100
|
+
expect(await client.send(rows)).toBeInstanceOf(Ok);
|
|
101
|
+
expect(client.acked).toEqual([rows]);
|
|
102
|
+
});
|
|
103
|
+
});
|
|
104
|
+
});
|