@crvouga/mockingbird-service-otel 0.1.0

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.
@@ -0,0 +1,3582 @@
1
+ // ../core/dist/clock.js
2
+ var createClock = (source = Date.now) => {
3
+ let offsetMs = 0;
4
+ let frozenAt;
5
+ const now = () => frozenAt ?? source() + offsetMs;
6
+ return {
7
+ now,
8
+ set: (epochMs) => {
9
+ if (frozenAt !== void 0)
10
+ frozenAt = epochMs;
11
+ else
12
+ offsetMs = epochMs - source();
13
+ },
14
+ advance: (deltaMs) => {
15
+ if (frozenAt !== void 0)
16
+ frozenAt += deltaMs;
17
+ else
18
+ offsetMs += deltaMs;
19
+ },
20
+ freeze: () => {
21
+ frozenAt = now();
22
+ },
23
+ unfreeze: () => {
24
+ if (frozenAt === void 0)
25
+ return;
26
+ offsetMs = frozenAt - source();
27
+ frozenAt = void 0;
28
+ },
29
+ reset: () => {
30
+ offsetMs = 0;
31
+ frozenAt = void 0;
32
+ },
33
+ state: () => ({ now: now(), frozen: frozenAt !== void 0, offsetMs })
34
+ };
35
+ };
36
+
37
+ // ../core/dist/collection.js
38
+ var Collection = class {
39
+ sqlite;
40
+ namespace;
41
+ name;
42
+ constructor(sqlite, namespace, name) {
43
+ this.sqlite = sqlite;
44
+ this.namespace = namespace;
45
+ this.name = name;
46
+ }
47
+ bumpCollectionSeq() {
48
+ const row = this.sqlite.prepare("SELECT value FROM mockingbird_sequences WHERE namespace = ? AND name = ? AND kind = 'collection'").get(this.namespace, this.name);
49
+ const next = (row?.value ?? 0) + 1;
50
+ this.sqlite.prepare(`INSERT INTO mockingbird_sequences (namespace, name, kind, value) VALUES (?, ?, 'collection', ?)
51
+ ON CONFLICT(namespace, name, kind) DO UPDATE SET value = excluded.value`).run(this.namespace, this.name, next);
52
+ return next;
53
+ }
54
+ nextSequence() {
55
+ return this.sqlite.transaction(() => this.bumpCollectionSeq());
56
+ }
57
+ get(id2) {
58
+ const row = this.sqlite.prepare("SELECT value FROM mockingbird_records WHERE namespace = ? AND collection = ? AND id = ?").get(this.namespace, this.name, id2);
59
+ if (!row)
60
+ return void 0;
61
+ return JSON.parse(row.value).value;
62
+ }
63
+ has(id2) {
64
+ const row = this.sqlite.prepare("SELECT 1 AS ok FROM mockingbird_records WHERE namespace = ? AND collection = ? AND id = ?").get(this.namespace, this.name, id2);
65
+ return row !== void 0;
66
+ }
67
+ /** Insert a new record, assigning it the next sequence number. */
68
+ insert(id2, value) {
69
+ return this.sqlite.transaction(() => {
70
+ const seq = this.bumpCollectionSeq();
71
+ const stored = { seq, value };
72
+ this.sqlite.prepare(`INSERT INTO mockingbird_records (namespace, collection, id, seq, value)
73
+ VALUES (?, ?, ?, ?, ?)
74
+ ON CONFLICT(namespace, collection, id) DO UPDATE SET seq = excluded.seq, value = excluded.value`).run(this.namespace, this.name, id2, seq, JSON.stringify(stored));
75
+ return stored;
76
+ });
77
+ }
78
+ /** Replace an existing record's value, keeping its position. */
79
+ update(id2, value) {
80
+ return this.sqlite.transaction(() => {
81
+ const row = this.sqlite.prepare("SELECT seq, value FROM mockingbird_records WHERE namespace = ? AND collection = ? AND id = ?").get(this.namespace, this.name, id2);
82
+ if (!row)
83
+ return void 0;
84
+ const stored = { seq: row.seq, value };
85
+ this.sqlite.prepare("UPDATE mockingbird_records SET value = ? WHERE namespace = ? AND collection = ? AND id = ?").run(JSON.stringify(stored), this.namespace, this.name, id2);
86
+ return stored;
87
+ });
88
+ }
89
+ delete(id2) {
90
+ const result = this.sqlite.prepare("DELETE FROM mockingbird_records WHERE namespace = ? AND collection = ? AND id = ?").run(this.namespace, this.name, id2);
91
+ return result.changes > 0;
92
+ }
93
+ /** How many records the collection holds, without reading them. */
94
+ count() {
95
+ const row = this.sqlite.prepare("SELECT COUNT(*) AS n FROM mockingbird_records WHERE namespace = ? AND collection = ?").get(this.namespace, this.name);
96
+ return Number(row?.n ?? 0);
97
+ }
98
+ list(options = {}) {
99
+ const rows = this.sqlite.prepare("SELECT id, seq, value FROM mockingbird_records WHERE namespace = ? AND collection = ?").all(this.namespace, this.name);
100
+ const out = [];
101
+ for (const row of rows) {
102
+ const stored = JSON.parse(row.value);
103
+ if (options.where && !options.where(stored.value, stored.seq))
104
+ continue;
105
+ out.push({ id: row.id, seq: stored.seq, value: stored.value });
106
+ }
107
+ out.sort((a, b) => options.order === "oldest" ? a.seq - b.seq : b.seq - a.seq);
108
+ return out;
109
+ }
110
+ };
111
+
112
+ // ../core/dist/control.js
113
+ var HEALTH_PATH = "/health";
114
+ var ADMIN_PREFIX = "/__admin";
115
+ var ADMIN_KEY_HEADER = "x-mockingbird-admin-key";
116
+ var NAMESPACE_HEADER = "x-mockingbird-namespace";
117
+ var json = (status, body) => new Response(JSON.stringify(body), {
118
+ status,
119
+ headers: { "content-type": "application/json" }
120
+ });
121
+ var adminError = (status, message) => json(status, { error: { type: "mockingbird_admin", message } });
122
+ var UNITS = {
123
+ ms: 1,
124
+ s: 1e3,
125
+ m: 6e4,
126
+ h: 36e5,
127
+ d: 864e5
128
+ };
129
+ var parseDuration = (value) => {
130
+ if (typeof value === "number" && Number.isFinite(value))
131
+ return value;
132
+ if (typeof value !== "string")
133
+ return void 0;
134
+ const match = /^(-?\d+(?:\.\d+)?)\s*(ms|s|m|h|d)$/.exec(value.trim());
135
+ if (!match)
136
+ return void 0;
137
+ return Number(match[1]) * UNITS[match[2]];
138
+ };
139
+ var parseInstant = (value) => {
140
+ if (typeof value === "number" && Number.isFinite(value))
141
+ return value;
142
+ if (typeof value !== "string")
143
+ return void 0;
144
+ const parsed = Date.parse(value);
145
+ return Number.isNaN(parsed) ? void 0 : parsed;
146
+ };
147
+ var matchRoute = (pattern, path) => {
148
+ const want = pattern.split("/").filter(Boolean);
149
+ const have = path.split("/").filter(Boolean);
150
+ if (want.length !== have.length)
151
+ return void 0;
152
+ const params = {};
153
+ for (let i = 0; i < want.length; i++) {
154
+ const segment = want[i];
155
+ const actual = have[i];
156
+ if (segment.startsWith(":"))
157
+ params[segment.slice(1)] = decodeURIComponent(actual);
158
+ else if (segment !== actual)
159
+ return void 0;
160
+ }
161
+ return params;
162
+ };
163
+ var readJson = async (request) => {
164
+ const text2 = await request.text();
165
+ if (text2.trim() === "")
166
+ return void 0;
167
+ return JSON.parse(text2);
168
+ };
169
+ var isRecord = (value) => typeof value === "object" && value !== null && !Array.isArray(value);
170
+ var createControlPlane = (context) => {
171
+ const snapshots = /* @__PURE__ */ new Map();
172
+ let snapshotCounter = 0;
173
+ const headerNamespace = (request) => request.headers.get(NAMESPACE_HEADER) ?? context.defaultNamespace;
174
+ const adminNamespace = (request, url) => url.searchParams.get("namespace") ?? headerNamespace(request);
175
+ const builtin = {
176
+ "GET /": () => json(200, {
177
+ service: context.name,
178
+ routes: [...Object.keys(builtin), ...Object.keys(context.routes)].sort()
179
+ }),
180
+ "POST /reset": async ({ url, namespace }) => {
181
+ const target = url.searchParams.get("all") === "1" ? "*" : namespace;
182
+ await context.reset(target);
183
+ return json(200, { status: "ok", reset: target === "*" ? context.namespaces() : [target] });
184
+ },
185
+ "GET /namespaces": () => json(200, { default: context.defaultNamespace, namespaces: context.namespaces() }),
186
+ "GET /clock": () => json(200, context.clock.state()),
187
+ "POST /clock": ({ body }) => {
188
+ if (!isRecord(body))
189
+ return adminError(400, "expected a JSON object");
190
+ if (body.reset === true)
191
+ context.clock.reset();
192
+ if (body.set !== void 0) {
193
+ const instant = parseInstant(body.set);
194
+ if (instant === void 0)
195
+ return adminError(400, "set: expected epoch ms or ISO-8601");
196
+ context.clock.set(instant);
197
+ }
198
+ if (body.advance !== void 0) {
199
+ const delta = parseDuration(body.advance);
200
+ if (delta === void 0)
201
+ return adminError(400, 'advance: expected ms or "15m"-style');
202
+ context.clock.advance(delta);
203
+ }
204
+ if (body.freeze === true)
205
+ context.clock.freeze();
206
+ if (body.freeze === false)
207
+ context.clock.unfreeze();
208
+ return json(200, context.clock.state());
209
+ },
210
+ "GET /faults": () => json(200, { faults: context.faults.list() }),
211
+ "POST /faults": ({ body, namespace }) => {
212
+ if (isRecord(body) && typeof body.preset === "string") {
213
+ if (!context.applyPreset)
214
+ return adminError(400, `${context.name} has no fault presets`);
215
+ const { preset, ...overrides } = body;
216
+ try {
217
+ return json(201, {
218
+ preset,
219
+ rules: context.applyPreset(preset, namespace, overrides)
220
+ });
221
+ } catch (error) {
222
+ return adminError(404, error instanceof Error ? error.message : String(error));
223
+ }
224
+ }
225
+ if (!isRecord(body) || typeof body.status !== "number" && typeof body.delayMs !== "number" && typeof body.latencyMs !== "number" && body.drop !== true && typeof body.effect !== "string") {
226
+ return adminError(400, "a fault needs a numeric status, a delayMs/latencyMs, drop: true, an effect, or a preset");
227
+ }
228
+ const rule = {
229
+ // Scoped to the caller's namespace unless it asks for every one, so one worker's
230
+ // injected failure never lands on another's request.
231
+ namespace,
232
+ ...body,
233
+ id: typeof body.id === "string" ? body.id : `fault_${context.faults.list().length + 1}`
234
+ };
235
+ return json(201, context.faults.add(rule));
236
+ },
237
+ "DELETE /faults": ({ url }) => {
238
+ const id2 = url.searchParams.get("id");
239
+ if (id2 === null) {
240
+ context.faults.clear();
241
+ return json(200, { status: "ok" });
242
+ }
243
+ return context.faults.remove(id2) ? json(200, { status: "ok" }) : adminError(404, `no fault ${id2}`);
244
+ },
245
+ "POST /snapshots": ({ namespace }) => {
246
+ const point = context.timeTravel.checkpoint(namespace, "main");
247
+ context.timeTravel.retain(namespace, point.id);
248
+ snapshotCounter++;
249
+ const id2 = `snap_${snapshotCounter}`;
250
+ snapshots.set(id2, { namespace, checkpoint: point.id });
251
+ return json(201, { id: id2, namespace, records: point.records ?? 0 });
252
+ },
253
+ "POST /snapshots/:id/restore": ({ params, namespace }) => {
254
+ const alias = snapshots.get(params.id);
255
+ if (!alias)
256
+ return adminError(404, `no snapshot ${params.id}`);
257
+ if (alias.namespace !== namespace) {
258
+ return adminError(409, `snapshot ${params.id} belongs to namespace ${alias.namespace}`);
259
+ }
260
+ context.timeTravel.checkout(alias.checkpoint, { namespace, branch: "main" });
261
+ return json(200, { status: "ok", id: params.id, namespace });
262
+ },
263
+ "DELETE /snapshots/:id": ({ params }) => {
264
+ const id2 = params.id;
265
+ const alias = snapshots.get(id2);
266
+ if (!alias)
267
+ return adminError(404, `no snapshot ${id2}`);
268
+ snapshots.delete(id2);
269
+ context.timeTravel.release(alias.namespace, alias.checkpoint);
270
+ return json(200, { status: "ok" });
271
+ },
272
+ "GET /timeline": ({ namespace }) => json(200, context.timeTravel.inspect(namespace)),
273
+ "POST /checkpoints": ({ body, namespace }) => {
274
+ const branch = isRecord(body) && typeof body.branch === "string" ? body.branch : "main";
275
+ try {
276
+ return json(201, context.timeTravel.checkpoint(namespace, branch));
277
+ } catch (error) {
278
+ return adminError(409, error instanceof Error ? error.message : String(error));
279
+ }
280
+ },
281
+ "POST /branches/:name": ({ params, body, namespace }) => {
282
+ const at = isRecord(body) && typeof body.at === "string" ? body.at : void 0;
283
+ try {
284
+ return json(201, context.timeTravel.branch(params.name, {
285
+ namespace,
286
+ ...at !== void 0 ? { at } : {}
287
+ }));
288
+ } catch (error) {
289
+ return adminError(409, error instanceof Error ? error.message : String(error));
290
+ }
291
+ },
292
+ "POST /branches/:name/checkout": ({ params, body, namespace }) => {
293
+ if (!isRecord(body) || typeof body.checkpoint !== "string") {
294
+ return adminError(400, 'expected {"checkpoint":"cp_..."}');
295
+ }
296
+ try {
297
+ context.timeTravel.checkout(body.checkpoint, {
298
+ namespace,
299
+ branch: params.name
300
+ });
301
+ return json(200, { status: "ok", branch: params.name, checkpoint: body.checkpoint });
302
+ } catch (error) {
303
+ return adminError(409, error instanceof Error ? error.message : String(error));
304
+ }
305
+ },
306
+ "GET /requests": ({ url, namespace }) => {
307
+ const status = url.searchParams.get("status");
308
+ const since = url.searchParams.get("since");
309
+ const limit = url.searchParams.get("limit");
310
+ const sinceMs = since === null ? void 0 : parseInstant(/^\d+$/.test(since) ? Number(since) : since);
311
+ if (since !== null && sinceMs === void 0) {
312
+ return adminError(400, "since: expected epoch ms or ISO-8601");
313
+ }
314
+ if (status !== null && !/^\d{3}$/.test(status))
315
+ return adminError(400, "status: expected an HTTP status");
316
+ if (limit !== null && !/^\d+$/.test(limit))
317
+ return adminError(400, "limit: expected a count");
318
+ const operationId = url.searchParams.get("operationId");
319
+ const everyNamespace = url.searchParams.get("all") === "1";
320
+ return json(200, {
321
+ size: context.journal.size,
322
+ requests: context.journal.list({
323
+ ...everyNamespace ? {} : { namespace },
324
+ ...operationId !== null ? { operationId } : {},
325
+ ...status !== null ? { status: Number(status) } : {},
326
+ ...sinceMs !== void 0 ? { since: sinceMs } : {},
327
+ ...limit !== null ? { limit: Number(limit) } : {}
328
+ })
329
+ });
330
+ },
331
+ "DELETE /requests": ({ url, namespace }) => {
332
+ context.journal.clear(url.searchParams.get("all") === "1" ? void 0 : namespace);
333
+ return json(200, { status: "ok" });
334
+ },
335
+ "GET /metrics": () => json(200, context.metrics.report()),
336
+ "DELETE /metrics": () => {
337
+ context.metrics.reset();
338
+ return json(200, { status: "ok" });
339
+ }
340
+ };
341
+ const routes = [...Object.entries(context.routes), ...Object.entries(builtin)].map(([key, handler]) => {
342
+ const space = key.indexOf(" ");
343
+ return { method: key.slice(0, space), pattern: key.slice(space + 1), handler };
344
+ });
345
+ return {
346
+ namespaceOf: headerNamespace,
347
+ async handle(request) {
348
+ const url = new URL(request.url);
349
+ if (url.pathname === HEALTH_PATH && request.method === "GET") {
350
+ return json(200, {
351
+ status: "ok",
352
+ service: context.name,
353
+ uptimeMs: context.wallNow() - context.startedAt,
354
+ clock: context.clock.state(),
355
+ namespaces: context.namespaces().length,
356
+ ...context.describe()
357
+ });
358
+ }
359
+ if (url.pathname !== ADMIN_PREFIX && !url.pathname.startsWith(`${ADMIN_PREFIX}/`)) {
360
+ return void 0;
361
+ }
362
+ if (context.adminKey !== void 0 && request.headers.get(ADMIN_KEY_HEADER) !== context.adminKey) {
363
+ return adminError(401, `missing or wrong ${ADMIN_KEY_HEADER}`);
364
+ }
365
+ const path = url.pathname.slice(ADMIN_PREFIX.length) || "/";
366
+ for (const route of routes) {
367
+ if (route.method !== request.method)
368
+ continue;
369
+ const params = matchRoute(route.pattern, path);
370
+ if (!params)
371
+ continue;
372
+ let body;
373
+ try {
374
+ body = await readJson(request);
375
+ } catch {
376
+ return adminError(400, "request body is not valid JSON");
377
+ }
378
+ return route.handler({
379
+ request,
380
+ url,
381
+ params,
382
+ namespace: adminNamespace(request, url),
383
+ body
384
+ });
385
+ }
386
+ return adminError(404, `no admin route ${request.method} ${path}; GET ${ADMIN_PREFIX} lists them`);
387
+ }
388
+ };
389
+ };
390
+
391
+ // ../core/dist/credentials.js
392
+ var bearerToken = (request) => {
393
+ const header = request.headers.get("authorization");
394
+ if (!header)
395
+ return void 0;
396
+ const match = /^Bearer\s+(.+)$/i.exec(header.trim());
397
+ return match?.[1]?.trim() || void 0;
398
+ };
399
+ var basicAuth = (request) => {
400
+ const header = request.headers.get("authorization");
401
+ if (!header)
402
+ return void 0;
403
+ const match = /^Basic\s+(.+)$/i.exec(header.trim());
404
+ if (!match?.[1])
405
+ return void 0;
406
+ let decoded;
407
+ try {
408
+ decoded = atob(match[1].trim());
409
+ } catch {
410
+ return void 0;
411
+ }
412
+ const colon = decoded.indexOf(":");
413
+ if (colon < 0)
414
+ return { username: decoded, password: "" };
415
+ return { username: decoded.slice(0, colon), password: decoded.slice(colon + 1) };
416
+ };
417
+ var createCredentialRegistry = () => {
418
+ const map = /* @__PURE__ */ new Map();
419
+ return {
420
+ set: (credential, namespace) => {
421
+ map.set(credential, namespace);
422
+ },
423
+ get: (credential) => map.get(credential),
424
+ remove: (credential) => map.delete(credential),
425
+ clear: () => map.clear(),
426
+ entries: () => [...map].map(([credential, namespace]) => ({ credential, namespace })).sort((a, b) => a.credential.localeCompare(b.credential))
427
+ };
428
+ };
429
+ var maskCredential = (credential) => credential.length <= 8 ? `${credential.slice(0, 2)}\u2026` : `${credential.slice(0, 6)}\u2026${credential.slice(-2)}`;
430
+
431
+ // ../core/dist/rng.js
432
+ var seedFrom = (value) => {
433
+ let hash = 2166136261;
434
+ for (let i = 0; i < value.length; i++) {
435
+ hash ^= value.charCodeAt(i);
436
+ hash = Math.imul(hash, 16777619);
437
+ }
438
+ return hash >>> 0;
439
+ };
440
+ var createRng = (seed = 0) => {
441
+ const numeric = typeof seed === "string" ? seedFrom(seed) : seed >>> 0;
442
+ let state = numeric;
443
+ const next = () => {
444
+ state = state + 1831565813 >>> 0;
445
+ let t = state;
446
+ t = Math.imul(t ^ t >>> 15, t | 1);
447
+ t ^= t + Math.imul(t ^ t >>> 7, t | 61);
448
+ return ((t ^ t >>> 14) >>> 0) / 4294967296;
449
+ };
450
+ return {
451
+ next,
452
+ int: (min, max) => min + Math.floor(next() * (max - min + 1)),
453
+ reset: () => {
454
+ state = numeric;
455
+ },
456
+ state: () => state,
457
+ setState: (next2) => {
458
+ if (!Number.isSafeInteger(next2) || next2 < 0 || next2 > 4294967295) {
459
+ throw new RangeError("rng state must be an unsigned 32-bit integer");
460
+ }
461
+ state = next2 >>> 0;
462
+ },
463
+ seed: numeric
464
+ };
465
+ };
466
+
467
+ // ../core/dist/faults.js
468
+ var matches = (rule, candidate) => {
469
+ if (rule.namespace !== void 0 && rule.namespace !== "*" && rule.namespace !== candidate.namespace) {
470
+ return false;
471
+ }
472
+ if (rule.operationId !== void 0 && rule.operationId !== candidate.operationId)
473
+ return false;
474
+ if (rule.method !== void 0 && rule.method.toUpperCase() !== candidate.method.toUpperCase()) {
475
+ return false;
476
+ }
477
+ if (rule.pathPrefix !== void 0 && !candidate.path.startsWith(rule.pathPrefix))
478
+ return false;
479
+ return true;
480
+ };
481
+ var faultResponse = (rule) => {
482
+ const status = rule.status ?? 500;
483
+ const headers = { "content-type": "application/json", ...rule.headers };
484
+ if (typeof rule.body === "string")
485
+ return new Response(rule.body, { status, headers });
486
+ if (rule.body === null)
487
+ return new Response(null, { status, headers: rule.headers ?? {} });
488
+ const body = rule.body === void 0 ? { detail: "Injected by Mockingbird" } : rule.body;
489
+ return new Response(JSON.stringify(body), { status, headers });
490
+ };
491
+ var createFaultRegistry = (rng = createRng(0), sleep = (ms) => new Promise((resolve) => setTimeout(resolve, ms))) => {
492
+ const entries = [];
493
+ return {
494
+ add(rule) {
495
+ const existing = entries.findIndex((e) => e.rule.id === rule.id);
496
+ const entry = { rule, remaining: rule.count ?? null, hits: 0 };
497
+ if (existing >= 0)
498
+ entries[existing] = entry;
499
+ else
500
+ entries.push(entry);
501
+ return rule;
502
+ },
503
+ list: () => entries.map((e) => ({ ...e.rule, remaining: e.remaining, hits: e.hits })),
504
+ remove(id2) {
505
+ const index = entries.findIndex((e) => e.rule.id === id2);
506
+ if (index < 0)
507
+ return false;
508
+ entries.splice(index, 1);
509
+ return true;
510
+ },
511
+ clear() {
512
+ entries.length = 0;
513
+ },
514
+ async take(candidate) {
515
+ const hits = [];
516
+ for (const entry of entries) {
517
+ if (entry.remaining === 0)
518
+ continue;
519
+ if (!matches(entry.rule, candidate))
520
+ continue;
521
+ const rate = entry.rule.rate ?? 1;
522
+ if (rng.next() >= rate)
523
+ continue;
524
+ entry.hits++;
525
+ if (entry.remaining !== null)
526
+ entry.remaining--;
527
+ const delay = entry.rule.delayMs ?? entry.rule.latencyMs;
528
+ if (delay !== void 0 && delay > 0) {
529
+ await sleep(delay);
530
+ }
531
+ const hit = { id: entry.rule.id };
532
+ if (entry.rule.effect !== void 0) {
533
+ hit.effect = { name: entry.rule.effect, params: entry.rule.params ?? {} };
534
+ }
535
+ if (entry.rule.drop === true)
536
+ hit.drop = true;
537
+ else if (entry.rule.status !== void 0)
538
+ hit.response = faultResponse(entry.rule);
539
+ hits.push(hit);
540
+ if (hit.drop || hit.response)
541
+ break;
542
+ }
543
+ return hits;
544
+ }
545
+ };
546
+ };
547
+
548
+ // ../../openapi/core/dist/refs.js
549
+ var OpenAPIReferenceError = class extends Error {
550
+ ref;
551
+ constructor(ref) {
552
+ super(`unresolvable $ref: ${ref}`);
553
+ this.ref = ref;
554
+ this.name = "OpenAPIReferenceError";
555
+ }
556
+ };
557
+ var unescapePointer = (segment) => segment.replace(/~1/g, "/").replace(/~0/g, "~");
558
+ var isReference = (value) => typeof value === "object" && value !== null && typeof value.$ref === "string";
559
+ var resolveRef = (document2, ref) => {
560
+ if (!ref.startsWith("#/"))
561
+ throw new OpenAPIReferenceError(ref);
562
+ let cursor = document2;
563
+ for (const raw of ref.slice(2).split("/")) {
564
+ const segment = unescapePointer(raw);
565
+ if (typeof cursor !== "object" || cursor === null || !(segment in cursor)) {
566
+ throw new OpenAPIReferenceError(ref);
567
+ }
568
+ cursor = cursor[segment];
569
+ }
570
+ if (cursor === void 0)
571
+ throw new OpenAPIReferenceError(ref);
572
+ return cursor;
573
+ };
574
+ var deref = (document2, value) => {
575
+ let current = value;
576
+ const seen = /* @__PURE__ */ new Set();
577
+ while (isReference(current)) {
578
+ if (seen.has(current.$ref))
579
+ throw new OpenAPIReferenceError(`${current.$ref} (cycle)`);
580
+ seen.add(current.$ref);
581
+ current = resolveRef(document2, current.$ref);
582
+ }
583
+ return current;
584
+ };
585
+
586
+ // ../../openapi/core/dist/types.js
587
+ var HTTP_METHODS = [
588
+ "get",
589
+ "put",
590
+ "post",
591
+ "delete",
592
+ "options",
593
+ "head",
594
+ "patch",
595
+ "trace"
596
+ ];
597
+
598
+ // ../../openapi/core/dist/document.js
599
+ var mergeParameters = (document2, item, own) => {
600
+ const merged = /* @__PURE__ */ new Map();
601
+ for (const raw of item.parameters ?? []) {
602
+ const parameter = deref(document2, raw);
603
+ merged.set(`${parameter.in}:${parameter.name}`, parameter);
604
+ }
605
+ for (const raw of own ?? []) {
606
+ const parameter = deref(document2, raw);
607
+ merged.set(`${parameter.in}:${parameter.name}`, parameter);
608
+ }
609
+ return [...merged.values()];
610
+ };
611
+ var listOperations = (document2) => {
612
+ const operations = [];
613
+ for (const [path, item] of Object.entries(document2.paths)) {
614
+ for (const method of HTTP_METHODS) {
615
+ const operation = item[method];
616
+ if (operation?.operationId === void 0)
617
+ continue;
618
+ const responses = {};
619
+ for (const [status, response] of Object.entries(operation.responses)) {
620
+ responses[status] = deref(document2, response);
621
+ }
622
+ operations.push({
623
+ operationId: operation.operationId,
624
+ method,
625
+ path,
626
+ operation,
627
+ parameters: mergeParameters(document2, item, operation.parameters),
628
+ requestBody: operation.requestBody === void 0 ? void 0 : deref(document2, operation.requestBody),
629
+ responses
630
+ });
631
+ }
632
+ }
633
+ return operations;
634
+ };
635
+
636
+ // ../../http/codec/dist/form.js
637
+ var parsePath = (rawKey) => {
638
+ const open = rawKey.indexOf("[");
639
+ if (open === -1)
640
+ return [rawKey];
641
+ const path = [rawKey.slice(0, open)];
642
+ const rest = rawKey.slice(open);
643
+ const pattern = /\[([^\]]*)\]/g;
644
+ let match = pattern.exec(rest);
645
+ let consumed = 0;
646
+ while (match !== null) {
647
+ if (match.index !== consumed)
648
+ return [rawKey];
649
+ path.push(match[1] ?? "");
650
+ consumed = match.index + match[0].length;
651
+ match = pattern.exec(rest);
652
+ }
653
+ if (consumed !== rest.length)
654
+ return [rawKey];
655
+ return path;
656
+ };
657
+ var isIndex = (segment) => /^(0|[1-9][0-9]*)$/.test(segment);
658
+ var put = (target, key, value) => {
659
+ if (key === "__proto__") {
660
+ Object.defineProperty(target, key, {
661
+ value,
662
+ enumerable: true,
663
+ writable: true,
664
+ configurable: true
665
+ });
666
+ return;
667
+ }
668
+ ;
669
+ target[key] = value;
670
+ };
671
+ var assign = (target, path, value) => {
672
+ let cursor = target;
673
+ for (let i = 0; i < path.length; i++) {
674
+ const segment = path[i];
675
+ const last = i === path.length - 1;
676
+ if (Array.isArray(cursor)) {
677
+ const index = segment === "" ? cursor.length : isIndex(segment) ? Number(segment) : void 0;
678
+ if (index === void 0)
679
+ return;
680
+ if (last) {
681
+ put(cursor, index, value);
682
+ return;
683
+ }
684
+ const next = Object.hasOwn(cursor, index) ? cursor[index] : void 0;
685
+ if (next === void 0 || typeof next === "string") {
686
+ const created = path[i + 1] === "" || isIndex(path[i + 1]) ? [] : {};
687
+ put(cursor, index, created);
688
+ cursor = created;
689
+ } else {
690
+ cursor = next;
691
+ }
692
+ continue;
693
+ }
694
+ if (typeof cursor === "string")
695
+ return;
696
+ if (last) {
697
+ put(cursor, segment, value);
698
+ return;
699
+ }
700
+ const nextSegment = path[i + 1];
701
+ const existing = Object.hasOwn(cursor, segment) ? cursor[segment] : void 0;
702
+ if (existing === void 0 || typeof existing === "string") {
703
+ const created = nextSegment === "" || isIndex(nextSegment) ? [] : {};
704
+ put(cursor, segment, created);
705
+ cursor = created;
706
+ } else {
707
+ cursor = existing;
708
+ }
709
+ }
710
+ };
711
+ var decodeFormPairs = (pairs) => {
712
+ const out = {};
713
+ for (const [rawKey, value] of pairs)
714
+ assign(out, parsePath(rawKey), value);
715
+ return densify(out);
716
+ };
717
+ var densify = (value) => {
718
+ if (typeof value === "string")
719
+ return value;
720
+ if (Array.isArray(value))
721
+ return value.filter((item) => item !== void 0).map(densify);
722
+ const out = {};
723
+ for (const [key, item] of Object.entries(value))
724
+ put(out, key, densify(item));
725
+ return out;
726
+ };
727
+ var decodeForm = (text2) => {
728
+ const source = text2.startsWith("?") ? text2.slice(1) : text2;
729
+ return decodeFormPairs(new URLSearchParams(source).entries());
730
+ };
731
+
732
+ // ../../http/codec/dist/content.js
733
+ var JSON_MEDIA_TYPE = "application/json";
734
+ var FORM_MEDIA_TYPE = "application/x-www-form-urlencoded";
735
+ var mediaTypeOf = (contentType) => {
736
+ if (!contentType)
737
+ return void 0;
738
+ const essence = contentType.split(";")[0]?.trim().toLowerCase();
739
+ return essence ? essence : void 0;
740
+ };
741
+ var isJsonMediaType = (mediaType2) => mediaType2 === JSON_MEDIA_TYPE || mediaType2.endsWith("+json") || mediaType2 === "text/json";
742
+ var utf8 = new TextDecoder("utf-8", { fatal: false });
743
+ var decodeBody = (contentType, bytes) => {
744
+ if (bytes.byteLength === 0)
745
+ return { kind: "empty" };
746
+ const mediaType2 = mediaTypeOf(contentType);
747
+ if (mediaType2 === void 0)
748
+ return { kind: "bytes", value: bytes };
749
+ if (isJsonMediaType(mediaType2)) {
750
+ const text2 = utf8.decode(bytes);
751
+ try {
752
+ return { kind: "json", value: JSON.parse(text2) };
753
+ } catch (error) {
754
+ return {
755
+ kind: "invalid",
756
+ mediaType: mediaType2,
757
+ text: text2,
758
+ error: error instanceof Error ? error.message : String(error)
759
+ };
760
+ }
761
+ }
762
+ if (mediaType2 === FORM_MEDIA_TYPE) {
763
+ return { kind: "form", value: decodeForm(utf8.decode(bytes)) };
764
+ }
765
+ if (mediaType2.startsWith("text/"))
766
+ return { kind: "text", value: utf8.decode(bytes) };
767
+ return { kind: "bytes", value: bytes };
768
+ };
769
+ var readBody = async (message) => {
770
+ const bytes = new Uint8Array(await message.arrayBuffer());
771
+ return decodeBody(message.headers.get("content-type"), bytes);
772
+ };
773
+
774
+ // ../core/dist/http.js
775
+ var jsonRes = (status, body, headers = {}) => new Response(JSON.stringify(body), {
776
+ status,
777
+ headers: { "content-type": JSON_MEDIA_TYPE, ...headers }
778
+ });
779
+ var HttpError = class extends Error {
780
+ status;
781
+ body;
782
+ headers;
783
+ constructor(status, body, headers = {}) {
784
+ super(`HTTP ${status}`);
785
+ this.status = status;
786
+ this.body = body;
787
+ this.headers = headers;
788
+ this.name = "HttpError";
789
+ }
790
+ toResponse() {
791
+ const contentType = this.headers["content-type"]?.split(";", 1)[0]?.trim().toLowerCase();
792
+ if (contentType === "text/plain") {
793
+ return new Response(String(this.body), {
794
+ status: this.status,
795
+ headers: this.headers
796
+ });
797
+ }
798
+ return jsonRes(this.status, this.body, this.headers);
799
+ }
800
+ };
801
+
802
+ // ../core/dist/journal.js
803
+ var DEFAULT_JOURNAL_SIZE = 1e3;
804
+ var createJournal = (size = DEFAULT_JOURNAL_SIZE) => {
805
+ const capacity = Math.max(0, Math.floor(size));
806
+ const rings = /* @__PURE__ */ new Map();
807
+ let sequence = 0;
808
+ const order = /* @__PURE__ */ new WeakMap();
809
+ const inOrder = (ring) => ring.entries.length < capacity ? ring.entries : [...ring.entries.slice(ring.next), ...ring.entries.slice(0, ring.next)];
810
+ return {
811
+ size: capacity,
812
+ record(entry) {
813
+ if (capacity === 0)
814
+ return;
815
+ order.set(entry, sequence++);
816
+ let ring = rings.get(entry.namespace);
817
+ if (!ring) {
818
+ ring = { entries: [], next: 0 };
819
+ rings.set(entry.namespace, ring);
820
+ }
821
+ if (ring.entries.length < capacity)
822
+ ring.entries.push(entry);
823
+ else {
824
+ ring.entries[ring.next] = entry;
825
+ ring.next = (ring.next + 1) % capacity;
826
+ }
827
+ },
828
+ list(query = {}) {
829
+ const source = query.namespace !== void 0 ? inOrder(rings.get(query.namespace) ?? { entries: [], next: 0 }) : [...rings.values()].flatMap(inOrder).sort((a, b) => (order.get(a) ?? 0) - (order.get(b) ?? 0));
830
+ const matched = source.filter((entry) => (query.operationId === void 0 || entry.operationId === query.operationId) && (query.status === void 0 || entry.status === query.status) && (query.since === void 0 || Date.parse(entry.at) >= query.since));
831
+ return query.limit !== void 0 ? matched.slice(-Math.max(0, query.limit)) : matched;
832
+ },
833
+ clear(namespace) {
834
+ if (namespace === void 0)
835
+ rings.clear();
836
+ else
837
+ rings.delete(namespace);
838
+ }
839
+ };
840
+ };
841
+ var notes = /* @__PURE__ */ new WeakMap();
842
+ var annotateResponse = (response, extra) => {
843
+ const existing = notes.get(response);
844
+ notes.set(response, {
845
+ ...existing,
846
+ ...extra,
847
+ ...existing?.ids || extra.ids ? { ids: { ...existing?.ids, ...extra.ids } } : {}
848
+ });
849
+ return response;
850
+ };
851
+ var responseNotes = (response) => notes.get(response);
852
+
853
+ // ../core/dist/metrics.js
854
+ var createMetrics = () => {
855
+ let requests = 0;
856
+ let faults = 0;
857
+ let totalDurationMs = 0;
858
+ const byOperation = /* @__PURE__ */ new Map();
859
+ const unmatched = /* @__PURE__ */ new Map();
860
+ return {
861
+ record(entry) {
862
+ requests++;
863
+ totalDurationMs += entry.durationMs;
864
+ if (entry.faultId !== void 0)
865
+ faults++;
866
+ const key = `${entry.operationId ?? "(unmatched)"} ${entry.status}`;
867
+ byOperation.set(key, (byOperation.get(key) ?? 0) + 1);
868
+ if (entry.unmatched) {
869
+ const route = `${entry.method} ${entry.path}`;
870
+ unmatched.set(route, (unmatched.get(route) ?? 0) + 1);
871
+ }
872
+ },
873
+ report: () => ({
874
+ requests,
875
+ byOperation: Object.fromEntries([...byOperation].sort(([a], [b]) => a.localeCompare(b))),
876
+ unmatched: [...unmatched].sort((a, b) => b[1] - a[1] || a[0].localeCompare(b[0])).map(([route, count]) => {
877
+ const space = route.indexOf(" ");
878
+ return { method: route.slice(0, space), path: route.slice(space + 1), count };
879
+ }),
880
+ faults,
881
+ totalDurationMs
882
+ }),
883
+ reset() {
884
+ requests = 0;
885
+ faults = 0;
886
+ totalDurationMs = 0;
887
+ byOperation.clear();
888
+ unmatched.clear();
889
+ }
890
+ };
891
+ };
892
+
893
+ // ../../core/dist/timeline.js
894
+ var BRANCH_PATTERN = /^[A-Za-z0-9_.-]{1,64}$/;
895
+ var Timeline = class {
896
+ maxCheckpoints;
897
+ now;
898
+ makeId;
899
+ nodes = /* @__PURE__ */ new Map();
900
+ heads = /* @__PURE__ */ new Map();
901
+ /** Unreferenced nodes in the exact order they became collectible. */
902
+ evictable = /* @__PURE__ */ new Set();
903
+ /** Branch heads plus explicit retainers. Absent means zero. */
904
+ references = /* @__PURE__ */ new Map();
905
+ explicitPins = /* @__PURE__ */ new Map();
906
+ sequence = 0;
907
+ constructor(options = {}) {
908
+ const max = options.maxCheckpoints ?? 1e3;
909
+ if (!Number.isSafeInteger(max) || max < 1)
910
+ throw new RangeError("maxCheckpoints must be a positive integer");
911
+ this.maxCheckpoints = max;
912
+ this.now = options.now ?? (() => this.sequence);
913
+ this.makeId = options.id ?? ((sequence) => `cp_${sequence.toString(36).padStart(8, "0")}`);
914
+ }
915
+ /** Capture a new immutable value and move `branch` to it. */
916
+ commit(value, options = {}) {
917
+ const branch = options.branch ?? "main";
918
+ this.assertBranch(branch);
919
+ const parent = options.parent === void 0 ? this.heads.get(branch) ?? null : options.parent;
920
+ if (parent !== null && !this.nodes.has(parent))
921
+ throw new RangeError(`no checkpoint ${parent}`);
922
+ const id2 = this.makeId(++this.sequence);
923
+ if (this.nodes.has(id2))
924
+ throw new RangeError(`duplicate checkpoint id ${id2}`);
925
+ const checkpoint = Object.freeze({ id: id2, branch, parent, at: this.now(), value });
926
+ this.nodes.set(id2, checkpoint);
927
+ this.moveHead(branch, id2);
928
+ this.collect(this.maxCheckpoints);
929
+ return checkpoint;
930
+ }
931
+ /** Create a branch pointer without copying its checkpoint value. */
932
+ fork(branch, options = {}) {
933
+ this.assertBranch(branch);
934
+ if (this.heads.has(branch))
935
+ throw new RangeError(`branch already exists: ${branch}`);
936
+ const from = options.from ?? this.heads.get("main");
937
+ if (from === void 0)
938
+ return void 0;
939
+ const checkpoint = this.get(from);
940
+ this.moveHead(branch, checkpoint.id);
941
+ return checkpoint;
942
+ }
943
+ /** Move a branch pointer to an existing checkpoint. */
944
+ checkout(branch, id2) {
945
+ this.assertBranch(branch);
946
+ const checkpoint = this.get(id2);
947
+ this.moveHead(branch, checkpoint.id);
948
+ return checkpoint;
949
+ }
950
+ get(id2) {
951
+ const checkpoint = this.nodes.get(id2);
952
+ if (!checkpoint)
953
+ throw new RangeError(`no checkpoint ${id2}`);
954
+ return checkpoint;
955
+ }
956
+ head(branch = "main") {
957
+ const id2 = this.heads.get(branch);
958
+ return id2 === void 0 ? void 0 : this.get(id2);
959
+ }
960
+ hasBranch(branch) {
961
+ return this.heads.has(branch);
962
+ }
963
+ branches() {
964
+ return Object.freeze(Object.fromEntries([...this.heads].sort(([a], [b]) => a < b ? -1 : a > b ? 1 : 0)));
965
+ }
966
+ checkpoints() {
967
+ return [...this.nodes.values()];
968
+ }
969
+ /** Number of retained checkpoints without allocating an array. */
970
+ get size() {
971
+ return this.nodes.size;
972
+ }
973
+ /** Pin a checkpoint independently of branch heads (used by compatibility snapshot handles). */
974
+ retain(id2) {
975
+ const checkpoint = this.get(id2);
976
+ this.explicitPins.set(id2, (this.explicitPins.get(id2) ?? 0) + 1);
977
+ this.addReference(id2);
978
+ return checkpoint;
979
+ }
980
+ /** Release one explicit pin. Branch heads remain pinned until moved or deleted. */
981
+ release(id2) {
982
+ if (!this.nodes.has(id2))
983
+ return false;
984
+ const pins = this.explicitPins.get(id2) ?? 0;
985
+ if (pins === 0)
986
+ return false;
987
+ if (pins === 1)
988
+ this.explicitPins.delete(id2);
989
+ else
990
+ this.explicitPins.set(id2, pins - 1);
991
+ this.removeReference(id2);
992
+ this.collect(this.maxCheckpoints);
993
+ return true;
994
+ }
995
+ deleteBranch(branch) {
996
+ if (branch === "main")
997
+ throw new RangeError("cannot delete main branch");
998
+ const previous = this.heads.get(branch);
999
+ const deleted = this.heads.delete(branch);
1000
+ if (previous !== void 0)
1001
+ this.removeReference(previous);
1002
+ this.collect(this.maxCheckpoints);
1003
+ return deleted;
1004
+ }
1005
+ /**
1006
+ * Deterministically discard oldest unpinned checkpoints. Collection is O(number removed):
1007
+ * commits never scan pinned nodes or the retained history. Parents are metadata rather than a
1008
+ * storage dependency, so a retained node remains usable after pruning.
1009
+ */
1010
+ gc(max = this.maxCheckpoints) {
1011
+ if (!Number.isSafeInteger(max) || max < 1)
1012
+ throw new RangeError("max must be a positive integer");
1013
+ const removed = [];
1014
+ this.collect(max, removed);
1015
+ return removed;
1016
+ }
1017
+ collect(max, removed) {
1018
+ while (this.nodes.size > max && this.evictable.size > 0) {
1019
+ const id2 = this.evictable.values().next().value;
1020
+ this.evictable.delete(id2);
1021
+ this.nodes.delete(id2);
1022
+ removed?.push(id2);
1023
+ }
1024
+ }
1025
+ moveHead(branch, id2) {
1026
+ const previous = this.heads.get(branch);
1027
+ if (previous === id2)
1028
+ return;
1029
+ if (previous !== void 0)
1030
+ this.removeReference(previous);
1031
+ this.heads.set(branch, id2);
1032
+ this.addReference(id2);
1033
+ }
1034
+ addReference(id2) {
1035
+ this.references.set(id2, (this.references.get(id2) ?? 0) + 1);
1036
+ this.evictable.delete(id2);
1037
+ }
1038
+ removeReference(id2) {
1039
+ const next = (this.references.get(id2) ?? 0) - 1;
1040
+ if (next > 0)
1041
+ this.references.set(id2, next);
1042
+ else {
1043
+ this.references.delete(id2);
1044
+ if (this.nodes.has(id2))
1045
+ this.evictable.add(id2);
1046
+ }
1047
+ }
1048
+ assertBranch(branch) {
1049
+ if (!BRANCH_PATTERN.test(branch))
1050
+ throw new RangeError(`branch must match ${BRANCH_PATTERN}`);
1051
+ }
1052
+ };
1053
+
1054
+ // ../../sqlite/dist/default.js
1055
+ import { Database } from "@crvouga/mockingbird-service-sqlite";
1056
+ var createDefaultSqlite = () => new Database();
1057
+ var resolveSqlite = (sqlite) => sqlite ?? createDefaultSqlite();
1058
+
1059
+ // ../../sqlite/dist/migrate.js
1060
+ var ensureMigrationsTable = (sqlite) => {
1061
+ sqlite.exec(`
1062
+ CREATE TABLE IF NOT EXISTS schema_migrations (
1063
+ id TEXT PRIMARY KEY NOT NULL,
1064
+ applied_at INTEGER NOT NULL
1065
+ )
1066
+ `);
1067
+ };
1068
+ var migrate = (sqlite, migrations) => {
1069
+ ensureMigrationsTable(sqlite);
1070
+ const applied = new Set(sqlite.prepare("SELECT id FROM schema_migrations").all().map((row) => row.id));
1071
+ const pending = migrations.filter((migration) => !applied.has(migration.id));
1072
+ if (pending.length === 0)
1073
+ return;
1074
+ const insert = sqlite.prepare("INSERT INTO schema_migrations (id, applied_at) VALUES (?, ?)");
1075
+ const now = Math.floor(Date.now() / 1e3);
1076
+ sqlite.transaction(() => {
1077
+ for (const migration of pending) {
1078
+ sqlite.exec(migration.sql);
1079
+ insert.run(migration.id, now);
1080
+ }
1081
+ });
1082
+ };
1083
+
1084
+ // ../../sqlite/dist/schema.js
1085
+ var CORE_MIGRATIONS = [
1086
+ {
1087
+ id: "20260322_core_records_sequences",
1088
+ sql: `
1089
+ CREATE TABLE IF NOT EXISTS mockingbird_records (
1090
+ namespace TEXT NOT NULL,
1091
+ collection TEXT NOT NULL,
1092
+ id TEXT NOT NULL,
1093
+ seq INTEGER NOT NULL,
1094
+ value TEXT NOT NULL,
1095
+ PRIMARY KEY (namespace, collection, id)
1096
+ );
1097
+ CREATE INDEX IF NOT EXISTS mockingbird_records_seq
1098
+ ON mockingbird_records (namespace, collection, seq);
1099
+ CREATE TABLE IF NOT EXISTS mockingbird_sequences (
1100
+ namespace TEXT NOT NULL,
1101
+ name TEXT NOT NULL,
1102
+ kind TEXT NOT NULL,
1103
+ value INTEGER NOT NULL,
1104
+ PRIMARY KEY (namespace, name, kind)
1105
+ );
1106
+ `
1107
+ }
1108
+ ];
1109
+ var migrateCore = (sqlite) => {
1110
+ migrate(sqlite, CORE_MIGRATIONS);
1111
+ };
1112
+ var clearNamespace = (sqlite, namespace) => {
1113
+ sqlite.transaction(() => {
1114
+ sqlite.prepare("DELETE FROM mockingbird_records WHERE namespace = ?").run(namespace);
1115
+ sqlite.prepare("DELETE FROM mockingbird_sequences WHERE namespace = ?").run(namespace);
1116
+ });
1117
+ };
1118
+
1119
+ // ../../openapi/metadata/dist/types.js
1120
+ var EXTENSION_KEYS = {
1121
+ operation: "x-mockingbird",
1122
+ resource: "x-mockingbird-resource",
1123
+ resourceRef: "x-mockingbird-resource-ref",
1124
+ volatile: "x-mockingbird-volatile",
1125
+ scope: "x-mockingbird-scope",
1126
+ unsupported: "x-mockingbird-unsupported",
1127
+ parityHeader: "x-mockingbird-parity-header"
1128
+ };
1129
+
1130
+ // ../../openapi/metadata/dist/read.js
1131
+ var isRecord2 = (value) => typeof value === "object" && value !== null && !Array.isArray(value);
1132
+ var extensionOf = (holder, key) => holder[key];
1133
+ var operationMetadata = (operation) => {
1134
+ const raw = extensionOf(operation, EXTENSION_KEYS.operation);
1135
+ const ext = isRecord2(raw) ? raw : {};
1136
+ const supported = ext.supported ?? true;
1137
+ const parity = ext.parity ?? {};
1138
+ return {
1139
+ supported,
1140
+ reason: typeof ext.reason === "string" ? ext.reason : void 0,
1141
+ parity: {
1142
+ enabled: supported && (parity.enabled ?? true),
1143
+ safe: parity.safe ?? true,
1144
+ reason: typeof parity.reason === "string" ? parity.reason : void 0
1145
+ }
1146
+ };
1147
+ };
1148
+
1149
+ // ../core/dist/service.js
1150
+ import { Hono } from "hono";
1151
+ var defineOperations = (handlers) => handlers;
1152
+ var OperationRegistryError = class extends Error {
1153
+ problems;
1154
+ constructor(problems) {
1155
+ super(`operation registry is inconsistent:
1156
+ ${problems.map((p) => ` - ${p}`).join("\n")}`);
1157
+ this.problems = problems;
1158
+ this.name = "OperationRegistryError";
1159
+ }
1160
+ };
1161
+ var verifyOperations = (document2, handlers) => {
1162
+ const problems = [];
1163
+ const operations = listOperations(document2);
1164
+ const seen = /* @__PURE__ */ new Set();
1165
+ for (const operation of operations) {
1166
+ if (seen.has(operation.operationId))
1167
+ problems.push(`duplicate operationId ${operation.operationId}`);
1168
+ seen.add(operation.operationId);
1169
+ const supported = operationMetadata(operation.operation).supported;
1170
+ const handler = handlers[operation.operationId];
1171
+ if (supported && !handler)
1172
+ problems.push(`supported operation ${operation.operationId} has no handler`);
1173
+ if (!supported && handler)
1174
+ problems.push(`operation ${operation.operationId} is marked unsupported but has a handler`);
1175
+ }
1176
+ for (const id2 of Object.keys(handlers)) {
1177
+ if (!seen.has(id2))
1178
+ problems.push(`handler ${id2} has no OpenAPI operation`);
1179
+ }
1180
+ return problems;
1181
+ };
1182
+ var honoPath = (template) => template.replace(/\{([^}]+)\}/g, ":$1");
1183
+ var routeOrder = (a, b) => {
1184
+ const sa = a.path.split("/");
1185
+ const sb = b.path.split("/");
1186
+ for (let i = 0; i < Math.max(sa.length, sb.length); i++) {
1187
+ const x = sa[i] ?? "";
1188
+ const y = sb[i] ?? "";
1189
+ const px = x.startsWith("{");
1190
+ const py = y.startsWith("{");
1191
+ if (px !== py)
1192
+ return px ? 1 : -1;
1193
+ if (x !== y)
1194
+ return x < y ? -1 : 1;
1195
+ }
1196
+ return 0;
1197
+ };
1198
+ var queryOf = (url) => decodeFormPairs(url.searchParams.entries());
1199
+ var bootSqlite = (sqlite) => {
1200
+ const client = resolveSqlite(sqlite);
1201
+ migrateCore(client);
1202
+ return client;
1203
+ };
1204
+ var createService = (options) => {
1205
+ const problems = verifyOperations(options.document, options.handlers);
1206
+ if (problems.length > 0)
1207
+ throw new OperationRegistryError(problems);
1208
+ migrateCore(options.sqlite);
1209
+ const now = options.now ?? (() => Date.now());
1210
+ const app = new Hono();
1211
+ app.notFound((c) => options.notFound(c.req.raw));
1212
+ app.onError((error, c) => options.onError(error, c.req.raw));
1213
+ const operations = [...listOperations(options.document)].sort(routeOrder);
1214
+ for (const operation of operations) {
1215
+ const metadata = operationMetadata(operation.operation);
1216
+ const handler = options.handlers[operation.operationId];
1217
+ const route = async (c) => {
1218
+ const request = c.req.raw;
1219
+ if (!metadata.supported || !handler) {
1220
+ return options.unsupported ? options.unsupported(request, operation) : options.notFound(request);
1221
+ }
1222
+ const url = new URL(request.url);
1223
+ const context = {
1224
+ request,
1225
+ url,
1226
+ params: c.req.param(),
1227
+ query: queryOf(url),
1228
+ body: await readBody(request),
1229
+ sqlite: options.sqlite,
1230
+ namespace: options.namespace,
1231
+ operation,
1232
+ document: options.document,
1233
+ now
1234
+ };
1235
+ const short = await options.before?.(context);
1236
+ if (short)
1237
+ return short;
1238
+ return handler(context);
1239
+ };
1240
+ app.on(operation.method.toUpperCase(), honoPath(operation.path), route);
1241
+ }
1242
+ return {
1243
+ app,
1244
+ sqlite: options.sqlite,
1245
+ namespace: options.namespace,
1246
+ fetch: async (request) => app.fetch(request),
1247
+ reset: async () => {
1248
+ clearNamespace(options.sqlite, options.namespace);
1249
+ }
1250
+ };
1251
+ };
1252
+
1253
+ // ../core/dist/snapshot.js
1254
+ var snapshotNamespace = (sqlite, namespace) => ({
1255
+ namespace,
1256
+ records: sqlite.prepare("SELECT collection, id, seq, value FROM mockingbird_records WHERE namespace = ? ORDER BY collection, seq").all(namespace),
1257
+ sequences: sqlite.prepare("SELECT name, kind, value FROM mockingbird_sequences WHERE namespace = ? ORDER BY name, kind").all(namespace)
1258
+ });
1259
+ var restoreNamespace = (sqlite, namespace, snapshot) => {
1260
+ sqlite.transaction(() => {
1261
+ sqlite.prepare("DELETE FROM mockingbird_records WHERE namespace = ?").run(namespace);
1262
+ sqlite.prepare("DELETE FROM mockingbird_sequences WHERE namespace = ?").run(namespace);
1263
+ const record = sqlite.prepare("INSERT INTO mockingbird_records (namespace, collection, id, seq, value) VALUES (?, ?, ?, ?, ?)");
1264
+ for (const row of snapshot.records) {
1265
+ record.run(namespace, row.collection, row.id, row.seq, row.value);
1266
+ }
1267
+ const sequence = sqlite.prepare("INSERT INTO mockingbird_sequences (namespace, name, kind, value) VALUES (?, ?, ?, ?)");
1268
+ for (const row of snapshot.sequences) {
1269
+ sequence.run(namespace, row.name, row.kind, row.value);
1270
+ }
1271
+ });
1272
+ };
1273
+
1274
+ // ../core/dist/version.js
1275
+ var PACKAGE_VERSION = true ? "0.1.0" : UNRELEASED_VERSION;
1276
+
1277
+ // ../core/dist/webhooks.js
1278
+ var json2 = (status, body) => new Response(JSON.stringify(body), { status, headers: { "content-type": "application/json" } });
1279
+ var adminError2 = (status, message) => json2(status, { error: { type: "mockingbird_admin", message } });
1280
+ var isRecord3 = (value) => typeof value === "object" && value !== null && !Array.isArray(value);
1281
+ var parseEndpoint = (value) => {
1282
+ if (!isRecord3(value) || typeof value.url !== "string")
1283
+ return "each endpoint needs a url";
1284
+ try {
1285
+ new URL(value.url);
1286
+ } catch {
1287
+ return `not a URL: ${value.url}`;
1288
+ }
1289
+ const endpoint = { url: value.url };
1290
+ if (typeof value.id === "string")
1291
+ endpoint.id = value.id;
1292
+ if (typeof value.secret === "string")
1293
+ endpoint.secret = value.secret;
1294
+ if (typeof value.signUrl === "string")
1295
+ endpoint.signUrl = value.signUrl;
1296
+ const events = value.events ?? value.enabledEvents;
1297
+ if (Array.isArray(events))
1298
+ endpoint.events = events.map(String);
1299
+ if (isRecord3(value.tags)) {
1300
+ endpoint.tags = Object.fromEntries(Object.entries(value.tags).map(([k, v]) => [k, String(v)]));
1301
+ }
1302
+ if (typeof value.account === "string")
1303
+ endpoint.tags = { ...endpoint.tags, account: value.account };
1304
+ if (isRecord3(value.headers)) {
1305
+ endpoint.headers = Object.fromEntries(Object.entries(value.headers).map(([k, v]) => [k, String(v)]));
1306
+ }
1307
+ return endpoint;
1308
+ };
1309
+ var webhookAdminRoutes = (hub) => ({
1310
+ "GET /webhooks": ({ url, namespace }) => json2(200, {
1311
+ deliveries: hub.deliveries(url.searchParams.get("all") === "1" ? void 0 : namespace).filter((d) => {
1312
+ const type = url.searchParams.get("type");
1313
+ return type === null || d.type === type;
1314
+ })
1315
+ }),
1316
+ "GET /webhooks/events": ({ url, namespace }) => {
1317
+ const type = url.searchParams.get("type");
1318
+ return json2(200, {
1319
+ events: hub.messages(url.searchParams.get("all") === "1" ? void 0 : namespace).filter((m) => type === null || m.type === type).map((m) => ({ ...m, payload: parsePayload(m) }))
1320
+ });
1321
+ },
1322
+ "POST /webhooks/:id/replay": async ({ params }) => {
1323
+ const replayed = await hub.replay(params.id);
1324
+ return replayed ? json2(200, replayed) : adminError2(404, `no delivery ${params.id}`);
1325
+ },
1326
+ "POST /webhooks/flush": async () => {
1327
+ await hub.flush();
1328
+ return json2(200, { status: "ok" });
1329
+ },
1330
+ "POST /webhooks/faults": ({ body, namespace }) => {
1331
+ if (!isRecord3(body) || !["duplicate", "reorder", "drop"].includes(String(body.mode))) {
1332
+ return adminError2(400, "mode must be duplicate, reorder or drop");
1333
+ }
1334
+ const fault = { mode: body.mode };
1335
+ if (typeof body.count === "number")
1336
+ fault.count = body.count;
1337
+ hub.fault(namespace, fault);
1338
+ return json2(201, { namespace, ...fault });
1339
+ },
1340
+ "GET /webhook-endpoints": ({ namespace }) => json2(200, {
1341
+ endpoints: hub.endpoints(namespace).map(({ secret, ...rest }) => ({
1342
+ ...rest,
1343
+ secret: secret ? "(set)" : null
1344
+ }))
1345
+ }),
1346
+ "PUT /webhook-endpoints": ({ body, namespace }) => {
1347
+ const list2 = Array.isArray(body) ? body : isRecord3(body) ? body.endpoints : void 0;
1348
+ if (!Array.isArray(list2))
1349
+ return adminError2(400, "expected [{url, secret?, events?}]");
1350
+ const parsed = [];
1351
+ for (const each of list2) {
1352
+ const endpoint = parseEndpoint(each);
1353
+ if (typeof endpoint === "string")
1354
+ return adminError2(400, endpoint);
1355
+ parsed.push(endpoint);
1356
+ }
1357
+ const set = hub.setEndpoints(namespace, parsed);
1358
+ return json2(200, { endpoints: set.map((e) => ({ ...e, secret: e.secret ? "(set)" : null })) });
1359
+ },
1360
+ "DELETE /webhook-endpoints": ({ namespace }) => {
1361
+ hub.setEndpoints(namespace, []);
1362
+ return json2(200, { status: "ok" });
1363
+ }
1364
+ });
1365
+ var parsePayload = (message) => {
1366
+ if (message.contentType.startsWith("application/json")) {
1367
+ try {
1368
+ return JSON.parse(message.body);
1369
+ } catch {
1370
+ return message.body;
1371
+ }
1372
+ }
1373
+ if (message.contentType.startsWith("application/x-www-form-urlencoded")) {
1374
+ return Object.fromEntries(new URLSearchParams(message.body));
1375
+ }
1376
+ return message.body;
1377
+ };
1378
+
1379
+ // ../core/dist/runtime.js
1380
+ var MOCKINGBIRD_HEADER = "x-mockingbird";
1381
+ var BRANCH_HEADER = "x-mockingbird-branch";
1382
+ var AT_HEADER = "x-mockingbird-at";
1383
+ var CHECKPOINT_HEADER = "x-mockingbird-checkpoint";
1384
+ var DEFAULT_NAMESPACE = "default";
1385
+ var NAMESPACE_PATTERN = /^[A-Za-z0-9_.-]{1,64}$/;
1386
+ var PATH_PREFIX = /^\/ns\/([^/]+)(\/.*)?$/;
1387
+ var BRANCH_PATTERN2 = /^[A-Za-z0-9_.-]{1,64}$/;
1388
+ var MUTATING_METHODS = /* @__PURE__ */ new Set(["POST", "PUT", "PATCH", "DELETE"]);
1389
+ var effects = /* @__PURE__ */ new WeakMap();
1390
+ var reuseSorted = (fresh, previous, compare2, equal) => {
1391
+ if (!previous || previous.length === 0)
1392
+ return fresh.map((row) => Object.freeze(row));
1393
+ const result = new Array(fresh.length);
1394
+ let unchanged = fresh.length === previous.length;
1395
+ let oldIndex = 0;
1396
+ for (let index = 0; index < fresh.length; index++) {
1397
+ const row = fresh[index];
1398
+ while (oldIndex < previous.length && compare2(previous[oldIndex], row) < 0) {
1399
+ oldIndex++;
1400
+ }
1401
+ const old = previous[oldIndex];
1402
+ result[index] = old !== void 0 && compare2(old, row) === 0 && equal(old, row) ? old : Object.freeze(row);
1403
+ if (result[index] !== previous[index])
1404
+ unchanged = false;
1405
+ }
1406
+ return unchanged ? previous : result;
1407
+ };
1408
+ var faultEffects = (request) => (effects.get(request) ?? []).filter((e) => e !== void 0);
1409
+ var faultEffect = (request, name) => faultEffects(request).find((e) => e.name === name)?.params;
1410
+ var DroppedConnectionError = class extends TypeError {
1411
+ code = "MOCKINGBIRD_DROP";
1412
+ constructor() {
1413
+ super("fetch failed: connection dropped by Mockingbird fault");
1414
+ this.name = "TypeError";
1415
+ }
1416
+ };
1417
+ var operationMatcher = (document2) => {
1418
+ const matchers = listOperations(document2).map((operation) => ({
1419
+ operationId: operation.operationId,
1420
+ method: operation.method.toUpperCase(),
1421
+ pattern: new RegExp(`^${operation.path.split("/").map((segment) => segment.startsWith("{") ? "[^/]+" : segment.replace(/[.*+?^${}()|[\]\\]/g, "\\$&")).join("/")}/?$`),
1422
+ params: (operation.path.match(/\{/g) ?? []).length
1423
+ })).sort((a, b) => a.params - b.params);
1424
+ return (request, path) => matchers.find((m) => m.method === request.method && m.pattern.test(path))?.operationId;
1425
+ };
1426
+ var createRuntime = (options) => {
1427
+ const sqlite = bootSqlite(options.sqlite);
1428
+ const clock = options.clock ?? createClock();
1429
+ const rng = createRng(options.seed ?? 0);
1430
+ const wallNow = options.io?.wallNow ?? Date.now;
1431
+ const monotonicNow = options.io?.monotonicNow ?? (() => performance.now());
1432
+ const sleep = options.io?.sleep ?? ((ms) => new Promise((resolve) => setTimeout(resolve, ms)));
1433
+ const faults = createFaultRegistry(createRng(options.seed ?? 0), sleep);
1434
+ const metrics = createMetrics();
1435
+ const journal = createJournal(options.journalSize ?? DEFAULT_JOURNAL_SIZE);
1436
+ const version = options.version ?? PACKAGE_VERSION;
1437
+ const instances = /* @__PURE__ */ new Map();
1438
+ const publicNamespaces = /* @__PURE__ */ new Set();
1439
+ const branchRngs = /* @__PURE__ */ new Map();
1440
+ const timelines = /* @__PURE__ */ new Map();
1441
+ const branchStorage = /* @__PURE__ */ new Map();
1442
+ const captured = /* @__PURE__ */ new Map();
1443
+ const credentials = createCredentialRegistry();
1444
+ const operationIdFor = options.document ? operationMatcher(options.document) : () => void 0;
1445
+ const storageNamespace = (name) => name === DEFAULT_NAMESPACE ? options.name : `${options.name}:${name}`;
1446
+ const instanceFor = (key, publicNamespace = key, isolatedRng) => {
1447
+ const existing = instances.get(key);
1448
+ if (existing)
1449
+ return existing;
1450
+ if (!NAMESPACE_PATTERN.test(key) || !NAMESPACE_PATTERN.test(publicNamespace)) {
1451
+ throw new RangeError(`namespace must match ${NAMESPACE_PATTERN}: ${JSON.stringify(publicNamespace)}`);
1452
+ }
1453
+ const created = options.create({
1454
+ namespace: storageNamespace(key),
1455
+ publicNamespace,
1456
+ sqlite,
1457
+ clock,
1458
+ rng: isolatedRng ?? rng
1459
+ });
1460
+ instances.set(key, created);
1461
+ publicNamespaces.add(publicNamespace);
1462
+ if (isolatedRng)
1463
+ branchRngs.set(key, isolatedRng);
1464
+ return created;
1465
+ };
1466
+ const instance = (name = DEFAULT_NAMESPACE) => instanceFor(name);
1467
+ const capture = (storage) => {
1468
+ const fresh = snapshotNamespace(sqlite, storageNamespace(storage));
1469
+ const previous = captured.get(storage);
1470
+ const snapshot2 = {
1471
+ namespace: fresh.namespace,
1472
+ records: reuseSorted(fresh.records, previous?.records, (left, right) => left.collection < right.collection ? -1 : left.collection > right.collection ? 1 : left.seq - right.seq, (left, right) => left.id === right.id && left.value === right.value),
1473
+ sequences: reuseSorted(fresh.sequences, previous?.sequences, (left, right) => left.name < right.name ? -1 : left.name > right.name ? 1 : left.kind < right.kind ? -1 : left.kind > right.kind ? 1 : 0, (left, right) => left.value === right.value)
1474
+ };
1475
+ Object.freeze(snapshot2.records);
1476
+ Object.freeze(snapshot2.sequences);
1477
+ Object.freeze(snapshot2);
1478
+ captured.set(storage, snapshot2);
1479
+ return Object.freeze({
1480
+ snapshot: snapshot2,
1481
+ clock: Object.freeze(clock.state()),
1482
+ rngState: (branchRngs.get(storage) ?? rng).state()
1483
+ });
1484
+ };
1485
+ const timeline = (name = DEFAULT_NAMESPACE) => {
1486
+ let found = timelines.get(name);
1487
+ if (found)
1488
+ return found;
1489
+ instance(name);
1490
+ found = new Timeline({
1491
+ now: clock.now,
1492
+ ...options.maxCheckpoints !== void 0 ? { maxCheckpoints: options.maxCheckpoints } : {}
1493
+ });
1494
+ found.commit(capture(name));
1495
+ timelines.set(name, found);
1496
+ return found;
1497
+ };
1498
+ const physicalBranch = (namespace, branch2) => {
1499
+ if (branch2 === "main")
1500
+ return namespace;
1501
+ const mapKey = `${namespace}\0${branch2}`;
1502
+ const existing = branchStorage.get(mapKey);
1503
+ if (existing)
1504
+ return existing;
1505
+ const key = `branch_${seedFrom(`${options.name}\0${namespace}\0${branch2}`).toString(36)}`;
1506
+ branchStorage.set(mapKey, key);
1507
+ return key;
1508
+ };
1509
+ const ensureBranch = (namespace, branch2, at) => {
1510
+ if (!BRANCH_PATTERN2.test(branch2))
1511
+ throw new RangeError(`branch must match ${BRANCH_PATTERN2}`);
1512
+ const history = timeline(namespace);
1513
+ if (branch2 === "main") {
1514
+ if (at !== void 0) {
1515
+ const point = history.checkout("main", at);
1516
+ restoreNamespace(sqlite, storageNamespace(namespace), point.value.snapshot);
1517
+ captured.set(namespace, point.value.snapshot);
1518
+ rng.setState(point.value.rngState);
1519
+ clock.set(point.value.clock.now);
1520
+ if (point.value.clock.frozen)
1521
+ clock.freeze();
1522
+ else
1523
+ clock.unfreeze();
1524
+ }
1525
+ return namespace;
1526
+ }
1527
+ const storage = physicalBranch(namespace, branch2);
1528
+ if (!history.hasBranch(branch2)) {
1529
+ if (at === void 0)
1530
+ history.commit(capture(namespace));
1531
+ const point = history.fork(branch2, at === void 0 ? {} : { from: at });
1532
+ const branchRng = createRng(options.seed ?? 0);
1533
+ if (point)
1534
+ branchRng.setState(point.value.rngState);
1535
+ instanceFor(storage, namespace, branchRng);
1536
+ if (point)
1537
+ restoreNamespace(sqlite, storageNamespace(storage), point.value.snapshot);
1538
+ if (point)
1539
+ captured.set(storage, point.value.snapshot);
1540
+ } else if (at !== void 0 && history.head(branch2)?.id !== at) {
1541
+ const point = history.checkout(branch2, at);
1542
+ if (!instances.has(storage)) {
1543
+ const branchRng = createRng(options.seed ?? 0);
1544
+ branchRng.setState(point.value.rngState);
1545
+ instanceFor(storage, namespace, branchRng);
1546
+ }
1547
+ branchRngs.get(storage)?.setState(point.value.rngState);
1548
+ restoreNamespace(sqlite, storageNamespace(storage), point.value.snapshot);
1549
+ captured.set(storage, point.value.snapshot);
1550
+ } else {
1551
+ if (!instances.has(storage)) {
1552
+ const point = history.head(branch2);
1553
+ const branchRng = createRng(options.seed ?? 0);
1554
+ if (point)
1555
+ branchRng.setState(point.value.rngState);
1556
+ instanceFor(storage, namespace, branchRng);
1557
+ }
1558
+ }
1559
+ return storage;
1560
+ };
1561
+ const checkpoint = (namespace = DEFAULT_NAMESPACE, branch2 = "main") => {
1562
+ const storage = ensureBranch(namespace, branch2);
1563
+ return timeline(namespace).commit(capture(storage), { branch: branch2 });
1564
+ };
1565
+ const branch = (name, branchOptions = {}) => {
1566
+ const namespace = branchOptions.namespace ?? DEFAULT_NAMESPACE;
1567
+ ensureBranch(namespace, name, branchOptions.at);
1568
+ const head = timeline(namespace).head(name);
1569
+ if (!head)
1570
+ throw new RangeError(`branch ${name} has no checkpoint`);
1571
+ return head;
1572
+ };
1573
+ const checkout = (checkpointId, checkoutOptions = {}) => {
1574
+ const namespace = checkoutOptions.namespace ?? DEFAULT_NAMESPACE;
1575
+ const branchName = checkoutOptions.branch ?? "main";
1576
+ const history = timeline(namespace);
1577
+ const point = history.checkout(branchName, checkpointId);
1578
+ const storage = ensureBranch(namespace, branchName);
1579
+ restoreNamespace(sqlite, storageNamespace(storage), point.value.snapshot);
1580
+ captured.set(storage, point.value.snapshot);
1581
+ clock.set(point.value.clock.now);
1582
+ if (point.value.clock.frozen)
1583
+ clock.freeze();
1584
+ else
1585
+ clock.unfreeze();
1586
+ (branchRngs.get(storage) ?? rng).setState(point.value.rngState);
1587
+ };
1588
+ const reset = async (name = DEFAULT_NAMESPACE) => {
1589
+ if (name === "*") {
1590
+ options.webhooks?.clear();
1591
+ for (const each of instances.values())
1592
+ await each.reset();
1593
+ timelines.clear();
1594
+ branchStorage.clear();
1595
+ branchRngs.clear();
1596
+ captured.clear();
1597
+ return;
1598
+ }
1599
+ options.webhooks?.clear(name);
1600
+ const target = instances.get(name);
1601
+ if (target)
1602
+ await target.reset();
1603
+ else
1604
+ clearNamespace(sqlite, storageNamespace(name));
1605
+ for (const [mapping, storage] of branchStorage) {
1606
+ if (!mapping.startsWith(`${name}\0`))
1607
+ continue;
1608
+ const branchInstance = instances.get(storage);
1609
+ if (branchInstance)
1610
+ await branchInstance.reset();
1611
+ else
1612
+ clearNamespace(sqlite, storageNamespace(storage));
1613
+ branchStorage.delete(mapping);
1614
+ branchRngs.delete(storage);
1615
+ captured.delete(storage);
1616
+ }
1617
+ timelines.delete(name);
1618
+ captured.delete(name);
1619
+ };
1620
+ const snapshot = (name = DEFAULT_NAMESPACE) => {
1621
+ return checkpoint(name, "main").value.snapshot;
1622
+ };
1623
+ const restore = (from, name = DEFAULT_NAMESPACE) => {
1624
+ instance(name);
1625
+ restoreNamespace(sqlite, storageNamespace(name), from);
1626
+ captured.set(name, from);
1627
+ const history = timelines.get(name);
1628
+ if (history)
1629
+ history.commit(capture(name), { branch: "main" });
1630
+ else
1631
+ timeline(name);
1632
+ };
1633
+ const runtime = {
1634
+ name: options.name,
1635
+ sqlite,
1636
+ clock,
1637
+ faults,
1638
+ metrics,
1639
+ journal,
1640
+ rng,
1641
+ credentials,
1642
+ webhooks: options.webhooks,
1643
+ applyPreset: (name, namespace = DEFAULT_NAMESPACE, overrides = {}) => {
1644
+ const preset = options.presets?.[name];
1645
+ if (!preset)
1646
+ throw new RangeError(`no fault preset ${JSON.stringify(name)}`);
1647
+ const added = (preset.rules ?? []).map((rule, index) => faults.add({
1648
+ namespace,
1649
+ ...rule,
1650
+ ...overrides,
1651
+ preset: name,
1652
+ id: `${overrides.id ?? name}${(preset.rules ?? []).length > 1 ? `_${index + 1}` : ""}`
1653
+ }));
1654
+ if (preset.webhook && options.webhooks) {
1655
+ options.webhooks.fault(namespace, {
1656
+ ...preset.webhook,
1657
+ ...overrides.count !== void 0 ? { count: overrides.count } : {}
1658
+ });
1659
+ }
1660
+ return added;
1661
+ },
1662
+ instance,
1663
+ namespaces: () => [...publicNamespaces].sort(),
1664
+ reset,
1665
+ snapshot,
1666
+ restore,
1667
+ checkpoint,
1668
+ branch,
1669
+ checkout,
1670
+ timeline,
1671
+ fetch: async (incoming) => {
1672
+ let request = incoming;
1673
+ const prefixed = PATH_PREFIX.exec(new URL(request.url).pathname);
1674
+ if (prefixed) {
1675
+ const url2 = new URL(request.url);
1676
+ url2.pathname = prefixed[2] ?? "/";
1677
+ const headers = new Headers(request.headers);
1678
+ if (!headers.has(NAMESPACE_HEADER)) {
1679
+ headers.set(NAMESPACE_HEADER, decodeURIComponent(prefixed[1]));
1680
+ }
1681
+ const hasBody = request.method !== "GET" && request.method !== "HEAD";
1682
+ request = new Request(url2, {
1683
+ method: request.method,
1684
+ headers,
1685
+ ...hasBody ? { body: await request.arrayBuffer() } : {},
1686
+ signal: request.signal
1687
+ });
1688
+ }
1689
+ let namespace = control.namespaceOf(request);
1690
+ if (!request.headers.has(NAMESPACE_HEADER) && options.credential) {
1691
+ const credential = options.credential(request);
1692
+ const mapped = credential !== void 0 ? credentials.get(credential) : void 0;
1693
+ if (mapped !== void 0)
1694
+ namespace = mapped;
1695
+ }
1696
+ const selectedBranch = request.headers.get(BRANCH_HEADER) ?? "main";
1697
+ const at = request.headers.get(AT_HEADER) ?? void 0;
1698
+ const stamp = (response2) => {
1699
+ const value = NAMESPACE_PATTERN.test(namespace) ? `${options.name}@${version}; ns=${namespace}` : `${options.name}@${version}`;
1700
+ try {
1701
+ response2.headers.set(MOCKINGBIRD_HEADER, value);
1702
+ return response2;
1703
+ } catch {
1704
+ const copy = new Response(response2.body, response2);
1705
+ copy.headers.set(MOCKINGBIRD_HEADER, value);
1706
+ return copy;
1707
+ }
1708
+ };
1709
+ const handled = await control.handle(request);
1710
+ if (handled)
1711
+ return stamp(handled);
1712
+ const started = monotonicNow();
1713
+ const url = new URL(request.url);
1714
+ const operationId = operationIdFor(request, url.pathname);
1715
+ const log = (status, faultId, response2) => {
1716
+ const noted = response2 ? responseNotes(response2) : void 0;
1717
+ const entry = {
1718
+ service: options.name,
1719
+ namespace,
1720
+ operationId,
1721
+ method: request.method,
1722
+ path: url.pathname,
1723
+ status,
1724
+ durationMs: Math.round((monotonicNow() - started) * 100) / 100,
1725
+ unmatched: options.document !== void 0 && operationId === void 0,
1726
+ ...faultId !== void 0 ? { faultId } : {},
1727
+ ...noted?.ids && Object.keys(noted.ids).length > 0 ? { ids: noted.ids } : {},
1728
+ ...noted?.adopted ? { adopted: true } : {}
1729
+ };
1730
+ metrics.record(entry);
1731
+ journal.record({ ...entry, at: new Date(clock.now()).toISOString() });
1732
+ options.onLog?.(entry);
1733
+ };
1734
+ if (!NAMESPACE_PATTERN.test(namespace)) {
1735
+ log(400);
1736
+ return stamp(new Response(JSON.stringify({
1737
+ error: {
1738
+ type: "mockingbird_admin",
1739
+ message: `${NAMESPACE_HEADER} must match ${NAMESPACE_PATTERN}`
1740
+ }
1741
+ }), { status: 400, headers: { "content-type": "application/json" } }));
1742
+ }
1743
+ if (!BRANCH_PATTERN2.test(selectedBranch)) {
1744
+ log(400);
1745
+ return stamp(adminFail(400, `${BRANCH_HEADER} must match ${BRANCH_PATTERN2}`));
1746
+ }
1747
+ let storage;
1748
+ try {
1749
+ if (at !== void 0 && selectedBranch === "main" && !MUTATING_METHODS.has(request.method)) {
1750
+ const point = timeline(namespace).get(at);
1751
+ storage = physicalBranch(namespace, `at_${at}`);
1752
+ let viewRng = branchRngs.get(storage);
1753
+ if (!viewRng) {
1754
+ viewRng = createRng(options.seed ?? 0);
1755
+ instanceFor(storage, namespace, viewRng);
1756
+ }
1757
+ viewRng.setState(point.value.rngState);
1758
+ restoreNamespace(sqlite, storageNamespace(storage), point.value.snapshot);
1759
+ captured.set(storage, point.value.snapshot);
1760
+ } else {
1761
+ storage = ensureBranch(namespace, selectedBranch, at);
1762
+ }
1763
+ } catch (error) {
1764
+ log(409);
1765
+ return stamp(adminFail(409, error instanceof Error ? error.message : String(error)));
1766
+ }
1767
+ const hits = await faults.take({
1768
+ operationId,
1769
+ method: request.method,
1770
+ path: url.pathname,
1771
+ namespace
1772
+ });
1773
+ const final = hits.find((hit) => hit.drop || hit.response);
1774
+ if (final?.drop) {
1775
+ log(0, final.id);
1776
+ throw new DroppedConnectionError();
1777
+ }
1778
+ if (final?.response) {
1779
+ log(final.response.status, final.id);
1780
+ return stamp(final.response);
1781
+ }
1782
+ const fired = hits.filter((hit) => hit.effect !== void 0);
1783
+ if (fired.length > 0)
1784
+ effects.set(request, fired.map((hit) => hit.effect));
1785
+ let response = await instanceFor(storage, namespace).fetch(request);
1786
+ if (MUTATING_METHODS.has(request.method) && response.status >= 200 && response.status < 400) {
1787
+ const point = timeline(namespace).commit(capture(storage), { branch: selectedBranch });
1788
+ response = mutableResponse(response);
1789
+ response.headers.set(CHECKPOINT_HEADER, point.id);
1790
+ }
1791
+ if (selectedBranch !== "main") {
1792
+ response = mutableResponse(response);
1793
+ response.headers.set(BRANCH_HEADER, selectedBranch);
1794
+ }
1795
+ if (at !== void 0) {
1796
+ response = mutableResponse(response);
1797
+ response.headers.set(AT_HEADER, at);
1798
+ }
1799
+ log(response.status, fired[0]?.id, response);
1800
+ return stamp(response);
1801
+ }
1802
+ };
1803
+ const control = createControlPlane({
1804
+ name: options.name,
1805
+ startedAt: wallNow(),
1806
+ wallNow,
1807
+ clock,
1808
+ faults,
1809
+ metrics,
1810
+ journal,
1811
+ defaultNamespace: DEFAULT_NAMESPACE,
1812
+ namespaces: runtime.namespaces,
1813
+ reset,
1814
+ timeTravel: {
1815
+ checkpoint: (name, branchName) => {
1816
+ const point = checkpoint(name, branchName);
1817
+ return {
1818
+ id: point.id,
1819
+ branch: point.branch,
1820
+ parent: point.parent,
1821
+ at: point.at,
1822
+ records: point.value.snapshot.records.length
1823
+ };
1824
+ },
1825
+ branch: (branchName, branchOptions) => {
1826
+ const point = branch(branchName, branchOptions);
1827
+ return { id: point.id, branch: point.branch, parent: point.parent, at: point.at };
1828
+ },
1829
+ checkout: (checkpointId, checkoutOptions) => checkout(checkpointId, checkoutOptions),
1830
+ retain: (name, checkpointId) => {
1831
+ timeline(name).retain(checkpointId);
1832
+ },
1833
+ release: (name, checkpointId) => timeline(name).release(checkpointId),
1834
+ inspect: (name) => {
1835
+ const history = timeline(name);
1836
+ return {
1837
+ branches: history.branches(),
1838
+ checkpoints: history.checkpoints().map(({ id: id2, branch: branchName, parent, at }) => ({
1839
+ id: id2,
1840
+ branch: branchName,
1841
+ parent,
1842
+ at
1843
+ }))
1844
+ };
1845
+ }
1846
+ },
1847
+ describe: options.describe ?? (() => ({})),
1848
+ ...options.presets ? {
1849
+ applyPreset: (name, namespace, overrides) => runtime.applyPreset(name, namespace, overrides)
1850
+ } : {},
1851
+ routes: {
1852
+ ...credentialRoutes(credentials),
1853
+ ...options.presets ? presetRoutes(options.presets, runtime) : {},
1854
+ ...options.webhooks ? webhookAdminRoutes(options.webhooks) : {},
1855
+ ...options.admin?.(runtime) ?? {}
1856
+ },
1857
+ adminKey: options.adminKey
1858
+ });
1859
+ return runtime;
1860
+ };
1861
+ var mutableResponse = (response) => {
1862
+ try {
1863
+ response.headers.set("x-mockingbird-mutable-probe", "1");
1864
+ response.headers.delete("x-mockingbird-mutable-probe");
1865
+ return response;
1866
+ } catch {
1867
+ return new Response(response.body, response);
1868
+ }
1869
+ };
1870
+ var adminJson = (status, body) => new Response(JSON.stringify(body), { status, headers: { "content-type": "application/json" } });
1871
+ var adminFail = (status, message) => adminJson(status, { error: { type: "mockingbird_admin", message } });
1872
+ var isObject = (value) => typeof value === "object" && value !== null && !Array.isArray(value);
1873
+ var credentialRoutes = (registry) => ({
1874
+ "GET /credentials": () => adminJson(200, {
1875
+ credentials: registry.entries().map(({ credential, namespace }) => ({
1876
+ credential: maskCredential(credential),
1877
+ namespace
1878
+ }))
1879
+ }),
1880
+ "PUT /credentials": ({ body, namespace }) => {
1881
+ const pairs = [];
1882
+ const list2 = Array.isArray(body) ? body : isObject(body) ? body.credentials : void 0;
1883
+ if (Array.isArray(list2)) {
1884
+ for (const each of list2) {
1885
+ if (typeof each === "string")
1886
+ pairs.push([each, namespace]);
1887
+ else if (isObject(each) && typeof each.credential === "string") {
1888
+ pairs.push([
1889
+ each.credential,
1890
+ typeof each.namespace === "string" ? each.namespace : namespace
1891
+ ]);
1892
+ } else
1893
+ return adminFail(400, "each entry is a credential string or {credential, namespace}");
1894
+ }
1895
+ } else if (isObject(list2)) {
1896
+ for (const [credential, target] of Object.entries(list2)) {
1897
+ if (typeof target !== "string")
1898
+ return adminFail(400, `namespace for ${credential} must be a string`);
1899
+ pairs.push([credential, target]);
1900
+ }
1901
+ } else if (isObject(body) && typeof body.credential === "string") {
1902
+ pairs.push([body.credential, typeof body.namespace === "string" ? body.namespace : namespace]);
1903
+ } else {
1904
+ return adminFail(400, 'expected {"credentials": {"<credential>": "<namespace>"}}');
1905
+ }
1906
+ for (const [credential, target] of pairs) {
1907
+ if (!NAMESPACE_PATTERN.test(target))
1908
+ return adminFail(400, `bad namespace ${JSON.stringify(target)}`);
1909
+ registry.set(credential, target);
1910
+ }
1911
+ return adminJson(200, { mapped: pairs.length });
1912
+ },
1913
+ "DELETE /credentials": ({ url }) => {
1914
+ const credential = url.searchParams.get("credential");
1915
+ if (credential === null)
1916
+ registry.clear();
1917
+ else
1918
+ registry.remove(credential);
1919
+ return adminJson(200, { status: "ok" });
1920
+ }
1921
+ });
1922
+ var presetRoutes = (presets, runtime) => ({
1923
+ "GET /faults/presets": () => adminJson(200, {
1924
+ presets: Object.entries(presets).map(([name, preset]) => ({ name, ...preset }))
1925
+ }),
1926
+ "POST /faults/presets/:name": ({ params, body, namespace }) => {
1927
+ const name = params.name;
1928
+ if (!presets[name])
1929
+ return adminFail(404, `no fault preset ${name}; GET /__admin/faults/presets`);
1930
+ const overrides = isObject(body) ? body : {};
1931
+ return adminJson(201, { preset: name, rules: runtime.applyPreset(name, namespace, overrides) });
1932
+ }
1933
+ });
1934
+
1935
+ // src/generated/openapi.ts
1936
+ var document = JSON.parse(`{"openapi":"3.1.0","info":{"title":"OTLP/HTTP collector and OpenObserve search (Mockingbird subset)","description":"An OTLP/HTTP receiver (traces, logs, metrics; JSON and protobuf) in front of the\\nOpenObserve (O2) search API, over one store: a test emits a span or a structured \`event\`\\nlog and queries it back with the same SQL our clients send.\\n","version":"1","x-mockingbird-upstream":{"note":"OTLP/HTTP per opentelemetry-proto v1 (https://opentelemetry.io/docs/specs/otlp/); the O2 routes follow OpenObserve's API (/api-doc/openapi.json, v0.92) trimmed to what our clients call."}},"servers":[{"url":"https://otel.gogeviti.com"},{"url":"https://observe.gogeviti.com"}],"security":[{"bearerAuth":[]}],"paths":{"/v1/traces":{"post":{"operationId":"ExportTraces","summary":"OTLP/HTTP trace export (ExportTraceServiceRequest)","x-mockingbird":{"supported":true,"parity":{"enabled":true,"safe":false}},"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ExportTraceServiceRequest"}},"application/x-protobuf":{"schema":{"type":"string","format":"binary"}}}},"responses":{"200":{"description":"Accepted. JSON requests get \`{partialSuccess: {}}\`; protobuf requests get an empty protobuf response.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ExportResponse"}},"application/x-protobuf":{"schema":{"type":"string","format":"binary"}}}},"400":{"description":"Undecodable payload","content":{"application/json":{"schema":{"$ref":"#/components/schemas/RpcStatus"}}}},"401":{"description":"Missing or unknown bearer token","content":{"application/json":{"schema":{"$ref":"#/components/schemas/RpcStatus"}}}},"415":{"description":"Neither application/json nor application/x-protobuf","content":{"application/json":{"schema":{"$ref":"#/components/schemas/RpcStatus"}}}},"429":{"description":"Throttled (retried by the SDK), with retry-after","headers":{"retry-after":{"schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/RpcStatus"}}}},"500":{"description":"Collector error (not retried by the SDK)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/RpcStatus"}}}},"502":{"description":"Bad gateway (retried)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/RpcStatus"}}}},"503":{"description":"Unavailable (retried)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/RpcStatus"}}}},"504":{"description":"Gateway timeout (retried)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/RpcStatus"}}}}}}},"/v1/logs":{"post":{"operationId":"ExportLogs","summary":"OTLP/HTTP log export (ExportLogsServiceRequest)","x-mockingbird":{"supported":true,"parity":{"enabled":true,"safe":false}},"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ExportLogsServiceRequest"}},"application/x-protobuf":{"schema":{"type":"string","format":"binary"}}}},"responses":{"200":{"description":"Accepted. JSON requests get \`{partialSuccess: {}}\`; protobuf requests get an empty protobuf response.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ExportResponse"}},"application/x-protobuf":{"schema":{"type":"string","format":"binary"}}}},"400":{"description":"Undecodable payload","content":{"application/json":{"schema":{"$ref":"#/components/schemas/RpcStatus"}}}},"401":{"description":"Missing or unknown bearer token","content":{"application/json":{"schema":{"$ref":"#/components/schemas/RpcStatus"}}}},"415":{"description":"Neither application/json nor application/x-protobuf","content":{"application/json":{"schema":{"$ref":"#/components/schemas/RpcStatus"}}}},"429":{"description":"Throttled (retried by the SDK), with retry-after","headers":{"retry-after":{"schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/RpcStatus"}}}},"500":{"description":"Collector error (not retried by the SDK)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/RpcStatus"}}}},"502":{"description":"Bad gateway (retried)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/RpcStatus"}}}},"503":{"description":"Unavailable (retried)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/RpcStatus"}}}},"504":{"description":"Gateway timeout (retried)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/RpcStatus"}}}}}}},"/v1/metrics":{"post":{"operationId":"ExportMetrics","summary":"OTLP/HTTP metrics export: accepted and counted, never stored","x-mockingbird":{"supported":true,"parity":{"enabled":true,"safe":false}},"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ExportMetricsServiceRequest"}},"application/x-protobuf":{"schema":{"type":"string","format":"binary"}}}},"responses":{"200":{"description":"Accepted. JSON requests get \`{partialSuccess: {}}\`; protobuf requests get an empty protobuf response.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ExportResponse"}},"application/x-protobuf":{"schema":{"type":"string","format":"binary"}}}},"400":{"description":"Undecodable payload","content":{"application/json":{"schema":{"$ref":"#/components/schemas/RpcStatus"}}}},"401":{"description":"Missing or unknown bearer token","content":{"application/json":{"schema":{"$ref":"#/components/schemas/RpcStatus"}}}},"415":{"description":"Neither application/json nor application/x-protobuf","content":{"application/json":{"schema":{"$ref":"#/components/schemas/RpcStatus"}}}},"429":{"description":"Throttled (retried by the SDK), with retry-after","headers":{"retry-after":{"schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/RpcStatus"}}}},"500":{"description":"Collector error (not retried by the SDK)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/RpcStatus"}}}},"502":{"description":"Bad gateway (retried)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/RpcStatus"}}}},"503":{"description":"Unavailable (retried)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/RpcStatus"}}}},"504":{"description":"Gateway timeout (retried)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/RpcStatus"}}}}}}},"/api/organizations":{"get":{"operationId":"ListOrganizations","summary":"O2: the organizations the Basic user belongs to (display name \u2192 identifier)","security":[{"basicAuth":[]}],"x-mockingbird":{"supported":true,"parity":{"enabled":true,"safe":true}},"responses":{"200":{"description":"Organizations","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Organizations"}}}},"401":{"description":"Bad Basic credentials, or an org the user cannot see \u2014 including an org's display name in place of its identifier","content":{"text/plain":{"schema":{"type":"string"}}}}}}},"/api/{org}/streams":{"parameters":[{"name":"org","in":"path","required":true,"description":"The org **identifier** (not its display name)","schema":{"type":"string","x-mockingbird-resource-ref":{"type":"organization","missing":"no-such-org"}}}],"get":{"operationId":"ListStreams","summary":"O2: the org's streams of one type","security":[{"basicAuth":[]}],"x-mockingbird":{"supported":true,"parity":{"enabled":true,"safe":true}},"parameters":[{"name":"type","in":"query","required":false,"schema":{"type":"string","enum":["logs","traces"]}}],"responses":{"200":{"description":"Streams","content":{"application/json":{"schema":{"$ref":"#/components/schemas/StreamList"}}}},"401":{"description":"Bad Basic credentials, or an org the user cannot see \u2014 including an org's display name in place of its identifier","content":{"text/plain":{"schema":{"type":"string"}}}}}}},"/api/{org}/streams/{stream}/schema":{"parameters":[{"name":"org","in":"path","required":true,"description":"The org **identifier** (not its display name)","schema":{"type":"string","x-mockingbird-resource-ref":{"type":"organization","missing":"no-such-org"}}},{"name":"stream","in":"path","required":true,"schema":{"type":"string","enum":["default","other"]}}],"get":{"operationId":"GetStreamSchema","summary":"O2: a stream's schema (\`schema[].name\` are the queryable, lowercased, flattened fields)","security":[{"basicAuth":[]}],"x-mockingbird":{"supported":true,"parity":{"enabled":true,"safe":true}},"parameters":[{"name":"type","in":"query","required":false,"schema":{"type":"string","enum":["logs","traces"]}}],"responses":{"200":{"description":"Schema","content":{"application/json":{"schema":{"$ref":"#/components/schemas/StreamSchema"}}}},"401":{"description":"Bad Basic credentials, or an org the user cannot see \u2014 including an org's display name in place of its identifier","content":{"text/plain":{"schema":{"type":"string"}}}},"404":{"description":"Nothing has been ingested into this stream","content":{"application/json":{"schema":{"$ref":"#/components/schemas/O2Error"}}}}}}},"/api/{org}/_search":{"parameters":[{"name":"org","in":"path","required":true,"description":"The org **identifier** (not its display name)","schema":{"type":"string","x-mockingbird-resource-ref":{"type":"organization","missing":"no-such-org"}}}],"post":{"operationId":"Search","summary":"O2: SQL search over a stream","security":[{"basicAuth":[]}],"x-mockingbird":{"supported":true,"parity":{"enabled":true,"safe":true}},"parameters":[{"name":"type","in":"query","required":false,"schema":{"type":"string","enum":["logs","traces"]}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["query"],"properties":{"query":{"type":"object","required":["sql"],"properties":{"sql":{"anyOf":[{"type":"string","enum":["SELECT * FROM \\"default\\" WHERE event IS NOT NULL ORDER BY _timestamp DESC","SELECT * FROM \\"default\\" WHERE event IN ('checkout_completed') ORDER BY _timestamp DESC","SELECT service_name, count(*) AS n FROM \\"default\\" GROUP BY service_name ORDER BY n DESC","SELECT * FROM \\"default\\" WHERE severity_text IN ('ERROR', 'FATAL')","SELECT * FROM \\"default\\" WHERE clientUserId = '1'"]},{"type":"string","maxLength":200}]},"start_time":{"type":"integer","minimum":0,"description":"\xB5s since epoch"},"end_time":{"type":"integer","minimum":0,"description":"\xB5s since epoch"},"from":{"type":"integer","minimum":0,"maximum":100},"size":{"type":"integer","minimum":-1,"maximum":1000}}},"search_type":{"type":"string","maxLength":20}}}}}},"responses":{"200":{"description":"Hits","content":{"application/json":{"schema":{"$ref":"#/components/schemas/SearchResponse"}}}},"400":{"description":"Unsupported SQL, or an unknown field (O2 lowercases every field; a camelCase column is unknown)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/O2Error"}}}},"401":{"description":"Bad Basic credentials, or an org the user cannot see \u2014 including an org's display name in place of its identifier","content":{"text/plain":{"schema":{"type":"string"}}}}}}}},"components":{"securitySchemes":{"bearerAuth":{"type":"http","scheme":"bearer"},"basicAuth":{"type":"http","scheme":"basic"}},"schemas":{"KeyValue":{"type":"object","required":["key","value"],"properties":{"key":{"type":"string","enum":["service.name","event","clientUserId","http.method","deployment.environment.name","attempt"]},"value":{"type":"object","properties":{"stringValue":{"type":"string","maxLength":20},"intValue":{"type":["string","integer"]},"boolValue":{"type":"boolean"},"doubleValue":{"type":"number"}}}}},"ExportTraceServiceRequest":{"type":"object","properties":{"resourceSpans":{"type":"array","maxItems":2,"items":{"type":"object","properties":{"resource":{"type":"object","properties":{"attributes":{"type":"array","maxItems":4,"items":{"$ref":"#/components/schemas/KeyValue"}}}},"scopeSpans":{"type":"array","maxItems":2,"items":{"type":"object","properties":{"scope":{"type":"object","properties":{"name":{"type":"string","maxLength":20}}},"spans":{"type":"array","maxItems":3,"items":{"type":"object","required":["traceId","spanId","name"],"properties":{"traceId":{"type":"string","pattern":"^[0-9a-f]{32}$"},"spanId":{"type":"string","pattern":"^[0-9a-f]{16}$"},"parentSpanId":{"type":"string","pattern":"^[0-9a-f]{16}$"},"name":{"type":"string","maxLength":30},"kind":{"type":"integer","minimum":0,"maximum":5},"startTimeUnixNano":{"type":"string","pattern":"^1[0-9]{18}$"},"endTimeUnixNano":{"type":"string","pattern":"^1[0-9]{18}$"},"attributes":{"type":"array","maxItems":4,"items":{"$ref":"#/components/schemas/KeyValue"}},"status":{"type":"object","properties":{"code":{"type":"integer","minimum":0,"maximum":2},"message":{"type":"string","maxLength":40}}}}}}}}}}}}}},"ExportLogsServiceRequest":{"type":"object","properties":{"resourceLogs":{"type":"array","maxItems":2,"items":{"type":"object","properties":{"resource":{"type":"object","properties":{"attributes":{"type":"array","maxItems":4,"items":{"$ref":"#/components/schemas/KeyValue"}}}},"scopeLogs":{"type":"array","maxItems":2,"items":{"type":"object","properties":{"scope":{"type":"object","properties":{"name":{"type":"string","maxLength":20}}},"logRecords":{"type":"array","maxItems":3,"items":{"type":"object","properties":{"timeUnixNano":{"type":"string","pattern":"^1[0-9]{18}$"},"observedTimeUnixNano":{"type":"string","pattern":"^1[0-9]{18}$"},"severityNumber":{"type":"integer","minimum":0,"maximum":24},"severityText":{"type":"string","enum":["TRACE","DEBUG","INFO","WARN","ERROR","FATAL","info","error"]},"body":{"type":"object","properties":{"stringValue":{"type":"string","maxLength":40}}},"attributes":{"type":"array","maxItems":4,"items":{"$ref":"#/components/schemas/KeyValue"}},"traceId":{"type":"string","pattern":"^[0-9a-f]{32}$"},"spanId":{"type":"string","pattern":"^[0-9a-f]{16}$"}}}}}}}}}}}},"ExportMetricsServiceRequest":{"type":"object","properties":{"resourceMetrics":{"type":"array","maxItems":2,"items":{"type":"object","properties":{"resource":{"type":"object","properties":{"attributes":{"type":"array","maxItems":4,"items":{"$ref":"#/components/schemas/KeyValue"}}}},"scopeMetrics":{"type":"array","maxItems":2,"items":{"type":"object","properties":{"metrics":{"type":"array","maxItems":3,"items":{"type":"object","properties":{"name":{"type":"string","maxLength":30},"unit":{"type":"string","maxLength":10}}}}}}}}}}}},"ExportResponse":{"type":"object","required":["partialSuccess"],"properties":{"partialSuccess":{"type":"object","properties":{"rejectedSpans":{"type":["string","integer"]},"rejectedLogRecords":{"type":["string","integer"]},"rejectedDataPoints":{"type":["string","integer"]},"errorMessage":{"type":"string"}}}}},"RpcStatus":{"type":"object","required":["code","message"],"properties":{"code":{"type":"integer"},"message":{"type":"string"}}},"O2Error":{"type":"object","required":["code","message"],"properties":{"code":{"type":"integer"},"message":{"type":"string"},"error_detail":{"type":"string"}}},"Organizations":{"type":"object","required":["data"],"properties":{"data":{"type":"array","items":{"type":"object","required":["identifier","name"],"properties":{"id":{"type":"integer"},"identifier":{"type":"string","x-mockingbird-resource":{"type":"organization","identity":true}},"name":{"type":"string"},"type":{"type":"string"}}}}}},"StreamList":{"type":"object","required":["list"],"properties":{"list":{"type":"array","items":{"type":"object","required":["name","stream_type"],"properties":{"name":{"type":"string"},"stream_type":{"type":"string"},"storage_type":{"type":"string"},"stats":{"type":"object"}}}}}},"StreamSchema":{"type":"object","required":["name","stream_type","schema"],"properties":{"name":{"type":"string"},"stream_type":{"type":"string"},"storage_type":{"type":"string"},"stats":{"type":"object"},"schema":{"type":"array","items":{"type":"object","required":["name","type"],"properties":{"name":{"type":"string"},"type":{"type":"string","enum":["Utf8","Int64","Float64","Boolean"]}}}},"settings":{"type":"object"}}},"SearchResponse":{"type":"object","required":["took","hits","total","from","size"],"properties":{"took":{"type":"integer","x-mockingbird-volatile":{"kind":"opaque"}},"hits":{"type":"array","items":{"type":"object"}},"total":{"type":"integer"},"from":{"type":"integer"},"size":{"type":"integer"},"scan_size":{"type":"integer"},"scan_records":{"type":"integer"},"is_partial":{"type":"boolean"},"cached_ratio":{"type":"integer"}}}}}}`);
1937
+ var operationIds = ["ExportTraces", "ExportLogs", "ExportMetrics", "ListOrganizations", "ListStreams", "GetStreamSchema", "Search"];
1938
+ var supportedOperationIds = ["ExportTraces", "ExportLogs", "ExportMetrics", "ListOrganizations", "ListStreams", "GetStreamSchema", "Search"];
1939
+
1940
+ // src/otlp.ts
1941
+ var isRecord4 = (value) => typeof value === "object" && value !== null && !Array.isArray(value);
1942
+ var pick = (object, camel) => {
1943
+ if (object[camel] !== void 0) return object[camel];
1944
+ const snake = camel.replace(/[A-Z]/g, (c) => `_${c.toLowerCase()}`);
1945
+ return object[snake];
1946
+ };
1947
+ var list = (object, camel) => {
1948
+ const value = pick(object, camel);
1949
+ return Array.isArray(value) ? value.filter(isRecord4) : [];
1950
+ };
1951
+ var formatKey = (key) => key.toLowerCase().replace(/[^a-z0-9_]/g, "_");
1952
+ var nanos = (value) => {
1953
+ try {
1954
+ if (typeof value === "string" && /^\d+$/.test(value)) return BigInt(value);
1955
+ if (typeof value === "number" && Number.isFinite(value)) return BigInt(Math.trunc(value));
1956
+ if (isRecord4(value) && typeof value.low === "number" && typeof value.high === "number") {
1957
+ return BigInt(value.high >>> 0) << 32n | BigInt(value.low >>> 0);
1958
+ }
1959
+ } catch {
1960
+ }
1961
+ return 0n;
1962
+ };
1963
+ var id = (value, bytes) => {
1964
+ if (typeof value !== "string" || value === "") return void 0;
1965
+ let hex2 = value.toLowerCase();
1966
+ if (!new RegExp(`^[0-9a-f]{${bytes * 2}}$`).test(hex2)) {
1967
+ try {
1968
+ hex2 = Array.from(atob(value), (c) => c.charCodeAt(0).toString(16).padStart(2, "0")).join("");
1969
+ } catch {
1970
+ return void 0;
1971
+ }
1972
+ }
1973
+ return /^0+$/.test(hex2) ? void 0 : hex2;
1974
+ };
1975
+ var anyValue = (value) => {
1976
+ if (!isRecord4(value)) return null;
1977
+ const string = pick(value, "stringValue");
1978
+ if (typeof string === "string") return string;
1979
+ const bool = pick(value, "boolValue");
1980
+ if (typeof bool === "boolean") return bool;
1981
+ const int = pick(value, "intValue");
1982
+ if (typeof int === "string" || typeof int === "number") return Number(int);
1983
+ const double = pick(value, "doubleValue");
1984
+ if (typeof double === "number") return double;
1985
+ const bytes = pick(value, "bytesValue");
1986
+ if (typeof bytes === "string") return bytes;
1987
+ const array = pick(value, "arrayValue");
1988
+ if (isRecord4(array)) {
1989
+ return JSON.stringify(list(array, "values").map((v) => plain(anyValue(v))));
1990
+ }
1991
+ const kvlist = pick(value, "kvlistValue");
1992
+ if (isRecord4(kvlist)) {
1993
+ const out = {};
1994
+ for (const kv of list(kvlist, "values")) {
1995
+ if (typeof kv.key === "string") out[kv.key] = anyValue(kv.value);
1996
+ }
1997
+ return out;
1998
+ }
1999
+ return null;
2000
+ };
2001
+ var plain = (value) => value !== null && typeof value === "object" ? Object.fromEntries(Object.entries(value).map(([k, v]) => [k, plain(v)])) : value;
2002
+ var flattenInto = (row, attributes, prefix = "") => {
2003
+ const put2 = (key, value) => {
2004
+ if (value === null) return;
2005
+ if (typeof value === "object") {
2006
+ for (const [k, v] of Object.entries(value)) put2(`${key}_${formatKey(k)}`, v);
2007
+ return;
2008
+ }
2009
+ row[key] = value;
2010
+ };
2011
+ for (const kv of attributes) {
2012
+ if (typeof kv.key !== "string" || kv.key === "") continue;
2013
+ put2(`${prefix}${formatKey(kv.key)}`, anyValue(kv.value));
2014
+ }
2015
+ };
2016
+ var resourceColumns = (group) => {
2017
+ const resource = pick(group, "resource");
2018
+ const kvs = isRecord4(resource) ? list(resource, "attributes") : [];
2019
+ const attributes = {};
2020
+ const columns = {};
2021
+ for (const kv of kvs) {
2022
+ if (typeof kv.key !== "string") continue;
2023
+ const value = anyValue(kv.value);
2024
+ if (value !== null && typeof value !== "object") attributes[kv.key] = value;
2025
+ if (kv.key === "service.name") {
2026
+ if (value !== null && typeof value !== "object") columns.service_name = value;
2027
+ } else flattenInto(columns, [kv], "service_");
2028
+ }
2029
+ return { attributes, columns };
2030
+ };
2031
+ var bodyText = (value) => {
2032
+ if (value === null) return null;
2033
+ if (typeof value === "string") return value;
2034
+ if (typeof value === "object") return JSON.stringify(plain(value));
2035
+ return String(value);
2036
+ };
2037
+ var logRows = (request, options) => {
2038
+ const out = [];
2039
+ for (const group of list(request, "resourceLogs")) {
2040
+ const { attributes, columns } = resourceColumns(group);
2041
+ for (const scoped of list(group, "scopeLogs")) {
2042
+ for (const record of list(scoped, "logRecords")) {
2043
+ const row = { ...columns };
2044
+ flattenInto(row, list(record, "attributes"));
2045
+ const time = nanos(pick(record, "timeUnixNano")) || nanos(pick(record, "observedTimeUnixNano"));
2046
+ row._timestamp = time > 0n ? Number(time / 1000n) : options.nowMs * 1e3;
2047
+ const severityText = pick(record, "severityText");
2048
+ if (typeof severityText === "string" && severityText !== "") {
2049
+ row.severity_text = severityText;
2050
+ }
2051
+ const severityNumber = Number(pick(record, "severityNumber") ?? 0);
2052
+ if (Number.isFinite(severityNumber) && severityNumber > 0) {
2053
+ row.severity_number = severityNumber;
2054
+ }
2055
+ const traceId = id(pick(record, "traceId"), 16);
2056
+ if (traceId) row.trace_id = traceId;
2057
+ const spanId = id(pick(record, "spanId"), 8);
2058
+ if (spanId) row.span_id = spanId;
2059
+ const eventName = pick(record, "eventName");
2060
+ if (typeof eventName === "string" && eventName !== "") row.event_name = eventName;
2061
+ const body = bodyText(anyValue(pick(record, "body")));
2062
+ const hadBody = body !== null && body !== "";
2063
+ if (hadBody && options.keepBodies) row.body = body;
2064
+ out.push({ resource: attributes, row, hadBody });
2065
+ }
2066
+ }
2067
+ }
2068
+ return out;
2069
+ };
2070
+ var STATUS = ["UNSET", "OK", "ERROR"];
2071
+ var spanRows = (request, options) => {
2072
+ const out = [];
2073
+ for (const group of list(request, "resourceSpans")) {
2074
+ const { attributes, columns } = resourceColumns(group);
2075
+ for (const scoped of list(group, "scopeSpans")) {
2076
+ for (const span of list(scoped, "spans")) {
2077
+ const row = { ...columns };
2078
+ flattenInto(row, list(span, "attributes"));
2079
+ const start = nanos(pick(span, "startTimeUnixNano"));
2080
+ const end = nanos(pick(span, "endTimeUnixNano"));
2081
+ const startNs = start > 0n ? start : BigInt(options.nowMs) * 1000000n;
2082
+ const endNs = end > 0n ? end : startNs;
2083
+ row.start_time = Number(startNs);
2084
+ row.end_time = Number(endNs);
2085
+ row.duration = Number((endNs - startNs) / 1000n);
2086
+ row._timestamp = Number(startNs / 1000n);
2087
+ const traceId = id(pick(span, "traceId"), 16);
2088
+ if (traceId) row.trace_id = traceId;
2089
+ const spanId = id(pick(span, "spanId"), 8);
2090
+ if (spanId) row.span_id = spanId;
2091
+ const parent = id(pick(span, "parentSpanId"), 8);
2092
+ if (parent) {
2093
+ row.reference_parent_span_id = parent;
2094
+ row.reference_ref_type = "ChildOf";
2095
+ }
2096
+ const name = pick(span, "name");
2097
+ if (typeof name === "string") row.operation_name = name;
2098
+ row.span_kind = String(Number(pick(span, "kind") ?? 0));
2099
+ const status = pick(span, "status");
2100
+ const code = isRecord4(status) ? Number(pick(status, "code") ?? 0) : 0;
2101
+ row.span_status = STATUS[code] ?? "UNSET";
2102
+ const events = list(span, "events").map((event) => {
2103
+ const flat = {};
2104
+ flattenInto(flat, list(event, "attributes"));
2105
+ return {
2106
+ name: typeof event.name === "string" ? event.name : "",
2107
+ _timestamp: Number(nanos(pick(event, "timeUnixNano")) / 1000n),
2108
+ ...flat
2109
+ };
2110
+ });
2111
+ row.events = JSON.stringify(events);
2112
+ const scope = pick(scoped, "scope");
2113
+ if (isRecord4(scope) && typeof scope.name === "string" && scope.name !== "") {
2114
+ row.instrumentation_library_name = scope.name;
2115
+ }
2116
+ out.push({ resource: attributes, row, hadBody: false });
2117
+ }
2118
+ }
2119
+ }
2120
+ return out;
2121
+ };
2122
+ var metricCount = (request) => list(request, "resourceMetrics").reduce(
2123
+ (total, group) => total + list(group, "scopeMetrics").reduce((sum, scoped) => sum + list(scoped, "metrics").length, 0),
2124
+ 0
2125
+ );
2126
+
2127
+ // src/protobuf.ts
2128
+ var ProtobufError = class extends Error {
2129
+ constructor(message) {
2130
+ super(`invalid protobuf: ${message}`);
2131
+ this.name = "ProtobufError";
2132
+ }
2133
+ };
2134
+ var utf82 = new TextDecoder("utf-8", { fatal: false });
2135
+ var Reader = class {
2136
+ constructor(buf) {
2137
+ this.buf = buf;
2138
+ }
2139
+ buf;
2140
+ pos = 0;
2141
+ get done() {
2142
+ return this.pos >= this.buf.length;
2143
+ }
2144
+ varint() {
2145
+ let result = 0n;
2146
+ let shift = 0n;
2147
+ for (let i = 0; i < 10; i++) {
2148
+ if (this.pos >= this.buf.length) throw new ProtobufError("truncated varint");
2149
+ const byte = this.buf[this.pos++];
2150
+ result |= BigInt(byte & 127) << shift;
2151
+ if ((byte & 128) === 0) return result;
2152
+ shift += 7n;
2153
+ }
2154
+ throw new ProtobufError("varint longer than 10 bytes");
2155
+ }
2156
+ fixed64() {
2157
+ const bytes = this.take(8);
2158
+ let result = 0n;
2159
+ for (let i = 7; i >= 0; i--) result = result << 8n | BigInt(bytes[i]);
2160
+ return result;
2161
+ }
2162
+ fixed32() {
2163
+ const bytes = this.take(4);
2164
+ return new DataView(bytes.buffer, bytes.byteOffset, 4).getUint32(0, true);
2165
+ }
2166
+ double() {
2167
+ const bytes = this.take(8);
2168
+ return new DataView(bytes.buffer, bytes.byteOffset, 8).getFloat64(0, true);
2169
+ }
2170
+ bytes() {
2171
+ return this.take(Number(this.varint()));
2172
+ }
2173
+ take(length) {
2174
+ if (length < 0 || this.pos + length > this.buf.length) {
2175
+ throw new ProtobufError("length-delimited field runs past the end");
2176
+ }
2177
+ const out = this.buf.subarray(this.pos, this.pos + length);
2178
+ this.pos += length;
2179
+ return out;
2180
+ }
2181
+ skip(wireType) {
2182
+ if (wireType === 0) this.varint();
2183
+ else if (wireType === 1) this.take(8);
2184
+ else if (wireType === 2) this.bytes();
2185
+ else if (wireType === 5) this.take(4);
2186
+ else throw new ProtobufError(`unsupported wire type ${wireType}`);
2187
+ }
2188
+ };
2189
+ var walk = (bytes, handler) => {
2190
+ const reader = new Reader(bytes);
2191
+ while (!reader.done) {
2192
+ const key = Number(reader.varint());
2193
+ const field = key >>> 3;
2194
+ const wireType = key & 7;
2195
+ if (field === 0) throw new ProtobufError("field number 0");
2196
+ if (!handler(field, wireType, reader)) reader.skip(wireType);
2197
+ }
2198
+ };
2199
+ var hex = (bytes) => Array.from(bytes, (b) => b.toString(16).padStart(2, "0")).join("");
2200
+ var base64 = (bytes) => {
2201
+ let binary = "";
2202
+ for (const byte of bytes) binary += String.fromCharCode(byte);
2203
+ return btoa(binary);
2204
+ };
2205
+ var int64 = (value) => BigInt.asIntN(64, value).toString();
2206
+ var decodeAnyValue = (bytes) => {
2207
+ let out = {};
2208
+ walk(bytes, (field, wire, r) => {
2209
+ if (field === 1 && wire === 2) out = { stringValue: utf82.decode(r.bytes()) };
2210
+ else if (field === 2 && wire === 0) out = { boolValue: r.varint() !== 0n };
2211
+ else if (field === 3 && wire === 0) out = { intValue: int64(r.varint()) };
2212
+ else if (field === 4 && wire === 1) out = { doubleValue: r.double() };
2213
+ else if (field === 5 && wire === 2) out = { arrayValue: { values: decodeValues(r.bytes()) } };
2214
+ else if (field === 6 && wire === 2) {
2215
+ out = { kvlistValue: { values: decodeKeyValues(r.bytes()) } };
2216
+ } else if (field === 7 && wire === 2) out = { bytesValue: base64(r.bytes()) };
2217
+ else return false;
2218
+ return true;
2219
+ });
2220
+ return out;
2221
+ };
2222
+ var decodeValues = (bytes) => {
2223
+ const values = [];
2224
+ walk(bytes, (field, wire, r) => {
2225
+ if (field !== 1 || wire !== 2) return false;
2226
+ values.push(decodeAnyValue(r.bytes()));
2227
+ return true;
2228
+ });
2229
+ return values;
2230
+ };
2231
+ var decodeKeyValues = (bytes) => {
2232
+ const values = [];
2233
+ walk(bytes, (field, wire, r) => {
2234
+ if (field !== 1 || wire !== 2) return false;
2235
+ values.push(decodeKeyValue(r.bytes()));
2236
+ return true;
2237
+ });
2238
+ return values;
2239
+ };
2240
+ var decodeKeyValue = (bytes) => {
2241
+ const kv = { key: "" };
2242
+ walk(bytes, (field, wire, r) => {
2243
+ if (field === 1 && wire === 2) kv.key = utf82.decode(r.bytes());
2244
+ else if (field === 2 && wire === 2) kv.value = decodeAnyValue(r.bytes());
2245
+ else return false;
2246
+ return true;
2247
+ });
2248
+ return kv;
2249
+ };
2250
+ var decodeResource = (bytes) => {
2251
+ const attributes = [];
2252
+ walk(bytes, (field, wire, r) => {
2253
+ if (field !== 1 || wire !== 2) return false;
2254
+ attributes.push(decodeKeyValue(r.bytes()));
2255
+ return true;
2256
+ });
2257
+ return { attributes };
2258
+ };
2259
+ var decodeScope = (bytes) => {
2260
+ const scope = {};
2261
+ walk(bytes, (field, wire, r) => {
2262
+ if (field === 1 && wire === 2) scope.name = utf82.decode(r.bytes());
2263
+ else if (field === 2 && wire === 2) scope.version = utf82.decode(r.bytes());
2264
+ else return false;
2265
+ return true;
2266
+ });
2267
+ return scope;
2268
+ };
2269
+ var decodeEvent = (bytes) => {
2270
+ const event = { attributes: [] };
2271
+ walk(bytes, (field, wire, r) => {
2272
+ if (field === 1 && wire === 1) event.timeUnixNano = r.fixed64().toString();
2273
+ else if (field === 2 && wire === 2) event.name = utf82.decode(r.bytes());
2274
+ else if (field === 3 && wire === 2) {
2275
+ ;
2276
+ event.attributes.push(decodeKeyValue(r.bytes()));
2277
+ } else return false;
2278
+ return true;
2279
+ });
2280
+ return event;
2281
+ };
2282
+ var decodeStatus = (bytes) => {
2283
+ const status = {};
2284
+ walk(bytes, (field, wire, r) => {
2285
+ if (field === 2 && wire === 2) status.message = utf82.decode(r.bytes());
2286
+ else if (field === 3 && wire === 0) status.code = Number(r.varint());
2287
+ else return false;
2288
+ return true;
2289
+ });
2290
+ return status;
2291
+ };
2292
+ var decodeSpan = (bytes) => {
2293
+ const span = { attributes: [], events: [] };
2294
+ walk(bytes, (field, wire, r) => {
2295
+ if (field === 1 && wire === 2) span.traceId = hex(r.bytes());
2296
+ else if (field === 2 && wire === 2) span.spanId = hex(r.bytes());
2297
+ else if (field === 3 && wire === 2) span.traceState = utf82.decode(r.bytes());
2298
+ else if (field === 4 && wire === 2) span.parentSpanId = hex(r.bytes());
2299
+ else if (field === 5 && wire === 2) span.name = utf82.decode(r.bytes());
2300
+ else if (field === 6 && wire === 0) span.kind = Number(r.varint());
2301
+ else if (field === 7 && wire === 1) span.startTimeUnixNano = r.fixed64().toString();
2302
+ else if (field === 8 && wire === 1) span.endTimeUnixNano = r.fixed64().toString();
2303
+ else if (field === 9 && wire === 2) {
2304
+ ;
2305
+ span.attributes.push(decodeKeyValue(r.bytes()));
2306
+ } else if (field === 11 && wire === 2) span.events.push(decodeEvent(r.bytes()));
2307
+ else if (field === 15 && wire === 2) span.status = decodeStatus(r.bytes());
2308
+ else if (field === 16 && wire === 5) span.flags = r.fixed32();
2309
+ else return false;
2310
+ return true;
2311
+ });
2312
+ return span;
2313
+ };
2314
+ var decodeLogRecord = (bytes) => {
2315
+ const log = { attributes: [] };
2316
+ walk(bytes, (field, wire, r) => {
2317
+ if (field === 1 && wire === 1) log.timeUnixNano = r.fixed64().toString();
2318
+ else if (field === 2 && wire === 0) log.severityNumber = Number(r.varint());
2319
+ else if (field === 3 && wire === 2) log.severityText = utf82.decode(r.bytes());
2320
+ else if (field === 5 && wire === 2) log.body = decodeAnyValue(r.bytes());
2321
+ else if (field === 6 && wire === 2) {
2322
+ ;
2323
+ log.attributes.push(decodeKeyValue(r.bytes()));
2324
+ } else if (field === 8 && wire === 5) log.flags = r.fixed32();
2325
+ else if (field === 9 && wire === 2) log.traceId = hex(r.bytes());
2326
+ else if (field === 10 && wire === 2) log.spanId = hex(r.bytes());
2327
+ else if (field === 11 && wire === 1) log.observedTimeUnixNano = r.fixed64().toString();
2328
+ else if (field === 12 && wire === 2) log.eventName = utf82.decode(r.bytes());
2329
+ else return false;
2330
+ return true;
2331
+ });
2332
+ return log;
2333
+ };
2334
+ var decodeResourceGroup = (bytes, scopeKey, itemsKey, decodeItem) => {
2335
+ const group = { [scopeKey]: [] };
2336
+ walk(bytes, (field, wire, r) => {
2337
+ if (field === 1 && wire === 2) group.resource = decodeResource(r.bytes());
2338
+ else if (field === 2 && wire === 2) {
2339
+ const scoped = { [itemsKey]: [] };
2340
+ walk(r.bytes(), (f, w, inner) => {
2341
+ if (f === 1 && w === 2) scoped.scope = decodeScope(inner.bytes());
2342
+ else if (f === 2 && w === 2) scoped[itemsKey].push(decodeItem(inner.bytes()));
2343
+ else return false;
2344
+ return true;
2345
+ });
2346
+ group[scopeKey].push(scoped);
2347
+ } else return false;
2348
+ return true;
2349
+ });
2350
+ return group;
2351
+ };
2352
+ var decodeTraceRequest = (bytes) => {
2353
+ const resourceSpans = [];
2354
+ walk(bytes, (field, wire, r) => {
2355
+ if (field !== 1 || wire !== 2) return false;
2356
+ resourceSpans.push(decodeResourceGroup(r.bytes(), "scopeSpans", "spans", decodeSpan));
2357
+ return true;
2358
+ });
2359
+ return { resourceSpans };
2360
+ };
2361
+ var decodeLogsRequest = (bytes) => {
2362
+ const resourceLogs = [];
2363
+ walk(bytes, (field, wire, r) => {
2364
+ if (field !== 1 || wire !== 2) return false;
2365
+ resourceLogs.push(decodeResourceGroup(r.bytes(), "scopeLogs", "logRecords", decodeLogRecord));
2366
+ return true;
2367
+ });
2368
+ return { resourceLogs };
2369
+ };
2370
+ var countMetricsRequest = (bytes) => {
2371
+ let count = 0;
2372
+ walk(bytes, (field, wire, r) => {
2373
+ if (field !== 1 || wire !== 2) return false;
2374
+ r.bytes();
2375
+ count++;
2376
+ return true;
2377
+ });
2378
+ return count;
2379
+ };
2380
+
2381
+ // src/sql.ts
2382
+ var SqlError = class extends Error {
2383
+ constructor(message, kind = "syntax") {
2384
+ super(message);
2385
+ this.kind = kind;
2386
+ this.name = "SqlError";
2387
+ }
2388
+ kind;
2389
+ };
2390
+ var tokenize = (sql) => {
2391
+ const tokens = [];
2392
+ let i = 0;
2393
+ while (i < sql.length) {
2394
+ const c = sql[i];
2395
+ if (/\s/.test(c)) {
2396
+ i++;
2397
+ continue;
2398
+ }
2399
+ if (c === "-" && sql[i + 1] === "-") {
2400
+ while (i < sql.length && sql[i] !== "\n") i++;
2401
+ continue;
2402
+ }
2403
+ if (c === "'") {
2404
+ let value = "";
2405
+ i++;
2406
+ for (; ; ) {
2407
+ if (i >= sql.length) throw new SqlError("unterminated string literal");
2408
+ if (sql[i] === "'") {
2409
+ if (sql[i + 1] === "'") {
2410
+ value += "'";
2411
+ i += 2;
2412
+ continue;
2413
+ }
2414
+ i++;
2415
+ break;
2416
+ }
2417
+ value += sql[i++];
2418
+ }
2419
+ tokens.push({ t: "str", v: value });
2420
+ continue;
2421
+ }
2422
+ if (c === '"') {
2423
+ const end = sql.indexOf('"', i + 1);
2424
+ if (end < 0) throw new SqlError("unterminated quoted identifier");
2425
+ tokens.push({ t: "ident", v: sql.slice(i + 1, end), quoted: true });
2426
+ i = end + 1;
2427
+ continue;
2428
+ }
2429
+ const number = /^\d+(\.\d+)?/.exec(sql.slice(i));
2430
+ if (number) {
2431
+ tokens.push({ t: "num", v: Number(number[0]) });
2432
+ i += number[0].length;
2433
+ continue;
2434
+ }
2435
+ const word = /^[A-Za-z_][A-Za-z0-9_]*/.exec(sql.slice(i));
2436
+ if (word) {
2437
+ tokens.push({ t: "ident", v: word[0], quoted: false });
2438
+ i += word[0].length;
2439
+ continue;
2440
+ }
2441
+ const op = /^(<=|>=|<>|!=|=|<|>|\(|\)|,|\*|;|\+|-)/.exec(sql.slice(i));
2442
+ if (op) {
2443
+ tokens.push({ t: "op", v: op[0] });
2444
+ i += op[0].length;
2445
+ continue;
2446
+ }
2447
+ throw new SqlError(`unexpected character ${JSON.stringify(c)}`);
2448
+ }
2449
+ return tokens;
2450
+ };
2451
+ var KEYWORDS = /* @__PURE__ */ new Set([
2452
+ "select",
2453
+ "from",
2454
+ "where",
2455
+ "group",
2456
+ "by",
2457
+ "having",
2458
+ "order",
2459
+ "limit",
2460
+ "offset",
2461
+ "and",
2462
+ "or",
2463
+ "not",
2464
+ "is",
2465
+ "null",
2466
+ "in",
2467
+ "like",
2468
+ "ilike",
2469
+ "between",
2470
+ "as",
2471
+ "asc",
2472
+ "desc",
2473
+ "distinct",
2474
+ "true",
2475
+ "false"
2476
+ ]);
2477
+ var AGGREGATES = /* @__PURE__ */ new Set(["count", "min", "max", "sum", "avg"]);
2478
+ var FUNCTIONS = /* @__PURE__ */ new Set([
2479
+ ...AGGREGATES,
2480
+ "str_match",
2481
+ "str_match_ignore_case",
2482
+ "match_all",
2483
+ "match_all_ignore_case",
2484
+ "re_match",
2485
+ "lower",
2486
+ "upper",
2487
+ "tostring",
2488
+ "length",
2489
+ "coalesce"
2490
+ ]);
2491
+ var Parser = class {
2492
+ constructor(tokens) {
2493
+ this.tokens = tokens;
2494
+ }
2495
+ tokens;
2496
+ pos = 0;
2497
+ peek(offset = 0) {
2498
+ return this.tokens[this.pos + offset];
2499
+ }
2500
+ isKeyword(word, offset = 0) {
2501
+ const token = this.peek(offset);
2502
+ return token?.t === "ident" && !token.quoted && token.v.toLowerCase() === word;
2503
+ }
2504
+ isOp(op) {
2505
+ const token = this.peek();
2506
+ return token?.t === "op" && token.v === op;
2507
+ }
2508
+ keyword(word) {
2509
+ if (!this.isKeyword(word)) throw new SqlError(`expected ${word.toUpperCase()}`);
2510
+ this.pos++;
2511
+ }
2512
+ op(op) {
2513
+ if (!this.isOp(op)) throw new SqlError(`expected ${op}`);
2514
+ this.pos++;
2515
+ }
2516
+ accept(word) {
2517
+ if (!this.isKeyword(word)) return false;
2518
+ this.pos++;
2519
+ return true;
2520
+ }
2521
+ parse() {
2522
+ this.keyword("select");
2523
+ this.accept("distinct");
2524
+ let star = false;
2525
+ const items = [];
2526
+ do {
2527
+ if (this.isOp("*")) {
2528
+ this.pos++;
2529
+ star = true;
2530
+ continue;
2531
+ }
2532
+ const start = this.pos;
2533
+ const expr = this.expr();
2534
+ const end = this.pos;
2535
+ let alias;
2536
+ if (this.accept("as")) alias = this.identifier();
2537
+ else {
2538
+ const next = this.peek();
2539
+ if (next?.t === "ident" && (next.quoted || !KEYWORDS.has(next.v.toLowerCase()))) {
2540
+ alias = this.identifier();
2541
+ }
2542
+ }
2543
+ items.push({ expr, alias, text: this.text(start, end) });
2544
+ } while (this.isOp(",") && ++this.pos);
2545
+ this.keyword("from");
2546
+ const stream = this.identifier();
2547
+ let where;
2548
+ if (this.accept("where")) where = this.expr();
2549
+ const groupBy = [];
2550
+ if (this.accept("group")) {
2551
+ this.keyword("by");
2552
+ do
2553
+ groupBy.push(this.expr());
2554
+ while (this.isOp(",") && ++this.pos);
2555
+ }
2556
+ let having;
2557
+ if (this.accept("having")) having = this.expr();
2558
+ const orderBy = [];
2559
+ if (this.accept("order")) {
2560
+ this.keyword("by");
2561
+ do {
2562
+ const expr = this.expr();
2563
+ const desc = this.accept("desc");
2564
+ if (!desc) this.accept("asc");
2565
+ orderBy.push({ expr, desc });
2566
+ } while (this.isOp(",") && ++this.pos);
2567
+ }
2568
+ let limit;
2569
+ let offset = 0;
2570
+ if (this.accept("limit")) limit = this.integer();
2571
+ if (this.accept("offset")) offset = this.integer();
2572
+ if (this.isOp(";")) this.pos++;
2573
+ if (this.pos < this.tokens.length) throw new SqlError("unexpected tokens after the query");
2574
+ return { star, items, stream, where, groupBy, having, orderBy, limit, offset };
2575
+ }
2576
+ text(from, to) {
2577
+ return this.tokens.slice(from, to).map((t) => t.t === "str" ? `'${t.v}'` : String(t.v)).join("").toLowerCase();
2578
+ }
2579
+ integer() {
2580
+ const token = this.peek();
2581
+ if (token?.t !== "num" || !Number.isInteger(token.v)) throw new SqlError("expected an integer");
2582
+ this.pos++;
2583
+ return token.v;
2584
+ }
2585
+ identifier() {
2586
+ const token = this.peek();
2587
+ if (token?.t !== "ident") throw new SqlError("expected an identifier");
2588
+ this.pos++;
2589
+ return token.v;
2590
+ }
2591
+ expr() {
2592
+ let left = this.and();
2593
+ while (this.accept("or")) left = { k: "or", l: left, r: this.and() };
2594
+ return left;
2595
+ }
2596
+ and() {
2597
+ let left = this.not();
2598
+ while (this.accept("and")) left = { k: "and", l: left, r: this.not() };
2599
+ return left;
2600
+ }
2601
+ not() {
2602
+ if (this.accept("not")) return { k: "not", e: this.not() };
2603
+ return this.predicate();
2604
+ }
2605
+ predicate() {
2606
+ const e = this.primary();
2607
+ if (this.accept("is")) {
2608
+ const not2 = this.accept("not");
2609
+ this.keyword("null");
2610
+ return { k: "null", e, not: not2 };
2611
+ }
2612
+ const not = this.isKeyword("not") && (this.isKeyword("in", 1) || this.isKeyword("like", 1) || this.isKeyword("ilike", 1) || this.isKeyword("between", 1));
2613
+ if (not) this.pos++;
2614
+ if (this.accept("in")) {
2615
+ this.op("(");
2616
+ const list2 = [];
2617
+ do
2618
+ list2.push(this.primary());
2619
+ while (this.isOp(",") && ++this.pos);
2620
+ this.op(")");
2621
+ return { k: "in", e, list: list2, not };
2622
+ }
2623
+ if (this.isKeyword("like") || this.isKeyword("ilike")) {
2624
+ const ci = this.isKeyword("ilike");
2625
+ this.pos++;
2626
+ return { k: "like", e, pattern: this.primary(), not, ci };
2627
+ }
2628
+ if (this.accept("between")) {
2629
+ const lo = this.primary();
2630
+ this.keyword("and");
2631
+ return { k: "between", e, lo, hi: this.primary(), not };
2632
+ }
2633
+ if (not) throw new SqlError("expected IN, LIKE or BETWEEN after NOT");
2634
+ const token = this.peek();
2635
+ if (token?.t === "op" && ["=", "!=", "<>", "<", "<=", ">", ">="].includes(token.v)) {
2636
+ this.pos++;
2637
+ return { k: "cmp", op: token.v === "<>" ? "!=" : token.v, l: e, r: this.primary() };
2638
+ }
2639
+ return e;
2640
+ }
2641
+ primary() {
2642
+ const token = this.peek();
2643
+ if (!token) throw new SqlError("unexpected end of query");
2644
+ if (token.t === "str") {
2645
+ this.pos++;
2646
+ return { k: "lit", v: token.v };
2647
+ }
2648
+ if (token.t === "num") {
2649
+ this.pos++;
2650
+ return { k: "lit", v: token.v };
2651
+ }
2652
+ if (token.t === "op" && token.v === "-") {
2653
+ this.pos++;
2654
+ const next = this.peek();
2655
+ if (next?.t !== "num") throw new SqlError("expected a number after -");
2656
+ this.pos++;
2657
+ return { k: "lit", v: -next.v };
2658
+ }
2659
+ if (token.t === "op" && token.v === "(") {
2660
+ this.pos++;
2661
+ const inner = this.expr();
2662
+ this.op(")");
2663
+ return inner;
2664
+ }
2665
+ if (token.t === "op" && token.v === "*") {
2666
+ this.pos++;
2667
+ return { k: "star" };
2668
+ }
2669
+ if (token.t === "ident") {
2670
+ this.pos++;
2671
+ if (!token.quoted) {
2672
+ const lower = token.v.toLowerCase();
2673
+ if (lower === "null") return { k: "lit", v: null };
2674
+ if (lower === "true" || lower === "false") return { k: "lit", v: lower === "true" };
2675
+ if (this.isOp("(")) {
2676
+ if (!FUNCTIONS.has(lower)) throw new SqlError(`unsupported function ${token.v}`);
2677
+ this.pos++;
2678
+ const distinct = this.accept("distinct");
2679
+ const args = [];
2680
+ if (!this.isOp(")")) {
2681
+ do
2682
+ args.push(this.expr());
2683
+ while (this.isOp(",") && ++this.pos);
2684
+ }
2685
+ this.op(")");
2686
+ return { k: "fn", name: lower, args, distinct };
2687
+ }
2688
+ if (KEYWORDS.has(lower)) throw new SqlError(`unexpected keyword ${token.v.toUpperCase()}`);
2689
+ }
2690
+ return { k: "col", name: token.v };
2691
+ }
2692
+ throw new SqlError(`unexpected ${token.v}`);
2693
+ }
2694
+ };
2695
+ var parseSql = (sql) => new Parser(tokenize(sql)).parse();
2696
+ var referencedColumns = (query) => {
2697
+ const aliases = new Set(query.items.map((i) => i.alias).filter(Boolean));
2698
+ const out = /* @__PURE__ */ new Set();
2699
+ const visit = (e, aliasOk) => {
2700
+ if (!e) return;
2701
+ switch (e.k) {
2702
+ case "col":
2703
+ if (!(aliasOk && aliases.has(e.name))) out.add(e.name);
2704
+ return;
2705
+ case "fn":
2706
+ for (const a of e.args) visit(a, aliasOk);
2707
+ return;
2708
+ case "not":
2709
+ case "null":
2710
+ visit(e.e, aliasOk);
2711
+ return;
2712
+ case "and":
2713
+ case "or":
2714
+ case "cmp":
2715
+ visit(e.l, aliasOk);
2716
+ visit(e.r, aliasOk);
2717
+ return;
2718
+ case "in":
2719
+ visit(e.e, aliasOk);
2720
+ for (const x of e.list) visit(x, aliasOk);
2721
+ return;
2722
+ case "like":
2723
+ visit(e.e, aliasOk);
2724
+ visit(e.pattern, aliasOk);
2725
+ return;
2726
+ case "between":
2727
+ visit(e.e, aliasOk);
2728
+ visit(e.lo, aliasOk);
2729
+ visit(e.hi, aliasOk);
2730
+ return;
2731
+ default:
2732
+ return;
2733
+ }
2734
+ };
2735
+ for (const item of query.items) visit(item.expr, false);
2736
+ visit(query.where, false);
2737
+ for (const g of query.groupBy) visit(g, true);
2738
+ visit(query.having, true);
2739
+ for (const o of query.orderBy) visit(o.expr, true);
2740
+ return [...out];
2741
+ };
2742
+ var hasAggregate = (e) => {
2743
+ switch (e.k) {
2744
+ case "fn":
2745
+ return AGGREGATES.has(e.name) || e.args.some(hasAggregate);
2746
+ case "not":
2747
+ case "null":
2748
+ return hasAggregate(e.e);
2749
+ case "and":
2750
+ case "or":
2751
+ case "cmp":
2752
+ return hasAggregate(e.l) || hasAggregate(e.r);
2753
+ default:
2754
+ return false;
2755
+ }
2756
+ };
2757
+ var asMicros = (value) => {
2758
+ if (/^\d+(\.\d+)?$/.test(value)) return Number(value);
2759
+ const ms = Date.parse(value);
2760
+ return Number.isNaN(ms) ? void 0 : ms * 1e3;
2761
+ };
2762
+ var compare = (a, b) => {
2763
+ if (a === null || b === null) return null;
2764
+ if (typeof a === "number" && typeof b === "string") {
2765
+ const n = asMicros(b);
2766
+ return n === void 0 ? String(a).localeCompare(b) : a - n;
2767
+ }
2768
+ if (typeof a === "string" && typeof b === "number") {
2769
+ const n = asMicros(a);
2770
+ return n === void 0 ? a.localeCompare(String(b)) : n - b;
2771
+ }
2772
+ if (typeof a === "number" && typeof b === "number") return a - b;
2773
+ if (typeof a === "boolean" || typeof b === "boolean") return String(a) === String(b) ? 0 : 1;
2774
+ return a < b ? -1 : a > b ? 1 : 0;
2775
+ };
2776
+ var likeRegex = (pattern, ci) => new RegExp(
2777
+ `^${pattern.replace(/[.*+?^${}()|[\]\\]/g, "\\$&").replace(/%/g, ".*").replace(/_/g, ".")}$`,
2778
+ ci ? "is" : "s"
2779
+ );
2780
+ var text = (v) => v === null ? null : String(v);
2781
+ var truthy = (v) => v === true;
2782
+ var evaluate = (e, ctx) => {
2783
+ switch (e.k) {
2784
+ case "lit":
2785
+ return e.v;
2786
+ case "col":
2787
+ if (ctx.aliases && e.name in ctx.aliases) return ctx.aliases[e.name] ?? null;
2788
+ return ctx.row[e.name] ?? null;
2789
+ case "star":
2790
+ return null;
2791
+ case "not": {
2792
+ const v = evaluate(e.e, ctx);
2793
+ return v === null ? null : !truthy(v);
2794
+ }
2795
+ case "and": {
2796
+ const l = evaluate(e.l, ctx);
2797
+ if (l === false) return false;
2798
+ const r = evaluate(e.r, ctx);
2799
+ if (r === false) return false;
2800
+ return l === null || r === null ? null : truthy(l) && truthy(r);
2801
+ }
2802
+ case "or": {
2803
+ const l = evaluate(e.l, ctx);
2804
+ if (truthy(l)) return true;
2805
+ const r = evaluate(e.r, ctx);
2806
+ if (truthy(r)) return true;
2807
+ return l === null || r === null ? null : false;
2808
+ }
2809
+ case "cmp": {
2810
+ const c = compare(evaluate(e.l, ctx), evaluate(e.r, ctx));
2811
+ if (c === null) return null;
2812
+ switch (e.op) {
2813
+ case "=":
2814
+ return c === 0;
2815
+ case "!=":
2816
+ return c !== 0;
2817
+ case "<":
2818
+ return c < 0;
2819
+ case "<=":
2820
+ return c <= 0;
2821
+ case ">":
2822
+ return c > 0;
2823
+ default:
2824
+ return c >= 0;
2825
+ }
2826
+ }
2827
+ case "null": {
2828
+ const isNull = evaluate(e.e, ctx) === null;
2829
+ return e.not ? !isNull : isNull;
2830
+ }
2831
+ case "in": {
2832
+ const v = evaluate(e.e, ctx);
2833
+ if (v === null) return null;
2834
+ const found = e.list.some((x) => compare(v, evaluate(x, ctx)) === 0);
2835
+ return e.not ? !found : found;
2836
+ }
2837
+ case "like": {
2838
+ const v = text(evaluate(e.e, ctx));
2839
+ const p = text(evaluate(e.pattern, ctx));
2840
+ if (v === null || p === null) return null;
2841
+ const matched = likeRegex(p, e.ci).test(v);
2842
+ return e.not ? !matched : matched;
2843
+ }
2844
+ case "between": {
2845
+ const v = evaluate(e.e, ctx);
2846
+ const lo = compare(v, evaluate(e.lo, ctx));
2847
+ const hi = compare(v, evaluate(e.hi, ctx));
2848
+ if (lo === null || hi === null) return null;
2849
+ const inside = lo >= 0 && hi <= 0;
2850
+ return e.not ? !inside : inside;
2851
+ }
2852
+ case "fn":
2853
+ return call(e, ctx);
2854
+ }
2855
+ };
2856
+ var call = (e, ctx) => {
2857
+ if (AGGREGATES.has(e.name)) {
2858
+ const rows = ctx.group ?? [ctx.row];
2859
+ const arg = e.args[0];
2860
+ if (e.name === "count") {
2861
+ if (!arg || arg.k === "star") return rows.length;
2862
+ const values2 = rows.map((row) => evaluate(arg, { row })).filter((v) => v !== null);
2863
+ return e.distinct ? new Set(values2.map((v) => JSON.stringify(v))).size : values2.length;
2864
+ }
2865
+ if (!arg) throw new SqlError(`${e.name}() needs an argument`);
2866
+ const values = rows.map((row) => evaluate(arg, { row })).filter((v) => v !== null);
2867
+ if (values.length === 0) return null;
2868
+ if (e.name === "min" || e.name === "max") {
2869
+ return values.reduce((best, v) => {
2870
+ const c = compare(v, best) ?? 0;
2871
+ return (e.name === "min" ? c < 0 : c > 0) ? v : best;
2872
+ });
2873
+ }
2874
+ const numbers = values.map(Number).filter((n) => Number.isFinite(n));
2875
+ const sum = numbers.reduce((a2, b2) => a2 + b2, 0);
2876
+ return e.name === "sum" ? sum : numbers.length === 0 ? null : sum / numbers.length;
2877
+ }
2878
+ const args = e.args.map((a2) => evaluate(a2, ctx));
2879
+ const [a, b] = args;
2880
+ switch (e.name) {
2881
+ case "str_match":
2882
+ return a === null || b === null ? false : String(a).includes(String(b));
2883
+ case "str_match_ignore_case":
2884
+ return a === null || b === null ? false : String(a).toLowerCase().includes(String(b).toLowerCase());
2885
+ case "match_all":
2886
+ case "match_all_ignore_case": {
2887
+ if (a === null) return false;
2888
+ const needle = String(a).toLowerCase();
2889
+ return Object.values(ctx.row).some(
2890
+ (v) => typeof v === "string" && v.toLowerCase().includes(needle)
2891
+ );
2892
+ }
2893
+ case "re_match":
2894
+ if (a === null || b === null) return false;
2895
+ try {
2896
+ return new RegExp(String(b)).test(String(a));
2897
+ } catch {
2898
+ throw new SqlError(`invalid regular expression ${String(b)}`);
2899
+ }
2900
+ case "lower":
2901
+ return a === null || a === void 0 ? null : String(a).toLowerCase();
2902
+ case "upper":
2903
+ return a === null || a === void 0 ? null : String(a).toUpperCase();
2904
+ case "tostring":
2905
+ return a === null || a === void 0 ? null : String(a);
2906
+ case "length":
2907
+ return a === null || a === void 0 ? null : [...String(a)].length;
2908
+ case "coalesce":
2909
+ return args.find((v) => v !== null) ?? null;
2910
+ default:
2911
+ throw new SqlError(`unsupported function ${e.name}`);
2912
+ }
2913
+ };
2914
+ var columnName = (item) => item.alias ?? (item.expr.k === "col" ? item.expr.name : item.text);
2915
+ var project = (query, ctx) => {
2916
+ const out = {};
2917
+ if (query.star) Object.assign(out, ctx.row);
2918
+ for (const item of query.items) out[columnName(item)] = evaluate(item.expr, ctx);
2919
+ return out;
2920
+ };
2921
+ var sortBy = (items, keys) => [...items].sort((x, y) => {
2922
+ for (const key of keys) {
2923
+ const a = key.value(x);
2924
+ const b = key.value(y);
2925
+ if (a === null && b === null) continue;
2926
+ if (a === null) return 1;
2927
+ if (b === null) return -1;
2928
+ const c = compare(a, b) ?? 0;
2929
+ if (c !== 0) return key.desc ? -c : c;
2930
+ }
2931
+ return 0;
2932
+ });
2933
+ var compact = (row) => {
2934
+ const out = {};
2935
+ for (const [key, value] of Object.entries(row)) if (value !== null) out[key] = value;
2936
+ return out;
2937
+ };
2938
+ var execute = (query, rows) => {
2939
+ const where = query.where;
2940
+ const matched = where ? rows.filter((row) => truthy(evaluate(where, { row }))) : rows;
2941
+ const aggregated = query.groupBy.length > 0 || query.items.some((item) => hasAggregate(item.expr));
2942
+ let output;
2943
+ if (aggregated) {
2944
+ if (query.star) throw new SqlError("SELECT * cannot be combined with GROUP BY or aggregates");
2945
+ const groups = /* @__PURE__ */ new Map();
2946
+ if (query.groupBy.length === 0) groups.set("", matched);
2947
+ for (const row of query.groupBy.length > 0 ? matched : []) {
2948
+ const key = JSON.stringify(query.groupBy.map((g) => evaluate(g, { row })));
2949
+ groups.set(key, [...groups.get(key) ?? [], row]);
2950
+ }
2951
+ output = [];
2952
+ for (const group of groups.values()) {
2953
+ const ctx = { row: group[0] ?? {}, group };
2954
+ const projected = project(query, ctx);
2955
+ const having = query.having;
2956
+ if (having && !truthy(evaluate(having, { ...ctx, aliases: projected }))) continue;
2957
+ output.push(projected);
2958
+ }
2959
+ output = sortBy(
2960
+ output,
2961
+ query.orderBy.map((o) => ({
2962
+ desc: o.desc,
2963
+ value: (r) => evaluate(o.expr, { row: {}, aliases: r })
2964
+ }))
2965
+ );
2966
+ } else {
2967
+ const aliasExprs = new Map(
2968
+ query.items.filter((i) => i.alias).map((i) => [i.alias, i.expr])
2969
+ );
2970
+ const order = query.orderBy.length > 0 ? query.orderBy : [{ expr: { k: "col", name: "_timestamp" }, desc: true }];
2971
+ const sorted = sortBy(
2972
+ matched,
2973
+ order.map((o) => ({
2974
+ desc: o.desc,
2975
+ value: (row) => evaluate(
2976
+ o.expr.k === "col" && aliasExprs.has(o.expr.name) ? aliasExprs.get(o.expr.name) : o.expr,
2977
+ { row }
2978
+ )
2979
+ }))
2980
+ );
2981
+ output = sorted.map((row) => project(query, { row }));
2982
+ }
2983
+ const start = query.offset;
2984
+ const end = query.limit === void 0 ? void 0 : start + query.limit;
2985
+ return output.slice(start, end).map(compact);
2986
+ };
2987
+
2988
+ // src/state.ts
2989
+ var DEFAULT_ORGANIZATIONS = [
2990
+ { identifier: "default", name: "default" },
2991
+ { identifier: "30rBqcDevOrg7Hn2KmQ4xW9sLtY", name: "development" },
2992
+ { identifier: "3HSzeProdOrg5Jd8VpN1cR6gTfB", name: "production" }
2993
+ ];
2994
+ var DEFAULT_SETTINGS = {
2995
+ ingestTokens: [],
2996
+ searchUsers: [],
2997
+ organizations: [...DEFAULT_ORGANIZATIONS],
2998
+ routing: { byEnvironment: { production: "production" }, default: "development" },
2999
+ keepBodies: false
3000
+ };
3001
+ var typeOf = (value) => typeof value === "boolean" ? "Boolean" : typeof value === "number" ? Number.isInteger(value) ? "Int64" : "Float64" : "Utf8";
3002
+ var OtelState = class {
3003
+ constructor(sqlite, namespace, seed) {
3004
+ this.seed = seed;
3005
+ this.rows = new Collection(sqlite, namespace, "rows");
3006
+ this.schemas = new Collection(sqlite, namespace, "schemas");
3007
+ this.counters = new Collection(sqlite, namespace, "counters");
3008
+ this.settings = new Collection(sqlite, namespace, "settings");
3009
+ this.ensureSeeded();
3010
+ }
3011
+ seed;
3012
+ rows;
3013
+ schemas;
3014
+ counters;
3015
+ settings;
3016
+ ensureSeeded() {
3017
+ if (!this.settings.has("settings")) {
3018
+ this.settings.insert("settings", { ...DEFAULT_SETTINGS, ...this.seed.settings });
3019
+ }
3020
+ }
3021
+ current() {
3022
+ return this.settings.get("settings") ?? DEFAULT_SETTINGS;
3023
+ }
3024
+ update(patch) {
3025
+ const next = { ...this.current(), ...patch };
3026
+ this.settings.insert("settings", next);
3027
+ return next;
3028
+ }
3029
+ /** The org an export lands in, from its resource attributes. */
3030
+ routeOrg(resource) {
3031
+ const settings = this.current();
3032
+ const environment = String(
3033
+ resource["deployment.environment.name"] ?? resource["deployment.environment"] ?? ""
3034
+ );
3035
+ const name = settings.routing.byEnvironment[environment] ?? settings.routing.default;
3036
+ const org = settings.organizations.find((o) => o.name === name || o.identifier === name);
3037
+ return org?.identifier ?? name;
3038
+ }
3039
+ schemaKey(org, stream, type) {
3040
+ return `${org}|${stream}|${type}`;
3041
+ }
3042
+ schema(org, stream, type) {
3043
+ return this.schemas.get(this.schemaKey(org, stream, type));
3044
+ }
3045
+ streams(org, type) {
3046
+ return this.schemas.list({ order: "oldest" }).map((r) => r.id.split("|")).filter(([o, , t]) => o === org && t === type).map(([, stream]) => stream);
3047
+ }
3048
+ /** Store a row and widen its stream's schema (null-valued fields never appear). */
3049
+ ingest(row, extraFields = {}) {
3050
+ this.rows.insert(`${row.type}:${this.rows.nextSequence()}`, row);
3051
+ const key = this.schemaKey(row.org, row.stream, row.type);
3052
+ const fields = { ...this.schemas.get(key) ?? { _timestamp: "Int64" } };
3053
+ let changed = !this.schemas.has(key);
3054
+ for (const [name, value] of Object.entries({ ...row.row })) {
3055
+ if (!(name in fields)) {
3056
+ fields[name] = typeOf(value);
3057
+ changed = true;
3058
+ }
3059
+ }
3060
+ for (const [name, type] of Object.entries(extraFields)) {
3061
+ if (!(name in fields)) {
3062
+ fields[name] = type;
3063
+ changed = true;
3064
+ }
3065
+ }
3066
+ if (changed) this.schemas.insert(key, fields);
3067
+ }
3068
+ list(filter) {
3069
+ return this.rows.list({ order: "oldest", where: (value) => filter(value) }).map((stored) => stored.value);
3070
+ }
3071
+ count(metrics, bytes) {
3072
+ const current = this.counters.get("metrics") ?? { requests: 0, bytes: 0, metrics: 0 };
3073
+ const next = {
3074
+ requests: current.requests + 1,
3075
+ bytes: current.bytes + bytes,
3076
+ metrics: current.metrics + metrics
3077
+ };
3078
+ this.counters.insert("metrics", next);
3079
+ return next;
3080
+ }
3081
+ metrics() {
3082
+ return this.counters.get("metrics") ?? { requests: 0, bytes: 0, metrics: 0 };
3083
+ }
3084
+ };
3085
+
3086
+ // src/runtime.ts
3087
+ var rpc = (code, message) => ({ code, message });
3088
+ var OTEL_PRESETS = {
3089
+ rate_limited: {
3090
+ description: "Exports answer 429 with retry-after: 1 (the SDK retries)",
3091
+ rules: [
3092
+ {
3093
+ pathPrefix: "/v1/",
3094
+ status: 429,
3095
+ headers: { "retry-after": "1" },
3096
+ body: rpc(8, "rate limited")
3097
+ }
3098
+ ]
3099
+ },
3100
+ bad_gateway: {
3101
+ description: "Exports answer 502 (the SDK retries)",
3102
+ rules: [{ pathPrefix: "/v1/", status: 502, body: rpc(14, "bad gateway") }]
3103
+ },
3104
+ unavailable: {
3105
+ description: "Exports answer 503 (the SDK retries)",
3106
+ rules: [{ pathPrefix: "/v1/", status: 503, body: rpc(14, "unavailable") }]
3107
+ },
3108
+ gateway_timeout: {
3109
+ description: "Exports answer 504 (the SDK retries)",
3110
+ rules: [{ pathPrefix: "/v1/", status: 504, body: rpc(4, "deadline exceeded") }]
3111
+ },
3112
+ server_error: {
3113
+ description: "Exports answer 500 (the SDK drops the batch: not retryable)",
3114
+ rules: [{ pathPrefix: "/v1/", status: 500, body: rpc(13, "internal error") }]
3115
+ },
3116
+ unauthorized: {
3117
+ description: "Exports answer 401 as if OTEL_AUTH_TOKEN were wrong (dropped, never retried)",
3118
+ rules: [{ pathPrefix: "/v1/", status: 401, body: rpc(16, "Unauthenticated") }]
3119
+ },
3120
+ partial_success: {
3121
+ description: "Exports answer 200 with partialSuccess rejecting every item (JSON only); nothing is stored",
3122
+ rules: [{ pathPrefix: "/v1/", effect: "partial_success" }]
3123
+ },
3124
+ search_unavailable: {
3125
+ description: "O2 search answers 503 (our clients degrade: empty hops, error result)",
3126
+ rules: [
3127
+ { operationId: "Search", status: 503, body: { code: 503, message: "Service Unavailable" } }
3128
+ ]
3129
+ }
3130
+ };
3131
+ var json3 = (status, body) => new Response(JSON.stringify(body), { status, headers: { "content-type": "application/json" } });
3132
+ var adminError3 = (status, message) => json3(status, { error: { type: "mockingbird_admin", message } });
3133
+ var isRecord5 = (value) => typeof value === "object" && value !== null && !Array.isArray(value);
3134
+ var same = (a, b) => a !== void 0 && a !== null && String(a) === String(b);
3135
+ var matchesWhere = (row, where) => Object.entries(where).every(([key, value]) => {
3136
+ if (key in row) return same(row[key], value);
3137
+ const formatted = formatKey(key);
3138
+ if (formatted in row) return same(row[formatted], value);
3139
+ return same(row[`service_${formatted}`], value);
3140
+ });
3141
+ var WAIT_POLL_MS = 20;
3142
+ var MAX_WAIT_MS = 6e4;
3143
+ var adminRoutes = (runtime) => {
3144
+ const select = (namespace, kind, where, org) => {
3145
+ const api = runtime.instance(namespace);
3146
+ return (kind === "log" ? api.logs() : api.spans()).filter(
3147
+ (stored) => (org === void 0 || org === null || stored.org === org) && matchesWhere(stored.row, where)
3148
+ );
3149
+ };
3150
+ return {
3151
+ "GET /logs": ({ url, namespace }) => {
3152
+ const where = {};
3153
+ const service = url.searchParams.get("service");
3154
+ const event = url.searchParams.get("event");
3155
+ const trace = url.searchParams.get("trace_id");
3156
+ if (service !== null) where.service_name = service;
3157
+ if (event !== null) where.event = event;
3158
+ if (trace !== null) where.trace_id = trace;
3159
+ const severity = url.searchParams.get("severity")?.toLowerCase();
3160
+ const logs = select(namespace, "log", where, url.searchParams.get("org")).filter(
3161
+ (stored) => severity === void 0 || String(stored.row.severity_text ?? "").toLowerCase() === severity
3162
+ );
3163
+ return json3(200, { logs: logs.map(adminRow) });
3164
+ },
3165
+ "GET /spans": ({ url, namespace }) => {
3166
+ const where = {};
3167
+ const service = url.searchParams.get("service");
3168
+ const name = url.searchParams.get("name");
3169
+ const trace = url.searchParams.get("trace_id");
3170
+ if (service !== null) where.service_name = service;
3171
+ if (name !== null) where.operation_name = name;
3172
+ if (trace !== null) where.trace_id = trace;
3173
+ return json3(200, {
3174
+ spans: select(namespace, "span", where, url.searchParams.get("org")).map(adminRow)
3175
+ });
3176
+ },
3177
+ /**
3178
+ * `{kind: "log"|"span", where: {event: "…"}, count?: 1, timeoutMs?: 5000, org?}`:
3179
+ * long-polls until `count` rows match, then answers them; 408 with what matched so far.
3180
+ */
3181
+ "POST /wait": async ({ body, namespace }) => {
3182
+ if (!isRecord5(body)) return adminError3(400, 'expected {"kind": "log", "where": {...}}');
3183
+ const kind = body.kind ?? "log";
3184
+ if (kind !== "log" && kind !== "span") return adminError3(400, 'kind must be "log" or "span"');
3185
+ const where = body.where ?? {};
3186
+ if (!isRecord5(where)) return adminError3(400, "where must be an object of column: value");
3187
+ const count = typeof body.count === "number" && body.count > 0 ? body.count : 1;
3188
+ const timeoutMs = Math.min(
3189
+ typeof body.timeoutMs === "number" && body.timeoutMs >= 0 ? body.timeoutMs : 5e3,
3190
+ MAX_WAIT_MS
3191
+ );
3192
+ const org = typeof body.org === "string" ? body.org : void 0;
3193
+ const deadline = Date.now() + timeoutMs;
3194
+ for (; ; ) {
3195
+ const matched = select(namespace, kind, where, org);
3196
+ if (matched.length >= count) {
3197
+ return json3(200, { matched: matched.map(adminRow), count: matched.length });
3198
+ }
3199
+ if (Date.now() >= deadline) {
3200
+ return json3(408, {
3201
+ error: {
3202
+ type: "mockingbird_admin",
3203
+ message: `timed out after ${timeoutMs} ms: ${matched.length}/${count} ${kind}s matched`
3204
+ },
3205
+ matched: matched.map(adminRow),
3206
+ count: matched.length
3207
+ });
3208
+ }
3209
+ await new Promise((resolve) => setTimeout(resolve, WAIT_POLL_MS));
3210
+ }
3211
+ },
3212
+ "GET /otlp-metrics": ({ namespace }) => json3(200, runtime.instance(namespace).state.metrics()),
3213
+ "GET /settings": ({ namespace }) => json3(200, runtime.instance(namespace).state.current()),
3214
+ "PUT /settings": ({ body, namespace }) => {
3215
+ if (!isRecord5(body)) return adminError3(400, "expected a JSON object");
3216
+ const patch = {};
3217
+ if (body.ingestTokens !== void 0) {
3218
+ if (!Array.isArray(body.ingestTokens)) return adminError3(400, "ingestTokens: string[]");
3219
+ patch.ingestTokens = body.ingestTokens.map(String);
3220
+ }
3221
+ if (body.searchUsers !== void 0) {
3222
+ if (!Array.isArray(body.searchUsers) || !body.searchUsers.every(isRecord5)) {
3223
+ return adminError3(400, "searchUsers: [{username, password}]");
3224
+ }
3225
+ patch.searchUsers = body.searchUsers.map((u) => ({
3226
+ username: String(u.username),
3227
+ password: String(u.password ?? "")
3228
+ }));
3229
+ }
3230
+ if (body.organizations !== void 0) {
3231
+ if (!Array.isArray(body.organizations) || !body.organizations.every(
3232
+ (o) => isRecord5(o) && typeof o.identifier === "string" && typeof o.name === "string"
3233
+ )) {
3234
+ return adminError3(400, "organizations: [{identifier, name}]");
3235
+ }
3236
+ patch.organizations = body.organizations;
3237
+ }
3238
+ if (body.routing !== void 0) {
3239
+ const routing = body.routing;
3240
+ if (!isRecord5(routing) || typeof routing.default !== "string" || !isRecord5(routing.byEnvironment)) {
3241
+ return adminError3(
3242
+ 400,
3243
+ "routing: {byEnvironment: {<env>: <org name>}, default: <org name>}"
3244
+ );
3245
+ }
3246
+ patch.routing = {
3247
+ default: routing.default,
3248
+ byEnvironment: Object.fromEntries(
3249
+ Object.entries(routing.byEnvironment).map(([k, v]) => [k, String(v)])
3250
+ )
3251
+ };
3252
+ }
3253
+ if (body.keepBodies !== void 0) {
3254
+ if (typeof body.keepBodies !== "boolean") return adminError3(400, "keepBodies: boolean");
3255
+ patch.keepBodies = body.keepBodies;
3256
+ }
3257
+ return json3(200, runtime.instance(namespace).state.update(patch));
3258
+ }
3259
+ };
3260
+ };
3261
+ var createRuntime2 = (options = {}) => createRuntime({
3262
+ name: OTEL_NAMESPACE,
3263
+ document,
3264
+ ...options.sqlite ? { sqlite: options.sqlite } : {},
3265
+ ...options.clock ? { clock: options.clock } : {},
3266
+ ...options.seed !== void 0 ? { seed: options.seed } : {},
3267
+ ...options.adminKey !== void 0 ? { adminKey: options.adminKey } : {},
3268
+ ...options.onLog ? { onLog: options.onLog } : {},
3269
+ credential: otelCredential,
3270
+ presets: OTEL_PRESETS,
3271
+ create: ({ sqlite, namespace, clock }) => new OtelAPI({
3272
+ sqlite,
3273
+ namespace,
3274
+ now: clock.now,
3275
+ ...options.settings ? { settings: options.settings } : {}
3276
+ }),
3277
+ describe: () => ({ keepBodies: options.settings?.keepBodies === true }),
3278
+ admin: adminRoutes
3279
+ });
3280
+
3281
+ // src/index.ts
3282
+ var OTEL_NAMESPACE = "otel";
3283
+ var PROTOBUF = "application/x-protobuf";
3284
+ var otelCredential = (request) => bearerToken(request) ?? basicAuth(request)?.username;
3285
+ var isRecord6 = (value) => typeof value === "object" && value !== null && !Array.isArray(value);
3286
+ var mediaType = (request) => request.headers.get("content-type")?.split(";")[0]?.trim().toLowerCase() ?? "";
3287
+ var rpcStatus = (status, code, message) => jsonRes(status, { code, message });
3288
+ var unauthorized = () => new Response("Unauthorized Access", {
3289
+ status: 401,
3290
+ headers: { "content-type": "text/plain; charset=utf-8" }
3291
+ });
3292
+ var o2Error = (status, message) => jsonRes(status, { code: status, message });
3293
+ var OtelAPI = class {
3294
+ app;
3295
+ sqlite;
3296
+ state;
3297
+ service;
3298
+ now;
3299
+ constructor(options = {}) {
3300
+ const sqlite = bootSqlite(options.sqlite);
3301
+ const namespace = options.namespace ?? OTEL_NAMESPACE;
3302
+ this.now = options.now ?? (() => Date.now());
3303
+ this.state = new OtelState(sqlite, namespace, { settings: options.settings ?? {} });
3304
+ const handlers = defineOperations({
3305
+ ExportTraces: (context) => this.export(context, "traces"),
3306
+ ExportLogs: (context) => this.export(context, "logs"),
3307
+ ExportMetrics: (context) => this.export(context, "metrics"),
3308
+ ListOrganizations: () => this.organizations(),
3309
+ ListStreams: (context) => this.streams(context),
3310
+ GetStreamSchema: (context) => this.schema(context),
3311
+ Search: (context) => this.search(context)
3312
+ });
3313
+ this.service = createService({
3314
+ document,
3315
+ handlers,
3316
+ sqlite,
3317
+ namespace,
3318
+ now: this.now,
3319
+ notFound: () => jsonRes(404, { code: 404, message: "Not Found" }),
3320
+ onError: (error) => {
3321
+ if (error instanceof HttpError) return error.toResponse();
3322
+ throw error;
3323
+ },
3324
+ before: (context) => this.gate(context)
3325
+ });
3326
+ this.app = this.service.app;
3327
+ this.sqlite = this.service.sqlite;
3328
+ }
3329
+ /** Inflate `content-encoding: gzip` bodies (the exporters' `compression: "gzip"`). */
3330
+ async fetch(request) {
3331
+ const encoding = request.headers.get("content-encoding")?.toLowerCase();
3332
+ if (encoding !== "gzip" || request.body === null) return this.service.fetch(request);
3333
+ let inflated;
3334
+ try {
3335
+ inflated = await new Response(
3336
+ request.body.pipeThrough(new DecompressionStream("gzip"))
3337
+ ).arrayBuffer();
3338
+ } catch {
3339
+ return rpcStatus(400, 3, "invalid gzip body");
3340
+ }
3341
+ const headers = new Headers(request.headers);
3342
+ headers.delete("content-encoding");
3343
+ headers.delete("content-length");
3344
+ return this.service.fetch(
3345
+ new Request(request.url, { method: request.method, headers, body: inflated })
3346
+ );
3347
+ }
3348
+ async reset() {
3349
+ await this.service.reset();
3350
+ this.state.ensureSeeded();
3351
+ }
3352
+ /** Every stored log row (with its org and stream), oldest first. */
3353
+ logs() {
3354
+ return this.state.list((r) => r.type === "logs");
3355
+ }
3356
+ /** Every stored span row, oldest first. */
3357
+ spans() {
3358
+ return this.state.list((r) => r.type === "traces");
3359
+ }
3360
+ settings() {
3361
+ return this.state.current();
3362
+ }
3363
+ gate(context) {
3364
+ const operationId = context.operation.operationId;
3365
+ if (operationId.startsWith("Export")) {
3366
+ const token = bearerToken(context.request);
3367
+ const accepted = this.settings().ingestTokens;
3368
+ if (!token || accepted.length > 0 && !accepted.includes(token)) {
3369
+ return rpcStatus(401, 16, "Unauthenticated");
3370
+ }
3371
+ return void 0;
3372
+ }
3373
+ const credentials = basicAuth(context.request);
3374
+ if (!credentials) return unauthorized();
3375
+ const users = this.settings().searchUsers;
3376
+ if (users.length > 0 && !users.some((u) => u.username === credentials.username && u.password === credentials.password)) {
3377
+ return unauthorized();
3378
+ }
3379
+ const org = context.params.org;
3380
+ if (org !== void 0 && !this.settings().organizations.some((o) => o.identifier === org)) {
3381
+ return unauthorized();
3382
+ }
3383
+ return void 0;
3384
+ }
3385
+ decode(context, kind) {
3386
+ const type = mediaType(context.request);
3387
+ const body = context.body;
3388
+ if (type === PROTOBUF) {
3389
+ const bytes = body.kind === "bytes" ? body.value : new Uint8Array(0);
3390
+ try {
3391
+ const payload = kind === "traces" ? decodeTraceRequest(bytes) : kind === "logs" ? decodeLogsRequest(bytes) : { resourceMetrics: new Array(countMetricsRequest(bytes)).fill({}) };
3392
+ return { payload, protobuf: true, bytes: bytes.length };
3393
+ } catch (error) {
3394
+ if (error instanceof ProtobufError) return rpcStatus(400, 3, error.message);
3395
+ throw error;
3396
+ }
3397
+ }
3398
+ if (type !== "application/json") {
3399
+ return rpcStatus(415, 3, `unsupported content type ${type || "(none)"}`);
3400
+ }
3401
+ if (body.kind === "empty") return { payload: {}, protobuf: false, bytes: 0 };
3402
+ if (body.kind !== "json" || !isRecord6(body.value)) {
3403
+ return rpcStatus(400, 3, "request body is not an OTLP/JSON export request");
3404
+ }
3405
+ return { payload: body.value, protobuf: false, bytes: JSON.stringify(body.value).length };
3406
+ }
3407
+ export(context, kind) {
3408
+ const decoded = this.decode(context, kind);
3409
+ if (decoded instanceof Response) return decoded;
3410
+ const { payload, protobuf } = decoded;
3411
+ const ok = (partial = {}) => protobuf ? new Response(new Uint8Array(0), { status: 200, headers: { "content-type": PROTOBUF } }) : jsonRes(200, { partialSuccess: partial });
3412
+ if (kind === "metrics") {
3413
+ const count = protobuf && Array.isArray(payload.resourceMetrics) ? payload.resourceMetrics.length : metricCount(payload);
3414
+ this.state.count(count, decoded.bytes);
3415
+ return ok();
3416
+ }
3417
+ const nowMs = this.now();
3418
+ const rows = kind === "traces" ? spanRows(payload, { nowMs }) : logRows(payload, { keepBodies: this.settings().keepBodies, nowMs });
3419
+ const rejected = faultEffect(context.request, "partial_success");
3420
+ if (rejected !== void 0) {
3421
+ const key = kind === "traces" ? "rejectedSpans" : "rejectedLogRecords";
3422
+ return ok({
3423
+ [key]: String(rows.length),
3424
+ errorMessage: String(rejected.message ?? "rejected by Mockingbird partial_success")
3425
+ });
3426
+ }
3427
+ const stream = context.request.headers.get("stream-name")?.trim() || "default";
3428
+ const type = kind === "traces" ? "traces" : "logs";
3429
+ const orgs = /* @__PURE__ */ new Set();
3430
+ this.sqlite.transaction(() => {
3431
+ for (const ingested of rows) {
3432
+ const org = this.state.routeOrg(ingested.resource);
3433
+ orgs.add(org);
3434
+ this.state.ingest(
3435
+ { org, stream, type, row: ingested.row },
3436
+ ingested.hadBody ? { body: "Utf8" } : {}
3437
+ );
3438
+ }
3439
+ });
3440
+ return annotateResponse(ok(), {
3441
+ ids: {
3442
+ accepted: String(rows.length),
3443
+ ...orgs.size > 0 ? { org: [...orgs].join(",") } : {}
3444
+ }
3445
+ });
3446
+ }
3447
+ organizations() {
3448
+ return jsonRes(200, {
3449
+ data: this.settings().organizations.map((org, index) => ({
3450
+ id: index + 1,
3451
+ identifier: org.identifier,
3452
+ name: org.name,
3453
+ type: org.identifier === "default" ? "default" : "custom"
3454
+ }))
3455
+ });
3456
+ }
3457
+ streamType(context) {
3458
+ return context.url.searchParams.get("type") === "traces" ? "traces" : "logs";
3459
+ }
3460
+ streams(context) {
3461
+ const org = context.params.org;
3462
+ const type = this.streamType(context);
3463
+ return jsonRes(200, {
3464
+ list: this.state.streams(org, type).map((name) => ({
3465
+ name,
3466
+ stream_type: type,
3467
+ storage_type: "disk",
3468
+ stats: {
3469
+ doc_num: this.state.list((r) => r.org === org && r.stream === name && r.type === type).length
3470
+ }
3471
+ }))
3472
+ });
3473
+ }
3474
+ schema(context) {
3475
+ const org = context.params.org;
3476
+ const stream = context.params.stream;
3477
+ const type = this.streamType(context);
3478
+ const fields = this.state.schema(org, stream, type);
3479
+ if (!fields) return o2Error(404, `stream ${stream} not found`);
3480
+ const names = Object.keys(fields).sort(
3481
+ (a, b) => a === "_timestamp" ? -1 : b === "_timestamp" ? 1 : a.localeCompare(b)
3482
+ );
3483
+ return jsonRes(200, {
3484
+ name: stream,
3485
+ stream_type: type,
3486
+ storage_type: "disk",
3487
+ stats: {
3488
+ doc_num: this.state.list((r) => r.org === org && r.stream === stream && r.type === type).length
3489
+ },
3490
+ schema: names.map((name) => ({ name, type: fields[name] })),
3491
+ settings: { partition_keys: {}, full_text_search_keys: [], data_retention: 30 }
3492
+ });
3493
+ }
3494
+ search(context) {
3495
+ const org = context.params.org;
3496
+ const body = context.body.kind === "json" ? context.body.value : void 0;
3497
+ const query = isRecord6(body) && isRecord6(body.query) ? body.query : void 0;
3498
+ if (!query || typeof query.sql !== "string") {
3499
+ return o2Error(400, "Search SQL not supported: query.sql is required");
3500
+ }
3501
+ let parsed;
3502
+ try {
3503
+ parsed = parseSql(query.sql);
3504
+ } catch (error) {
3505
+ if (error instanceof SqlError)
3506
+ return o2Error(400, `Search SQL not supported: ${error.message}`);
3507
+ throw error;
3508
+ }
3509
+ const type = this.streamType(context);
3510
+ const from = typeof query.from === "number" && query.from > 0 ? query.from : 0;
3511
+ const size = typeof query.size === "number" ? query.size : -1;
3512
+ const empty = (hits2, total, scanned) => annotateResponse(
3513
+ jsonRes(200, {
3514
+ took: 1,
3515
+ hits: hits2,
3516
+ total,
3517
+ from,
3518
+ size,
3519
+ cached_ratio: 0,
3520
+ scan_size: 0,
3521
+ scan_records: scanned,
3522
+ is_partial: false
3523
+ }),
3524
+ { ids: { org } }
3525
+ );
3526
+ const fields = this.state.schema(org, parsed.stream, type);
3527
+ if (!fields) return empty([], 0, 0);
3528
+ const unknown = referencedColumns(parsed).find((name) => !(name in fields));
3529
+ if (unknown !== void 0) {
3530
+ return o2Error(
3531
+ 400,
3532
+ `Search SQL execute error: Schema error: No field named ${unknown}. Valid fields are ${Object.keys(fields).sort().join(", ")}.`
3533
+ );
3534
+ }
3535
+ const start = typeof query.start_time === "number" ? query.start_time : 0;
3536
+ const end = typeof query.end_time === "number" && query.end_time > 0 ? query.end_time : Infinity;
3537
+ const rows = this.state.list((r) => r.org === org && r.stream === parsed.stream && r.type === type).map((r) => r.row).filter((row) => {
3538
+ const ts = Number(row._timestamp);
3539
+ return ts >= start && ts <= end;
3540
+ });
3541
+ let hits;
3542
+ try {
3543
+ hits = execute(parsed, rows);
3544
+ } catch (error) {
3545
+ if (error instanceof SqlError)
3546
+ return o2Error(400, `Search SQL not supported: ${error.message}`);
3547
+ throw error;
3548
+ }
3549
+ const page = size < 0 ? hits.slice(from) : hits.slice(from, from + size);
3550
+ return empty(page, hits.length, rows.length);
3551
+ }
3552
+ };
3553
+ var adminRow = (stored) => ({
3554
+ _org: stored.org,
3555
+ _stream: stored.stream,
3556
+ ...stored.row
3557
+ });
3558
+
3559
+ export {
3560
+ document,
3561
+ operationIds,
3562
+ supportedOperationIds,
3563
+ formatKey,
3564
+ logRows,
3565
+ spanRows,
3566
+ ProtobufError,
3567
+ decodeTraceRequest,
3568
+ decodeLogsRequest,
3569
+ SqlError,
3570
+ parseSql,
3571
+ referencedColumns,
3572
+ execute,
3573
+ DEFAULT_ORGANIZATIONS,
3574
+ DEFAULT_SETTINGS,
3575
+ OTEL_PRESETS,
3576
+ createRuntime2 as createRuntime,
3577
+ OTEL_NAMESPACE,
3578
+ otelCredential,
3579
+ OtelAPI,
3580
+ adminRow
3581
+ };
3582
+ //# sourceMappingURL=chunk-RQFUL7RM.js.map