@lunora/runtime 1.0.0-alpha.37 → 1.0.0-alpha.39

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.
Files changed (51) hide show
  1. package/dist/index.d.mts +359 -8
  2. package/dist/index.d.ts +359 -8
  3. package/dist/index.mjs +1 -22
  4. package/dist/packem_shared/DEFAULT_LOG_COLUMNS-CpZ9kd-q.mjs +1 -0
  5. package/dist/packem_shared/DEFAULT_REGISTRY_CACHE_TTL_MS-BBf3mNrD.mjs +1 -0
  6. package/dist/packem_shared/GET_AUTH_AUDIT_LOG_OP-DmxHQbZT.mjs +1 -0
  7. package/dist/packem_shared/HEALTH_PATH-DiZqjEGp.mjs +1 -0
  8. package/dist/packem_shared/LOG_ARCHIVE_NOT_CONFIGURED-CDpi4yFD.mjs +1 -0
  9. package/dist/packem_shared/LOG_ARCHIVE_PATH-e8U3ftfM.mjs +1 -0
  10. package/dist/packem_shared/LunoraError-C08OP5Uq.mjs +1 -0
  11. package/dist/packem_shared/NOOP_EXECUTION_CONTEXT-YmXqH-jH.mjs +1 -0
  12. package/dist/packem_shared/analyticsEngineSink-DOoyBx4o.mjs +1 -0
  13. package/dist/packem_shared/applyJurisdiction-uRQLx282.mjs +1 -0
  14. package/dist/packem_shared/argsFromQuery-0KrWTkNx.mjs +1 -0
  15. package/dist/packem_shared/composeIdentityResolvers-DlBbYmBJ.mjs +1 -0
  16. package/dist/packem_shared/composeWorker-D7Zi6kZR.mjs +6 -0
  17. package/dist/packem_shared/createCrossShardRelationCapabilities-B2EKbSEs.mjs +1 -0
  18. package/dist/packem_shared/createKvCursorStore-C24tEuYk.mjs +3 -0
  19. package/dist/packem_shared/createQueryCoordinator-DLsrcOkB.mjs +1 -0
  20. package/dist/packem_shared/createShardClient-62qcYKGl.mjs +1 -0
  21. package/dist/packem_shared/decorateResponse-C6TZSzID.mjs +1 -0
  22. package/dist/packem_shared/emitLogEvent-CEbCh6FQ.mjs +1 -0
  23. package/dist/packem_shared/memoizeIdentity-DRYIK1yT.mjs +1 -0
  24. package/dist/packem_shared/method-guard-BbuR0VfS.mjs +1 -0
  25. package/dist/packem_shared/observability-B0MYwNDB.mjs +1 -0
  26. package/dist/packem_shared/otlp-resource-cAjGEywx.mjs +1 -0
  27. package/dist/packem_shared/pipeline-log-reader-tDuvc_Ty.mjs +1 -0
  28. package/dist/packem_shared/toAirbyteMessages-DBTuFjb5.mjs +1 -0
  29. package/package.json +1 -1
  30. package/dist/packem_shared/DEFAULT_LOG_COLUMNS-B7H3YdJ3.mjs +0 -2
  31. package/dist/packem_shared/DEFAULT_REGISTRY_CACHE_TTL_MS-B3pA7aXp.mjs +0 -79
  32. package/dist/packem_shared/HEALTH_PATH-e5J_NHBx.mjs +0 -150
  33. package/dist/packem_shared/LOG_ARCHIVE_NOT_CONFIGURED-acNcguqc.mjs +0 -3
  34. package/dist/packem_shared/LOG_ARCHIVE_PATH-CNs0bznX.mjs +0 -125
  35. package/dist/packem_shared/LunoraError-Bpb9EFJ3.mjs +0 -22
  36. package/dist/packem_shared/NOOP_EXECUTION_CONTEXT-CCTu0Bf1.mjs +0 -8
  37. package/dist/packem_shared/analyticsEngineSink-yLFNjHDt.mjs +0 -429
  38. package/dist/packem_shared/applyJurisdiction-BkZtTkct.mjs +0 -20
  39. package/dist/packem_shared/argsFromQuery-c-U1WRy-.mjs +0 -121
  40. package/dist/packem_shared/composeIdentityResolvers-XGjO7V1J.mjs +0 -55
  41. package/dist/packem_shared/composeWorker-DiWwOXXt.mjs +0 -3738
  42. package/dist/packem_shared/createCrossShardRelationCapabilities-CbcWjkAn.mjs +0 -63
  43. package/dist/packem_shared/createKvCursorStore-g8aA6B4L.mjs +0 -165
  44. package/dist/packem_shared/createQueryCoordinator-DNCJzOZE.mjs +0 -847
  45. package/dist/packem_shared/decorateResponse-DRWQFNhF.mjs +0 -273
  46. package/dist/packem_shared/emitLogEvent-BlGMnKZK.mjs +0 -1
  47. package/dist/packem_shared/method-guard-Qzw99aCj.mjs +0 -3
  48. package/dist/packem_shared/observability--NOFYBFc.mjs +0 -47
  49. package/dist/packem_shared/otlp-resource-Dow6-F_u.mjs +0 -163
  50. package/dist/packem_shared/pipeline-log-reader-BXULGNC3.mjs +0 -135
  51. package/dist/packem_shared/toAirbyteMessages-DrHdplb4.mjs +0 -39
