@lunora/runtime 1.0.0-alpha.25 → 1.0.0-alpha.26

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/index.d.mts CHANGED
@@ -682,6 +682,16 @@ interface ObservabilityEvent {
682
682
  ok: boolean;
683
683
  /** Shard key for single-shard calls; absent for fan-outs. */
684
684
  shardKey?: string;
685
+ /**
686
+ * W3C trace context for this dispatch, generated once at dispatch entry (32-
687
+ * and 16-hex). A sink (e.g. `otlpSink`) reuses these for the dispatch's span
688
+ * instead of minting fresh ids, and the runtime propagates them to the shard
689
+ * as a `traceparent` so a container the handler calls can stitch its spans
690
+ * under the same trace. Absent on paths that don't originate a trace (a sink
691
+ * falls back to random ids).
692
+ */
693
+ spanId?: string;
694
+ traceId?: string;
685
695
  }
686
696
  /** Severity of a {@link LogEvent}, mirroring the usual console levels. */
687
697
  type LogLevel = "debug" | "error" | "info" | "log" | "warn";
package/dist/index.d.ts CHANGED
@@ -682,6 +682,16 @@ interface ObservabilityEvent {
682
682
  ok: boolean;
683
683
  /** Shard key for single-shard calls; absent for fan-outs. */
684
684
  shardKey?: string;
685
+ /**
686
+ * W3C trace context for this dispatch, generated once at dispatch entry (32-
687
+ * and 16-hex). A sink (e.g. `otlpSink`) reuses these for the dispatch's span
688
+ * instead of minting fresh ids, and the runtime propagates them to the shard
689
+ * as a `traceparent` so a container the handler calls can stitch its spans
690
+ * under the same trace. Absent on paths that don't originate a trace (a sink
691
+ * falls back to random ids).
692
+ */
693
+ spanId?: string;
694
+ traceId?: string;
685
695
  }
686
696
  /** Severity of a {@link LogEvent}, mirroring the usual console levels. */
687
697
  type LogLevel = "debug" | "error" | "info" | "log" | "warn";
package/dist/index.mjs CHANGED
@@ -1,10 +1,10 @@
1
1
  export { toAirbyteMessages, toFivetranResponse } from './packem_shared/toAirbyteMessages-DrHdplb4.mjs';
2
- export { composeWorker, createLunoraHandler, createWorker, defineRpcEnvelope, resolveLunoraOptions, withFrameworkWorker } from './packem_shared/composeWorker-CxwkPZUl.mjs';
2
+ export { composeWorker, createLunoraHandler, createWorker, defineRpcEnvelope, resolveLunoraOptions, withFrameworkWorker } from './packem_shared/composeWorker-BMxMYwKq.mjs';
3
3
  export { createCrossShardRelationCapabilities } from './packem_shared/createCrossShardRelationCapabilities-CbcWjkAn.mjs';
4
4
  export { DEFAULT_REGISTRY_CACHE_TTL_MS, SHARD_REGISTRY_DO_NAME, createDynamicShardRegistry } from './packem_shared/DEFAULT_REGISTRY_CACHE_TTL_MS-B3pA7aXp.mjs';
5
5
  export { LunoraError, toErrorResponse } from './packem_shared/LunoraError-Bpb9EFJ3.mjs';
6
6
  export { emitLogEvent, emitRpcEvent } from './packem_shared/emitLogEvent-pEdtqAK8.mjs';
7
- export { analyticsEngineSink, combineSinks, consoleSink, otlpSink, sentrySink, webhookSink } from './packem_shared/analyticsEngineSink-BpJlA7e9.mjs';
7
+ export { analyticsEngineSink, combineSinks, consoleSink, otlpSink, sentrySink, webhookSink } from './packem_shared/analyticsEngineSink-F-5ZAdUA.mjs';
8
8
  export { createQueryCoordinator, createStaticShardRegistry, mergeStrategyForAggregate } from './packem_shared/createQueryCoordinator-DNCJzOZE.mjs';
9
9
  export { applyJurisdiction, resolveShard } from './packem_shared/applyJurisdiction-BkZtTkct.mjs';
10
10
  export { decorateResponse, enforceOrigin, handleCorsPreflight, resolveSecurity } from './packem_shared/decorateResponse-DRWQFNhF.mjs';
