@debugkit/adapters 0.0.0-stage → 0.2.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,124 @@
1
+ "use strict";Object.defineProperty(exports, "__esModule", {value: true}); function _nullishCoalesce(lhs, rhsFn) { if (lhs != null) { return lhs; } else { return rhsFn(); } }
2
+
3
+
4
+ var _chunkG5LEIFH7cjs = require('./chunk-G5LEIFH7.cjs');
5
+
6
+ // src/firestore.ts
7
+ var _firestore = require('firebase-admin/firestore');
8
+ var FirestoreStore = class {
9
+ constructor(db, opts = {}) {
10
+ this.db = db;
11
+ this.opts = opts;
12
+ }
13
+
14
+
15
+ get root() {
16
+ return this.db.collection(_nullishCoalesce(this.opts.root, () => ( "debug_apps")));
17
+ }
18
+ app(app) {
19
+ return this.root.doc(app);
20
+ }
21
+ async registerApp(app) {
22
+ await this.app(app).set({ id: app, updatedAt: _firestore.FieldValue.serverTimestamp() }, { merge: true });
23
+ }
24
+ async writeEvents(events) {
25
+ const days = _nullishCoalesce(this.opts.retentionDays, () => ( 30));
26
+ for (const chunk of chunks(events, 400)) {
27
+ const batch = this.db.batch();
28
+ for (const e of chunk) {
29
+ const expireAt = _firestore.Timestamp.fromMillis(Date.parse(e.receivedAt) + days * 864e5);
30
+ batch.set(this.app(e.app).collection("events").doc(e.event.id), { ...strip(e), ts: e.event.ts, expireAt });
31
+ }
32
+ await batch.commit();
33
+ }
34
+ }
35
+ async upsertIssues(updates) {
36
+ await Promise.all(
37
+ updates.map(
38
+ (u) => this.db.runTransaction(async (tx) => {
39
+ const ref = this.app(u.app).collection("issues").doc(u.fingerprint);
40
+ const snap = await tx.get(ref);
41
+ const cur = snap.data();
42
+ const newer = !cur || u.ts >= cur.lastSeen;
43
+ tx.set(
44
+ ref,
45
+ strip({
46
+ app: u.app,
47
+ fingerprint: u.fingerprint,
48
+ title: u.title,
49
+ culprit: u.culprit,
50
+ flow: u.flow,
51
+ firstSeen: cur && cur.firstSeen < u.firstTs ? cur.firstSeen : u.firstTs,
52
+ lastSeen: newer ? u.ts : cur.lastSeen,
53
+ count: _firestore.FieldValue.increment(u.by),
54
+ ...newer ? { lastEvent: strip(u.lastEvent) } : {}
55
+ }),
56
+ { merge: true }
57
+ );
58
+ })
59
+ )
60
+ );
61
+ }
62
+ async incrementRollups(incs) {
63
+ const rows = _chunkG5LEIFH7cjs.coalesceRollups.call(void 0, incs);
64
+ for (const chunk of chunks(rows, 400)) {
65
+ const batch = this.db.batch();
66
+ for (const r of chunk) {
67
+ batch.set(
68
+ this.app(r.app).collection("rollups").doc(_chunkG5LEIFH7cjs.rollupId.call(void 0, r)),
69
+ {
70
+ app: r.app,
71
+ day: r.day,
72
+ dimension: r.dimension,
73
+ key: r.key,
74
+ errors: _firestore.FieldValue.increment(r.errors),
75
+ events: _firestore.FieldValue.increment(r.events)
76
+ },
77
+ { merge: true }
78
+ );
79
+ }
80
+ await batch.commit();
81
+ }
82
+ }
83
+ async listApps() {
84
+ const snap = await this.root.select().get();
85
+ return snap.docs.map((d) => d.id).sort();
86
+ }
87
+ async listIssues(app, { limit = 200 } = {}) {
88
+ const snap = await this.app(app).collection("issues").orderBy("lastSeen", "desc").limit(limit).get();
89
+ return snap.docs.map((d) => d.data());
90
+ }
91
+ async getIssue(app, fp) {
92
+ const snap = await this.app(app).collection("issues").doc(fp).get();
93
+ return snap.exists ? snap.data() : null;
94
+ }
95
+ async getRollups(app, dimension, fromDay, toDay) {
96
+ const snap = await this.app(app).collection("rollups").where("dimension", "==", dimension).where("day", ">=", fromDay).where("day", "<=", toDay).get();
97
+ return snap.docs.map((d) => d.data());
98
+ }
99
+ async listIssueEvents(app, fp, { limit = 50 } = {}) {
100
+ const snap = await this.app(app).collection("events").where("fingerprint", "==", fp).orderBy("ts", "desc").limit(limit).get();
101
+ return snap.docs.map((d) => {
102
+ const { expireAt: _e, ts: _t, ...rest } = d.data();
103
+ return rest;
104
+ });
105
+ }
106
+ };
107
+ function strip(v) {
108
+ if (Array.isArray(v)) return v.map(strip);
109
+ if (v && typeof v === "object" && v.constructor === Object) {
110
+ return Object.fromEntries(
111
+ Object.entries(v).filter(([, x]) => x !== void 0).map(([k, x]) => [k, strip(x)])
112
+ );
113
+ }
114
+ return v;
115
+ }
116
+ function chunks(xs, n) {
117
+ const out = [];
118
+ for (let i = 0; i < xs.length; i += n) out.push(xs.slice(i, i + n));
119
+ return out;
120
+ }
121
+
122
+
123
+ exports.FirestoreStore = FirestoreStore;
124
+ //# sourceMappingURL=firestore.cjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["/Users/sivemwanda/Work/Tanstack/deBug/packages/adapters/dist/firestore.cjs","../src/firestore.ts"],"names":[],"mappings":"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","file":"/Users/sivemwanda/Work/Tanstack/deBug/packages/adapters/dist/firestore.cjs","sourcesContent":[null,"import type { Firestore } from \"firebase-admin/firestore\";\nimport { FieldValue, Timestamp } from \"firebase-admin/firestore\";\nimport type { Issue, RollupDimension, RollupIncrement, RollupRow, StoredEvent } from \"@debugkit/schema\";\nimport { coalesceRollups, rollupId } from \"./rollups\";\nimport type { IssueUpsert, Store } from \"./index\";\n\n/**\n * Firestore layout (all under one root collection so it's easy to find / secure / delete):\n *\n * {root}/{app} app marker\n * {root}/{app}/issues/{fingerprint} Issue\n * {root}/{app}/rollups/{day__dim__key} RollupRow (counters via FieldValue.increment)\n * {root}/{app}/events/{eventId} StoredEvent + expireAt (enable a TTL policy on `expireAt`)\n *\n * Composite index needed: rollups (dimension ASC, day ASC). See firestore.indexes.json.\n */\nexport class FirestoreStore implements Store {\n constructor(\n private db: Firestore,\n private opts: { root?: string; retentionDays?: number } = {},\n ) {}\n\n private get root() {\n return this.db.collection(this.opts.root ?? \"debug_apps\");\n }\n private app(app: string) {\n return this.root.doc(app);\n }\n\n async registerApp(app: string) {\n await this.app(app).set({ id: app, updatedAt: FieldValue.serverTimestamp() }, { merge: true });\n }\n\n async writeEvents(events: StoredEvent[]) {\n const days = this.opts.retentionDays ?? 30;\n for (const chunk of chunks(events, 400)) {\n const batch = this.db.batch();\n for (const e of chunk) {\n const expireAt = Timestamp.fromMillis(Date.parse(e.receivedAt) + days * 86_400_000);\n batch.set(this.app(e.app).collection(\"events\").doc(e.event.id), { ...strip(e), ts: e.event.ts, expireAt });\n }\n await batch.commit();\n }\n }\n\n async upsertIssues(updates: IssueUpsert[]) {\n // A transaction per issue keeps firstSeen/lastSeen correct under concurrent ingest.\n await Promise.all(\n updates.map((u) =>\n this.db.runTransaction(async (tx) => {\n const ref = this.app(u.app).collection(\"issues\").doc(u.fingerprint);\n const snap = await tx.get(ref);\n const cur = snap.data() as Issue | undefined;\n const newer = !cur || u.ts >= cur.lastSeen;\n tx.set(\n ref,\n strip({\n app: u.app,\n fingerprint: u.fingerprint,\n title: u.title,\n culprit: u.culprit,\n flow: u.flow,\n firstSeen: cur && cur.firstSeen < u.firstTs ? cur.firstSeen : u.firstTs,\n lastSeen: newer ? u.ts : cur!.lastSeen,\n count: FieldValue.increment(u.by),\n ...(newer ? { lastEvent: strip(u.lastEvent) } : {}),\n }),\n { merge: true },\n );\n }),\n ),\n );\n }\n\n async incrementRollups(incs: RollupIncrement[]) {\n const rows = coalesceRollups(incs);\n for (const chunk of chunks(rows, 400)) {\n const batch = this.db.batch();\n for (const r of chunk) {\n batch.set(\n this.app(r.app).collection(\"rollups\").doc(rollupId(r)),\n {\n app: r.app,\n day: r.day,\n dimension: r.dimension,\n key: r.key,\n errors: FieldValue.increment(r.errors),\n events: FieldValue.increment(r.events),\n },\n { merge: true },\n );\n }\n await batch.commit();\n }\n }\n\n async listApps() {\n const snap = await this.root.select().get();\n return snap.docs.map((d) => d.id).sort();\n }\n async listIssues(app: string, { limit = 200 } = {}) {\n const snap = await this.app(app).collection(\"issues\").orderBy(\"lastSeen\", \"desc\").limit(limit).get();\n return snap.docs.map((d) => d.data() as Issue);\n }\n async getIssue(app: string, fp: string) {\n const snap = await this.app(app).collection(\"issues\").doc(fp).get();\n return snap.exists ? (snap.data() as Issue) : null;\n }\n async getRollups(app: string, dimension: RollupDimension, fromDay: string, toDay: string) {\n const snap = await this.app(app)\n .collection(\"rollups\")\n .where(\"dimension\", \"==\", dimension)\n .where(\"day\", \">=\", fromDay)\n .where(\"day\", \"<=\", toDay)\n .get();\n return snap.docs.map((d) => d.data() as RollupRow);\n }\n async listIssueEvents(app: string, fp: string, { limit = 50 } = {}) {\n const snap = await this.app(app)\n .collection(\"events\")\n .where(\"fingerprint\", \"==\", fp)\n .orderBy(\"ts\", \"desc\")\n .limit(limit)\n .get();\n return snap.docs.map((d) => {\n const { expireAt: _e, ts: _t, ...rest } = d.data();\n return rest as StoredEvent;\n });\n }\n}\n\n/** Firestore rejects `undefined` values; drop them recursively. */\nfunction strip<T>(v: T): T {\n if (Array.isArray(v)) return v.map(strip) as T;\n if (v && typeof v === \"object\" && v.constructor === Object) {\n return Object.fromEntries(\n Object.entries(v as Record<string, unknown>).filter(([, x]) => x !== undefined).map(([k, x]) => [k, strip(x)]),\n ) as T;\n }\n return v;\n}\n\nfunction chunks<T>(xs: T[], n: number): T[][] {\n const out: T[][] = [];\n for (let i = 0; i < xs.length; i += n) out.push(xs.slice(i, i + n));\n return out;\n}\n"]}
@@ -0,0 +1,39 @@
1
+ import { Firestore } from 'firebase-admin/firestore';
2
+ import { StoredEvent, RollupIncrement, Issue, RollupDimension, RollupRow } from '@debugkit/schema';
3
+ import { Store, IssueUpsert } from './index.cjs';
4
+
5
+ /**
6
+ * Firestore layout (all under one root collection so it's easy to find / secure / delete):
7
+ *
8
+ * {root}/{app} app marker
9
+ * {root}/{app}/issues/{fingerprint} Issue
10
+ * {root}/{app}/rollups/{day__dim__key} RollupRow (counters via FieldValue.increment)
11
+ * {root}/{app}/events/{eventId} StoredEvent + expireAt (enable a TTL policy on `expireAt`)
12
+ *
13
+ * Composite index needed: rollups (dimension ASC, day ASC). See firestore.indexes.json.
14
+ */
15
+ declare class FirestoreStore implements Store {
16
+ private db;
17
+ private opts;
18
+ constructor(db: Firestore, opts?: {
19
+ root?: string;
20
+ retentionDays?: number;
21
+ });
22
+ private get root();
23
+ private app;
24
+ registerApp(app: string): Promise<void>;
25
+ writeEvents(events: StoredEvent[]): Promise<void>;
26
+ upsertIssues(updates: IssueUpsert[]): Promise<void>;
27
+ incrementRollups(incs: RollupIncrement[]): Promise<void>;
28
+ listApps(): Promise<string[]>;
29
+ listIssues(app: string, { limit }?: {
30
+ limit?: number | undefined;
31
+ }): Promise<Issue[]>;
32
+ getIssue(app: string, fp: string): Promise<Issue | null>;
33
+ getRollups(app: string, dimension: RollupDimension, fromDay: string, toDay: string): Promise<RollupRow[]>;
34
+ listIssueEvents(app: string, fp: string, { limit }?: {
35
+ limit?: number | undefined;
36
+ }): Promise<StoredEvent[]>;
37
+ }
38
+
39
+ export { FirestoreStore };
@@ -0,0 +1,39 @@
1
+ import { Firestore } from 'firebase-admin/firestore';
2
+ import { StoredEvent, RollupIncrement, Issue, RollupDimension, RollupRow } from '@debugkit/schema';
3
+ import { Store, IssueUpsert } from './index.js';
4
+
5
+ /**
6
+ * Firestore layout (all under one root collection so it's easy to find / secure / delete):
7
+ *
8
+ * {root}/{app} app marker
9
+ * {root}/{app}/issues/{fingerprint} Issue
10
+ * {root}/{app}/rollups/{day__dim__key} RollupRow (counters via FieldValue.increment)
11
+ * {root}/{app}/events/{eventId} StoredEvent + expireAt (enable a TTL policy on `expireAt`)
12
+ *
13
+ * Composite index needed: rollups (dimension ASC, day ASC). See firestore.indexes.json.
14
+ */
15
+ declare class FirestoreStore implements Store {
16
+ private db;
17
+ private opts;
18
+ constructor(db: Firestore, opts?: {
19
+ root?: string;
20
+ retentionDays?: number;
21
+ });
22
+ private get root();
23
+ private app;
24
+ registerApp(app: string): Promise<void>;
25
+ writeEvents(events: StoredEvent[]): Promise<void>;
26
+ upsertIssues(updates: IssueUpsert[]): Promise<void>;
27
+ incrementRollups(incs: RollupIncrement[]): Promise<void>;
28
+ listApps(): Promise<string[]>;
29
+ listIssues(app: string, { limit }?: {
30
+ limit?: number | undefined;
31
+ }): Promise<Issue[]>;
32
+ getIssue(app: string, fp: string): Promise<Issue | null>;
33
+ getRollups(app: string, dimension: RollupDimension, fromDay: string, toDay: string): Promise<RollupRow[]>;
34
+ listIssueEvents(app: string, fp: string, { limit }?: {
35
+ limit?: number | undefined;
36
+ }): Promise<StoredEvent[]>;
37
+ }
38
+
39
+ export { FirestoreStore };
@@ -0,0 +1,124 @@
1
+ import {
2
+ coalesceRollups,
3
+ rollupId
4
+ } from "./chunk-IUP4NDEQ.js";
5
+
6
+ // src/firestore.ts
7
+ import { FieldValue, Timestamp } from "firebase-admin/firestore";
8
+ var FirestoreStore = class {
9
+ constructor(db, opts = {}) {
10
+ this.db = db;
11
+ this.opts = opts;
12
+ }
13
+ db;
14
+ opts;
15
+ get root() {
16
+ return this.db.collection(this.opts.root ?? "debug_apps");
17
+ }
18
+ app(app) {
19
+ return this.root.doc(app);
20
+ }
21
+ async registerApp(app) {
22
+ await this.app(app).set({ id: app, updatedAt: FieldValue.serverTimestamp() }, { merge: true });
23
+ }
24
+ async writeEvents(events) {
25
+ const days = this.opts.retentionDays ?? 30;
26
+ for (const chunk of chunks(events, 400)) {
27
+ const batch = this.db.batch();
28
+ for (const e of chunk) {
29
+ const expireAt = Timestamp.fromMillis(Date.parse(e.receivedAt) + days * 864e5);
30
+ batch.set(this.app(e.app).collection("events").doc(e.event.id), { ...strip(e), ts: e.event.ts, expireAt });
31
+ }
32
+ await batch.commit();
33
+ }
34
+ }
35
+ async upsertIssues(updates) {
36
+ await Promise.all(
37
+ updates.map(
38
+ (u) => this.db.runTransaction(async (tx) => {
39
+ const ref = this.app(u.app).collection("issues").doc(u.fingerprint);
40
+ const snap = await tx.get(ref);
41
+ const cur = snap.data();
42
+ const newer = !cur || u.ts >= cur.lastSeen;
43
+ tx.set(
44
+ ref,
45
+ strip({
46
+ app: u.app,
47
+ fingerprint: u.fingerprint,
48
+ title: u.title,
49
+ culprit: u.culprit,
50
+ flow: u.flow,
51
+ firstSeen: cur && cur.firstSeen < u.firstTs ? cur.firstSeen : u.firstTs,
52
+ lastSeen: newer ? u.ts : cur.lastSeen,
53
+ count: FieldValue.increment(u.by),
54
+ ...newer ? { lastEvent: strip(u.lastEvent) } : {}
55
+ }),
56
+ { merge: true }
57
+ );
58
+ })
59
+ )
60
+ );
61
+ }
62
+ async incrementRollups(incs) {
63
+ const rows = coalesceRollups(incs);
64
+ for (const chunk of chunks(rows, 400)) {
65
+ const batch = this.db.batch();
66
+ for (const r of chunk) {
67
+ batch.set(
68
+ this.app(r.app).collection("rollups").doc(rollupId(r)),
69
+ {
70
+ app: r.app,
71
+ day: r.day,
72
+ dimension: r.dimension,
73
+ key: r.key,
74
+ errors: FieldValue.increment(r.errors),
75
+ events: FieldValue.increment(r.events)
76
+ },
77
+ { merge: true }
78
+ );
79
+ }
80
+ await batch.commit();
81
+ }
82
+ }
83
+ async listApps() {
84
+ const snap = await this.root.select().get();
85
+ return snap.docs.map((d) => d.id).sort();
86
+ }
87
+ async listIssues(app, { limit = 200 } = {}) {
88
+ const snap = await this.app(app).collection("issues").orderBy("lastSeen", "desc").limit(limit).get();
89
+ return snap.docs.map((d) => d.data());
90
+ }
91
+ async getIssue(app, fp) {
92
+ const snap = await this.app(app).collection("issues").doc(fp).get();
93
+ return snap.exists ? snap.data() : null;
94
+ }
95
+ async getRollups(app, dimension, fromDay, toDay) {
96
+ const snap = await this.app(app).collection("rollups").where("dimension", "==", dimension).where("day", ">=", fromDay).where("day", "<=", toDay).get();
97
+ return snap.docs.map((d) => d.data());
98
+ }
99
+ async listIssueEvents(app, fp, { limit = 50 } = {}) {
100
+ const snap = await this.app(app).collection("events").where("fingerprint", "==", fp).orderBy("ts", "desc").limit(limit).get();
101
+ return snap.docs.map((d) => {
102
+ const { expireAt: _e, ts: _t, ...rest } = d.data();
103
+ return rest;
104
+ });
105
+ }
106
+ };
107
+ function strip(v) {
108
+ if (Array.isArray(v)) return v.map(strip);
109
+ if (v && typeof v === "object" && v.constructor === Object) {
110
+ return Object.fromEntries(
111
+ Object.entries(v).filter(([, x]) => x !== void 0).map(([k, x]) => [k, strip(x)])
112
+ );
113
+ }
114
+ return v;
115
+ }
116
+ function chunks(xs, n) {
117
+ const out = [];
118
+ for (let i = 0; i < xs.length; i += n) out.push(xs.slice(i, i + n));
119
+ return out;
120
+ }
121
+ export {
122
+ FirestoreStore
123
+ };
124
+ //# sourceMappingURL=firestore.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/firestore.ts"],"sourcesContent":["import type { Firestore } from \"firebase-admin/firestore\";\nimport { FieldValue, Timestamp } from \"firebase-admin/firestore\";\nimport type { Issue, RollupDimension, RollupIncrement, RollupRow, StoredEvent } from \"@debugkit/schema\";\nimport { coalesceRollups, rollupId } from \"./rollups\";\nimport type { IssueUpsert, Store } from \"./index\";\n\n/**\n * Firestore layout (all under one root collection so it's easy to find / secure / delete):\n *\n * {root}/{app} app marker\n * {root}/{app}/issues/{fingerprint} Issue\n * {root}/{app}/rollups/{day__dim__key} RollupRow (counters via FieldValue.increment)\n * {root}/{app}/events/{eventId} StoredEvent + expireAt (enable a TTL policy on `expireAt`)\n *\n * Composite index needed: rollups (dimension ASC, day ASC). See firestore.indexes.json.\n */\nexport class FirestoreStore implements Store {\n constructor(\n private db: Firestore,\n private opts: { root?: string; retentionDays?: number } = {},\n ) {}\n\n private get root() {\n return this.db.collection(this.opts.root ?? \"debug_apps\");\n }\n private app(app: string) {\n return this.root.doc(app);\n }\n\n async registerApp(app: string) {\n await this.app(app).set({ id: app, updatedAt: FieldValue.serverTimestamp() }, { merge: true });\n }\n\n async writeEvents(events: StoredEvent[]) {\n const days = this.opts.retentionDays ?? 30;\n for (const chunk of chunks(events, 400)) {\n const batch = this.db.batch();\n for (const e of chunk) {\n const expireAt = Timestamp.fromMillis(Date.parse(e.receivedAt) + days * 86_400_000);\n batch.set(this.app(e.app).collection(\"events\").doc(e.event.id), { ...strip(e), ts: e.event.ts, expireAt });\n }\n await batch.commit();\n }\n }\n\n async upsertIssues(updates: IssueUpsert[]) {\n // A transaction per issue keeps firstSeen/lastSeen correct under concurrent ingest.\n await Promise.all(\n updates.map((u) =>\n this.db.runTransaction(async (tx) => {\n const ref = this.app(u.app).collection(\"issues\").doc(u.fingerprint);\n const snap = await tx.get(ref);\n const cur = snap.data() as Issue | undefined;\n const newer = !cur || u.ts >= cur.lastSeen;\n tx.set(\n ref,\n strip({\n app: u.app,\n fingerprint: u.fingerprint,\n title: u.title,\n culprit: u.culprit,\n flow: u.flow,\n firstSeen: cur && cur.firstSeen < u.firstTs ? cur.firstSeen : u.firstTs,\n lastSeen: newer ? u.ts : cur!.lastSeen,\n count: FieldValue.increment(u.by),\n ...(newer ? { lastEvent: strip(u.lastEvent) } : {}),\n }),\n { merge: true },\n );\n }),\n ),\n );\n }\n\n async incrementRollups(incs: RollupIncrement[]) {\n const rows = coalesceRollups(incs);\n for (const chunk of chunks(rows, 400)) {\n const batch = this.db.batch();\n for (const r of chunk) {\n batch.set(\n this.app(r.app).collection(\"rollups\").doc(rollupId(r)),\n {\n app: r.app,\n day: r.day,\n dimension: r.dimension,\n key: r.key,\n errors: FieldValue.increment(r.errors),\n events: FieldValue.increment(r.events),\n },\n { merge: true },\n );\n }\n await batch.commit();\n }\n }\n\n async listApps() {\n const snap = await this.root.select().get();\n return snap.docs.map((d) => d.id).sort();\n }\n async listIssues(app: string, { limit = 200 } = {}) {\n const snap = await this.app(app).collection(\"issues\").orderBy(\"lastSeen\", \"desc\").limit(limit).get();\n return snap.docs.map((d) => d.data() as Issue);\n }\n async getIssue(app: string, fp: string) {\n const snap = await this.app(app).collection(\"issues\").doc(fp).get();\n return snap.exists ? (snap.data() as Issue) : null;\n }\n async getRollups(app: string, dimension: RollupDimension, fromDay: string, toDay: string) {\n const snap = await this.app(app)\n .collection(\"rollups\")\n .where(\"dimension\", \"==\", dimension)\n .where(\"day\", \">=\", fromDay)\n .where(\"day\", \"<=\", toDay)\n .get();\n return snap.docs.map((d) => d.data() as RollupRow);\n }\n async listIssueEvents(app: string, fp: string, { limit = 50 } = {}) {\n const snap = await this.app(app)\n .collection(\"events\")\n .where(\"fingerprint\", \"==\", fp)\n .orderBy(\"ts\", \"desc\")\n .limit(limit)\n .get();\n return snap.docs.map((d) => {\n const { expireAt: _e, ts: _t, ...rest } = d.data();\n return rest as StoredEvent;\n });\n }\n}\n\n/** Firestore rejects `undefined` values; drop them recursively. */\nfunction strip<T>(v: T): T {\n if (Array.isArray(v)) return v.map(strip) as T;\n if (v && typeof v === \"object\" && v.constructor === Object) {\n return Object.fromEntries(\n Object.entries(v as Record<string, unknown>).filter(([, x]) => x !== undefined).map(([k, x]) => [k, strip(x)]),\n ) as T;\n }\n return v;\n}\n\nfunction chunks<T>(xs: T[], n: number): T[][] {\n const out: T[][] = [];\n for (let i = 0; i < xs.length; i += n) out.push(xs.slice(i, i + n));\n return out;\n}\n"],"mappings":"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","names":[]}
package/dist/index.cjs ADDED
@@ -0,0 +1,13 @@
1
+ "use strict";Object.defineProperty(exports, "__esModule", {value: true});
2
+
3
+ var _chunk237HVZKRcjs = require('./chunk-237HVZKR.cjs');
4
+
5
+
6
+
7
+ var _chunkG5LEIFH7cjs = require('./chunk-G5LEIFH7.cjs');
8
+
9
+
10
+
11
+
12
+ exports.MemoryStore = _chunk237HVZKRcjs.MemoryStore; exports.coalesceRollups = _chunkG5LEIFH7cjs.coalesceRollups; exports.rollupId = _chunkG5LEIFH7cjs.rollupId;
13
+ //# sourceMappingURL=index.cjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["/Users/sivemwanda/Work/Tanstack/deBug/packages/adapters/dist/index.cjs"],"names":[],"mappings":"AAAA;AACE;AACF,wDAA6B;AAC7B;AACE;AACA;AACF,wDAA6B;AAC7B;AACE;AACA;AACA;AACF,gKAAC","file":"/Users/sivemwanda/Work/Tanstack/deBug/packages/adapters/dist/index.cjs"}
@@ -0,0 +1,65 @@
1
+ import { RollupIncrement, RollupRow, StoredEvent, Issue, RollupDimension } from '@debugkit/schema';
2
+
3
+ /** Merge increments with the same target so each batch does one write per row. */
4
+ declare function coalesceRollups(incs: RollupIncrement[]): RollupRow[];
5
+ declare const rollupId: (r: {
6
+ app: string;
7
+ day: string;
8
+ dimension: string;
9
+ key: string;
10
+ }) => string;
11
+
12
+ /** In-process store for tests and the dashboard's demo mode. */
13
+ declare class MemoryStore implements Store {
14
+ apps: Set<string>;
15
+ events: StoredEvent[];
16
+ issues: Map<string, Issue>;
17
+ rollups: Map<string, RollupRow>;
18
+ registerApp(app: string): Promise<void>;
19
+ writeEvents(events: StoredEvent[]): Promise<void>;
20
+ upsertIssues(updates: IssueUpsert[]): Promise<void>;
21
+ incrementRollups(incs: RollupIncrement[]): Promise<void>;
22
+ listApps(): Promise<string[]>;
23
+ listIssues(app: string, { limit }?: {
24
+ limit?: number | undefined;
25
+ }): Promise<Issue[]>;
26
+ getIssue(app: string, fp: string): Promise<Issue | null>;
27
+ getRollups(app: string, dimension: RollupDimension, fromDay: string, toDay: string): Promise<RollupRow[]>;
28
+ listIssueEvents(app: string, fp: string, { limit }?: {
29
+ limit?: number | undefined;
30
+ }): Promise<StoredEvent[]>;
31
+ }
32
+
33
+ interface IssueUpsert {
34
+ app: string;
35
+ fingerprint: string;
36
+ title: string;
37
+ culprit?: string;
38
+ flow?: string;
39
+ ts: string;
40
+ firstTs: string;
41
+ by: number;
42
+ lastEvent: StoredEvent;
43
+ }
44
+ /** Write side, used by the ingest function. */
45
+ interface WriteStore {
46
+ registerApp(app: string): Promise<void>;
47
+ writeEvents(events: StoredEvent[]): Promise<void>;
48
+ upsertIssues(updates: IssueUpsert[]): Promise<void>;
49
+ incrementRollups(incs: RollupIncrement[]): Promise<void>;
50
+ }
51
+ /** Read side, used by the dashboard. */
52
+ interface ReadStore {
53
+ listApps(): Promise<string[]>;
54
+ listIssues(app: string, opts?: {
55
+ limit?: number;
56
+ }): Promise<Issue[]>;
57
+ getIssue(app: string, fingerprint: string): Promise<Issue | null>;
58
+ getRollups(app: string, dimension: RollupDimension, fromDay: string, toDay: string): Promise<RollupRow[]>;
59
+ listIssueEvents(app: string, fingerprint: string, opts?: {
60
+ limit?: number;
61
+ }): Promise<StoredEvent[]>;
62
+ }
63
+ type Store = WriteStore & ReadStore;
64
+
65
+ export { type IssueUpsert, MemoryStore, type ReadStore, type Store, type WriteStore, coalesceRollups, rollupId };
@@ -0,0 +1,65 @@
1
+ import { RollupIncrement, RollupRow, StoredEvent, Issue, RollupDimension } from '@debugkit/schema';
2
+
3
+ /** Merge increments with the same target so each batch does one write per row. */
4
+ declare function coalesceRollups(incs: RollupIncrement[]): RollupRow[];
5
+ declare const rollupId: (r: {
6
+ app: string;
7
+ day: string;
8
+ dimension: string;
9
+ key: string;
10
+ }) => string;
11
+
12
+ /** In-process store for tests and the dashboard's demo mode. */
13
+ declare class MemoryStore implements Store {
14
+ apps: Set<string>;
15
+ events: StoredEvent[];
16
+ issues: Map<string, Issue>;
17
+ rollups: Map<string, RollupRow>;
18
+ registerApp(app: string): Promise<void>;
19
+ writeEvents(events: StoredEvent[]): Promise<void>;
20
+ upsertIssues(updates: IssueUpsert[]): Promise<void>;
21
+ incrementRollups(incs: RollupIncrement[]): Promise<void>;
22
+ listApps(): Promise<string[]>;
23
+ listIssues(app: string, { limit }?: {
24
+ limit?: number | undefined;
25
+ }): Promise<Issue[]>;
26
+ getIssue(app: string, fp: string): Promise<Issue | null>;
27
+ getRollups(app: string, dimension: RollupDimension, fromDay: string, toDay: string): Promise<RollupRow[]>;
28
+ listIssueEvents(app: string, fp: string, { limit }?: {
29
+ limit?: number | undefined;
30
+ }): Promise<StoredEvent[]>;
31
+ }
32
+
33
+ interface IssueUpsert {
34
+ app: string;
35
+ fingerprint: string;
36
+ title: string;
37
+ culprit?: string;
38
+ flow?: string;
39
+ ts: string;
40
+ firstTs: string;
41
+ by: number;
42
+ lastEvent: StoredEvent;
43
+ }
44
+ /** Write side, used by the ingest function. */
45
+ interface WriteStore {
46
+ registerApp(app: string): Promise<void>;
47
+ writeEvents(events: StoredEvent[]): Promise<void>;
48
+ upsertIssues(updates: IssueUpsert[]): Promise<void>;
49
+ incrementRollups(incs: RollupIncrement[]): Promise<void>;
50
+ }
51
+ /** Read side, used by the dashboard. */
52
+ interface ReadStore {
53
+ listApps(): Promise<string[]>;
54
+ listIssues(app: string, opts?: {
55
+ limit?: number;
56
+ }): Promise<Issue[]>;
57
+ getIssue(app: string, fingerprint: string): Promise<Issue | null>;
58
+ getRollups(app: string, dimension: RollupDimension, fromDay: string, toDay: string): Promise<RollupRow[]>;
59
+ listIssueEvents(app: string, fingerprint: string, opts?: {
60
+ limit?: number;
61
+ }): Promise<StoredEvent[]>;
62
+ }
63
+ type Store = WriteStore & ReadStore;
64
+
65
+ export { type IssueUpsert, MemoryStore, type ReadStore, type Store, type WriteStore, coalesceRollups, rollupId };
package/dist/index.js ADDED
@@ -0,0 +1,13 @@
1
+ import {
2
+ MemoryStore
3
+ } from "./chunk-FH2JU2AA.js";
4
+ import {
5
+ coalesceRollups,
6
+ rollupId
7
+ } from "./chunk-IUP4NDEQ.js";
8
+ export {
9
+ MemoryStore,
10
+ coalesceRollups,
11
+ rollupId
12
+ };
13
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":[],"sourcesContent":[],"mappings":"","names":[]}
@@ -0,0 +1,8 @@
1
+ "use strict";Object.defineProperty(exports, "__esModule", {value: true});
2
+
3
+ var _chunk237HVZKRcjs = require('./chunk-237HVZKR.cjs');
4
+ require('./chunk-G5LEIFH7.cjs');
5
+
6
+
7
+ exports.MemoryStore = _chunk237HVZKRcjs.MemoryStore;
8
+ //# sourceMappingURL=memory.cjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["/Users/sivemwanda/Work/Tanstack/deBug/packages/adapters/dist/memory.cjs"],"names":[],"mappings":"AAAA;AACE;AACF,wDAA6B;AAC7B,gCAA6B;AAC7B;AACE;AACF,oDAAC","file":"/Users/sivemwanda/Work/Tanstack/deBug/packages/adapters/dist/memory.cjs"}
@@ -0,0 +1,2 @@
1
+ import '@debugkit/schema';
2
+ export { MemoryStore } from './index.cjs';
@@ -0,0 +1,2 @@
1
+ import '@debugkit/schema';
2
+ export { MemoryStore } from './index.js';
package/dist/memory.js ADDED
@@ -0,0 +1,8 @@
1
+ import {
2
+ MemoryStore
3
+ } from "./chunk-FH2JU2AA.js";
4
+ import "./chunk-IUP4NDEQ.js";
5
+ export {
6
+ MemoryStore
7
+ };
8
+ //# sourceMappingURL=memory.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":[],"sourcesContent":[],"mappings":"","names":[]}
@@ -0,0 +1,28 @@
1
+ {
2
+ "indexes": [
3
+ {
4
+ "collectionGroup": "rollups",
5
+ "queryScope": "COLLECTION",
6
+ "fields": [
7
+ { "fieldPath": "dimension", "order": "ASCENDING" },
8
+ { "fieldPath": "day", "order": "ASCENDING" }
9
+ ]
10
+ },
11
+ {
12
+ "collectionGroup": "events",
13
+ "queryScope": "COLLECTION",
14
+ "fields": [
15
+ { "fieldPath": "fingerprint", "order": "ASCENDING" },
16
+ { "fieldPath": "ts", "order": "DESCENDING" }
17
+ ]
18
+ }
19
+ ],
20
+ "fieldOverrides": [
21
+ {
22
+ "collectionGroup": "events",
23
+ "fieldPath": "expireAt",
24
+ "ttl": true,
25
+ "indexes": []
26
+ }
27
+ ]
28
+ }