@@ -1,125 +0,0 @@
1
- import { createR2Sql } from '@lunora/bindings/r2sql';
2
- import { LOG_ARCHIVE_NOT_CONFIGURED } from './LOG_ARCHIVE_NOT_CONFIGURED-acNcguqc.mjs';
3
- import { L as LOG_LEVEL_ORDER, c as createPipelineLogReader } from './pipeline-log-reader-BXULGNC3.mjs';
4
- import { LunoraError } from './LunoraError-Bpb9EFJ3.mjs';
5
-
6
- const LOG_ARCHIVE_PATH = "/_lunora/admin/logs/archive";
7
- const LOG_ARCHIVE_TABLE_ENV = "LUNORA_LOG_ARCHIVE_TABLE";
8
- const LOG_ARCHIVE_NAMESPACE_ENV = "LUNORA_LOG_ARCHIVE_NAMESPACE";
9
- const resolveLogArchiveFromEnv = (environment) => {
10
- if (typeof environment !== "object" || environment === null) {
11
- return void 0;
12
- }
13
- const record = environment;
14
- const table = record[LOG_ARCHIVE_TABLE_ENV];
15
- if (typeof table !== "string" || table === "") {
16
- return void 0;
17
- }
18
- const namespace = record[LOG_ARCHIVE_NAMESPACE_ENV];
19
- return { table, ...typeof namespace === "string" && namespace !== "" ? { namespace } : {} };
20
- };
21
- const LOG_LEVELS = new Set(LOG_LEVEL_ORDER);
22
- const optionalString = (value) => typeof value === "string" && value !== "" ? value : void 0;
23
- const parseLevel = (value, field) => {
24
- if (value === void 0) {
25
- return void 0;
26
- }
27
- if (typeof value !== "string" || !LOG_LEVELS.has(value)) {
28
- throw new LunoraError(`logs archive: invalid \`${field}\` — expected one of ${LOG_LEVEL_ORDER.join(", ")}`, { code: "BAD_REQUEST", status: 400 });
29
- }
30
- return value;
31
- };
32
- const parseFiniteNumber = (value, field) => {
33
- if (value === void 0) {
34
- return void 0;
35
- }
36
- if (typeof value !== "number" || !Number.isFinite(value)) {
37
- throw new LunoraError(`logs archive: invalid \`${field}\` — expected a finite number`, { code: "BAD_REQUEST", status: 400 });
38
- }
39
- return value;
40
- };
41
- const parseQuery = (body) => {
42
- const query = {};
43
- const assignString = (field) => {
44
- const value = optionalString(body[field]);
45
- if (value !== void 0) {
46
- query[field] = value;
47
- }
48
- };
49
- assignString("functionPath");
50
- assignString("functionPathPrefix");
51
- assignString("traceId");
52
- assignString("shardKey");
53
- assignString("userId");
54
- const level = parseLevel(body["level"], "level");
55
- if (level !== void 0) {
56
- query.level = level;
57
- }
58
- const minLevel = parseLevel(body["minLevel"], "minLevel");
59
- if (minLevel !== void 0) {
60
- query.minLevel = minLevel;
61
- }
62
- const sinceTs = parseFiniteNumber(body["sinceTs"], "sinceTs");
63
- if (sinceTs !== void 0) {
64
- query.sinceTs = sinceTs;
65
- }
66
- const untilTs = parseFiniteNumber(body["untilTs"], "untilTs");
67
- if (untilTs !== void 0) {
68
- query.untilTs = untilTs;
69
- }
70
- const limit = parseFiniteNumber(body["limit"], "limit");
71
- if (limit !== void 0) {
72
- query.limit = limit;
73
- }
74
- const rawCursor = body["cursor"];
75
- if (typeof rawCursor === "object" && rawCursor !== null) {
76
- const cursorTs = parseFiniteNumber(rawCursor["ts"], "cursor.ts");
77
- if (cursorTs !== void 0) {
78
- query.cursor = { ts: cursorTs };
79
- }
80
- }
81
- return query;
82
- };
83
- const resolveCredentials = (environment) => {
84
- const accountId = environment.R2_SQL_ACCOUNT_ID ?? environment.CLOUDFLARE_ACCOUNT_ID;
85
- const apiToken = environment.R2_SQL_TOKEN;
86
- const bucket = environment.R2_SQL_BUCKET;
87
- const missing = [];
88
- if (accountId === void 0 || accountId === "") {
89
- missing.push("R2_SQL_ACCOUNT_ID");
90
- }
91
- if (apiToken === void 0 || apiToken === "") {
92
- missing.push("R2_SQL_TOKEN");
93
- }
94
- if (bucket === void 0 || bucket === "") {
95
- missing.push("R2_SQL_BUCKET");
96
- }
97
- if (missing.length > 0) {
98
- throw new LunoraError(
99
- `log archive not configured (missing ${missing.join(", ")}). The Pipeline must write to an R2 Data Catalog (Iceberg) table, and R2_SQL_ACCOUNT_ID / R2_SQL_TOKEN / R2_SQL_BUCKET must be set — see the observability docs.`,
100
- { code: LOG_ARCHIVE_NOT_CONFIGURED, status: 400 }
101
- );
102
- }
103
- return { accountId, apiToken, bucket };
104
- };
105
- const buildLogArchiveAdminRoutes = (deps) => {
106
- const { createReader = createPipelineLogReader, readJsonBody, requireAdminOption } = deps;
107
- const handleLogArchive = async (request, env) => {
108
- if (request.method !== "POST") {
109
- throw new LunoraError("Log-archive endpoint requires POST", { code: "METHOD_NOT_ALLOWED", status: 405 });
110
- }
111
- const config = requireAdminOption(request, deps.logArchive, {
112
- code: LOG_ARCHIVE_NOT_CONFIGURED,
113
- message: "log archive requires a `logArchive` config (an R2 Data Catalog table) on the worker — see the observability docs"
114
- });
115
- const { accountId, apiToken, bucket } = resolveCredentials(env ?? {});
116
- const query = parseQuery(await readJsonBody(request));
117
- const client = createR2Sql({ accountId, apiToken, bucket });
118
- const reader = createReader(client, { columnMap: config.columnMap, namespace: config.namespace, table: config.table });
119
- const page = await reader.query(query);
120
- return Response.json(page, { headers: { "content-type": "application/json" }, status: 200 });
121
- };
122
- return { [LOG_ARCHIVE_PATH]: handleLogArchive };
123
- };
124
-
125
- export { LOG_ARCHIVE_NOT_CONFIGURED, LOG_ARCHIVE_PATH, buildLogArchiveAdminRoutes, resolveLogArchiveFromEnv };
@@ -1,22 +0,0 @@
1
- import { LunoraError as LunoraError$1, toErrorBody } from '@lunora/errors';
2
-
3
- const toErrorResponse = (error) => {
4
- const { body, redacted, status } = toErrorBody(error, { fallbackCode: "INTERNAL", redactedMessage: "Internal error" });
5
- if (redacted) {
6
- console.error("[lunora] internal error:", error);
7
- }
8
- return Response.json({ error: body }, {
9
- headers: { "content-type": "application/json" },
10
- status
11
- });
12
- };
13
- class LunoraError extends LunoraError$1 {
14
- constructor(message, options) {
15
- super(options?.code ?? "INTERNAL", message, { cause: options?.cause, status: options?.status });
16
- }
17
- toResponse() {
18
- return toErrorResponse(this);
19
- }
20
- }
21
-
22
- export { LunoraError, toErrorResponse };
@@ -1,8 +0,0 @@
1
- const NOOP_EXECUTION_CONTEXT = {
2
- passThroughOnException: () => {
3
- },
4
- waitUntil: () => {
5
- }
6
- };
7
-
8
- export { NOOP_EXECUTION_CONTEXT };
@@ -1,429 +0,0 @@
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
-
3
- const stringifyFieldValue = (value) => {
4
- if (typeof value === "string") {
5
- return value;
6
- }
7
- try {
8
- return JSON.stringify(value) ?? String(value);
9
- } catch {
10
- return String(value);
11
- }
12
- };
13
- const coerceFieldValue = (value) => typeof value === "boolean" || typeof value === "number" || typeof value === "string" ? value : stringifyFieldValue(value);
14
-
15
- const otlpTraceBody = (event, serviceName, endMs, resourceAttributes) => {
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
- }
36
- if (event.shardKey !== void 0) {
37
- attributes.push(encodeAttribute("lunora.shard_key", event.shardKey));
38
- }
39
- attributes.push(encodeAttribute("http.response.status_code", event.error?.status ?? 200));
40
- if (event.error) {
41
- attributes.push(encodeAttribute("error.type", event.error.code), encodeAttribute("lunora.error_status", event.error.status));
42
- }
43
- if (event.fanOut) {
44
- attributes.push(
45
- encodeAttribute("lunora.fanout.table", event.fanOut.table),
46
- encodeAttribute("lunora.fanout.shards", event.fanOut.shards),
47
- encodeAttribute("lunora.fanout.failed", event.fanOut.failed)
48
- );
49
- }
50
- const span = {
51
- attributes,
52
- endTimeUnixNano: otlpUnixNano(endMs),
53
- // SPAN_KIND_SERVER — a dispatched RPC is server-side request handling.
54
- kind: 2,
55
- name: event.functionPath,
56
- // Reuse the dispatch's trace context when the runtime set it (so this span
57
- // shares the id it propagated as `traceparent`); else mint fresh ids.
58
- spanId: event.spanId ?? otlpRandomHex(8),
59
- startTimeUnixNano: otlpUnixNano(endMs - event.durationMs),
60
- // STATUS_CODE_OK (1) / STATUS_CODE_ERROR (2).
61
- status: event.ok ? { code: 1 } : { code: 2, message: event.error?.message ?? "" },
62
- traceId: event.traceId ?? otlpRandomHex(16)
63
- };
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);
80
- };
81
- const encodeSignalAttributes = (reserved, caller) => {
82
- const byKey = /* @__PURE__ */ new Map([["lunora.function_path", encodeAttribute("lunora.function_path", reserved.functionPath)]]);
83
- if (reserved.shardKey !== void 0) {
84
- byKey.set("lunora.shard_key", encodeAttribute("lunora.shard_key", reserved.shardKey));
85
- }
86
- if (reserved.userId !== void 0) {
87
- byKey.set("lunora.user_id", encodeAttribute("lunora.user_id", reserved.userId));
88
- }
89
- if (reserved.errorType !== void 0) {
90
- byKey.set("error.type", encodeAttribute("error.type", reserved.errorType));
91
- }
92
- for (const [key, value] of Object.entries(caller ?? {})) {
93
- byKey.set(key, encodeAttribute(key, coerceFieldValue(value)));
94
- }
95
- return [...byKey.values()];
96
- };
97
- const otlpSpanBody = (event, serviceName, resourceAttributes) => {
98
- const span = {
99
- attributes: encodeSignalAttributes(
100
- { errorType: event.error?.type, functionPath: event.functionPath, shardKey: event.shardKey, userId: event.userId },
101
- event.attributes
102
- ),
103
- endTimeUnixNano: otlpUnixNano(event.startTs + event.durationMs),
104
- // Always SPAN_KIND_INTERNAL: only `ctx.trace` spans reach a sink. The
105
- // synthetic dispatch span is buffered for the Studio waterfall and never
106
- // exported, because the runtime already emits that dispatch to `onRpc` as
107
- // a SERVER span — encoding it here too would duplicate it in every trace.
108
- kind: 1,
109
- name: event.name,
110
- parentSpanId: event.parentSpanId,
111
- spanId: event.spanId,
112
- startTimeUnixNano: otlpUnixNano(event.startTs),
113
- // STATUS_CODE_OK (1) / STATUS_CODE_ERROR (2).
114
- status: event.ok ? { code: 1 } : { code: 2, message: event.error?.message ?? "" },
115
- traceId: event.traceId
116
- };
117
- return wrapResourceSpans(span, "@lunora/runtime", serviceName, resourceAttributes);
118
- };
119
- const otlpMetricBody = (event, serviceName, resourceAttributes) => {
120
- const timeUnixNano = otlpUnixNano(event.ts);
121
- const attributes = encodeSignalAttributes({ functionPath: event.functionPath, shardKey: event.shardKey }, event.attributes);
122
- const dataPoint = { asDouble: event.value, attributes, timeUnixNano };
123
- if (event.kind === "gauge") {
124
- return wrapResourceMetrics({ gauge: { dataPoints: [dataPoint] }, name: event.name }, "@lunora/runtime", serviceName, resourceAttributes);
125
- }
126
- if (event.kind === "histogram") {
127
- return wrapResourceMetrics(
128
- {
129
- histogram: {
130
- aggregationTemporality: 1,
131
- dataPoints: [
132
- {
133
- attributes,
134
- bucketCounts: ["1"],
135
- count: "1",
136
- explicitBounds: [],
137
- max: event.value,
138
- min: event.value,
139
- // `startTimeUnixNano` omitted for the same reason as
140
- // the Sum data point above — see the comment there.
141
- sum: event.value,
142
- timeUnixNano
143
- }
144
- ]
145
- },
146
- name: event.name
147
- },
148
- "@lunora/runtime",
149
- serviceName,
150
- resourceAttributes
151
- );
152
- }
153
- return wrapResourceMetrics(
154
- { name: event.name, sum: { aggregationTemporality: 1, dataPoints: [dataPoint], isMonotonic: true } },
155
- "@lunora/runtime",
156
- serviceName,
157
- resourceAttributes
158
- );
159
- };
160
- const otlpLogBody = (event, serviceName, resourceAttributes) => {
161
- const logRecord = {
162
- // Caller-supplied structured fields become log-record attributes so a
163
- // pipeline can filter/index on them; precedence per `encodeSignalAttributes`.
164
- attributes: encodeSignalAttributes({ functionPath: event.functionPath, shardKey: event.shardKey, userId: event.userId }, event.fields),
165
- body: { stringValue: event.message },
166
- severityNumber: OTLP_SEVERITY[event.level],
167
- severityText: event.level.toUpperCase(),
168
- timeUnixNano: otlpUnixNano(event.ts)
169
- };
170
- if (event.traceId !== void 0) {
171
- logRecord.traceId = event.traceId;
172
- }
173
- if (event.spanId !== void 0) {
174
- logRecord.spanId = event.spanId;
175
- }
176
- return wrapResourceLogs(logRecord, "@lunora/runtime", serviceName, resourceAttributes);
177
- };
178
- const OTLP_GZIP_THRESHOLD = 1024;
179
- const gzipEncode = async (text) => {
180
- const stream = new Blob([text]).stream().pipeThrough(new CompressionStream("gzip"));
181
- return new Response(stream).arrayBuffer();
182
- };
183
- const otlpPost = (url, body, headers, context) => {
184
- try {
185
- const json = JSON.stringify(body);
186
- const sent = (json.length < OTLP_GZIP_THRESHOLD ? fetch(url, { body: json, headers, method: "POST" }) : gzipEncode(json).then((gz) => fetch(url, { body: gz, headers: { ...headers, "content-encoding": "gzip" }, method: "POST" }))).catch(() => {
187
- });
188
- if (context?.waitUntil) {
189
- context.waitUntil(sent);
190
- }
191
- } catch {
192
- }
193
- };
194
-
195
- const shouldSkip = (event, onlyErrors) => onlyErrors === true && event.ok;
196
- const consoleSink = (options = {}) => {
197
- const { onlyErrors } = options;
198
- return {
199
- onLog: (event) => {
200
- if (event.level === "error" || event.level === "fatal") {
201
- console.error("[lunora:log]", event.functionPath, event.message);
202
- } else {
203
- console.log("[lunora:log]", event.functionPath, event.message);
204
- }
205
- },
206
- onMetric: (event) => {
207
- console.log("[lunora:metric]", `${event.name}=${String(event.value)}`, event.kind, event.functionPath);
208
- },
209
- onRpc: (event) => {
210
- if (shouldSkip(event, onlyErrors)) {
211
- return;
212
- }
213
- if (event.ok) {
214
- console.log("[lunora:rpc]", event);
215
- } else {
216
- console.error("[lunora:rpc]", event);
217
- }
218
- },
219
- onSpan: (event) => {
220
- const status = event.ok ? "ok" : `error ${event.error?.type ?? ""}`.trim();
221
- console.log("[lunora:span]", event.name, `${String(event.durationMs)}ms`, status, event.functionPath);
222
- }
223
- };
224
- };
225
- const webhookSink = (options) => {
226
- const { headers, onlyErrors, transform, transformLog, url } = options;
227
- const mergedHeaders = mergeHeaders({ "content-type": "application/json" }, headers);
228
- const post = (payload, context) => {
229
- try {
230
- const sent = fetch(url, { body: JSON.stringify(payload), headers: mergedHeaders, method: "POST" }).catch(() => {
231
- });
232
- if (context?.waitUntil) {
233
- context.waitUntil(sent);
234
- }
235
- } catch {
236
- }
237
- };
238
- return {
239
- onLog: (event, context) => {
240
- let payload = event;
241
- if (transformLog) {
242
- try {
243
- payload = transformLog(event);
244
- } catch {
245
- return;
246
- }
247
- }
248
- if (payload === null || payload === void 0) {
249
- return;
250
- }
251
- post(payload, context);
252
- },
253
- onRpc: (event, context) => {
254
- if (shouldSkip(event, onlyErrors)) {
255
- return;
256
- }
257
- try {
258
- let payload = event;
259
- if (transform) {
260
- try {
261
- payload = transform(event);
262
- } catch {
263
- return;
264
- }
265
- }
266
- if (payload === null || payload === void 0) {
267
- return;
268
- }
269
- post(payload, context);
270
- } catch {
271
- }
272
- }
273
- };
274
- };
275
- const sentrySink = (options) => {
276
- const { capture, captureLog } = options;
277
- const onlyErrors = options.onlyErrors ?? true;
278
- return {
279
- // Only forward log lines when the caller wired `captureLog`; otherwise
280
- // `ctx.log` output stays out of Sentry.
281
- onLog: captureLog ? (event) => {
282
- try {
283
- captureLog(event);
284
- } catch {
285
- }
286
- } : void 0,
287
- onRpc: (event) => {
288
- if (shouldSkip(event, onlyErrors)) {
289
- return;
290
- }
291
- try {
292
- capture(event);
293
- } catch {
294
- }
295
- }
296
- };
297
- };
298
- const analyticsEngineSink = (options) => {
299
- const { dataset, onlyErrors } = options;
300
- return {
301
- onRpc: (event) => {
302
- if (shouldSkip(event, onlyErrors)) {
303
- return;
304
- }
305
- try {
306
- dataset.writeDataPoint({
307
- blobs: [event.functionPath, event.ok ? "ok" : "error", event.shardKey ?? "", event.error?.code ?? "", event.fanOut?.table ?? ""],
308
- doubles: [event.durationMs, event.ok ? 0 : 1, event.fanOut?.shards ?? 0, event.fanOut?.failed ?? 0],
309
- indexes: [event.functionPath]
310
- });
311
- } catch {
312
- }
313
- }
314
- };
315
- };
316
- const pipelineLogSink = (options) => {
317
- const { pipeline, serializeFields } = options;
318
- return {
319
- onLog: (event, context) => {
320
- try {
321
- const record = {
322
- functionPath: event.functionPath,
323
- level: event.level,
324
- message: event.message,
325
- ts: event.ts
326
- };
327
- if (event.fields) {
328
- record.fields = serializeFields === true ? JSON.stringify(event.fields) : event.fields;
329
- }
330
- if (event.shardKey !== void 0) {
331
- record.shardKey = event.shardKey;
332
- }
333
- if (event.userId !== void 0) {
334
- record.userId = event.userId;
335
- }
336
- if (event.traceId !== void 0) {
337
- record.traceId = event.traceId;
338
- }
339
- if (event.spanId !== void 0) {
340
- record.spanId = event.spanId;
341
- }
342
- const sent = pipeline.send([record]).catch(() => {
343
- });
344
- if (context?.waitUntil) {
345
- context.waitUntil(sent);
346
- }
347
- } catch {
348
- }
349
- }
350
- };
351
- };
352
- const otlpSink = (options) => {
353
- const { deploymentEnvironment, detectResources, endpoint, headers, onlyErrors, resourceAttributes, serviceNamespace, serviceVersion, token } = options;
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
- };
374
- let base = endpoint;
375
- while (base.endsWith("/")) {
376
- base = base.slice(0, -1);
377
- }
378
- const tracesUrl = `${base}/v1/traces`;
379
- const logsUrl = `${base}/v1/logs`;
380
- const metricsUrl = `${base}/v1/metrics`;
381
- const mergedHeaders = mergeHeaders({ "content-type": "application/json" }, headers, token);
382
- return {
383
- onLog: (event, context) => {
384
- otlpPost(logsUrl, otlpLogBody(event, serviceName, resourceAttributesFor(context)), mergedHeaders, context);
385
- },
386
- onMetric: (event, context) => {
387
- otlpPost(metricsUrl, otlpMetricBody(event, serviceName, resourceAttributesFor(context)), mergedHeaders, context);
388
- },
389
- onRpc: (event, context) => {
390
- if (shouldSkip(event, onlyErrors)) {
391
- return;
392
- }
393
- otlpPost(tracesUrl, otlpTraceBody(event, serviceName, Date.now(), resourceAttributesFor(context)), mergedHeaders, context);
394
- },
395
- onSpan: (event, context) => {
396
- otlpPost(tracesUrl, otlpSpanBody(event, serviceName, resourceAttributesFor(context)), mergedHeaders, context);
397
- }
398
- };
399
- };
400
- const combineSinks = (...sinks) => {
401
- const fanOut = (method, event, context) => {
402
- for (const sink of sinks) {
403
- const handler = sink[method];
404
- if (!handler) {
405
- continue;
406
- }
407
- try {
408
- handler.call(sink, event, context);
409
- } catch {
410
- }
411
- }
412
- };
413
- return {
414
- onLog: (event, context) => {
415
- fanOut("onLog", event, context);
416
- },
417
- onMetric: (event, context) => {
418
- fanOut("onMetric", event, context);
419
- },
420
- onRpc: (event, context) => {
421
- fanOut("onRpc", event, context);
422
- },
423
- onSpan: (event, context) => {
424
- fanOut("onSpan", event, context);
425
- }
426
- };
427
- };
428
-
429
- export { analyticsEngineSink, combineSinks, consoleSink, otlpSink, pipelineLogSink, sentrySink, webhookSink };
@@ -1,20 +0,0 @@
1
- const applyJurisdiction = (namespace, jurisdiction) => {
2
- if (jurisdiction === void 0) {
3
- return namespace;
4
- }
5
- if (typeof namespace.jurisdiction !== "function") {
6
- throw new TypeError(
7
- `@lunora/runtime: Durable Object namespace does not support jurisdiction("${jurisdiction}") — update @cloudflare/workers-types or remove the jurisdiction option`
8
- );
9
- }
10
- return namespace.jurisdiction(jurisdiction);
11
- };
12
- const resolveShard = (namespace, shardKey) => {
13
- if (typeof namespace.getByName === "function") {
14
- return namespace.getByName(shardKey);
15
- }
16
- const id = namespace.idFromName(shardKey);
17
- return namespace.get(id);
18
- };
19
-
20
- export { applyJurisdiction, resolveShard };