@@ -1,81 +1,4 @@
1
- const OTLP_SEVERITY = {
2
- debug: 5,
3
- // DEBUG
4
- error: 17,
5
- // ERROR
6
- info: 9,
7
- // INFO
8
- log: 9,
9
- // INFO
10
- warn: 13
11
- // WARN
12
- };
13
- const otlpUnixNano = (ms) => `${String(Math.round(ms))}000000`;
14
- const otlpRandomHex = (bytes) => {
15
- const buffer = new Uint8Array(bytes);
16
- crypto.getRandomValues(buffer);
17
- let hex = "";
18
- for (const byte of buffer) {
19
- hex += byte.toString(16).padStart(2, "0");
20
- }
21
- return hex;
22
- };
23
- const encodeAttribute = (key, value) => {
24
- if (typeof value === "boolean") {
25
- return { key, value: { boolValue: value } };
26
- }
27
- if (typeof value === "number") {
28
- if (!Number.isFinite(value)) {
29
- return { key, value: { stringValue: String(value) } };
30
- }
31
- return Number.isSafeInteger(value) ? { key, value: { intValue: String(value) } } : { key, value: { doubleValue: value } };
32
- }
33
- return { key, value: { stringValue: value } };
34
- };
35
- const mergeHeaders = (defaults, overrides, token) => {
36
- const merged = {};
37
- const seen = /* @__PURE__ */ new Map();
38
- const put = (name, value) => {
39
- const lower = name.toLowerCase();
40
- const existing = seen.get(lower);
41
- if (existing === void 0) {
42
- seen.set(lower, name);
43
- merged[name] = value;
44
- } else {
45
- merged[existing] = value;
46
- }
47
- };
48
- for (const [name, value] of Object.entries(defaults)) {
49
- put(name, value);
50
- }
51
- for (const [name, value] of Object.entries(overrides ?? {})) {
52
- put(name, value);
53
- }
54
- if (token !== void 0 && token.length > 0) {
55
- put("authorization", `Bearer ${token}`);
56
- }
57
- return merged;
58
- };
59
- const wrapResourceSpans = (span, scopeName, serviceName) => {
60
- return {
61
- resourceSpans: [
62
- {
63
- resource: { attributes: [encodeAttribute("service.name", serviceName)] },
64
- scopeSpans: [{ scope: { name: scopeName }, spans: [span] }]
65
- }
66
- ]
67
- };
68
- };
69
- const wrapResourceLogs = (logRecord, scopeName, serviceName) => {
70
- return {
71
- resourceLogs: [
72
- {
73
- resource: { attributes: [encodeAttribute("service.name", serviceName)] },
74
- scopeLogs: [{ logRecords: [logRecord], scope: { name: scopeName } }]
75
- }
76
- ]
77
- };
78
- };
1
+ import { m as mergeHeaders, o as otlpRandomHex, w as wrapResourceSpans, a as wrapResourceLogs, e as encodeAttribute, O as OTLP_SEVERITY, c as otlpUnixNano } from './otlp-D_YzGXu1.mjs';
79
2
 
80
3
  const shouldSkip = (event, onlyErrors) => onlyErrors === true && event.ok;
81
4
  const otlpTraceBody = (event, serviceName, endMs) => {
@@ -99,11 +22,13 @@ const otlpTraceBody = (event, serviceName, endMs) => {
99
22
  // SPAN_KIND_SERVER — a dispatched RPC is server-side request handling.
100
23
  kind: 2,
101
24
  name: event.functionPath,
102
- spanId: otlpRandomHex(8),
25
+ // Reuse the dispatch's trace context when the runtime set it (so this span
26
+ // shares the id it propagated as `traceparent`); else mint fresh ids.
27
+ spanId: event.spanId ?? otlpRandomHex(8),
103
28
  startTimeUnixNano: otlpUnixNano(endMs - event.durationMs),
104
29
  // STATUS_CODE_OK (1) / STATUS_CODE_ERROR (2).
105
30
  status: event.ok ? { code: 1 } : { code: 2, message: event.error?.message ?? "" },
106
- traceId: otlpRandomHex(16)
31
+ traceId: event.traceId ?? otlpRandomHex(16)
107
32
  };
108
33
  return wrapResourceSpans(span, "@lunora/runtime", serviceName);
109
34
  };
@@ -1,5 +1,6 @@
1
1
  import { isLunoraError, toErrorBody } from '@lunora/errors';
2
2
  import { NOOP_EXECUTION_CONTEXT } from './NOOP_EXECUTION_CONTEXT-CCTu0Bf1.mjs';
3
+ import { o as otlpRandomHex, b as buildTraceparent } from './otlp-D_YzGXu1.mjs';
3
4
  import { LunoraError, toErrorResponse } from './LunoraError-Bpb9EFJ3.mjs';
4
5
  import { wrapResolverWithContract } from './composeIdentityResolvers-XGjO7V1J.mjs';
5
6
  export { composeIdentityResolvers, routeIdentityResolvers } from './composeIdentityResolvers-XGjO7V1J.mjs';
