@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.
- package/dist/index.d.mts +197 -2
- package/dist/index.d.ts +197 -2
- package/dist/index.mjs +3 -3
- package/dist/packem_shared/{analyticsEngineSink-DgL64WVC.mjs → analyticsEngineSink-yLFNjHDt.mjs} +74 -17
- package/dist/packem_shared/{argsFromQuery-BqPiQTPc.mjs → argsFromQuery-c-U1WRy-.mjs} +9 -2
- package/dist/packem_shared/{composeWorker-UwHY2r8O.mjs → composeWorker-DiWwOXXt.mjs} +205 -37
- package/dist/packem_shared/otlp-resource-Dow6-F_u.mjs +163 -0
- package/package.json +1 -1
- package/dist/packem_shared/otlp-DKZJCkdD.mjs +0 -95
package/dist/packem_shared/{analyticsEngineSink-DgL64WVC.mjs → analyticsEngineSink-yLFNjHDt.mjs}
RENAMED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import {
|
|
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
|
|
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
|
-
|
|
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({
|
|
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-
|
|
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
|
|
2951
|
-
const
|
|
2952
|
-
|
|
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
|
-
{
|
|
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
|
|
3024
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
3164
|
-
|
|
3165
|
-
|
|
3166
|
-
|
|
3167
|
-
|
|
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
|
-
|
|
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
|
|
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);
|