@lunora/runtime 1.0.0-alpha.25 → 1.0.0-alpha.27
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 +21 -0
- package/dist/index.d.ts +21 -0
- package/dist/index.mjs +2 -2
- package/dist/packem_shared/{analyticsEngineSink-BpJlA7e9.mjs → analyticsEngineSink-F-5ZAdUA.mjs} +5 -80
- package/dist/packem_shared/{composeWorker-CxwkPZUl.mjs → composeWorker-UZKPkF3Q.mjs} +93 -8
- package/dist/packem_shared/otlp-D_YzGXu1.mjs +81 -0
- package/package.json +1 -1
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";
|
|
@@ -2338,6 +2348,17 @@ interface WorkerOptions {
|
|
|
2338
2348
|
*/
|
|
2339
2349
|
vectorIntrospector?: VectorIntrospector;
|
|
2340
2350
|
/**
|
|
2351
|
+
* Voice-session Durable Object namespaces, keyed by the agent's
|
|
2352
|
+
* `lunora/agents.ts` export name (e.g. `{ support: env.VOICE_SUPPORT }`).
|
|
2353
|
+
* Codegen wires this for every voice-enabled agent. When set, the worker
|
|
2354
|
+
* exposes `/_lunora/voice/<agentExportName>` — a WebSocket upgrade that
|
|
2355
|
+
* resolves the caller's identity, forwards it on the server-minted
|
|
2356
|
+
* `x-lunora-userid` / `x-lunora-identity` headers, and hands the socket to
|
|
2357
|
+
* the agent's `VoiceSessionDO`. Omit it (voice-free apps) and the route does
|
|
2358
|
+
* not exist.
|
|
2359
|
+
*/
|
|
2360
|
+
voiceAgents?: Record<string, ShardNamespaceLike>;
|
|
2361
|
+
/**
|
|
2341
2362
|
* Resolver for the Cloudflare Workflows REST client, built from the
|
|
2342
2363
|
* deployment `env` (its `CLOUDFLARE_ACCOUNT_ID` / `CLOUDFLARE_API_TOKEN`).
|
|
2343
2364
|
* Set by the codegen-emitted worker entry (which depends on
|
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";
|
|
@@ -2338,6 +2348,17 @@ interface WorkerOptions {
|
|
|
2338
2348
|
*/
|
|
2339
2349
|
vectorIntrospector?: VectorIntrospector;
|
|
2340
2350
|
/**
|
|
2351
|
+
* Voice-session Durable Object namespaces, keyed by the agent's
|
|
2352
|
+
* `lunora/agents.ts` export name (e.g. `{ support: env.VOICE_SUPPORT }`).
|
|
2353
|
+
* Codegen wires this for every voice-enabled agent. When set, the worker
|
|
2354
|
+
* exposes `/_lunora/voice/<agentExportName>` — a WebSocket upgrade that
|
|
2355
|
+
* resolves the caller's identity, forwards it on the server-minted
|
|
2356
|
+
* `x-lunora-userid` / `x-lunora-identity` headers, and hands the socket to
|
|
2357
|
+
* the agent's `VoiceSessionDO`. Omit it (voice-free apps) and the route does
|
|
2358
|
+
* not exist.
|
|
2359
|
+
*/
|
|
2360
|
+
voiceAgents?: Record<string, ShardNamespaceLike>;
|
|
2361
|
+
/**
|
|
2341
2362
|
* Resolver for the Cloudflare Workflows REST client, built from the
|
|
2342
2363
|
* deployment `env` (its `CLOUDFLARE_ACCOUNT_ID` / `CLOUDFLARE_API_TOKEN`).
|
|
2343
2364
|
* Set by the codegen-emitted worker entry (which depends on
|
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-
|
|
2
|
+
export { composeWorker, createLunoraHandler, createWorker, defineRpcEnvelope, resolveLunoraOptions, withFrameworkWorker } from './packem_shared/composeWorker-UZKPkF3Q.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-
|
|
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';
|
package/dist/packem_shared/{analyticsEngineSink-BpJlA7e9.mjs → analyticsEngineSink-F-5ZAdUA.mjs}
RENAMED
|
@@ -1,81 +1,4 @@
|
|
|
1
|
-
|
|
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
|
-
|
|
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';
|
|
@@ -1916,12 +1917,24 @@ const NDJSON_ENCODER = new TextEncoder();
|
|
|
1916
1917
|
const RPC_PATH = "/_lunora/rpc";
|
|
1917
1918
|
const RPC_BATCH_PATH = "/_lunora/rpc-batch";
|
|
1918
1919
|
const WS_PATH = "/_lunora/ws";
|
|
1920
|
+
const VOICE_PATH_PREFIX = "/_lunora/voice/";
|
|
1919
1921
|
const SCHEDULER_DISPATCH_PATH = "/_lunora/scheduler/dispatch";
|
|
1920
1922
|
const CRON_JOBS_RUN_PATH = "/_lunora/admin/cron-jobs/run";
|
|
1921
1923
|
const ADMIN_PATH_PREFIX = "/_lunora/admin/";
|
|
1922
1924
|
const MIGRATE_PATH = "/_lunora/migrate";
|
|
1923
1925
|
const STATUS_PATH = "/_lunora/status";
|
|
1924
1926
|
const isAdminPath = (pathname) => pathname.startsWith(ADMIN_PATH_PREFIX) || pathname === MIGRATE_PATH;
|
|
1927
|
+
const readForwardedIdentity = (request) => {
|
|
1928
|
+
const forwardedUserId = request.headers.get("x-lunora-userid");
|
|
1929
|
+
const forwardedIdentity = request.headers.get("x-lunora-identity");
|
|
1930
|
+
if (forwardedUserId === null && forwardedIdentity === null) {
|
|
1931
|
+
return void 0;
|
|
1932
|
+
}
|
|
1933
|
+
return {
|
|
1934
|
+
...forwardedIdentity === null ? {} : { identity: forwardedIdentity },
|
|
1935
|
+
...forwardedUserId === null ? {} : { userId: forwardedUserId }
|
|
1936
|
+
};
|
|
1937
|
+
};
|
|
1925
1938
|
const DEFAULT_AUTH_BASE_PATH = "/api/auth";
|
|
1926
1939
|
const RECORD_AUTH_EVENT_OP = "__lunora_admin__:recordAuthEvent";
|
|
1927
1940
|
const AUTH_ATTEMPT_SEGMENTS = ["/sign-in", "/sign-up", "/callback"];
|
|
@@ -2239,7 +2252,7 @@ const createWorker = (options) => {
|
|
|
2239
2252
|
resolveForwardContext: resolveAdminForwardContext,
|
|
2240
2253
|
shardDO
|
|
2241
2254
|
});
|
|
2242
|
-
const dispatchToShard = async (functionPath, args, shardKey, mutationId) => {
|
|
2255
|
+
const dispatchToShard = async (functionPath, args, shardKey, mutationId, forwardedIdentity) => {
|
|
2243
2256
|
if (options.authorizeShard) {
|
|
2244
2257
|
const allowed = await options.authorizeShard(null, shardKey);
|
|
2245
2258
|
if (!allowed) {
|
|
@@ -2247,6 +2260,12 @@ const createWorker = (options) => {
|
|
|
2247
2260
|
}
|
|
2248
2261
|
}
|
|
2249
2262
|
const headers = { "content-type": "application/json", "x-lunora-system": "1" };
|
|
2263
|
+
if (forwardedIdentity?.userId !== void 0 && forwardedIdentity.userId.length > 0) {
|
|
2264
|
+
headers["x-lunora-userid"] = forwardedIdentity.userId;
|
|
2265
|
+
}
|
|
2266
|
+
if (forwardedIdentity?.identity !== void 0 && forwardedIdentity.identity.length > 0) {
|
|
2267
|
+
headers["x-lunora-identity"] = forwardedIdentity.identity;
|
|
2268
|
+
}
|
|
2250
2269
|
if (mutationId !== void 0 && mutationId.length > 0) {
|
|
2251
2270
|
headers["x-lunora-mutation-id"] = mutationId;
|
|
2252
2271
|
}
|
|
@@ -2257,16 +2276,17 @@ const createWorker = (options) => {
|
|
|
2257
2276
|
});
|
|
2258
2277
|
return forwardToShard(shardDO, shardKey, forwarded);
|
|
2259
2278
|
};
|
|
2260
|
-
const
|
|
2279
|
+
const startWorkflowInstance = async (binding, args, env, label) => {
|
|
2261
2280
|
const candidate = env?.[binding];
|
|
2262
2281
|
if (!candidate || typeof candidate.create !== "function") {
|
|
2263
|
-
throw new LunoraError(
|
|
2282
|
+
throw new LunoraError(`${label} targets workflow binding "${binding}", which is not bound on env`, {
|
|
2264
2283
|
code: "CRON_JOB_FAILED",
|
|
2265
2284
|
status: 500
|
|
2266
2285
|
});
|
|
2267
2286
|
}
|
|
2268
|
-
await candidate.create({ params:
|
|
2287
|
+
await candidate.create({ params: args });
|
|
2269
2288
|
};
|
|
2289
|
+
const startCronWorkflow = async (binding, job, env) => startWorkflowInstance(binding, job.args ?? {}, env, `cron job "${job.name}"`);
|
|
2270
2290
|
const runOneCronJob = async (job, env) => {
|
|
2271
2291
|
if (job.workflow) {
|
|
2272
2292
|
await startCronWorkflow(job.workflow, job, env);
|
|
@@ -2363,13 +2383,18 @@ const createWorker = (options) => {
|
|
|
2363
2383
|
throw new LunoraError("Scheduler dispatch body must be valid JSON", { code: "BAD_REQUEST", status: 400 });
|
|
2364
2384
|
}
|
|
2365
2385
|
const candidate = body ?? {};
|
|
2386
|
+
const args = candidate.args ?? {};
|
|
2387
|
+
if (typeof candidate.workflow === "string" && candidate.workflow.length > 0) {
|
|
2388
|
+
await startWorkflowInstance(candidate.workflow, args, env, "scheduled workflow");
|
|
2389
|
+
return Response.json({ ok: true }, { status: 200 });
|
|
2390
|
+
}
|
|
2366
2391
|
if (typeof candidate.functionPath !== "string" || candidate.functionPath.length === 0) {
|
|
2367
2392
|
throw new LunoraError("Scheduler dispatch is missing `functionPath`", { code: "BAD_REQUEST", status: 400 });
|
|
2368
2393
|
}
|
|
2369
|
-
const args = candidate.args ?? {};
|
|
2370
2394
|
const shardKey = typeof candidate.shardKey === "string" && candidate.shardKey.length > 0 ? candidate.shardKey : defaultShard;
|
|
2371
2395
|
const mutationId = typeof candidate.id === "string" && candidate.id.length > 0 ? candidate.id : void 0;
|
|
2372
|
-
const
|
|
2396
|
+
const identity = readForwardedIdentity(request);
|
|
2397
|
+
const response = await dispatchToShard(candidate.functionPath, args, shardKey, mutationId, identity);
|
|
2373
2398
|
await releasePoolSlot(candidate);
|
|
2374
2399
|
return response;
|
|
2375
2400
|
};
|
|
@@ -2562,6 +2587,59 @@ const createWorker = (options) => {
|
|
|
2562
2587
|
}
|
|
2563
2588
|
return forwardToShard(shardDO, shardKey, new Request(request, { headers: upgradeHeaders }));
|
|
2564
2589
|
};
|
|
2590
|
+
const handleVoiceUpgrade = async (request, env, url) => {
|
|
2591
|
+
const { voiceAgents } = options;
|
|
2592
|
+
if (voiceAgents === void 0) {
|
|
2593
|
+
return new Response("Not found", { status: 404 });
|
|
2594
|
+
}
|
|
2595
|
+
if (request.headers.get("Upgrade") !== "websocket") {
|
|
2596
|
+
return new Response("Expected a WebSocket upgrade", { headers: { allow: "GET" }, status: 426 });
|
|
2597
|
+
}
|
|
2598
|
+
const blockedUpgrade = enforceWebSocketOrigin(request, resolvedSecurity);
|
|
2599
|
+
if (blockedUpgrade) {
|
|
2600
|
+
return blockedUpgrade;
|
|
2601
|
+
}
|
|
2602
|
+
let agentName;
|
|
2603
|
+
try {
|
|
2604
|
+
agentName = decodeURIComponent(url.pathname.slice(VOICE_PATH_PREFIX.length));
|
|
2605
|
+
} catch {
|
|
2606
|
+
return new Response("Unknown voice agent", { status: 404 });
|
|
2607
|
+
}
|
|
2608
|
+
const namespace = Object.hasOwn(voiceAgents, agentName) ? voiceAgents[agentName] : void 0;
|
|
2609
|
+
if (namespace === void 0) {
|
|
2610
|
+
return new Response("Unknown voice agent", { status: 404 });
|
|
2611
|
+
}
|
|
2612
|
+
const threadKey = url.searchParams.get("threadKey");
|
|
2613
|
+
if (threadKey === null || threadKey.length === 0) {
|
|
2614
|
+
return new Response("Missing threadKey", { status: 400 });
|
|
2615
|
+
}
|
|
2616
|
+
const { headers: forwardedHeaders, identity } = await resolveForwardContext(request, env, publicResolveIdentity);
|
|
2617
|
+
if (options.authorizeShard) {
|
|
2618
|
+
const allowed = await options.authorizeShard(identity, threadKey);
|
|
2619
|
+
if (!allowed) {
|
|
2620
|
+
return new Response("Forbidden", { status: 403 });
|
|
2621
|
+
}
|
|
2622
|
+
} else {
|
|
2623
|
+
guardUnauthenticatedShardAccess("shard");
|
|
2624
|
+
}
|
|
2625
|
+
const upgradeHeaders = new Headers(request.headers);
|
|
2626
|
+
upgradeHeaders.delete("x-lunora-userid");
|
|
2627
|
+
upgradeHeaders.delete("x-lunora-identity");
|
|
2628
|
+
upgradeHeaders.delete("x-lunora-identity-exp");
|
|
2629
|
+
const forwardedUserId = forwardedHeaders["x-lunora-userid"];
|
|
2630
|
+
const forwardedIdentity = forwardedHeaders["x-lunora-identity"];
|
|
2631
|
+
const forwardedExp = forwardedHeaders["x-lunora-identity-exp"];
|
|
2632
|
+
if (forwardedUserId !== void 0) {
|
|
2633
|
+
upgradeHeaders.set("x-lunora-userid", forwardedUserId);
|
|
2634
|
+
}
|
|
2635
|
+
if (forwardedIdentity !== void 0) {
|
|
2636
|
+
upgradeHeaders.set("x-lunora-identity", forwardedIdentity);
|
|
2637
|
+
}
|
|
2638
|
+
if (forwardedExp !== void 0) {
|
|
2639
|
+
upgradeHeaders.set("x-lunora-identity-exp", forwardedExp);
|
|
2640
|
+
}
|
|
2641
|
+
return forwardToShard(namespace, threadKey, new Request(request, { headers: upgradeHeaders }));
|
|
2642
|
+
};
|
|
2565
2643
|
const authorizeFanOutEnvelope = async (fanOut, functionPath, identity) => {
|
|
2566
2644
|
if (options.authorizeFanOut) {
|
|
2567
2645
|
const allowed = await options.authorizeFanOut(identity, fanOut.table, functionPath);
|
|
@@ -2605,9 +2683,11 @@ const createWorker = (options) => {
|
|
|
2605
2683
|
const dispatchSingleShard = async (functionPath, args, shardKey, forwardedHeaders, sinkContext) => {
|
|
2606
2684
|
const rpcStartedAt = Date.now();
|
|
2607
2685
|
const { observability } = options;
|
|
2686
|
+
const traceId = otlpRandomHex(16);
|
|
2687
|
+
const spanId = otlpRandomHex(8);
|
|
2608
2688
|
const forwarded = new Request(`https://shard.internal/rpc`, {
|
|
2609
2689
|
body: JSON.stringify({ args, functionPath }),
|
|
2610
|
-
headers: forwardedHeaders,
|
|
2690
|
+
headers: { ...forwardedHeaders, traceparent: buildTraceparent(traceId, spanId) },
|
|
2611
2691
|
method: "POST"
|
|
2612
2692
|
});
|
|
2613
2693
|
try {
|
|
@@ -2619,13 +2699,15 @@ const createWorker = (options) => {
|
|
|
2619
2699
|
functionPath,
|
|
2620
2700
|
ok: response.ok,
|
|
2621
2701
|
shardKey,
|
|
2702
|
+
spanId,
|
|
2703
|
+
traceId,
|
|
2622
2704
|
...response.ok ? {} : { error: { code: "SHARD_ERROR", message: `shard returned ${String(response.status)}`, status: response.status } }
|
|
2623
2705
|
},
|
|
2624
2706
|
sinkContext
|
|
2625
2707
|
);
|
|
2626
2708
|
return response;
|
|
2627
2709
|
} catch (error) {
|
|
2628
|
-
emitRpcEvent(observability, buildErrorEvent(functionPath, Date.now() - rpcStartedAt, error, { shardKey }), sinkContext);
|
|
2710
|
+
emitRpcEvent(observability, { ...buildErrorEvent(functionPath, Date.now() - rpcStartedAt, error, { shardKey }), spanId, traceId }, sinkContext);
|
|
2629
2711
|
throw error;
|
|
2630
2712
|
}
|
|
2631
2713
|
};
|
|
@@ -3086,6 +3168,9 @@ const createWorker = (options) => {
|
|
|
3086
3168
|
await applyAdminGate(request, url.pathname);
|
|
3087
3169
|
return internalRoute(request, env, url, context);
|
|
3088
3170
|
}
|
|
3171
|
+
if (options.voiceAgents !== void 0 && url.pathname.startsWith(VOICE_PATH_PREFIX)) {
|
|
3172
|
+
return handleVoiceUpgrade(request, env, url);
|
|
3173
|
+
}
|
|
3089
3174
|
const httpRouteResponse = await dispatchHttpRoute(request, env, context);
|
|
3090
3175
|
if (httpRouteResponse) {
|
|
3091
3176
|
return httpRouteResponse;
|
|
@@ -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 };
|