@@ -2605,9 +2606,11 @@ const createWorker = (options) => {
2605
2606
  const dispatchSingleShard = async (functionPath, args, shardKey, forwardedHeaders, sinkContext) => {
2606
2607
  const rpcStartedAt = Date.now();
2607
2608
  const { observability } = options;
2609
+ const traceId = otlpRandomHex(16);
2610
+ const spanId = otlpRandomHex(8);
2608
2611
  const forwarded = new Request(`https://shard.internal/rpc`, {
2609
2612
  body: JSON.stringify({ args, functionPath }),
2610
- headers: forwardedHeaders,
2613
+ headers: { ...forwardedHeaders, traceparent: buildTraceparent(traceId, spanId) },
2611
2614
  method: "POST"
2612
2615
  });
2613
2616
  try {
@@ -2619,13 +2622,15 @@ const createWorker = (options) => {
2619
2622
  functionPath,
2620
2623
  ok: response.ok,
2621
2624
  shardKey,
2625
+ spanId,
2626
+ traceId,
2622
2627
  ...response.ok ? {} : { error: { code: "SHARD_ERROR", message: `shard returned ${String(response.status)}`, status: response.status } }
2623
2628
  },
2624
2629
  sinkContext
2625
2630
  );
2626
2631
  return response;
2627
2632
  } catch (error) {
2628
- emitRpcEvent(observability, buildErrorEvent(functionPath, Date.now() - rpcStartedAt, error, { shardKey }), sinkContext);
2633
+ emitRpcEvent(observability, { ...buildErrorEvent(functionPath, Date.now() - rpcStartedAt, error, { shardKey }), spanId, traceId }, sinkContext);
2629
2634
  throw error;
2630
2635
  }
2631
2636
  };
@@ -0,0 +1,81 @@
1
+ const OTLP_SEVERITY = {
2
+ debug: 5,
3
+ // DEBUG
4
+ error: 17,
5
+ // ERROR
6
+ info: 9,
7
+ // INFO
8
+ log: 9,
9
+ // INFO
10
+ warn: 13
11
+ // WARN
12
+ };
13
+ const otlpUnixNano = (ms) => `${String(Math.round(ms))}000000`;
14
+ const otlpRandomHex = (bytes) => {
15
+ const buffer = new Uint8Array(bytes);
16
+ crypto.getRandomValues(buffer);
17
+ let hex = "";
18
+ for (const byte of buffer) {
19
+ hex += byte.toString(16).padStart(2, "0");
20
+ }
21
+ return hex;
22
+ };
23
+ const buildTraceparent = (traceId, spanId) => `00-${traceId}-${spanId}-01`;
24
+ const encodeAttribute = (key, value) => {
25
+ if (typeof value === "boolean") {
26
+ return { key, value: { boolValue: value } };
27
+ }
28
+ if (typeof value === "number") {
29
+ if (!Number.isFinite(value)) {
30
+ return { key, value: { stringValue: String(value) } };
31
+ }
32
+ return Number.isSafeInteger(value) ? { key, value: { intValue: String(value) } } : { key, value: { doubleValue: value } };
33
+ }
34
+ return { key, value: { stringValue: value } };
35
+ };
36
+ const mergeHeaders = (defaults, overrides, token) => {
37
+ const merged = {};
38
+ const seen = /* @__PURE__ */ new Map();
39
+ const put = (name, value) => {
40
+ const lower = name.toLowerCase();
41
+ const existing = seen.get(lower);
42
+ if (existing === void 0) {
43
+ seen.set(lower, name);
44
+ merged[name] = value;
45
+ } else {
46
+ merged[existing] = value;
47
+ }
48
+ };
49
+ for (const [name, value] of Object.entries(defaults)) {
50
+ put(name, value);
51
+ }
52
+ for (const [name, value] of Object.entries(overrides ?? {})) {
53
+ put(name, value);
54
+ }
55
+ if (token !== void 0 && token.length > 0) {
56
+ put("authorization", `Bearer ${token}`);
57
+ }
58
+ return merged;
59
+ };
60
+ const wrapResourceSpans = (span, scopeName, serviceName) => {
61
+ return {
62
+ resourceSpans: [
63
+ {
64
+ resource: { attributes: [encodeAttribute("service.name", serviceName)] },
65
+ scopeSpans: [{ scope: { name: scopeName }, spans: [span] }]
66
+ }
67
+ ]
68
+ };
69
+ };
70
+ const wrapResourceLogs = (logRecord, scopeName, serviceName) => {
71
+ return {
72
+ resourceLogs: [
73
+ {
74
+ resource: { attributes: [encodeAttribute("service.name", serviceName)] },
75
+ scopeLogs: [{ logRecords: [logRecord], scope: { name: scopeName } }]
76
+ }
77
+ ]
78
+ };
79
+ };
80
+
81
+ export { OTLP_SEVERITY as O, wrapResourceLogs as a, buildTraceparent as b, otlpUnixNano as c, encodeAttribute as e, mergeHeaders as m, otlpRandomHex as o, wrapResourceSpans as w };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@lunora/runtime",
3
- "version": "1.0.0-alpha.25",
3
+ "version": "1.0.0-alpha.26",
4
4
  "description": "Lunora Worker runtime: the RPC router, shard resolver, and query coordinator",
5
5
  "keywords": [
6
6
  "cloudflare",