@lunora/runtime 1.0.0-alpha.35 → 1.0.0-alpha.36

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.
@@ -1,4 +1,4 @@
1
- import { m as mergeHeaders, w as wrapResourceSpans, o as otlpRandomHex, a as wrapResourceMetrics, c as wrapResourceLogs, e as encodeAttribute, O as OTLP_SEVERITY, d as otlpUnixNano } from './otlp-DKZJCkdD.mjs';
1
+ import { w as wrapResourceSpans, o as otlpRandomHex, c as wrapResourceMetrics, e as wrapResourceLogs, f as encodeAttribute, g as otlpUnixNano, O as OTLP_SEVERITY, h as mergeHeaders, m as mergeResourceAttributes } from './otlp-resource-Dow6-F_u.mjs';
2
2
 
3
3
  const stringifyFieldValue = (value) => {
4
4
  if (typeof value === "string") {
@@ -12,12 +12,31 @@ const stringifyFieldValue = (value) => {
12
12
  };
13
13
  const coerceFieldValue = (value) => typeof value === "boolean" || typeof value === "number" || typeof value === "string" ? value : stringifyFieldValue(value);
14
14
 
15
- const shouldSkip = (event, onlyErrors) => onlyErrors === true && event.ok;
16
- const otlpTraceBody = (event, serviceName, endMs) => {
15
+ const otlpTraceBody = (event, serviceName, endMs, resourceAttributes) => {
17
16
  const attributes = [encodeAttribute("lunora.function_path", event.functionPath), encodeAttribute("lunora.ok", event.ok)];
17
+ if (event.method !== void 0) {
18
+ attributes.push(encodeAttribute("http.request.method", event.method));
19
+ }
20
+ if (event.path !== void 0) {
21
+ attributes.push(encodeAttribute("url.path", event.path));
22
+ }
23
+ attributes.push(encodeAttribute("http.route", event.functionPath));
24
+ if (event.scheme !== void 0) {
25
+ attributes.push(encodeAttribute("url.scheme", event.scheme));
26
+ }
27
+ if (event.host !== void 0) {
28
+ attributes.push(encodeAttribute("server.address", event.host));
29
+ }
30
+ if (event.port !== void 0) {
31
+ attributes.push(encodeAttribute("server.port", event.port));
32
+ }
33
+ if (event.userAgent !== void 0) {
34
+ attributes.push(encodeAttribute("user_agent.original", event.userAgent));
35
+ }
18
36
  if (event.shardKey !== void 0) {
19
37
  attributes.push(encodeAttribute("lunora.shard_key", event.shardKey));
20
38
  }
39
+ attributes.push(encodeAttribute("http.response.status_code", event.error?.status ?? 200));
21
40
  if (event.error) {
22
41
  attributes.push(encodeAttribute("error.type", event.error.code), encodeAttribute("lunora.error_status", event.error.status));
23
42
  }
@@ -42,7 +61,22 @@ const otlpTraceBody = (event, serviceName, endMs) => {
42
61
  status: event.ok ? { code: 1 } : { code: 2, message: event.error?.message ?? "" },
43
62
  traceId: event.traceId ?? otlpRandomHex(16)
44
63
  };
45
- return wrapResourceSpans(span, "@lunora/runtime", serviceName);
64
+ if (event.parentSpanId !== void 0) {
65
+ span.parentSpanId = event.parentSpanId;
66
+ }
67
+ if (event.traceFlags !== void 0) {
68
+ span.flags = event.traceFlags;
69
+ }
70
+ if (event.error) {
71
+ span.events = [
72
+ {
73
+ attributes: [encodeAttribute("exception.type", event.error.code), encodeAttribute("exception.message", event.error.message)],
74
+ name: "exception",
75
+ timeUnixNano: otlpUnixNano(endMs)
76
+ }
77
+ ];
78
+ }
79
+ return wrapResourceSpans(span, "@lunora/runtime", serviceName, resourceAttributes);
46
80
  };
47
81
  const encodeSignalAttributes = (reserved, caller) => {
48
82
  const byKey = /* @__PURE__ */ new Map([["lunora.function_path", encodeAttribute("lunora.function_path", reserved.functionPath)]]);
@@ -60,7 +94,7 @@ const encodeSignalAttributes = (reserved, caller) => {
60
94
  }
61
95
  return [...byKey.values()];
62
96
  };
63
- const otlpSpanBody = (event, serviceName) => {
97
+ const otlpSpanBody = (event, serviceName, resourceAttributes) => {
64
98
  const span = {
65
99
  attributes: encodeSignalAttributes(
66
100
  { errorType: event.error?.type, functionPath: event.functionPath, shardKey: event.shardKey, userId: event.userId },
@@ -80,14 +114,14 @@ const otlpSpanBody = (event, serviceName) => {
80
114
  status: event.ok ? { code: 1 } : { code: 2, message: event.error?.message ?? "" },
81
115
  traceId: event.traceId
82
116
  };
83
- return wrapResourceSpans(span, "@lunora/runtime", serviceName);
117
+ return wrapResourceSpans(span, "@lunora/runtime", serviceName, resourceAttributes);
84
118
  };
85
- const otlpMetricBody = (event, serviceName) => {
119
+ const otlpMetricBody = (event, serviceName, resourceAttributes) => {
86
120
  const timeUnixNano = otlpUnixNano(event.ts);
87
121
  const attributes = encodeSignalAttributes({ functionPath: event.functionPath, shardKey: event.shardKey }, event.attributes);
88
122
  const dataPoint = { asDouble: event.value, attributes, timeUnixNano };
89
123
  if (event.kind === "gauge") {
90
- return wrapResourceMetrics({ gauge: { dataPoints: [dataPoint] }, name: event.name }, "@lunora/runtime", serviceName);
124
+ return wrapResourceMetrics({ gauge: { dataPoints: [dataPoint] }, name: event.name }, "@lunora/runtime", serviceName, resourceAttributes);
91
125
  }
92
126
  if (event.kind === "histogram") {
93
127
  return wrapResourceMetrics(
@@ -112,16 +146,18 @@ const otlpMetricBody = (event, serviceName) => {
112
146
  name: event.name
113
147
  },
114
148
  "@lunora/runtime",
115
- serviceName
149
+ serviceName,
150
+ resourceAttributes
116
151
  );
117
152
  }
118
153
  return wrapResourceMetrics(
119
154
  { name: event.name, sum: { aggregationTemporality: 1, dataPoints: [dataPoint], isMonotonic: true } },
120
155
  "@lunora/runtime",
121
- serviceName
156
+ serviceName,
157
+ resourceAttributes
122
158
  );
123
159
  };
124
- const otlpLogBody = (event, serviceName) => {
160
+ const otlpLogBody = (event, serviceName, resourceAttributes) => {
125
161
  const logRecord = {
126
162
  // Caller-supplied structured fields become log-record attributes so a
127
163
  // pipeline can filter/index on them; precedence per `encodeSignalAttributes`.
@@ -137,7 +173,7 @@ const otlpLogBody = (event, serviceName) => {
137
173
  if (event.spanId !== void 0) {
138
174
  logRecord.spanId = event.spanId;
139
175
  }
140
- return wrapResourceLogs(logRecord, "@lunora/runtime", serviceName);
176
+ return wrapResourceLogs(logRecord, "@lunora/runtime", serviceName, resourceAttributes);
141
177
  };
142
178
  const OTLP_GZIP_THRESHOLD = 1024;
143
179
  const gzipEncode = async (text) => {
@@ -155,6 +191,8 @@ const otlpPost = (url, body, headers, context) => {
155
191
  } catch {
156
192
  }
157
193
  };
194
+
195
+ const shouldSkip = (event, onlyErrors) => onlyErrors === true && event.ok;
158
196
  const consoleSink = (options = {}) => {
159
197
  const { onlyErrors } = options;
160
198
  return {
@@ -312,8 +350,27 @@ const pipelineLogSink = (options) => {
312
350
  };
313
351
  };
314
352
  const otlpSink = (options) => {
315
- const { endpoint, headers, onlyErrors, token } = options;
353
+ const { deploymentEnvironment, detectResources, endpoint, headers, onlyErrors, resourceAttributes, serviceNamespace, serviceVersion, token } = options;
316
354
  const serviceName = options.serviceName ?? "lunora";
355
+ const staticAttributes = {
356
+ ...serviceVersion === void 0 ? {} : { "service.version": serviceVersion },
357
+ ...serviceNamespace === void 0 ? {} : { "service.namespace": serviceNamespace },
358
+ ...deploymentEnvironment === void 0 ? {} : { "deployment.environment": deploymentEnvironment },
359
+ ...resourceAttributes
360
+ };
361
+ const mergedByContext = /* @__PURE__ */ new WeakMap();
362
+ const resourceAttributesFor = (context) => {
363
+ if (detectResources !== true || context?.resourceAttributes === void 0) {
364
+ return staticAttributes;
365
+ }
366
+ const memoized = mergedByContext.get(context);
367
+ if (memoized !== void 0) {
368
+ return memoized;
369
+ }
370
+ const merged = mergeResourceAttributes(context.resourceAttributes(), staticAttributes);
371
+ mergedByContext.set(context, merged);
372
+ return merged;
373
+ };
317
374
  let base = endpoint;
318
375
  while (base.endsWith("/")) {
319
376
  base = base.slice(0, -1);
@@ -324,19 +381,19 @@ const otlpSink = (options) => {
324
381
  const mergedHeaders = mergeHeaders({ "content-type": "application/json" }, headers, token);
325
382
  return {
326
383
  onLog: (event, context) => {
327
- otlpPost(logsUrl, otlpLogBody(event, serviceName), mergedHeaders, context);
384
+ otlpPost(logsUrl, otlpLogBody(event, serviceName, resourceAttributesFor(context)), mergedHeaders, context);
328
385
  },
329
386
  onMetric: (event, context) => {
330
- otlpPost(metricsUrl, otlpMetricBody(event, serviceName), mergedHeaders, context);
387
+ otlpPost(metricsUrl, otlpMetricBody(event, serviceName, resourceAttributesFor(context)), mergedHeaders, context);
331
388
  },
332
389
  onRpc: (event, context) => {
333
390
  if (shouldSkip(event, onlyErrors)) {
334
391
  return;
335
392
  }
336
- otlpPost(tracesUrl, otlpTraceBody(event, serviceName, Date.now()), mergedHeaders, context);
393
+ otlpPost(tracesUrl, otlpTraceBody(event, serviceName, Date.now(), resourceAttributesFor(context)), mergedHeaders, context);
337
394
  },
338
395
  onSpan: (event, context) => {
339
- otlpPost(tracesUrl, otlpSpanBody(event, serviceName), mergedHeaders, context);
396
+ otlpPost(tracesUrl, otlpSpanBody(event, serviceName, resourceAttributesFor(context)), mergedHeaders, context);
340
397
  }
341
398
  };
342
399
  };
@@ -71,7 +71,7 @@ const buildRestRoutes = (deps) => {
71
71
  const routes = {};
72
72
  for (const entry of restSurfaceFromRegistry(functions)) {
73
73
  const allowed = entry.kind === "query" ? ["GET", "POST"] : ["POST"];
74
- routes[entry.path] = async (request, env) => {
74
+ routes[entry.path] = async (request, env, _url, context) => {
75
75
  const wrongMethod = methodGuard(request, allowed);
76
76
  if (wrongMethod) {
77
77
  return wrongMethod;
@@ -90,7 +90,14 @@ const buildRestRoutes = (deps) => {
90
90
  args = request.body === null ? {} : await readJsonBody(request);
91
91
  }
92
92
  const shardKey = readShardKey(url, request);
93
- return invoke({ args, env, functionPath: entry.functionPath, request, ...shardKey === void 0 ? {} : { shardKey } });
93
+ return invoke({
94
+ args,
95
+ env,
96
+ functionPath: entry.functionPath,
97
+ request,
98
+ ...shardKey === void 0 ? {} : { shardKey },
99
+ ...context?.waitUntil === void 0 ? {} : { waitUntil: (promise) => context.waitUntil?.(promise) }
100
+ });
94
101
  };
95
102
  }
96
103
  return routes;
@@ -1,7 +1,5 @@
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-DKZJCkdD.mjs';
4
- import { r as resolveTraceSampling, a as emitRpcEvent } from './observability--NOFYBFc.mjs';
5
3
  import { LunoraError, toErrorResponse } from './LunoraError-Bpb9EFJ3.mjs';
6
4
  import { runExportTap } from './createKvCursorStore-g8aA6B4L.mjs';
7
5
  import { m as methodGuard } from './method-guard-Qzw99aCj.mjs';
@@ -9,8 +7,10 @@ import { buildHealthRoutes, durableObjectProbe, d1Probe, presenceProbe } from '.
9
7
  import { wrapResolverWithContract } from './composeIdentityResolvers-XGjO7V1J.mjs';
10
8
  export { composeIdentityResolvers, routeIdentityResolvers } from './composeIdentityResolvers-XGjO7V1J.mjs';
11
9
  import { buildLogArchiveAdminRoutes } from './LOG_ARCHIVE_PATH-CNs0bznX.mjs';
10
+ import { r as resolveTraceSampling, a as emitRpcEvent } from './observability--NOFYBFc.mjs';
11
+ import { o as otlpRandomHex, b as buildTraceparent, p as parseTraceparent, m as mergeResourceAttributes, d as detectCloudflareResource, a as detectServiceResource, r as readerFromRecord } from './otlp-resource-Dow6-F_u.mjs';
12
12
  import { resolveShard, applyJurisdiction } from './applyJurisdiction-BkZtTkct.mjs';
13
- import { buildRestRoutes } from './argsFromQuery-BqPiQTPc.mjs';
13
+ import { buildRestRoutes } from './argsFromQuery-c-U1WRy-.mjs';
14
14
  import { resolveSecurity, handleCorsPreflight, enforceOrigin, decorateResponse, enforceWebSocketOrigin } from './decorateResponse-DRWQFNhF.mjs';
15
15
 
16
16
  const evictOldestEntry = (map, capacity) => {
@@ -1753,6 +1753,83 @@ const buildOrchestrationAdminRoutes = (deps) => {
1753
1753
  };
1754
1754
  };
1755
1755
 
1756
+ const SAMPLED_FLAG = 1;
1757
+ const UNSAMPLED_FLAG = 0;
1758
+ const MAX_TRACE_STATE_MEMBERS = 32;
1759
+ const MAX_TRACE_STATE_LENGTH = 512;
1760
+ const TRACE_STATE_MEMBER = /^[a-z][\d_a-z*/-]{0,255}(?:@[a-z][\d_a-z*/-]{0,13})?=[\u0020-\u002B\u002D-\u003C\u003E-\u007E]{1,256}$/;
1761
+ const sanitizeTraceState = (header) => {
1762
+ if (header === null || header === void 0) {
1763
+ return void 0;
1764
+ }
1765
+ const trimmed = header.trim();
1766
+ if (trimmed.length === 0 || trimmed.length > MAX_TRACE_STATE_LENGTH) {
1767
+ return void 0;
1768
+ }
1769
+ const members = trimmed.split(",");
1770
+ if (members.length > MAX_TRACE_STATE_MEMBERS) {
1771
+ return void 0;
1772
+ }
1773
+ for (const member of members) {
1774
+ if (!TRACE_STATE_MEMBER.test(member.trim())) {
1775
+ return void 0;
1776
+ }
1777
+ }
1778
+ return trimmed;
1779
+ };
1780
+ const extractTraceContext = (request) => {
1781
+ const parsed = parseTraceparent(request.headers.get("traceparent"));
1782
+ if (parsed === void 0) {
1783
+ return void 0;
1784
+ }
1785
+ const traceState = sanitizeTraceState(request.headers.get("tracestate"));
1786
+ return {
1787
+ parentSpanId: parsed.parentSpanId,
1788
+ sampled: parsed.sampled,
1789
+ traceId: parsed.traceId,
1790
+ ...traceState === void 0 ? {} : { traceState }
1791
+ };
1792
+ };
1793
+ const beginDispatchTrace = (request, options = {}) => {
1794
+ const upstream = extractTraceContext(request);
1795
+ const trusted = options.trustInbound === true ? upstream : void 0;
1796
+ const spanId = otlpRandomHex(8);
1797
+ const traceId = trusted?.traceId ?? otlpRandomHex(16);
1798
+ const decision = resolveTraceSampling(options.sampling, trusted === void 0 ? spanId : traceId);
1799
+ const sampled = decision.isTraced && (trusted === void 0 || trusted.sampled);
1800
+ return {
1801
+ decision,
1802
+ // A parseable upstream trace existed, and we chose not to join it.
1803
+ ignoredUpstream: upstream !== void 0 && trusted === void 0,
1804
+ trace: {
1805
+ sampled,
1806
+ spanId,
1807
+ traceFlags: sampled ? SAMPLED_FLAG : UNSAMPLED_FLAG,
1808
+ traceId,
1809
+ ...trusted?.parentSpanId === void 0 ? {} : { parentSpanId: trusted.parentSpanId },
1810
+ ...trusted?.traceState === void 0 ? {} : { traceState: trusted.traceState }
1811
+ }
1812
+ };
1813
+ };
1814
+ const injectTraceContext = (trace, headers) => {
1815
+ headers.traceparent = buildTraceparent(trace.traceId, trace.spanId, trace.sampled);
1816
+ if (trace.traceState !== void 0) {
1817
+ headers.tracestate = trace.traceState;
1818
+ }
1819
+ };
1820
+
1821
+ const createResourceAttributeResolver = (environment, request) => {
1822
+ let resolved;
1823
+ return () => {
1824
+ if (resolved === void 0) {
1825
+ const read = readerFromRecord(environment);
1826
+ const cf = request === void 0 ? void 0 : request.cf;
1827
+ resolved = mergeResourceAttributes(detectServiceResource(read), detectCloudflareResource(read, cf));
1828
+ }
1829
+ return resolved;
1830
+ };
1831
+ };
1832
+
1756
1833
  const SCHEDULED_PATH = "/_lunora/admin/scheduled";
1757
1834
  const SCHEDULED_STATUS_PATH = "/_lunora/admin/scheduled/status";
1758
1835
  const SCHEDULED_WS_PATH = "/_lunora/admin/scheduled/ws";
@@ -1934,6 +2011,51 @@ const buildStorageAdminRoutes = (deps) => {
1934
2011
  };
1935
2012
  };
1936
2013
 
2014
+ const cfString = (request, ...path) => {
2015
+ let current = request.cf;
2016
+ for (const key of path) {
2017
+ if (typeof current !== "object" || current === null) {
2018
+ return void 0;
2019
+ }
2020
+ current = current[key];
2021
+ }
2022
+ return typeof current === "string" ? current : void 0;
2023
+ };
2024
+ const SIGNAL_CHECKS = {
2025
+ // Edge-verified client certificate — the platform sets `certVerified`, so
2026
+ // unlike a header this cannot be spoofed by the caller.
2027
+ mtls: (request) => cfString(request, "tlsClientAuth", "certVerified") === "SUCCESS"
2028
+ };
2029
+ const resolveTraceTrust = (option) => {
2030
+ if (option === void 0 || option === false) {
2031
+ return () => false;
2032
+ }
2033
+ if (option === true) {
2034
+ return () => true;
2035
+ }
2036
+ if (typeof option === "function") {
2037
+ return option;
2038
+ }
2039
+ const check = Object.entries(SIGNAL_CHECKS).find(([signal]) => signal === option)?.[1];
2040
+ return check ?? (() => false);
2041
+ };
2042
+ const createDroppedTraceNotice = (option) => {
2043
+ if (option !== void 0) {
2044
+ return () => {
2045
+ };
2046
+ }
2047
+ let notified = false;
2048
+ return () => {
2049
+ if (notified) {
2050
+ return;
2051
+ }
2052
+ notified = true;
2053
+ console.warn(
2054
+ '[lunora] Ignored an inbound `traceparent`, so this request starts a new trace instead of joining the caller\'s. That is the safe default: the header is caller-supplied, and trusting it lets any client choose which trace its spans and logs join. If this worker sits behind a gateway, service mesh, or Cloudflare Access that sets `traceparent` itself, set `trustInboundTraceContext: true` on createWorker() (or `"mtls"` to trust only edge-verified client certificates). Set it to `false` to keep this behaviour and silence this message.'
2055
+ );
2056
+ };
2057
+ };
2058
+
1937
2059
  const VECTOR_INDEXES_PATH = "/_lunora/admin/vector/indexes";
1938
2060
  const VECTOR_QUERY_PATH = "/_lunora/admin/vector/query";
1939
2061
  const buildVectorAdminRoutes = (deps) => {
@@ -2078,6 +2200,39 @@ const NDJSON_ENCODER = new TextEncoder();
2078
2200
  const RPC_PATH = "/_lunora/rpc";
2079
2201
  const RPC_BATCH_PATH = "/_lunora/rpc-batch";
2080
2202
  const WS_PATH = "/_lunora/ws";
2203
+ const buildSinkContext = (environment, request, waitUntil) => {
2204
+ return {
2205
+ resourceAttributes: createResourceAttributeResolver(environment, request),
2206
+ ...waitUntil === void 0 ? {} : { waitUntil }
2207
+ };
2208
+ };
2209
+ const traceEventFields = (trace) => {
2210
+ return {
2211
+ spanId: trace.spanId,
2212
+ traceFlags: trace.traceFlags,
2213
+ traceId: trace.traceId,
2214
+ ...trace.parentSpanId === void 0 ? {} : { parentSpanId: trace.parentSpanId }
2215
+ };
2216
+ };
2217
+ const requestTelemetryMeta = (request) => {
2218
+ const { method } = request;
2219
+ const userAgent = request.headers.get("user-agent") ?? void 0;
2220
+ let url;
2221
+ try {
2222
+ url = new URL(request.url);
2223
+ } catch {
2224
+ return { method, userAgent };
2225
+ }
2226
+ const port = url.port === "" ? void 0 : Number(url.port);
2227
+ return {
2228
+ host: url.hostname,
2229
+ method,
2230
+ path: url.pathname,
2231
+ port: Number.isNaN(port) ? void 0 : port,
2232
+ scheme: url.protocol.replace(":", ""),
2233
+ userAgent
2234
+ };
2235
+ };
2081
2236
  const VOICE_PATH_PREFIX = "/_lunora/voice/";
2082
2237
  const SCHEDULER_DISPATCH_PATH = "/_lunora/scheduler/dispatch";
2083
2238
  const CRON_JOBS_RUN_PATH = "/_lunora/admin/cron-jobs/run";
@@ -2388,6 +2543,8 @@ const detectBindingProbe = (key, value) => {
2388
2543
  return void 0;
2389
2544
  };
2390
2545
  const createWorker = (options) => {
2546
+ const isTrustedUpstream = resolveTraceTrust(options.trustInboundTraceContext);
2547
+ const noticeDroppedTrace = createDroppedTraceNotice(options.trustInboundTraceContext);
2391
2548
  const defaultShard = options.defaultShardKey ?? "__root__";
2392
2549
  const publicResolveIdentity = wrapResolverWithContract(options.resolveIdentity, options.identity);
2393
2550
  const shardDO = applyJurisdiction(options.shardDO, options.jurisdiction);
@@ -2944,19 +3101,25 @@ const createWorker = (options) => {
2944
3101
  guardUnauthenticatedShardAccess("shard");
2945
3102
  }
2946
3103
  };
2947
- const dispatchSingleShard = async (functionPath, args, shardKey, forwardedHeaders, sinkContext) => {
3104
+ const dispatchSingleShard = async (request, functionPath, args, shardKey, forwardedHeaders, sinkContext) => {
2948
3105
  const rpcStartedAt = Date.now();
2949
3106
  const { observability, sampling } = options;
2950
- const traceId = otlpRandomHex(16);
2951
- const spanId = otlpRandomHex(8);
2952
- const decision = resolveTraceSampling(sampling, traceId);
3107
+ const requestMeta = requestTelemetryMeta(request);
3108
+ const { decision, ignoredUpstream, trace } = beginDispatchTrace(request, {
3109
+ ...sampling === void 0 ? {} : { sampling },
3110
+ trustInbound: isTrustedUpstream(request)
3111
+ });
3112
+ if (ignoredUpstream) {
3113
+ noticeDroppedTrace();
3114
+ }
3115
+ const outgoingHeaders = {
3116
+ ...forwardedHeaders,
3117
+ "x-lunora-sample-errors": decision.keepErrors ? "1" : "0"
3118
+ };
3119
+ injectTraceContext(trace, outgoingHeaders);
2953
3120
  const forwarded = new Request(`https://shard.internal/rpc`, {
2954
3121
  body: JSON.stringify({ args, functionPath }),
2955
- headers: {
2956
- ...forwardedHeaders,
2957
- traceparent: buildTraceparent(traceId, spanId, decision.isTraced),
2958
- "x-lunora-sample-errors": decision.keepErrors ? "1" : "0"
2959
- },
3122
+ headers: outgoingHeaders,
2960
3123
  method: "POST"
2961
3124
  });
2962
3125
  try {
@@ -2964,12 +3127,12 @@ const createWorker = (options) => {
2964
3127
  emitRpcEvent(
2965
3128
  observability,
2966
3129
  {
3130
+ ...requestMeta,
3131
+ ...traceEventFields(trace),
2967
3132
  durationMs: Date.now() - rpcStartedAt,
2968
3133
  functionPath,
2969
3134
  ok: response.ok,
2970
3135
  shardKey,
2971
- spanId,
2972
- traceId,
2973
3136
  ...response.ok ? {} : { error: { code: "SHARD_ERROR", message: `shard returned ${String(response.status)}`, status: response.status } }
2974
3137
  },
2975
3138
  sinkContext,
@@ -2979,7 +3142,11 @@ const createWorker = (options) => {
2979
3142
  } catch (error) {
2980
3143
  emitRpcEvent(
2981
3144
  observability,
2982
- { ...buildErrorEvent(functionPath, Date.now() - rpcStartedAt, error, { shardKey }), spanId, traceId },
3145
+ {
3146
+ ...requestMeta,
3147
+ ...traceEventFields(trace),
3148
+ ...buildErrorEvent(functionPath, Date.now() - rpcStartedAt, error, { shardKey })
3149
+ },
2983
3150
  sinkContext,
2984
3151
  sampling
2985
3152
  );
@@ -3020,11 +3187,8 @@ const createWorker = (options) => {
3020
3187
  {
3021
3188
  const rpcStartedAt = Date.now();
3022
3189
  const { observability } = options;
3023
- const sinkContext = context ? {
3024
- waitUntil: (promise) => {
3025
- context.waitUntil?.(promise);
3026
- }
3027
- } : void 0;
3190
+ const requestMeta = requestTelemetryMeta(request);
3191
+ const sinkContext = buildSinkContext(env, request, context && ((promise) => context.waitUntil?.(promise)));
3028
3192
  if (envelope.fanOut) {
3029
3193
  const coordinator = options.queryCoordinator;
3030
3194
  if (!coordinator) {
@@ -3050,6 +3214,7 @@ const createWorker = (options) => {
3050
3214
  table: envelope.fanOut.table
3051
3215
  },
3052
3216
  functionPath: envelope.functionPath,
3217
+ ...requestMeta,
3053
3218
  ok: true
3054
3219
  },
3055
3220
  sinkContext
@@ -3061,14 +3226,17 @@ const createWorker = (options) => {
3061
3226
  } catch (error) {
3062
3227
  emitRpcEvent(
3063
3228
  observability,
3064
- buildErrorEvent(envelope.functionPath, Date.now() - rpcStartedAt, error, { fanOut: { table: envelope.fanOut.table } }),
3229
+ {
3230
+ ...buildErrorEvent(envelope.functionPath, Date.now() - rpcStartedAt, error, { fanOut: { table: envelope.fanOut.table } }),
3231
+ ...requestMeta
3232
+ },
3065
3233
  sinkContext
3066
3234
  );
3067
3235
  throw error;
3068
3236
  }
3069
3237
  }
3070
3238
  const shardKey = envelope.shardKey ?? defaultShard;
3071
- const dispatch = () => dispatchSingleShard(envelope.functionPath, envelope.args ?? {}, shardKey, forwardedHeaders, sinkContext);
3239
+ const dispatch = () => dispatchSingleShard(request, envelope.functionPath, envelope.args ?? {}, shardKey, forwardedHeaders, sinkContext);
3072
3240
  if (x402Tag && options.x402Charge) {
3073
3241
  return options.x402Charge(request, { functionPath: envelope.functionPath, price: x402Tag.price }, dispatch);
3074
3242
  }
@@ -3114,11 +3282,8 @@ const createWorker = (options) => {
3114
3282
  )
3115
3283
  );
3116
3284
  const { observability } = options;
3117
- const sinkContext = context ? {
3118
- waitUntil: (promise) => {
3119
- context.waitUntil?.(promise);
3120
- }
3121
- } : void 0;
3285
+ const sinkContext = buildSinkContext(env, request, context && ((promise) => context.waitUntil?.(promise)));
3286
+ const requestMeta = requestTelemetryMeta(request);
3122
3287
  const results = [];
3123
3288
  const bookmarks = [];
3124
3289
  const slotError = (entry, status, code, message) => {
@@ -3139,6 +3304,7 @@ const createWorker = (options) => {
3139
3304
  {
3140
3305
  durationMs,
3141
3306
  functionPath: entry.functionPath,
3307
+ ...requestMeta,
3142
3308
  ok,
3143
3309
  shardKey,
3144
3310
  ...ok ? {} : { error: { code: "SHARD_ERROR", message: `batched call returned ${String(status)}`, status } }
@@ -3159,13 +3325,12 @@ const createWorker = (options) => {
3159
3325
  } catch (error) {
3160
3326
  const durationMs2 = Date.now() - subStartedAt;
3161
3327
  const { body: errorBody } = toErrorBody(error, { fallbackCode: "SHARD_UNAVAILABLE", redactedMessage: "shard unavailable" });
3162
- failSubBatch(
3163
- entries,
3164
- 502,
3165
- errorBody.code,
3166
- errorBody.message,
3167
- (entry) => buildErrorEvent(entry.functionPath, durationMs2, error, { shardKey })
3168
- );
3328
+ failSubBatch(entries, 502, errorBody.code, errorBody.message, (entry) => {
3329
+ return {
3330
+ ...buildErrorEvent(entry.functionPath, durationMs2, error, { shardKey }),
3331
+ ...requestMeta
3332
+ };
3333
+ });
3169
3334
  return;
3170
3335
  }
3171
3336
  const durationMs = Date.now() - subStartedAt;
@@ -3183,6 +3348,7 @@ const createWorker = (options) => {
3183
3348
  durationMs,
3184
3349
  error: { code: "SHARD_ERROR", message, status: response.status },
3185
3350
  functionPath: entry.functionPath,
3351
+ ...requestMeta,
3186
3352
  ok: false,
3187
3353
  shardKey
3188
3354
  };
@@ -3217,7 +3383,8 @@ const createWorker = (options) => {
3217
3383
  const { headers: forwardedHeaders, identity } = await resolveForwardContext(request, env, publicResolveIdentity);
3218
3384
  await authorizeRpcEnvelope({ functionPath, shardKey: callOptions.shardKey }, identity);
3219
3385
  const shardKey = callOptions.shardKey ?? defaultShard;
3220
- return await dispatchSingleShard(functionPath, args, shardKey, forwardedHeaders);
3386
+ const serverSinkContext = buildSinkContext(env, request, callOptions.waitUntil);
3387
+ return await dispatchSingleShard(request, functionPath, args, shardKey, forwardedHeaders, serverSinkContext);
3221
3388
  } catch (error) {
3222
3389
  return toErrorResponse(error);
3223
3390
  }
@@ -3358,12 +3525,13 @@ const createWorker = (options) => {
3358
3525
  }
3359
3526
  return authResponse;
3360
3527
  };
3361
- const invokeExposed = async ({ args, env, functionPath, request, shardKey }) => {
3528
+ const invokeExposed = async ({ args, env, functionPath, request, shardKey, waitUntil }) => {
3362
3529
  const envelope = { functionPath, ...shardKey === void 0 ? {} : { shardKey } };
3363
3530
  const { headers: forwardedHeaders, identity } = await resolveForwardContext(request, env, publicResolveIdentity);
3364
3531
  await authorizeRpcEnvelope(envelope, identity);
3365
3532
  const resolvedShardKey = shardKey ?? defaultShard;
3366
- const dispatch = () => dispatchSingleShard(functionPath, args, resolvedShardKey, forwardedHeaders);
3533
+ const restSinkContext = buildSinkContext(env, request, waitUntil);
3534
+ const dispatch = () => dispatchSingleShard(request, functionPath, args, resolvedShardKey, forwardedHeaders, restSinkContext);
3367
3535
  const x402Tag = resolveX402Charge(envelope, options);
3368
3536
  if (x402Tag && options.x402Charge) {
3369
3537
  return options.x402Charge(request, { functionPath, price: x402Tag.price }, dispatch);