@lunora/do 1.0.0-alpha.34 → 1.0.0-alpha.35

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.
@@ -0,0 +1,165 @@
1
+ import { isLunoraError } from '@lunora/errors';
2
+
3
+ const stringifyFieldValue = (value) => {
4
+ if (typeof value === "string") {
5
+ return value;
6
+ }
7
+ try {
8
+ return JSON.stringify(value) ?? String(value);
9
+ } catch {
10
+ return String(value);
11
+ }
12
+ };
13
+ const coerceFieldValue = (value) => typeof value === "boolean" || typeof value === "number" || typeof value === "string" ? value : stringifyFieldValue(value);
14
+ const normalizeLogFields = (perCall, bound) => {
15
+ if (perCall === void 0 && bound === void 0) {
16
+ return void 0;
17
+ }
18
+ const merged = {};
19
+ if (bound !== void 0) {
20
+ for (const [key, value] of Object.entries(bound)) {
21
+ merged[key] = coerceFieldValue(value);
22
+ }
23
+ }
24
+ if (perCall !== void 0) {
25
+ for (const [key, value] of Object.entries(perCall)) {
26
+ merged[key] = coerceFieldValue(value);
27
+ }
28
+ }
29
+ return Object.keys(merged).length === 0 ? void 0 : merged;
30
+ };
31
+
32
+ const otlpRandomHex = (bytes) => {
33
+ const buffer = new Uint8Array(bytes);
34
+ crypto.getRandomValues(buffer);
35
+ let hex = "";
36
+ for (const byte of buffer) {
37
+ hex += byte.toString(16).padStart(2, "0");
38
+ }
39
+ return hex;
40
+ };
41
+ const HEX_ONLY = /^[0-9a-f]+$/;
42
+ const parseTraceparent = (header) => {
43
+ if (header === null || header === void 0) {
44
+ return void 0;
45
+ }
46
+ const parts = header.trim().toLowerCase().split("-");
47
+ const [version, traceId, parentSpanId, flags] = parts;
48
+ if (parts.length < 4 || version === void 0 || version.length !== 2 || !HEX_ONLY.test(version) || version === "ff" || // Version 00 forbids trailing fields; only a future version may carry them.
49
+ version === "00" && parts.length !== 4 || traceId === void 0 || parentSpanId === void 0 || flags === void 0 || flags.length !== 2 || !HEX_ONLY.test(flags) || traceId.length !== 32 || parentSpanId.length !== 16 || !HEX_ONLY.test(traceId) || !HEX_ONLY.test(parentSpanId) || traceId === "00000000000000000000000000000000" || parentSpanId === "0000000000000000") {
50
+ return void 0;
51
+ }
52
+ return { parentSpanId, traceId };
53
+ };
54
+
55
+ const resolveTraceAnchor = (traceparent) => {
56
+ const inbound = parseTraceparent(traceparent);
57
+ return { rootSpanId: inbound?.parentSpanId ?? otlpRandomHex(8), traceId: inbound?.traceId ?? otlpRandomHex(16) };
58
+ };
59
+ const toErrorType = (error) => {
60
+ if (isLunoraError(error)) {
61
+ return error.code;
62
+ }
63
+ return error instanceof Error ? error.constructor.name : "Error";
64
+ };
65
+
66
+ const createTracer = (deps) => {
67
+ const { anchor, functionPath, record, shardKey, userId } = deps;
68
+ const tracerFor = (parentSpanId) => async (name, function_, attributes) => {
69
+ const spanId = otlpRandomHex(8);
70
+ const startTs = Date.now();
71
+ const normalized = normalizeLogFields(attributes);
72
+ let ok = true;
73
+ let error;
74
+ try {
75
+ return await function_(tracerFor(spanId));
76
+ } catch (error_) {
77
+ ok = false;
78
+ error = {
79
+ message: error_ instanceof Error ? error_.message : String(error_),
80
+ // Prefer a LunoraError's stable `code` over the class name so
81
+ // spans group by the same taxonomy the RPC spans use.
82
+ type: toErrorType(error_)
83
+ };
84
+ throw error_;
85
+ } finally {
86
+ try {
87
+ record({
88
+ ...normalized === void 0 ? {} : { attributes: normalized },
89
+ durationMs: Date.now() - startTs,
90
+ ...error === void 0 ? {} : { error },
91
+ functionPath,
92
+ name,
93
+ ok,
94
+ parentSpanId,
95
+ shardKey,
96
+ spanId,
97
+ startTs,
98
+ traceId: anchor.traceId,
99
+ userId: userId()
100
+ });
101
+ } catch {
102
+ }
103
+ }
104
+ };
105
+ return tracerFor(anchor.rootSpanId);
106
+ };
107
+ const createMetrics = (deps) => {
108
+ const { functionPath, record, shardKey } = deps;
109
+ const emit = (kind, name, value, attributes) => {
110
+ if (!Number.isFinite(value)) {
111
+ return;
112
+ }
113
+ const normalized = normalizeLogFields(attributes);
114
+ try {
115
+ record({
116
+ ...normalized === void 0 ? {} : { attributes: normalized },
117
+ functionPath,
118
+ kind,
119
+ name,
120
+ shardKey,
121
+ ts: Date.now(),
122
+ value
123
+ });
124
+ } catch {
125
+ }
126
+ };
127
+ return {
128
+ count: (name, value = 1, attributes) => {
129
+ emit("counter", name, value, attributes);
130
+ },
131
+ gauge: (name, value, attributes) => {
132
+ emit("gauge", name, value, attributes);
133
+ },
134
+ record: (name, value, attributes) => {
135
+ emit("histogram", name, value, attributes);
136
+ }
137
+ };
138
+ };
139
+ const dispatchRootSpan = (input) => {
140
+ const { anchor, durationMs, failure, functionPath, shardKey, startTs, userId } = input;
141
+ return {
142
+ dispatch: true,
143
+ durationMs,
144
+ ...failure === void 0 ? {} : {
145
+ error: {
146
+ message: failure.thrown instanceof Error ? failure.thrown.message : String(failure.thrown),
147
+ type: toErrorType(failure.thrown)
148
+ }
149
+ },
150
+ functionPath,
151
+ name: functionPath,
152
+ ok: failure === void 0,
153
+ // Empty: this span IS the trace's root locally. The worker's own RPC span
154
+ // sits above it in a full collector-side trace, but it is not in this
155
+ // buffer, so naming it here would dangle.
156
+ parentSpanId: "",
157
+ shardKey,
158
+ spanId: anchor.rootSpanId,
159
+ startTs,
160
+ traceId: anchor.traceId,
161
+ userId
162
+ };
163
+ };
164
+
165
+ export { createTracer as a, createMetrics as c, dispatchRootSpan as d, normalizeLogFields as n, resolveTraceAnchor as r };
@@ -0,0 +1 @@
1
+ export { c as createMetrics, a as createTracer, d as dispatchRootSpan } from './context-telemetry-DWfYDxCS.mjs';
@@ -1,5 +1,5 @@
1
1
  import { LunoraError } from '@lunora/errors';
2
- import { RELATION_FUNCTION_PREFIX } from './ADMIN_FUNCTIONS-CAHLZMj8.mjs';
2
+ import { RELATION_FUNCTION_PREFIX } from './ADMIN_FUNCTIONS-CqVmUVNs.mjs';
3
3
 
4
4
  const serveRelationFanout = async (schema, database, functionPath, args) => {
5
5
  const table = typeof args["table"] === "string" ? args["table"] : "";
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@lunora/do",
3
- "version": "1.0.0-alpha.34",
3
+ "version": "1.0.0-alpha.35",
4
4
  "description": "Lunora Durable Objects: ShardDO (SQLite, OCC, hibernated WebSocket subscriptions) and SessionDO",
5
5
  "keywords": [
6
6
  "cloudflare",