@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.
- package/dist/index.d.mts +359 -8
- package/dist/index.d.ts +359 -8
- package/dist/index.mjs +1 -22
- package/dist/packem_shared/DEFAULT_LOG_COLUMNS-CpZ9kd-q.mjs +1 -0
- package/dist/packem_shared/DEFAULT_REGISTRY_CACHE_TTL_MS-BBf3mNrD.mjs +1 -0
- package/dist/packem_shared/GET_AUTH_AUDIT_LOG_OP-DmxHQbZT.mjs +1 -0
- package/dist/packem_shared/HEALTH_PATH-DiZqjEGp.mjs +1 -0
- package/dist/packem_shared/LOG_ARCHIVE_NOT_CONFIGURED-CDpi4yFD.mjs +1 -0
- package/dist/packem_shared/LOG_ARCHIVE_PATH-e8U3ftfM.mjs +1 -0
- package/dist/packem_shared/LunoraError-C08OP5Uq.mjs +1 -0
- package/dist/packem_shared/NOOP_EXECUTION_CONTEXT-YmXqH-jH.mjs +1 -0
- package/dist/packem_shared/analyticsEngineSink-DOoyBx4o.mjs +1 -0
- package/dist/packem_shared/applyJurisdiction-uRQLx282.mjs +1 -0
- package/dist/packem_shared/argsFromQuery-0KrWTkNx.mjs +1 -0
- package/dist/packem_shared/composeIdentityResolvers-DlBbYmBJ.mjs +1 -0
- package/dist/packem_shared/composeWorker-D7Zi6kZR.mjs +6 -0
- package/dist/packem_shared/createCrossShardRelationCapabilities-B2EKbSEs.mjs +1 -0
- package/dist/packem_shared/createKvCursorStore-C24tEuYk.mjs +3 -0
- package/dist/packem_shared/createQueryCoordinator-DLsrcOkB.mjs +1 -0
- package/dist/packem_shared/createShardClient-62qcYKGl.mjs +1 -0
- package/dist/packem_shared/decorateResponse-C6TZSzID.mjs +1 -0
- package/dist/packem_shared/emitLogEvent-CEbCh6FQ.mjs +1 -0
- package/dist/packem_shared/memoizeIdentity-DRYIK1yT.mjs +1 -0
- package/dist/packem_shared/method-guard-BbuR0VfS.mjs +1 -0
- package/dist/packem_shared/observability-B0MYwNDB.mjs +1 -0
- package/dist/packem_shared/otlp-resource-cAjGEywx.mjs +1 -0
- package/dist/packem_shared/pipeline-log-reader-tDuvc_Ty.mjs +1 -0
- package/dist/packem_shared/toAirbyteMessages-DBTuFjb5.mjs +1 -0
- package/package.json +1 -1
- package/dist/packem_shared/DEFAULT_LOG_COLUMNS-B7H3YdJ3.mjs +0 -2
- package/dist/packem_shared/DEFAULT_REGISTRY_CACHE_TTL_MS-B3pA7aXp.mjs +0 -79
- package/dist/packem_shared/HEALTH_PATH-e5J_NHBx.mjs +0 -150
- package/dist/packem_shared/LOG_ARCHIVE_NOT_CONFIGURED-acNcguqc.mjs +0 -3
- package/dist/packem_shared/LOG_ARCHIVE_PATH-CNs0bznX.mjs +0 -125
- package/dist/packem_shared/LunoraError-Bpb9EFJ3.mjs +0 -22
- package/dist/packem_shared/NOOP_EXECUTION_CONTEXT-CCTu0Bf1.mjs +0 -8
- package/dist/packem_shared/analyticsEngineSink-yLFNjHDt.mjs +0 -429
- package/dist/packem_shared/applyJurisdiction-BkZtTkct.mjs +0 -20
- package/dist/packem_shared/argsFromQuery-c-U1WRy-.mjs +0 -121
- package/dist/packem_shared/composeIdentityResolvers-XGjO7V1J.mjs +0 -55
- package/dist/packem_shared/composeWorker-DiWwOXXt.mjs +0 -3738
- package/dist/packem_shared/createCrossShardRelationCapabilities-CbcWjkAn.mjs +0 -63
- package/dist/packem_shared/createKvCursorStore-g8aA6B4L.mjs +0 -165
- package/dist/packem_shared/createQueryCoordinator-DNCJzOZE.mjs +0 -847
- package/dist/packem_shared/decorateResponse-DRWQFNhF.mjs +0 -273
- package/dist/packem_shared/emitLogEvent-BlGMnKZK.mjs +0 -1
- package/dist/packem_shared/method-guard-Qzw99aCj.mjs +0 -3
- package/dist/packem_shared/observability--NOFYBFc.mjs +0 -47
- package/dist/packem_shared/otlp-resource-Dow6-F_u.mjs +0 -163
- package/dist/packem_shared/pipeline-log-reader-BXULGNC3.mjs +0 -135
- package/dist/packem_shared/toAirbyteMessages-DrHdplb4.mjs +0 -39
|
@@ -1,273 +0,0 @@
|
|
|
1
|
-
import { LunoraError } from './LunoraError-Bpb9EFJ3.mjs';
|
|
2
|
-
|
|
3
|
-
const DEFAULT_CSP = "default-src 'none'; frame-ancestors 'none'; base-uri 'none'; form-action 'none'";
|
|
4
|
-
const htmlCspFor = (frameOptions) => {
|
|
5
|
-
const parts = ["base-uri 'none'", "object-src 'none'"];
|
|
6
|
-
if (frameOptions === "DENY") {
|
|
7
|
-
parts.push("frame-ancestors 'none'");
|
|
8
|
-
} else if (frameOptions === "SAMEORIGIN") {
|
|
9
|
-
parts.push("frame-ancestors 'self'");
|
|
10
|
-
}
|
|
11
|
-
return parts.join("; ");
|
|
12
|
-
};
|
|
13
|
-
const DEFAULT_PERMISSIONS_POLICY = "accelerometer=(), autoplay=(), camera=(), display-capture=(), geolocation=(), gyroscope=(), magnetometer=(), microphone=(), payment=(), usb=()";
|
|
14
|
-
const DEFAULT_CORS_HEADERS = ["Authorization", "Content-Type", "X-D1-Bookmark", "X-Lunora-Mutation-Id"];
|
|
15
|
-
const DEFAULT_CORS_METHODS = ["DELETE", "GET", "HEAD", "PATCH", "POST", "PUT"];
|
|
16
|
-
const ONE_YEAR_SECONDS = 31536e3;
|
|
17
|
-
const SAFE_METHODS = /* @__PURE__ */ new Set(["GET", "HEAD", "OPTIONS"]);
|
|
18
|
-
const resolveHstsHeader = (hsts) => {
|
|
19
|
-
if (hsts === false) {
|
|
20
|
-
return void 0;
|
|
21
|
-
}
|
|
22
|
-
const config = hsts === void 0 || hsts === true ? {} : hsts;
|
|
23
|
-
const maxAge = config.maxAge ?? ONE_YEAR_SECONDS;
|
|
24
|
-
const includeSubDomains = config.includeSubDomains ?? true;
|
|
25
|
-
return `max-age=${String(maxAge)}${includeSubDomains ? "; includeSubDomains" : ""}${config.preload ? "; preload" : ""}`;
|
|
26
|
-
};
|
|
27
|
-
const resolveCspHeader = (csp, htmlDefault) => {
|
|
28
|
-
if (csp === false) {
|
|
29
|
-
return void 0;
|
|
30
|
-
}
|
|
31
|
-
if (typeof csp === "string") {
|
|
32
|
-
return { htmlValue: csp, value: csp };
|
|
33
|
-
}
|
|
34
|
-
return { htmlValue: htmlDefault, value: DEFAULT_CSP };
|
|
35
|
-
};
|
|
36
|
-
const resolveHeaders = (input) => {
|
|
37
|
-
if (input === false) {
|
|
38
|
-
return {
|
|
39
|
-
coop: void 0,
|
|
40
|
-
csp: void 0,
|
|
41
|
-
enabled: false,
|
|
42
|
-
frameOptions: void 0,
|
|
43
|
-
hsts: void 0,
|
|
44
|
-
permissionsPolicy: void 0,
|
|
45
|
-
referrerPolicy: void 0
|
|
46
|
-
};
|
|
47
|
-
}
|
|
48
|
-
const options = input === void 0 || input === true ? {} : input;
|
|
49
|
-
const frameOptions = options.frameOptions === false ? void 0 : options.frameOptions ?? "SAMEORIGIN";
|
|
50
|
-
return {
|
|
51
|
-
coop: "same-origin",
|
|
52
|
-
csp: resolveCspHeader(options.csp, htmlCspFor(frameOptions)),
|
|
53
|
-
enabled: true,
|
|
54
|
-
frameOptions,
|
|
55
|
-
hsts: resolveHstsHeader(options.hsts),
|
|
56
|
-
permissionsPolicy: options.permissionsPolicy === false ? void 0 : options.permissionsPolicy ?? DEFAULT_PERMISSIONS_POLICY,
|
|
57
|
-
referrerPolicy: options.referrerPolicy === false ? void 0 : options.referrerPolicy ?? "strict-origin-when-cross-origin"
|
|
58
|
-
};
|
|
59
|
-
};
|
|
60
|
-
const resolveCors = (input) => {
|
|
61
|
-
const disabled = {
|
|
62
|
-
allowCredentials: false,
|
|
63
|
-
allowedHeaders: DEFAULT_CORS_HEADERS,
|
|
64
|
-
allowedMethods: DEFAULT_CORS_METHODS,
|
|
65
|
-
enabled: false,
|
|
66
|
-
isAllowed: () => false,
|
|
67
|
-
isExplicitlyAllowed: () => false,
|
|
68
|
-
maxAge: 600
|
|
69
|
-
};
|
|
70
|
-
if (input === void 0 || input === false) {
|
|
71
|
-
return disabled;
|
|
72
|
-
}
|
|
73
|
-
const allowCredentials = input.allowCredentials ?? false;
|
|
74
|
-
const origins = input.allowedOrigins;
|
|
75
|
-
let isAllowed;
|
|
76
|
-
let isExplicitlyAllowed;
|
|
77
|
-
if (typeof origins === "function") {
|
|
78
|
-
isAllowed = origins;
|
|
79
|
-
isExplicitlyAllowed = origins;
|
|
80
|
-
const credentialsNote = allowCredentials ? " AND reflects matching origins with credentials (`allowCredentials: true`)" : "";
|
|
81
|
-
console.warn(
|
|
82
|
-
`@lunora/runtime: security.cors uses a custom \`allowedOrigins\` predicate. It is trusted by the CSRF and WebSocket origin checks${credentialsNote} — ensure it matches ONLY trusted origins by exact equality; an over-broad predicate (e.g. \`() => true\`, or \`endsWith\`/\`includes\` checks) defeats the allowlist.`
|
|
83
|
-
);
|
|
84
|
-
} else {
|
|
85
|
-
const originsList = origins;
|
|
86
|
-
if (originsList.includes("*") && allowCredentials) {
|
|
87
|
-
throw new LunoraError(
|
|
88
|
-
'@lunora/runtime: security.cors cannot combine a wildcard origin ("*") with allowCredentials: true — browsers reject it and it defeats the allowlist.'
|
|
89
|
-
);
|
|
90
|
-
}
|
|
91
|
-
isAllowed = (origin) => originsList.includes("*") || originsList.includes(origin);
|
|
92
|
-
isExplicitlyAllowed = (origin) => originsList.includes(origin);
|
|
93
|
-
}
|
|
94
|
-
return {
|
|
95
|
-
allowCredentials,
|
|
96
|
-
allowedHeaders: input.allowedHeaders ?? DEFAULT_CORS_HEADERS,
|
|
97
|
-
allowedMethods: input.allowedMethods ?? DEFAULT_CORS_METHODS,
|
|
98
|
-
enabled: true,
|
|
99
|
-
isAllowed,
|
|
100
|
-
isExplicitlyAllowed,
|
|
101
|
-
maxAge: input.maxAge ?? 600
|
|
102
|
-
};
|
|
103
|
-
};
|
|
104
|
-
const resolveCsrf = (input) => {
|
|
105
|
-
if (input === false) {
|
|
106
|
-
return { enabled: false, trustedOrigins: [] };
|
|
107
|
-
}
|
|
108
|
-
const options = input === void 0 || input === true ? {} : input;
|
|
109
|
-
return { enabled: true, trustedOrigins: options.trustedOrigins ?? [] };
|
|
110
|
-
};
|
|
111
|
-
const DISABLED_ENV_VALUES = /* @__PURE__ */ new Set(["0", "disabled", "false", "no", "off"]);
|
|
112
|
-
const ENABLED_ENV_VALUES = /* @__PURE__ */ new Set(["1", "enabled", "on", "true", "yes"]);
|
|
113
|
-
const isEnvDisabled = (value) => typeof value === "string" && DISABLED_ENV_VALUES.has(value.trim().toLowerCase());
|
|
114
|
-
const isEnvEnabled = (value) => typeof value === "string" && ENABLED_ENV_VALUES.has(value.trim().toLowerCase());
|
|
115
|
-
const parseEnvCors = (env) => {
|
|
116
|
-
const raw = env?.["LUNORA_ALLOWED_ORIGINS"];
|
|
117
|
-
if (typeof raw !== "string") {
|
|
118
|
-
return void 0;
|
|
119
|
-
}
|
|
120
|
-
const allowedOrigins = raw.split(",").map((origin) => origin.trim()).filter((origin) => origin.length > 0);
|
|
121
|
-
if (allowedOrigins.length === 0) {
|
|
122
|
-
return void 0;
|
|
123
|
-
}
|
|
124
|
-
const wildcard = allowedOrigins.includes("*");
|
|
125
|
-
const allowCredentials = !wildcard && isEnvEnabled(env?.["LUNORA_CORS_ALLOW_CREDENTIALS"]);
|
|
126
|
-
return { allowCredentials, allowedOrigins };
|
|
127
|
-
};
|
|
128
|
-
const resolveSecurity = (security, env) => {
|
|
129
|
-
const headers = security?.headers ?? (isEnvDisabled(env?.["LUNORA_SECURITY_HEADERS"]) ? false : void 0);
|
|
130
|
-
const csrf = security?.csrf ?? (isEnvDisabled(env?.["LUNORA_SECURITY_CSRF"]) ? false : void 0);
|
|
131
|
-
const cors = security?.cors ?? parseEnvCors(env);
|
|
132
|
-
return {
|
|
133
|
-
cors: resolveCors(cors),
|
|
134
|
-
csrf: resolveCsrf(csrf),
|
|
135
|
-
headers: resolveHeaders(headers)
|
|
136
|
-
};
|
|
137
|
-
};
|
|
138
|
-
const originOf = (value) => {
|
|
139
|
-
if (!value) {
|
|
140
|
-
return void 0;
|
|
141
|
-
}
|
|
142
|
-
try {
|
|
143
|
-
return new URL(value).origin;
|
|
144
|
-
} catch {
|
|
145
|
-
return void 0;
|
|
146
|
-
}
|
|
147
|
-
};
|
|
148
|
-
const isTrustedOrigin = (origin, selfOrigin, resolved) => {
|
|
149
|
-
if (origin === selfOrigin) {
|
|
150
|
-
return true;
|
|
151
|
-
}
|
|
152
|
-
if (resolved.csrf.trustedOrigins.includes(origin)) {
|
|
153
|
-
return true;
|
|
154
|
-
}
|
|
155
|
-
return resolved.cors.enabled && resolved.cors.isExplicitlyAllowed(origin);
|
|
156
|
-
};
|
|
157
|
-
const enforceOrigin = (request, resolved) => {
|
|
158
|
-
if (!resolved.csrf.enabled || SAFE_METHODS.has(request.method) || !request.headers.get("cookie")) {
|
|
159
|
-
return void 0;
|
|
160
|
-
}
|
|
161
|
-
const selfOrigin = new URL(request.url).origin;
|
|
162
|
-
const source = originOf(request.headers.get("origin")) ?? originOf(request.headers.get("referer"));
|
|
163
|
-
if (source !== void 0 && isTrustedOrigin(source, selfOrigin, resolved)) {
|
|
164
|
-
return void 0;
|
|
165
|
-
}
|
|
166
|
-
return Response.json(
|
|
167
|
-
{ error: { code: "FORBIDDEN_ORIGIN", message: "cross-origin state-changing request rejected" } },
|
|
168
|
-
{ headers: { "content-type": "application/json" }, status: 403 }
|
|
169
|
-
);
|
|
170
|
-
};
|
|
171
|
-
const enforceWebSocketOrigin = (request, resolved) => {
|
|
172
|
-
if (!resolved.csrf.enabled || !request.headers.get("cookie")) {
|
|
173
|
-
return void 0;
|
|
174
|
-
}
|
|
175
|
-
const selfOrigin = new URL(request.url).origin;
|
|
176
|
-
const source = originOf(request.headers.get("origin"));
|
|
177
|
-
if (source !== void 0 && isTrustedOrigin(source, selfOrigin, resolved)) {
|
|
178
|
-
return void 0;
|
|
179
|
-
}
|
|
180
|
-
return Response.json(
|
|
181
|
-
{ error: { code: "FORBIDDEN_ORIGIN", message: "cross-origin websocket upgrade rejected" } },
|
|
182
|
-
{ headers: { "content-type": "application/json" }, status: 403 }
|
|
183
|
-
);
|
|
184
|
-
};
|
|
185
|
-
const corsResponseHeaders = (origin, cors) => {
|
|
186
|
-
const headers = new Headers();
|
|
187
|
-
headers.set("access-control-allow-origin", origin);
|
|
188
|
-
headers.append("vary", "Origin");
|
|
189
|
-
if (cors.allowCredentials) {
|
|
190
|
-
headers.set("access-control-allow-credentials", "true");
|
|
191
|
-
}
|
|
192
|
-
return headers;
|
|
193
|
-
};
|
|
194
|
-
const handleCorsPreflight = (request, resolved) => {
|
|
195
|
-
if (!resolved.cors.enabled || request.method !== "OPTIONS") {
|
|
196
|
-
return void 0;
|
|
197
|
-
}
|
|
198
|
-
const origin = request.headers.get("origin");
|
|
199
|
-
if (!origin || !request.headers.get("access-control-request-method") || !resolved.cors.isAllowed(origin)) {
|
|
200
|
-
return void 0;
|
|
201
|
-
}
|
|
202
|
-
const headers = corsResponseHeaders(origin, resolved.cors);
|
|
203
|
-
const requested = request.headers.get("access-control-request-headers");
|
|
204
|
-
headers.set("access-control-allow-methods", resolved.cors.allowedMethods.join(", "));
|
|
205
|
-
let allowedHeaders;
|
|
206
|
-
if (requested === null) {
|
|
207
|
-
allowedHeaders = resolved.cors.allowedHeaders.join(", ");
|
|
208
|
-
} else {
|
|
209
|
-
const permitted = new Set(resolved.cors.allowedHeaders.map((name) => name.toLowerCase()));
|
|
210
|
-
allowedHeaders = requested.split(",").map((name) => name.trim()).filter((name) => name.length > 0 && permitted.has(name.toLowerCase())).join(", ");
|
|
211
|
-
}
|
|
212
|
-
headers.set("access-control-allow-headers", allowedHeaders);
|
|
213
|
-
headers.set("access-control-max-age", String(resolved.cors.maxAge));
|
|
214
|
-
return new Response(null, { headers, status: 204 });
|
|
215
|
-
};
|
|
216
|
-
const isHtmlResponse = (response) => (response.headers.get("content-type") ?? "").toLowerCase().includes("text/html");
|
|
217
|
-
const setIfAbsent = (headers, name, value) => {
|
|
218
|
-
if (!headers.has(name)) {
|
|
219
|
-
headers.set(name, value);
|
|
220
|
-
}
|
|
221
|
-
};
|
|
222
|
-
const applyBaselineHeaders = (headers, request, response, config) => {
|
|
223
|
-
if (config.hsts !== void 0 && new URL(request.url).protocol === "https:") {
|
|
224
|
-
setIfAbsent(headers, "strict-transport-security", config.hsts);
|
|
225
|
-
}
|
|
226
|
-
setIfAbsent(headers, "x-content-type-options", "nosniff");
|
|
227
|
-
if (config.frameOptions !== void 0) {
|
|
228
|
-
setIfAbsent(headers, "x-frame-options", config.frameOptions);
|
|
229
|
-
}
|
|
230
|
-
if (config.referrerPolicy !== void 0) {
|
|
231
|
-
setIfAbsent(headers, "referrer-policy", config.referrerPolicy);
|
|
232
|
-
}
|
|
233
|
-
if (config.permissionsPolicy !== void 0) {
|
|
234
|
-
setIfAbsent(headers, "permissions-policy", config.permissionsPolicy);
|
|
235
|
-
}
|
|
236
|
-
if (config.coop !== void 0) {
|
|
237
|
-
setIfAbsent(headers, "cross-origin-opener-policy", config.coop);
|
|
238
|
-
}
|
|
239
|
-
if (config.csp !== void 0) {
|
|
240
|
-
const cspValue = isHtmlResponse(response) ? config.csp.htmlValue : config.csp.value;
|
|
241
|
-
if (cspValue !== void 0) {
|
|
242
|
-
setIfAbsent(headers, "content-security-policy", cspValue);
|
|
243
|
-
}
|
|
244
|
-
}
|
|
245
|
-
};
|
|
246
|
-
const applyCorsHeaders = (headers, request, cors) => {
|
|
247
|
-
const origin = request.headers.get("origin");
|
|
248
|
-
if (!origin || !cors.isAllowed(origin)) {
|
|
249
|
-
return;
|
|
250
|
-
}
|
|
251
|
-
for (const [name, value] of corsResponseHeaders(origin, cors).entries()) {
|
|
252
|
-
if (name === "vary") {
|
|
253
|
-
headers.append("vary", value);
|
|
254
|
-
} else {
|
|
255
|
-
setIfAbsent(headers, name, value);
|
|
256
|
-
}
|
|
257
|
-
}
|
|
258
|
-
};
|
|
259
|
-
const decorateResponse = (response, request, resolved) => {
|
|
260
|
-
if (response.status === 101 || response.webSocket) {
|
|
261
|
-
return response;
|
|
262
|
-
}
|
|
263
|
-
const headers = new Headers(response.headers);
|
|
264
|
-
if (resolved.headers.enabled) {
|
|
265
|
-
applyBaselineHeaders(headers, request, response, resolved.headers);
|
|
266
|
-
}
|
|
267
|
-
if (resolved.cors.enabled) {
|
|
268
|
-
applyCorsHeaders(headers, request, resolved.cors);
|
|
269
|
-
}
|
|
270
|
-
return new Response(response.body, { headers, status: response.status, statusText: response.statusText });
|
|
271
|
-
};
|
|
272
|
-
|
|
273
|
-
export { decorateResponse, enforceOrigin, enforceWebSocketOrigin, handleCorsPreflight, resolveSecurity };
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
export { e as emitLogEvent, a as emitRpcEvent } from './observability--NOFYBFc.mjs';
|
|
@@ -1,47 +0,0 @@
|
|
|
1
|
-
const DEFAULT_TRACE_HEAD_RATE = 1;
|
|
2
|
-
const traceIdToUnitInterval = (traceId) => {
|
|
3
|
-
const int = Number.parseInt(traceId.slice(0, 8), 16);
|
|
4
|
-
return Number.isFinite(int) ? int / 4294967296 : 0;
|
|
5
|
-
};
|
|
6
|
-
const isTraceHeadSampled = (traceId, headRate = DEFAULT_TRACE_HEAD_RATE) => {
|
|
7
|
-
if (headRate >= 1) {
|
|
8
|
-
return true;
|
|
9
|
-
}
|
|
10
|
-
if (headRate <= 0) {
|
|
11
|
-
return false;
|
|
12
|
-
}
|
|
13
|
-
return traceIdToUnitInterval(traceId) < headRate;
|
|
14
|
-
};
|
|
15
|
-
const resolveTraceSampling = (config, traceId) => {
|
|
16
|
-
return {
|
|
17
|
-
isTraced: isTraceHeadSampled(traceId, config?.headRate ?? DEFAULT_TRACE_HEAD_RATE),
|
|
18
|
-
keepErrors: config?.alwaysSampleErrors ?? true
|
|
19
|
-
};
|
|
20
|
-
};
|
|
21
|
-
const shouldExportTrace = (decision, traceHasError) => {
|
|
22
|
-
return decision.isTraced || decision.keepErrors && traceHasError;
|
|
23
|
-
};
|
|
24
|
-
|
|
25
|
-
const emitRpcEvent = (sink, event, context, sampling) => {
|
|
26
|
-
if (!sink?.onRpc) {
|
|
27
|
-
return;
|
|
28
|
-
}
|
|
29
|
-
if (sampling !== void 0 && event.traceId !== void 0 && !shouldExportTrace(resolveTraceSampling(sampling, event.traceId), !event.ok)) {
|
|
30
|
-
return;
|
|
31
|
-
}
|
|
32
|
-
try {
|
|
33
|
-
sink.onRpc(event, context);
|
|
34
|
-
} catch {
|
|
35
|
-
}
|
|
36
|
-
};
|
|
37
|
-
const emitLogEvent = (sink, event, context) => {
|
|
38
|
-
if (!sink?.onLog) {
|
|
39
|
-
return;
|
|
40
|
-
}
|
|
41
|
-
try {
|
|
42
|
-
sink.onLog(event, context);
|
|
43
|
-
} catch {
|
|
44
|
-
}
|
|
45
|
-
};
|
|
46
|
-
|
|
47
|
-
export { emitRpcEvent as a, emitLogEvent as e, resolveTraceSampling as r };
|
|
@@ -1,163 +0,0 @@
|
|
|
1
|
-
const OTLP_SEVERITY = {
|
|
2
|
-
debug: 5,
|
|
3
|
-
// DEBUG
|
|
4
|
-
error: 17,
|
|
5
|
-
// ERROR
|
|
6
|
-
fatal: 21,
|
|
7
|
-
// FATAL
|
|
8
|
-
info: 9,
|
|
9
|
-
// INFO
|
|
10
|
-
log: 9,
|
|
11
|
-
// INFO
|
|
12
|
-
trace: 1,
|
|
13
|
-
// TRACE
|
|
14
|
-
warn: 13
|
|
15
|
-
// WARN
|
|
16
|
-
};
|
|
17
|
-
const otlpUnixNano = (ms) => `${String(Math.round(ms))}000000`;
|
|
18
|
-
const otlpRandomHex = (bytes) => {
|
|
19
|
-
const buffer = new Uint8Array(bytes);
|
|
20
|
-
crypto.getRandomValues(buffer);
|
|
21
|
-
let hex = "";
|
|
22
|
-
for (const byte of buffer) {
|
|
23
|
-
hex += byte.toString(16).padStart(2, "0");
|
|
24
|
-
}
|
|
25
|
-
return hex;
|
|
26
|
-
};
|
|
27
|
-
const HEX_ONLY = /^[0-9a-f]+$/;
|
|
28
|
-
const buildTraceparent = (traceId, spanId, sampled = true) => `00-${traceId}-${spanId}-${sampled ? "01" : "00"}`;
|
|
29
|
-
const parseTraceparent = (header) => {
|
|
30
|
-
if (header === null || header === void 0) {
|
|
31
|
-
return void 0;
|
|
32
|
-
}
|
|
33
|
-
const parts = header.trim().toLowerCase().split("-");
|
|
34
|
-
const [version, traceId, parentSpanId, flags] = parts;
|
|
35
|
-
if (parts.length < 4 || version === void 0 || version.length !== 2 || !HEX_ONLY.test(version) || version === "ff" || // Version 00 forbids trailing fields; only a future version may carry them.
|
|
36
|
-
version === "00" && parts.length !== 4 || traceId === void 0 || parentSpanId === void 0 || flags === void 0 || flags.length !== 2 || !HEX_ONLY.test(flags) || traceId.length !== 32 || parentSpanId.length !== 16 || !HEX_ONLY.test(traceId) || !HEX_ONLY.test(parentSpanId) || traceId === "00000000000000000000000000000000" || parentSpanId === "0000000000000000") {
|
|
37
|
-
return void 0;
|
|
38
|
-
}
|
|
39
|
-
return { parentSpanId, sampled: (Number.parseInt(flags, 16) & 1) === 1, traceId };
|
|
40
|
-
};
|
|
41
|
-
const encodeAttribute = (key, value) => {
|
|
42
|
-
if (typeof value === "boolean") {
|
|
43
|
-
return { key, value: { boolValue: value } };
|
|
44
|
-
}
|
|
45
|
-
if (typeof value === "number") {
|
|
46
|
-
if (!Number.isFinite(value)) {
|
|
47
|
-
return { key, value: { stringValue: String(value) } };
|
|
48
|
-
}
|
|
49
|
-
return Number.isSafeInteger(value) ? { key, value: { intValue: String(value) } } : { key, value: { doubleValue: value } };
|
|
50
|
-
}
|
|
51
|
-
return { key, value: { stringValue: value } };
|
|
52
|
-
};
|
|
53
|
-
const mergeHeaders = (defaults, overrides, token) => {
|
|
54
|
-
const merged = {};
|
|
55
|
-
const seen = /* @__PURE__ */ new Map();
|
|
56
|
-
const put = (name, value) => {
|
|
57
|
-
const lower = name.toLowerCase();
|
|
58
|
-
const existing = seen.get(lower);
|
|
59
|
-
if (existing === void 0) {
|
|
60
|
-
seen.set(lower, name);
|
|
61
|
-
merged[name] = value;
|
|
62
|
-
} else {
|
|
63
|
-
merged[existing] = value;
|
|
64
|
-
}
|
|
65
|
-
};
|
|
66
|
-
for (const [name, value] of Object.entries(defaults)) {
|
|
67
|
-
put(name, value);
|
|
68
|
-
}
|
|
69
|
-
for (const [name, value] of Object.entries(overrides ?? {})) {
|
|
70
|
-
put(name, value);
|
|
71
|
-
}
|
|
72
|
-
if (token !== void 0 && token.length > 0) {
|
|
73
|
-
put("authorization", `Bearer ${token}`);
|
|
74
|
-
}
|
|
75
|
-
return merged;
|
|
76
|
-
};
|
|
77
|
-
const buildResourceAttributes = (serviceName, extra) => {
|
|
78
|
-
const merged = { "service.name": serviceName };
|
|
79
|
-
for (const [key, value] of Object.entries(extra ?? {})) {
|
|
80
|
-
merged[key] = value;
|
|
81
|
-
}
|
|
82
|
-
return Object.entries(merged).map(([key, value]) => encodeAttribute(key, value));
|
|
83
|
-
};
|
|
84
|
-
const wrapResourceSpans = (span, scopeName, serviceName, resourceAttributes) => {
|
|
85
|
-
return {
|
|
86
|
-
resourceSpans: [
|
|
87
|
-
{
|
|
88
|
-
resource: { attributes: buildResourceAttributes(serviceName, resourceAttributes) },
|
|
89
|
-
scopeSpans: [{ scope: { name: scopeName }, spans: [span] }]
|
|
90
|
-
}
|
|
91
|
-
]
|
|
92
|
-
};
|
|
93
|
-
};
|
|
94
|
-
const wrapResourceLogs = (logRecord, scopeName, serviceName, resourceAttributes) => {
|
|
95
|
-
return {
|
|
96
|
-
resourceLogs: [
|
|
97
|
-
{
|
|
98
|
-
resource: { attributes: buildResourceAttributes(serviceName, resourceAttributes) },
|
|
99
|
-
scopeLogs: [{ logRecords: [logRecord], scope: { name: scopeName } }]
|
|
100
|
-
}
|
|
101
|
-
]
|
|
102
|
-
};
|
|
103
|
-
};
|
|
104
|
-
const wrapResourceMetrics = (metric, scopeName, serviceName, resourceAttributes) => {
|
|
105
|
-
return {
|
|
106
|
-
resourceMetrics: [
|
|
107
|
-
{
|
|
108
|
-
resource: { attributes: buildResourceAttributes(serviceName, resourceAttributes) },
|
|
109
|
-
scopeMetrics: [{ metrics: [metric], scope: { name: scopeName } }]
|
|
110
|
-
}
|
|
111
|
-
]
|
|
112
|
-
};
|
|
113
|
-
};
|
|
114
|
-
|
|
115
|
-
const readerFromRecord = (environment) => (key) => {
|
|
116
|
-
const value = environment?.[key];
|
|
117
|
-
return typeof value === "string" && value.length > 0 ? value : void 0;
|
|
118
|
-
};
|
|
119
|
-
const stringProperty = (object, key) => {
|
|
120
|
-
if (typeof object !== "object" || object === null) {
|
|
121
|
-
return void 0;
|
|
122
|
-
}
|
|
123
|
-
const value = object[key];
|
|
124
|
-
return typeof value === "string" && value.length > 0 ? value : void 0;
|
|
125
|
-
};
|
|
126
|
-
const detectServiceResource = (read) => {
|
|
127
|
-
const detected = {};
|
|
128
|
-
const serviceVersion = read("SERVICE_VERSION") ?? read("CF_VERSION_METADATA") ?? read("VERCEL_GIT_COMMIT_SHA") ?? read("GITHUB_SHA") ?? read("COMMIT_SHA");
|
|
129
|
-
if (serviceVersion !== void 0) {
|
|
130
|
-
detected["service.version"] = serviceVersion;
|
|
131
|
-
}
|
|
132
|
-
const deploymentEnvironment = read("DEPLOYMENT_ENVIRONMENT") ?? read("ENVIRONMENT") ?? read("NODE_ENV");
|
|
133
|
-
if (deploymentEnvironment !== void 0) {
|
|
134
|
-
detected["deployment.environment"] = deploymentEnvironment;
|
|
135
|
-
}
|
|
136
|
-
return detected;
|
|
137
|
-
};
|
|
138
|
-
const detectCloudflareResource = (read, cf) => {
|
|
139
|
-
const isCloudflare = cf !== void 0 || read("CLOUDFLARE") !== void 0 || read("CF_ACCOUNT_ID") !== void 0;
|
|
140
|
-
if (!isCloudflare) {
|
|
141
|
-
return {};
|
|
142
|
-
}
|
|
143
|
-
const detected = { "cloud.provider": "cloudflare" };
|
|
144
|
-
const colo = stringProperty(cf, "colo") ?? read("CF_COLO") ?? read("CLOUDFLARE_COLO");
|
|
145
|
-
if (colo !== void 0) {
|
|
146
|
-
detected["cloud.region"] = colo;
|
|
147
|
-
}
|
|
148
|
-
return detected;
|
|
149
|
-
};
|
|
150
|
-
const mergeResourceAttributes = (...bags) => {
|
|
151
|
-
const merged = {};
|
|
152
|
-
for (const bag of bags) {
|
|
153
|
-
if (bag === void 0) {
|
|
154
|
-
continue;
|
|
155
|
-
}
|
|
156
|
-
for (const [key, value] of Object.entries(bag)) {
|
|
157
|
-
merged[key] = value;
|
|
158
|
-
}
|
|
159
|
-
}
|
|
160
|
-
return merged;
|
|
161
|
-
};
|
|
162
|
-
|
|
163
|
-
export { OTLP_SEVERITY as O, detectServiceResource as a, buildTraceparent as b, wrapResourceMetrics as c, detectCloudflareResource as d, wrapResourceLogs as e, encodeAttribute as f, otlpUnixNano as g, mergeHeaders as h, mergeResourceAttributes as m, otlpRandomHex as o, parseTraceparent as p, readerFromRecord as r, wrapResourceSpans as w };
|
|
@@ -1,135 +0,0 @@
|
|
|
1
|
-
import { raw, sql, desc } from '@lunora/bindings/r2sql';
|
|
2
|
-
|
|
3
|
-
const LOG_LEVEL_ORDER = ["trace", "debug", "log", "info", "warn", "error", "fatal"];
|
|
4
|
-
|
|
5
|
-
const DEFAULT_COLUMNS = {
|
|
6
|
-
fields: "fields",
|
|
7
|
-
functionPath: "functionPath",
|
|
8
|
-
level: "level",
|
|
9
|
-
message: "message",
|
|
10
|
-
shardKey: "shardKey",
|
|
11
|
-
spanId: "spanId",
|
|
12
|
-
traceId: "traceId",
|
|
13
|
-
ts: "ts",
|
|
14
|
-
userId: "userId"
|
|
15
|
-
};
|
|
16
|
-
const DEFAULT_LIMIT = 500;
|
|
17
|
-
const MAX_LIMIT = 1e4;
|
|
18
|
-
const clampLimit = (limit) => {
|
|
19
|
-
if (limit === void 0 || !Number.isFinite(limit)) {
|
|
20
|
-
return DEFAULT_LIMIT;
|
|
21
|
-
}
|
|
22
|
-
const floored = Math.floor(limit);
|
|
23
|
-
if (floored < 1) {
|
|
24
|
-
return 1;
|
|
25
|
-
}
|
|
26
|
-
if (floored > MAX_LIMIT) {
|
|
27
|
-
return MAX_LIMIT;
|
|
28
|
-
}
|
|
29
|
-
return floored;
|
|
30
|
-
};
|
|
31
|
-
const toTs = (value) => typeof value === "number" ? value : Number(value);
|
|
32
|
-
const renderCell = (value) => value === null || value === void 0 ? "" : String(value);
|
|
33
|
-
const decodeFields = (value) => {
|
|
34
|
-
if (typeof value !== "string") {
|
|
35
|
-
return value;
|
|
36
|
-
}
|
|
37
|
-
try {
|
|
38
|
-
return JSON.parse(value);
|
|
39
|
-
} catch {
|
|
40
|
-
return value;
|
|
41
|
-
}
|
|
42
|
-
};
|
|
43
|
-
const DEFAULT_LOG_COLUMNS = DEFAULT_COLUMNS;
|
|
44
|
-
const DEFAULT_LOG_LIMIT = DEFAULT_LIMIT;
|
|
45
|
-
const createPipelineLogReader = (client, options) => {
|
|
46
|
-
const columns = { ...DEFAULT_COLUMNS, ...options.columnMap };
|
|
47
|
-
const tableReference = options.namespace === void 0 ? options.table : `${options.namespace}.${options.table}`;
|
|
48
|
-
return {
|
|
49
|
-
query: async (query = {}) => {
|
|
50
|
-
const limit = clampLimit(query.limit);
|
|
51
|
-
const fetchLimit = Math.min(limit + 1, MAX_LIMIT);
|
|
52
|
-
const builder = client.from(tableReference).select(
|
|
53
|
-
raw(columns.functionPath),
|
|
54
|
-
raw(columns.level),
|
|
55
|
-
raw(columns.message),
|
|
56
|
-
raw(columns.ts),
|
|
57
|
-
raw(columns.fields),
|
|
58
|
-
raw(columns.shardKey),
|
|
59
|
-
raw(columns.userId),
|
|
60
|
-
raw(columns.traceId),
|
|
61
|
-
raw(columns.spanId)
|
|
62
|
-
);
|
|
63
|
-
if (query.sinceTs !== void 0) {
|
|
64
|
-
builder.where(sql`${raw(columns.ts)} >= ${query.sinceTs}`);
|
|
65
|
-
}
|
|
66
|
-
if (query.untilTs !== void 0) {
|
|
67
|
-
builder.where(sql`${raw(columns.ts)} <= ${query.untilTs}`);
|
|
68
|
-
}
|
|
69
|
-
if (query.level !== void 0) {
|
|
70
|
-
builder.where(sql`${raw(columns.level)} = ${query.level}`);
|
|
71
|
-
} else if (query.minLevel !== void 0) {
|
|
72
|
-
const floorIndex = LOG_LEVEL_ORDER.indexOf(query.minLevel);
|
|
73
|
-
const allowed = floorIndex === -1 ? [...LOG_LEVEL_ORDER] : LOG_LEVEL_ORDER.slice(floorIndex);
|
|
74
|
-
builder.where(sql`${raw(columns.level)} IN ${allowed}`);
|
|
75
|
-
}
|
|
76
|
-
if (query.functionPath !== void 0) {
|
|
77
|
-
builder.where(sql`${raw(columns.functionPath)} = ${query.functionPath}`);
|
|
78
|
-
}
|
|
79
|
-
if (query.functionPathPrefix !== void 0) {
|
|
80
|
-
builder.where(sql`${raw(columns.functionPath)} LIKE ${`${query.functionPathPrefix}%`}`);
|
|
81
|
-
}
|
|
82
|
-
if (query.traceId !== void 0) {
|
|
83
|
-
builder.where(sql`${raw(columns.traceId)} = ${query.traceId}`);
|
|
84
|
-
}
|
|
85
|
-
if (query.shardKey !== void 0) {
|
|
86
|
-
builder.where(sql`${raw(columns.shardKey)} = ${query.shardKey}`);
|
|
87
|
-
}
|
|
88
|
-
if (query.userId !== void 0) {
|
|
89
|
-
builder.where(sql`${raw(columns.userId)} = ${query.userId}`);
|
|
90
|
-
}
|
|
91
|
-
if (query.cursor !== void 0) {
|
|
92
|
-
builder.where(sql`${raw(columns.ts)} < ${query.cursor.ts}`);
|
|
93
|
-
}
|
|
94
|
-
builder.orderBy(desc(raw(columns.ts))).limit(fetchLimit);
|
|
95
|
-
const { rows } = await builder.run();
|
|
96
|
-
const hasMore = rows.length > limit;
|
|
97
|
-
const pageRows = hasMore ? rows.slice(0, limit) : rows;
|
|
98
|
-
const overflow = hasMore ? rows[limit] : void 0;
|
|
99
|
-
const decoded = pageRows.map((row) => {
|
|
100
|
-
const out = {
|
|
101
|
-
functionPath: renderCell(row[columns.functionPath]),
|
|
102
|
-
// The stored `level` is one of the canonical severities; the
|
|
103
|
-
// reader trusts the writer's contract here rather than re-validating.
|
|
104
|
-
level: renderCell(row[columns.level]),
|
|
105
|
-
message: renderCell(row[columns.message]),
|
|
106
|
-
ts: toTs(row[columns.ts])
|
|
107
|
-
};
|
|
108
|
-
const fields = row[columns.fields];
|
|
109
|
-
if (fields !== void 0 && fields !== null) {
|
|
110
|
-
out.fields = decodeFields(fields);
|
|
111
|
-
}
|
|
112
|
-
const shardKey = row[columns.shardKey];
|
|
113
|
-
if (shardKey !== void 0 && shardKey !== null) {
|
|
114
|
-
out.shardKey = renderCell(shardKey);
|
|
115
|
-
}
|
|
116
|
-
const userId = row[columns.userId];
|
|
117
|
-
if (userId !== void 0 && userId !== null) {
|
|
118
|
-
out.userId = renderCell(userId);
|
|
119
|
-
}
|
|
120
|
-
const traceId = row[columns.traceId];
|
|
121
|
-
if (traceId !== void 0 && traceId !== null) {
|
|
122
|
-
out.traceId = renderCell(traceId);
|
|
123
|
-
}
|
|
124
|
-
const spanId = row[columns.spanId];
|
|
125
|
-
if (spanId !== void 0 && spanId !== null) {
|
|
126
|
-
out.spanId = renderCell(spanId);
|
|
127
|
-
}
|
|
128
|
-
return out;
|
|
129
|
-
});
|
|
130
|
-
return overflow === void 0 ? { rows: decoded } : { nextCursor: { ts: toTs(overflow[columns.ts]) }, rows: decoded };
|
|
131
|
-
}
|
|
132
|
-
};
|
|
133
|
-
};
|
|
134
|
-
|
|
135
|
-
export { DEFAULT_LOG_COLUMNS as D, LOG_LEVEL_ORDER as L, DEFAULT_LOG_LIMIT as a, createPipelineLogReader as c };
|
|
@@ -1,39 +0,0 @@
|
|
|
1
|
-
const DEFAULT_PRIMARY_KEY = "_id";
|
|
2
|
-
const toFivetranResponse = (page, primaryKey = DEFAULT_PRIMARY_KEY) => {
|
|
3
|
-
const insert = {};
|
|
4
|
-
const update = {};
|
|
5
|
-
const remove = {};
|
|
6
|
-
const schema = {};
|
|
7
|
-
const pkFor = (table) => typeof primaryKey === "string" ? primaryKey : primaryKey[table] ?? DEFAULT_PRIMARY_KEY;
|
|
8
|
-
const bucketFor = (target, table) => {
|
|
9
|
-
const existing = target[table];
|
|
10
|
-
if (existing) {
|
|
11
|
-
return existing;
|
|
12
|
-
}
|
|
13
|
-
const created = [];
|
|
14
|
-
target[table] = created;
|
|
15
|
-
return created;
|
|
16
|
-
};
|
|
17
|
-
for (const change of page.changes) {
|
|
18
|
-
schema[change.table] ??= { primary_key: [pkFor(change.table)] };
|
|
19
|
-
if (change.op === "delete") {
|
|
20
|
-
bucketFor(remove, change.table).push(change.doc);
|
|
21
|
-
} else if (change.op === "update") {
|
|
22
|
-
bucketFor(update, change.table).push(change.doc);
|
|
23
|
-
} else {
|
|
24
|
-
bucketFor(insert, change.table).push(change.doc);
|
|
25
|
-
}
|
|
26
|
-
}
|
|
27
|
-
return { delete: remove, hasMore: page.hasMore, insert, schema, state: { cursor: page.nextCursor }, update };
|
|
28
|
-
};
|
|
29
|
-
const toAirbyteMessages = (page, emittedAt = Date.now()) => {
|
|
30
|
-
const messages = [];
|
|
31
|
-
for (const change of page.changes) {
|
|
32
|
-
const data = change.op === "delete" ? { ...change.doc, _lunora_deleted: true } : change.doc;
|
|
33
|
-
messages.push({ record: { data, emitted_at: emittedAt, stream: change.table }, type: "RECORD" });
|
|
34
|
-
}
|
|
35
|
-
messages.push({ state: { data: { cursor: page.nextCursor } }, type: "STATE" });
|
|
36
|
-
return messages;
|
|
37
|
-
};
|
|
38
|
-
|
|
39
|
-
export { toAirbyteMessages, toFivetranResponse };
|