@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.
- package/dist/chunk-237HVZKR.cjs +64 -0
- package/dist/chunk-237HVZKR.cjs.map +1 -0
- package/dist/chunk-FH2JU2AA.js +64 -0
- package/dist/chunk-FH2JU2AA.js.map +1 -0
- package/dist/chunk-G5LEIFH7.cjs +19 -0
- package/dist/chunk-G5LEIFH7.cjs.map +1 -0
- package/dist/chunk-IUP4NDEQ.js +19 -0
- package/dist/chunk-IUP4NDEQ.js.map +1 -0
- package/dist/dynamodb.cjs +152 -0
- package/dist/dynamodb.cjs.map +1 -0
- package/dist/dynamodb.d.cts +39 -0
- package/dist/dynamodb.d.ts +39 -0
- package/dist/dynamodb.js +152 -0
- package/dist/dynamodb.js.map +1 -0
- package/dist/firestore.cjs +124 -0
- package/dist/firestore.cjs.map +1 -0
- package/dist/firestore.d.cts +39 -0
- package/dist/firestore.d.ts +39 -0
- package/dist/firestore.js +124 -0
- package/dist/firestore.js.map +1 -0
- package/dist/index.cjs +13 -0
- package/dist/index.cjs.map +1 -0
- package/dist/index.d.cts +65 -0
- package/dist/index.d.ts +65 -0
- package/dist/index.js +13 -0
- package/dist/index.js.map +1 -0
- package/dist/memory.cjs +8 -0
- package/dist/memory.cjs.map +1 -0
- package/dist/memory.d.cts +2 -0
- package/dist/memory.d.ts +2 -0
- package/dist/memory.js +8 -0
- package/dist/memory.js.map +1 -0
- package/firestore.indexes.json +28 -0
- package/package.json +83 -3
- package/README.md +0 -3
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
"use strict";Object.defineProperty(exports, "__esModule", {value: true}); function _nullishCoalesce(lhs, rhsFn) { if (lhs != null) { return lhs; } else { return rhsFn(); } } function _optionalChain(ops) { let lastAccessLHS = undefined; let value = ops[0]; let i = 1; while (i < ops.length) { const op = ops[i]; const fn = ops[i + 1]; i += 2; if ((op === 'optionalAccess' || op === 'optionalCall') && value == null) { return undefined; } if (op === 'access' || op === 'optionalAccess') { lastAccessLHS = value; value = fn(value); } else if (op === 'call' || op === 'optionalCall') { value = fn((...args) => value.call(lastAccessLHS, ...args)); lastAccessLHS = undefined; } } return value; } var _class;
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
var _chunkG5LEIFH7cjs = require('./chunk-G5LEIFH7.cjs');
|
|
5
|
+
|
|
6
|
+
// src/memory.ts
|
|
7
|
+
var MemoryStore = (_class = class {constructor() { _class.prototype.__init.call(this);_class.prototype.__init2.call(this);_class.prototype.__init3.call(this);_class.prototype.__init4.call(this); }
|
|
8
|
+
__init() {this.apps = /* @__PURE__ */ new Set()}
|
|
9
|
+
__init2() {this.events = []}
|
|
10
|
+
__init3() {this.issues = /* @__PURE__ */ new Map()}
|
|
11
|
+
__init4() {this.rollups = /* @__PURE__ */ new Map()}
|
|
12
|
+
async registerApp(app) {
|
|
13
|
+
this.apps.add(app);
|
|
14
|
+
}
|
|
15
|
+
async writeEvents(events) {
|
|
16
|
+
this.events.push(...events);
|
|
17
|
+
}
|
|
18
|
+
async upsertIssues(updates) {
|
|
19
|
+
for (const u of updates) {
|
|
20
|
+
const k = `${u.app}/${u.fingerprint}`;
|
|
21
|
+
const cur = this.issues.get(k);
|
|
22
|
+
this.issues.set(k, {
|
|
23
|
+
app: u.app,
|
|
24
|
+
fingerprint: u.fingerprint,
|
|
25
|
+
title: u.title,
|
|
26
|
+
culprit: u.culprit,
|
|
27
|
+
flow: u.flow,
|
|
28
|
+
firstSeen: cur && cur.firstSeen < u.firstTs ? cur.firstSeen : u.firstTs,
|
|
29
|
+
lastSeen: cur && cur.lastSeen > u.ts ? cur.lastSeen : u.ts,
|
|
30
|
+
count: (_nullishCoalesce(_optionalChain([cur, 'optionalAccess', _ => _.count]), () => ( 0))) + u.by,
|
|
31
|
+
lastEvent: cur && cur.lastSeen > u.ts ? cur.lastEvent : u.lastEvent
|
|
32
|
+
});
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
async incrementRollups(incs) {
|
|
36
|
+
for (const r of _chunkG5LEIFH7cjs.coalesceRollups.call(void 0, incs)) {
|
|
37
|
+
const k = `${r.app}/${_chunkG5LEIFH7cjs.rollupId.call(void 0, r)}`;
|
|
38
|
+
const cur = this.rollups.get(k);
|
|
39
|
+
this.rollups.set(k, cur ? { ...cur, errors: cur.errors + r.errors, events: cur.events + r.events } : r);
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
async listApps() {
|
|
43
|
+
return [...this.apps].sort();
|
|
44
|
+
}
|
|
45
|
+
async listIssues(app, { limit = 200 } = {}) {
|
|
46
|
+
return [...this.issues.values()].filter((i) => i.app === app).sort((a, b) => b.lastSeen.localeCompare(a.lastSeen)).slice(0, limit);
|
|
47
|
+
}
|
|
48
|
+
async getIssue(app, fp) {
|
|
49
|
+
return _nullishCoalesce(this.issues.get(`${app}/${fp}`), () => ( null));
|
|
50
|
+
}
|
|
51
|
+
async getRollups(app, dimension, fromDay, toDay) {
|
|
52
|
+
return [...this.rollups.values()].filter(
|
|
53
|
+
(r) => r.app === app && r.dimension === dimension && r.day >= fromDay && r.day <= toDay
|
|
54
|
+
);
|
|
55
|
+
}
|
|
56
|
+
async listIssueEvents(app, fp, { limit = 50 } = {}) {
|
|
57
|
+
return this.events.filter((e) => e.app === app && e.fingerprint === fp).sort((a, b) => b.event.ts.localeCompare(a.event.ts)).slice(0, limit);
|
|
58
|
+
}
|
|
59
|
+
}, _class);
|
|
60
|
+
|
|
61
|
+
|
|
62
|
+
|
|
63
|
+
exports.MemoryStore = MemoryStore;
|
|
64
|
+
//# sourceMappingURL=chunk-237HVZKR.cjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["/Users/sivemwanda/Work/Tanstack/deBug/packages/adapters/dist/chunk-237HVZKR.cjs","../src/memory.ts"],"names":[],"mappings":"AAAA;AACE;AACA;AACF,wDAA6B;AAC7B;AACA;ACAO,IAAM,YAAA,YAAN,MAAmC;AAAA,iBACxC,KAAA,kBAAO,IAAI,GAAA,CAAY,EAAA;AAAA,kBACvB,OAAA,EAAwB,CAAC,EAAA;AAAA,kBACzB,OAAA,kBAAS,IAAI,GAAA,CAAmB,EAAA;AAAA,kBAChC,QAAA,kBAAU,IAAI,GAAA,CAAuB,EAAA;AAAA,EAErC,MAAM,WAAA,CAAY,GAAA,EAAa;AAC7B,IAAA,IAAA,CAAK,IAAA,CAAK,GAAA,CAAI,GAAG,CAAA;AAAA,EACnB;AAAA,EACA,MAAM,WAAA,CAAY,MAAA,EAAuB;AACvC,IAAA,IAAA,CAAK,MAAA,CAAO,IAAA,CAAK,GAAG,MAAM,CAAA;AAAA,EAC5B;AAAA,EACA,MAAM,YAAA,CAAa,OAAA,EAAwB;AACzC,IAAA,IAAA,CAAA,MAAW,EAAA,GAAK,OAAA,EAAS;AACvB,MAAA,MAAM,EAAA,EAAI,CAAA,EAAA;AACJ,MAAA;AACD,MAAA;AACI,QAAA;AACP,QAAA;AACS,QAAA;AACA,QAAA;AACD,QAAA;AACR,QAAA;AACA,QAAA;AACQ,QAAA;AACR,QAAA;AACD,MAAA;AACH,IAAA;AACF,EAAA;AACM,EAAA;AACO,IAAA;AACC,MAAA;AACJ,MAAA;AACD,MAAA;AACP,IAAA;AACF,EAAA;AAEM,EAAA;AACO,IAAA;AACb,EAAA;AACM,EAAA;AACO,IAAA;AAIb,EAAA;AACe,EAAA;AACD,IAAA;AACd,EAAA;AACM,EAAA;AACO,IAAA;AACA,MAAA;AACX,IAAA;AACF,EAAA;AACM,EAAA;AACQ,IAAA;AAId,EAAA;AACF;ADNiB;AACA;AACA;AACA","file":"/Users/sivemwanda/Work/Tanstack/deBug/packages/adapters/dist/chunk-237HVZKR.cjs","sourcesContent":[null,"import type { Issue, RollupDimension, RollupIncrement, RollupRow, StoredEvent } from \"@debugkit/schema\";\nimport { coalesceRollups, rollupId } from \"./rollups\";\nimport type { IssueUpsert, Store } from \"./index\";\n\n/** In-process store for tests and the dashboard's demo mode. */\nexport class MemoryStore implements Store {\n apps = new Set<string>();\n events: StoredEvent[] = [];\n issues = new Map<string, Issue>();\n rollups = new Map<string, RollupRow>();\n\n async registerApp(app: string) {\n this.apps.add(app);\n }\n async writeEvents(events: StoredEvent[]) {\n this.events.push(...events);\n }\n async upsertIssues(updates: IssueUpsert[]) {\n for (const u of updates) {\n const k = `${u.app}/${u.fingerprint}`;\n const cur = this.issues.get(k);\n this.issues.set(k, {\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: cur && cur.lastSeen > u.ts ? cur.lastSeen : u.ts,\n count: (cur?.count ?? 0) + u.by,\n lastEvent: cur && cur.lastSeen > u.ts ? cur.lastEvent : u.lastEvent,\n });\n }\n }\n async incrementRollups(incs: RollupIncrement[]) {\n for (const r of coalesceRollups(incs)) {\n const k = `${r.app}/${rollupId(r)}`;\n const cur = this.rollups.get(k);\n this.rollups.set(k, cur ? { ...cur, errors: cur.errors + r.errors, events: cur.events + r.events } : r);\n }\n }\n\n async listApps() {\n return [...this.apps].sort();\n }\n async listIssues(app: string, { limit = 200 } = {}) {\n return [...this.issues.values()]\n .filter((i) => i.app === app)\n .sort((a, b) => b.lastSeen.localeCompare(a.lastSeen))\n .slice(0, limit);\n }\n async getIssue(app: string, fp: string) {\n return this.issues.get(`${app}/${fp}`) ?? null;\n }\n async getRollups(app: string, dimension: RollupDimension, fromDay: string, toDay: string) {\n return [...this.rollups.values()].filter(\n (r) => r.app === app && r.dimension === dimension && r.day >= fromDay && r.day <= toDay,\n );\n }\n async listIssueEvents(app: string, fp: string, { limit = 50 } = {}) {\n return this.events\n .filter((e) => e.app === app && e.fingerprint === fp)\n .sort((a, b) => b.event.ts.localeCompare(a.event.ts))\n .slice(0, limit);\n }\n}\n"]}
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
import {
|
|
2
|
+
coalesceRollups,
|
|
3
|
+
rollupId
|
|
4
|
+
} from "./chunk-IUP4NDEQ.js";
|
|
5
|
+
|
|
6
|
+
// src/memory.ts
|
|
7
|
+
var MemoryStore = class {
|
|
8
|
+
apps = /* @__PURE__ */ new Set();
|
|
9
|
+
events = [];
|
|
10
|
+
issues = /* @__PURE__ */ new Map();
|
|
11
|
+
rollups = /* @__PURE__ */ new Map();
|
|
12
|
+
async registerApp(app) {
|
|
13
|
+
this.apps.add(app);
|
|
14
|
+
}
|
|
15
|
+
async writeEvents(events) {
|
|
16
|
+
this.events.push(...events);
|
|
17
|
+
}
|
|
18
|
+
async upsertIssues(updates) {
|
|
19
|
+
for (const u of updates) {
|
|
20
|
+
const k = `${u.app}/${u.fingerprint}`;
|
|
21
|
+
const cur = this.issues.get(k);
|
|
22
|
+
this.issues.set(k, {
|
|
23
|
+
app: u.app,
|
|
24
|
+
fingerprint: u.fingerprint,
|
|
25
|
+
title: u.title,
|
|
26
|
+
culprit: u.culprit,
|
|
27
|
+
flow: u.flow,
|
|
28
|
+
firstSeen: cur && cur.firstSeen < u.firstTs ? cur.firstSeen : u.firstTs,
|
|
29
|
+
lastSeen: cur && cur.lastSeen > u.ts ? cur.lastSeen : u.ts,
|
|
30
|
+
count: (cur?.count ?? 0) + u.by,
|
|
31
|
+
lastEvent: cur && cur.lastSeen > u.ts ? cur.lastEvent : u.lastEvent
|
|
32
|
+
});
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
async incrementRollups(incs) {
|
|
36
|
+
for (const r of coalesceRollups(incs)) {
|
|
37
|
+
const k = `${r.app}/${rollupId(r)}`;
|
|
38
|
+
const cur = this.rollups.get(k);
|
|
39
|
+
this.rollups.set(k, cur ? { ...cur, errors: cur.errors + r.errors, events: cur.events + r.events } : r);
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
async listApps() {
|
|
43
|
+
return [...this.apps].sort();
|
|
44
|
+
}
|
|
45
|
+
async listIssues(app, { limit = 200 } = {}) {
|
|
46
|
+
return [...this.issues.values()].filter((i) => i.app === app).sort((a, b) => b.lastSeen.localeCompare(a.lastSeen)).slice(0, limit);
|
|
47
|
+
}
|
|
48
|
+
async getIssue(app, fp) {
|
|
49
|
+
return this.issues.get(`${app}/${fp}`) ?? null;
|
|
50
|
+
}
|
|
51
|
+
async getRollups(app, dimension, fromDay, toDay) {
|
|
52
|
+
return [...this.rollups.values()].filter(
|
|
53
|
+
(r) => r.app === app && r.dimension === dimension && r.day >= fromDay && r.day <= toDay
|
|
54
|
+
);
|
|
55
|
+
}
|
|
56
|
+
async listIssueEvents(app, fp, { limit = 50 } = {}) {
|
|
57
|
+
return this.events.filter((e) => e.app === app && e.fingerprint === fp).sort((a, b) => b.event.ts.localeCompare(a.event.ts)).slice(0, limit);
|
|
58
|
+
}
|
|
59
|
+
};
|
|
60
|
+
|
|
61
|
+
export {
|
|
62
|
+
MemoryStore
|
|
63
|
+
};
|
|
64
|
+
//# sourceMappingURL=chunk-FH2JU2AA.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/memory.ts"],"sourcesContent":["import type { Issue, RollupDimension, RollupIncrement, RollupRow, StoredEvent } from \"@debugkit/schema\";\nimport { coalesceRollups, rollupId } from \"./rollups\";\nimport type { IssueUpsert, Store } from \"./index\";\n\n/** In-process store for tests and the dashboard's demo mode. */\nexport class MemoryStore implements Store {\n apps = new Set<string>();\n events: StoredEvent[] = [];\n issues = new Map<string, Issue>();\n rollups = new Map<string, RollupRow>();\n\n async registerApp(app: string) {\n this.apps.add(app);\n }\n async writeEvents(events: StoredEvent[]) {\n this.events.push(...events);\n }\n async upsertIssues(updates: IssueUpsert[]) {\n for (const u of updates) {\n const k = `${u.app}/${u.fingerprint}`;\n const cur = this.issues.get(k);\n this.issues.set(k, {\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: cur && cur.lastSeen > u.ts ? cur.lastSeen : u.ts,\n count: (cur?.count ?? 0) + u.by,\n lastEvent: cur && cur.lastSeen > u.ts ? cur.lastEvent : u.lastEvent,\n });\n }\n }\n async incrementRollups(incs: RollupIncrement[]) {\n for (const r of coalesceRollups(incs)) {\n const k = `${r.app}/${rollupId(r)}`;\n const cur = this.rollups.get(k);\n this.rollups.set(k, cur ? { ...cur, errors: cur.errors + r.errors, events: cur.events + r.events } : r);\n }\n }\n\n async listApps() {\n return [...this.apps].sort();\n }\n async listIssues(app: string, { limit = 200 } = {}) {\n return [...this.issues.values()]\n .filter((i) => i.app === app)\n .sort((a, b) => b.lastSeen.localeCompare(a.lastSeen))\n .slice(0, limit);\n }\n async getIssue(app: string, fp: string) {\n return this.issues.get(`${app}/${fp}`) ?? null;\n }\n async getRollups(app: string, dimension: RollupDimension, fromDay: string, toDay: string) {\n return [...this.rollups.values()].filter(\n (r) => r.app === app && r.dimension === dimension && r.day >= fromDay && r.day <= toDay,\n );\n }\n async listIssueEvents(app: string, fp: string, { limit = 50 } = {}) {\n return this.events\n .filter((e) => e.app === app && e.fingerprint === fp)\n .sort((a, b) => b.event.ts.localeCompare(a.event.ts))\n .slice(0, limit);\n }\n}\n"],"mappings":";;;;;;AAKO,IAAM,cAAN,MAAmC;AAAA,EACxC,OAAO,oBAAI,IAAY;AAAA,EACvB,SAAwB,CAAC;AAAA,EACzB,SAAS,oBAAI,IAAmB;AAAA,EAChC,UAAU,oBAAI,IAAuB;AAAA,EAErC,MAAM,YAAY,KAAa;AAC7B,SAAK,KAAK,IAAI,GAAG;AAAA,EACnB;AAAA,EACA,MAAM,YAAY,QAAuB;AACvC,SAAK,OAAO,KAAK,GAAG,MAAM;AAAA,EAC5B;AAAA,EACA,MAAM,aAAa,SAAwB;AACzC,eAAW,KAAK,SAAS;AACvB,YAAM,IAAI,GAAG,EAAE,GAAG,IAAI,EAAE,WAAW;AACnC,YAAM,MAAM,KAAK,OAAO,IAAI,CAAC;AAC7B,WAAK,OAAO,IAAI,GAAG;AAAA,QACjB,KAAK,EAAE;AAAA,QACP,aAAa,EAAE;AAAA,QACf,OAAO,EAAE;AAAA,QACT,SAAS,EAAE;AAAA,QACX,MAAM,EAAE;AAAA,QACR,WAAW,OAAO,IAAI,YAAY,EAAE,UAAU,IAAI,YAAY,EAAE;AAAA,QAChE,UAAU,OAAO,IAAI,WAAW,EAAE,KAAK,IAAI,WAAW,EAAE;AAAA,QACxD,QAAQ,KAAK,SAAS,KAAK,EAAE;AAAA,QAC7B,WAAW,OAAO,IAAI,WAAW,EAAE,KAAK,IAAI,YAAY,EAAE;AAAA,MAC5D,CAAC;AAAA,IACH;AAAA,EACF;AAAA,EACA,MAAM,iBAAiB,MAAyB;AAC9C,eAAW,KAAK,gBAAgB,IAAI,GAAG;AACrC,YAAM,IAAI,GAAG,EAAE,GAAG,IAAI,SAAS,CAAC,CAAC;AACjC,YAAM,MAAM,KAAK,QAAQ,IAAI,CAAC;AAC9B,WAAK,QAAQ,IAAI,GAAG,MAAM,EAAE,GAAG,KAAK,QAAQ,IAAI,SAAS,EAAE,QAAQ,QAAQ,IAAI,SAAS,EAAE,OAAO,IAAI,CAAC;AAAA,IACxG;AAAA,EACF;AAAA,EAEA,MAAM,WAAW;AACf,WAAO,CAAC,GAAG,KAAK,IAAI,EAAE,KAAK;AAAA,EAC7B;AAAA,EACA,MAAM,WAAW,KAAa,EAAE,QAAQ,IAAI,IAAI,CAAC,GAAG;AAClD,WAAO,CAAC,GAAG,KAAK,OAAO,OAAO,CAAC,EAC5B,OAAO,CAAC,MAAM,EAAE,QAAQ,GAAG,EAC3B,KAAK,CAAC,GAAG,MAAM,EAAE,SAAS,cAAc,EAAE,QAAQ,CAAC,EACnD,MAAM,GAAG,KAAK;AAAA,EACnB;AAAA,EACA,MAAM,SAAS,KAAa,IAAY;AACtC,WAAO,KAAK,OAAO,IAAI,GAAG,GAAG,IAAI,EAAE,EAAE,KAAK;AAAA,EAC5C;AAAA,EACA,MAAM,WAAW,KAAa,WAA4B,SAAiB,OAAe;AACxF,WAAO,CAAC,GAAG,KAAK,QAAQ,OAAO,CAAC,EAAE;AAAA,MAChC,CAAC,MAAM,EAAE,QAAQ,OAAO,EAAE,cAAc,aAAa,EAAE,OAAO,WAAW,EAAE,OAAO;AAAA,IACpF;AAAA,EACF;AAAA,EACA,MAAM,gBAAgB,KAAa,IAAY,EAAE,QAAQ,GAAG,IAAI,CAAC,GAAG;AAClE,WAAO,KAAK,OACT,OAAO,CAAC,MAAM,EAAE,QAAQ,OAAO,EAAE,gBAAgB,EAAE,EACnD,KAAK,CAAC,GAAG,MAAM,EAAE,MAAM,GAAG,cAAc,EAAE,MAAM,EAAE,CAAC,EACnD,MAAM,GAAG,KAAK;AAAA,EACnB;AACF;","names":[]}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
"use strict";Object.defineProperty(exports, "__esModule", {value: true}); function _nullishCoalesce(lhs, rhsFn) { if (lhs != null) { return lhs; } else { return rhsFn(); } }// src/rollups.ts
|
|
2
|
+
function coalesceRollups(incs) {
|
|
3
|
+
const map = /* @__PURE__ */ new Map();
|
|
4
|
+
for (const i of incs) {
|
|
5
|
+
const id = rollupId(i);
|
|
6
|
+
const row = _nullishCoalesce(map.get(id), () => ( { app: i.app, day: i.day, dimension: i.dimension, key: i.key, errors: 0, events: 0 }));
|
|
7
|
+
if (i.kind === "error") row.errors += i.by;
|
|
8
|
+
else row.events += i.by;
|
|
9
|
+
map.set(id, row);
|
|
10
|
+
}
|
|
11
|
+
return [...map.values()];
|
|
12
|
+
}
|
|
13
|
+
var rollupId = (r) => `${r.day}__${r.dimension}__${encodeURIComponent(r.key)}`;
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
exports.coalesceRollups = coalesceRollups; exports.rollupId = rollupId;
|
|
19
|
+
//# sourceMappingURL=chunk-G5LEIFH7.cjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["/Users/sivemwanda/Work/Tanstack/deBug/packages/adapters/dist/chunk-G5LEIFH7.cjs","../src/rollups.ts"],"names":[],"mappings":"AAAA;ACGO,SAAS,eAAA,CAAgB,IAAA,EAAsC;AACpE,EAAA,MAAM,IAAA,kBAAM,IAAI,GAAA,CAAuB,CAAA;AACvC,EAAA,IAAA,CAAA,MAAW,EAAA,GAAK,IAAA,EAAM;AACpB,IAAA,MAAM,GAAA,EAAK,QAAA,CAAS,CAAC,CAAA;AACrB,IAAA,MAAM,IAAA,mBAAM,GAAA,CAAI,GAAA,CAAI,EAAE,CAAA,UAAK,EAAE,GAAA,EAAK,CAAA,CAAE,GAAA,EAAK,GAAA,EAAK,CAAA,CAAE,GAAA,EAAK,SAAA,EAAW,CAAA,CAAE,SAAA,EAAW,GAAA,EAAK,CAAA,CAAE,GAAA,EAAK,MAAA,EAAQ,CAAA,EAAG,MAAA,EAAQ,EAAE,GAAA;AAC9G,IAAA,GAAA,CAAI,CAAA,CAAE,KAAA,IAAS,OAAA,EAAS,GAAA,CAAI,OAAA,GAAU,CAAA,CAAE,EAAA;AAAA,IAAA,KACnC,GAAA,CAAI,OAAA,GAAU,CAAA,CAAE,EAAA;AACrB,IAAA,GAAA,CAAI,GAAA,CAAI,EAAA,EAAI,GAAG,CAAA;AAAA,EACjB;AACA,EAAA,OAAO,CAAC,GAAG,GAAA,CAAI,MAAA,CAAO,CAAC,CAAA;AACzB;AAEO,IAAM,SAAA,EAAW,CAAC,CAAA,EAAA,GACvB,CAAA,EAAA;ADHqB;AACA;AACA;AACA;AACA","file":"/Users/sivemwanda/Work/Tanstack/deBug/packages/adapters/dist/chunk-G5LEIFH7.cjs","sourcesContent":[null,"import type { RollupIncrement, RollupRow } from \"@debugkit/schema\";\n\n/** Merge increments with the same target so each batch does one write per row. */\nexport function coalesceRollups(incs: RollupIncrement[]): RollupRow[] {\n const map = new Map<string, RollupRow>();\n for (const i of incs) {\n const id = rollupId(i);\n const row = map.get(id) ?? { app: i.app, day: i.day, dimension: i.dimension, key: i.key, errors: 0, events: 0 };\n if (i.kind === \"error\") row.errors += i.by;\n else row.events += i.by;\n map.set(id, row);\n }\n return [...map.values()];\n}\n\nexport const rollupId = (r: { app: string; day: string; dimension: string; key: string }) =>\n `${r.day}__${r.dimension}__${encodeURIComponent(r.key)}`;\n"]}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
// src/rollups.ts
|
|
2
|
+
function coalesceRollups(incs) {
|
|
3
|
+
const map = /* @__PURE__ */ new Map();
|
|
4
|
+
for (const i of incs) {
|
|
5
|
+
const id = rollupId(i);
|
|
6
|
+
const row = map.get(id) ?? { app: i.app, day: i.day, dimension: i.dimension, key: i.key, errors: 0, events: 0 };
|
|
7
|
+
if (i.kind === "error") row.errors += i.by;
|
|
8
|
+
else row.events += i.by;
|
|
9
|
+
map.set(id, row);
|
|
10
|
+
}
|
|
11
|
+
return [...map.values()];
|
|
12
|
+
}
|
|
13
|
+
var rollupId = (r) => `${r.day}__${r.dimension}__${encodeURIComponent(r.key)}`;
|
|
14
|
+
|
|
15
|
+
export {
|
|
16
|
+
coalesceRollups,
|
|
17
|
+
rollupId
|
|
18
|
+
};
|
|
19
|
+
//# sourceMappingURL=chunk-IUP4NDEQ.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/rollups.ts"],"sourcesContent":["import type { RollupIncrement, RollupRow } from \"@debugkit/schema\";\n\n/** Merge increments with the same target so each batch does one write per row. */\nexport function coalesceRollups(incs: RollupIncrement[]): RollupRow[] {\n const map = new Map<string, RollupRow>();\n for (const i of incs) {\n const id = rollupId(i);\n const row = map.get(id) ?? { app: i.app, day: i.day, dimension: i.dimension, key: i.key, errors: 0, events: 0 };\n if (i.kind === \"error\") row.errors += i.by;\n else row.events += i.by;\n map.set(id, row);\n }\n return [...map.values()];\n}\n\nexport const rollupId = (r: { app: string; day: string; dimension: string; key: string }) =>\n `${r.day}__${r.dimension}__${encodeURIComponent(r.key)}`;\n"],"mappings":";AAGO,SAAS,gBAAgB,MAAsC;AACpE,QAAM,MAAM,oBAAI,IAAuB;AACvC,aAAW,KAAK,MAAM;AACpB,UAAM,KAAK,SAAS,CAAC;AACrB,UAAM,MAAM,IAAI,IAAI,EAAE,KAAK,EAAE,KAAK,EAAE,KAAK,KAAK,EAAE,KAAK,WAAW,EAAE,WAAW,KAAK,EAAE,KAAK,QAAQ,GAAG,QAAQ,EAAE;AAC9G,QAAI,EAAE,SAAS,QAAS,KAAI,UAAU,EAAE;AAAA,QACnC,KAAI,UAAU,EAAE;AACrB,QAAI,IAAI,IAAI,GAAG;AAAA,EACjB;AACA,SAAO,CAAC,GAAG,IAAI,OAAO,CAAC;AACzB;AAEO,IAAM,WAAW,CAAC,MACvB,GAAG,EAAE,GAAG,KAAK,EAAE,SAAS,KAAK,mBAAmB,EAAE,GAAG,CAAC;","names":[]}
|
|
@@ -0,0 +1,152 @@
|
|
|
1
|
+
"use strict";Object.defineProperty(exports, "__esModule", {value: true}); function _nullishCoalesce(lhs, rhsFn) { if (lhs != null) { return lhs; } else { return rhsFn(); } }
|
|
2
|
+
|
|
3
|
+
var _chunkG5LEIFH7cjs = require('./chunk-G5LEIFH7.cjs');
|
|
4
|
+
|
|
5
|
+
// src/dynamodb.ts
|
|
6
|
+
var _libdynamodb = require('@aws-sdk/lib-dynamodb');
|
|
7
|
+
var DynamoStore = class {
|
|
8
|
+
constructor(doc, opts) {
|
|
9
|
+
this.doc = doc;
|
|
10
|
+
this.opts = opts;
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
get table() {
|
|
15
|
+
return this.opts.table;
|
|
16
|
+
}
|
|
17
|
+
async registerApp(app) {
|
|
18
|
+
await this.doc.send(new (0, _libdynamodb.PutCommand)({ TableName: this.table, Item: { pk: "APPS", sk: app } }));
|
|
19
|
+
}
|
|
20
|
+
async writeEvents(events) {
|
|
21
|
+
const ttl = (e) => Math.floor(Date.parse(e.receivedAt) / 1e3) + (_nullishCoalesce(this.opts.retentionDays, () => ( 30))) * 86400;
|
|
22
|
+
const items = events.flatMap((e) => {
|
|
23
|
+
const sk = `${e.event.ts}#${e.event.id}`;
|
|
24
|
+
const base = { ...e, ttl: ttl(e) };
|
|
25
|
+
const rows = [{ pk: `APP#${e.app}#EV#${e.day}`, sk, ...base }];
|
|
26
|
+
if (e.fingerprint) rows.push({ pk: `APP#${e.app}#ISSUEEV#${e.fingerprint}`, sk, ...base });
|
|
27
|
+
return rows;
|
|
28
|
+
});
|
|
29
|
+
for (let i = 0; i < items.length; i += 25) {
|
|
30
|
+
let req = {
|
|
31
|
+
[this.table]: items.slice(i, i + 25).map((Item) => ({ PutRequest: { Item } }))
|
|
32
|
+
};
|
|
33
|
+
for (let attempt = 0; req && Object.keys(req).length && attempt < 5; attempt++) {
|
|
34
|
+
const out = await this.doc.send(new (0, _libdynamodb.BatchWriteCommand)({ RequestItems: req }));
|
|
35
|
+
req = out.UnprocessedItems;
|
|
36
|
+
if (req && Object.keys(req).length) await new Promise((r) => setTimeout(r, 50 * 2 ** attempt));
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
async upsertIssues(updates) {
|
|
41
|
+
await Promise.all(
|
|
42
|
+
updates.map(async (u) => {
|
|
43
|
+
await this.doc.send(
|
|
44
|
+
new (0, _libdynamodb.UpdateCommand)({
|
|
45
|
+
TableName: this.table,
|
|
46
|
+
Key: { pk: `APP#${u.app}#ISSUE`, sk: u.fingerprint },
|
|
47
|
+
UpdateExpression: "ADD #count :by SET app = :app, fingerprint = :fp, title = :title, flow = :flow, culprit = :culprit, firstSeen = if_not_exists(firstSeen, :first)",
|
|
48
|
+
ExpressionAttributeNames: { "#count": "count" },
|
|
49
|
+
ExpressionAttributeValues: {
|
|
50
|
+
":by": u.by,
|
|
51
|
+
":app": u.app,
|
|
52
|
+
":fp": u.fingerprint,
|
|
53
|
+
":title": u.title,
|
|
54
|
+
":flow": _nullishCoalesce(u.flow, () => ( null)),
|
|
55
|
+
":culprit": _nullishCoalesce(u.culprit, () => ( null)),
|
|
56
|
+
":first": u.firstTs
|
|
57
|
+
}
|
|
58
|
+
})
|
|
59
|
+
);
|
|
60
|
+
await this.doc.send(
|
|
61
|
+
new (0, _libdynamodb.UpdateCommand)({
|
|
62
|
+
TableName: this.table,
|
|
63
|
+
Key: { pk: `APP#${u.app}#ISSUE`, sk: u.fingerprint },
|
|
64
|
+
UpdateExpression: "SET lastSeen = :ts, lastEvent = :ev",
|
|
65
|
+
ConditionExpression: "attribute_not_exists(lastSeen) OR lastSeen < :ts",
|
|
66
|
+
ExpressionAttributeValues: { ":ts": u.ts, ":ev": u.lastEvent }
|
|
67
|
+
})
|
|
68
|
+
).catch((e) => {
|
|
69
|
+
if (e.name !== "ConditionalCheckFailedException") throw e;
|
|
70
|
+
});
|
|
71
|
+
await this.doc.send(
|
|
72
|
+
new (0, _libdynamodb.UpdateCommand)({
|
|
73
|
+
TableName: this.table,
|
|
74
|
+
Key: { pk: `APP#${u.app}#ISSUE`, sk: u.fingerprint },
|
|
75
|
+
UpdateExpression: "SET firstSeen = :first",
|
|
76
|
+
ConditionExpression: "firstSeen > :first",
|
|
77
|
+
ExpressionAttributeValues: { ":first": u.firstTs }
|
|
78
|
+
})
|
|
79
|
+
).catch((e) => {
|
|
80
|
+
if (e.name !== "ConditionalCheckFailedException") throw e;
|
|
81
|
+
});
|
|
82
|
+
})
|
|
83
|
+
);
|
|
84
|
+
}
|
|
85
|
+
async incrementRollups(incs) {
|
|
86
|
+
await Promise.all(
|
|
87
|
+
_chunkG5LEIFH7cjs.coalesceRollups.call(void 0, incs).map(
|
|
88
|
+
(r) => this.doc.send(
|
|
89
|
+
new (0, _libdynamodb.UpdateCommand)({
|
|
90
|
+
TableName: this.table,
|
|
91
|
+
Key: { pk: `APP#${r.app}#ROLLUP#${r.dimension}`, sk: `${r.day}#${r.key}` },
|
|
92
|
+
UpdateExpression: "ADD errors :e, events :v SET app = :app, #day = :day, dimension = :dim, #key = :key",
|
|
93
|
+
ExpressionAttributeNames: { "#day": "day", "#key": "key" },
|
|
94
|
+
ExpressionAttributeValues: { ":e": r.errors, ":v": r.events, ":app": r.app, ":day": r.day, ":dim": r.dimension, ":key": r.key }
|
|
95
|
+
})
|
|
96
|
+
)
|
|
97
|
+
)
|
|
98
|
+
);
|
|
99
|
+
}
|
|
100
|
+
async listApps() {
|
|
101
|
+
return (await this.queryAll({ pk: "APPS" })).map((i) => i.sk).sort();
|
|
102
|
+
}
|
|
103
|
+
async listIssues(app, { limit = 200 } = {}) {
|
|
104
|
+
const items = await this.queryAll({ pk: `APP#${app}#ISSUE` });
|
|
105
|
+
return items.sort((a, b) => b.lastSeen.localeCompare(a.lastSeen)).slice(0, limit).map(clean);
|
|
106
|
+
}
|
|
107
|
+
async getIssue(app, fp) {
|
|
108
|
+
const out = await this.doc.send(new (0, _libdynamodb.GetCommand)({ TableName: this.table, Key: { pk: `APP#${app}#ISSUE`, sk: fp } }));
|
|
109
|
+
return out.Item ? clean(out.Item) : null;
|
|
110
|
+
}
|
|
111
|
+
async getRollups(app, dimension, fromDay, toDay) {
|
|
112
|
+
const items = await this.queryAll({ pk: `APP#${app}#ROLLUP#${dimension}`, between: [`${fromDay}#`, `${toDay}#\uFFFF`] });
|
|
113
|
+
return items.map((i) => clean(i));
|
|
114
|
+
}
|
|
115
|
+
async listIssueEvents(app, fp, { limit = 50 } = {}) {
|
|
116
|
+
const out = await this.doc.send(
|
|
117
|
+
new (0, _libdynamodb.QueryCommand)({
|
|
118
|
+
TableName: this.table,
|
|
119
|
+
KeyConditionExpression: "pk = :pk",
|
|
120
|
+
ExpressionAttributeValues: { ":pk": `APP#${app}#ISSUEEV#${fp}` },
|
|
121
|
+
ScanIndexForward: false,
|
|
122
|
+
Limit: limit
|
|
123
|
+
})
|
|
124
|
+
);
|
|
125
|
+
return (_nullishCoalesce(out.Items, () => ( []))).map((i) => clean(i));
|
|
126
|
+
}
|
|
127
|
+
async queryAll({ pk, between }) {
|
|
128
|
+
const items = [];
|
|
129
|
+
let ExclusiveStartKey;
|
|
130
|
+
do {
|
|
131
|
+
const out = await this.doc.send(
|
|
132
|
+
new (0, _libdynamodb.QueryCommand)({
|
|
133
|
+
TableName: this.table,
|
|
134
|
+
KeyConditionExpression: between ? "pk = :pk AND sk BETWEEN :a AND :b" : "pk = :pk",
|
|
135
|
+
ExpressionAttributeValues: between ? { ":pk": pk, ":a": between[0], ":b": between[1] } : { ":pk": pk },
|
|
136
|
+
ExclusiveStartKey
|
|
137
|
+
})
|
|
138
|
+
);
|
|
139
|
+
items.push(..._nullishCoalesce(out.Items, () => ( [])));
|
|
140
|
+
ExclusiveStartKey = out.LastEvaluatedKey;
|
|
141
|
+
} while (ExclusiveStartKey);
|
|
142
|
+
return items;
|
|
143
|
+
}
|
|
144
|
+
};
|
|
145
|
+
function clean(item) {
|
|
146
|
+
const { pk: _p, sk: _s, ttl: _t, ...rest } = item;
|
|
147
|
+
return rest;
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
|
|
151
|
+
exports.DynamoStore = DynamoStore;
|
|
152
|
+
//# sourceMappingURL=dynamodb.cjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["/Users/sivemwanda/Work/Tanstack/deBug/packages/adapters/dist/dynamodb.cjs","../src/dynamodb.ts"],"names":[],"mappings":"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","file":"/Users/sivemwanda/Work/Tanstack/deBug/packages/adapters/dist/dynamodb.cjs","sourcesContent":[null,"import type { DynamoDBDocumentClient } from \"@aws-sdk/lib-dynamodb\";\nimport { BatchWriteCommand, type BatchWriteCommandOutput, GetCommand, PutCommand, QueryCommand, UpdateCommand } from \"@aws-sdk/lib-dynamodb\";\nimport type { Issue, RollupDimension, RollupIncrement, RollupRow, StoredEvent } from \"@debugkit/schema\";\nimport { coalesceRollups } from \"./rollups\";\nimport type { IssueUpsert, Store } from \"./index\";\n\n/**\n * Single-table layout. Table needs: partition key `pk` (S), sort key `sk` (S),\n * and TTL enabled on attribute `ttl`.\n *\n * APPS | {app} app marker\n * APP#{app}#ISSUE | {fingerprint} Issue\n * APP#{app}#ROLLUP#{dimension} | {day}#{key} RollupRow (ADD counters)\n * APP#{app}#ISSUEEV#{fp} | {ts}#{id} StoredEvent (error events by issue)\n * APP#{app}#EV#{day} | {ts}#{id} StoredEvent (all events by day)\n */\nexport class DynamoStore implements Store {\n constructor(\n private doc: DynamoDBDocumentClient,\n private opts: { table: string; retentionDays?: number },\n ) {}\n\n private get table() {\n return this.opts.table;\n }\n\n async registerApp(app: string) {\n await this.doc.send(new PutCommand({ TableName: this.table, Item: { pk: \"APPS\", sk: app } }));\n }\n\n async writeEvents(events: StoredEvent[]) {\n const ttl = (e: StoredEvent) => Math.floor(Date.parse(e.receivedAt) / 1000) + (this.opts.retentionDays ?? 30) * 86_400;\n const items = events.flatMap((e) => {\n const sk = `${e.event.ts}#${e.event.id}`;\n const base = { ...e, ttl: ttl(e) };\n const rows = [{ pk: `APP#${e.app}#EV#${e.day}`, sk, ...base }];\n if (e.fingerprint) rows.push({ pk: `APP#${e.app}#ISSUEEV#${e.fingerprint}`, sk, ...base });\n return rows;\n });\n for (let i = 0; i < items.length; i += 25) {\n let req: Record<string, any> | undefined = {\n [this.table]: items.slice(i, i + 25).map((Item) => ({ PutRequest: { Item } })),\n };\n for (let attempt = 0; req && Object.keys(req).length && attempt < 5; attempt++) {\n const out: BatchWriteCommandOutput = await this.doc.send(new BatchWriteCommand({ RequestItems: req }));\n req = out.UnprocessedItems;\n if (req && Object.keys(req).length) await new Promise((r) => setTimeout(r, 50 * 2 ** attempt));\n }\n }\n }\n\n async upsertIssues(updates: IssueUpsert[]) {\n await Promise.all(\n updates.map(async (u) => {\n // Counters + firstSeen in one atomic update.\n await this.doc.send(\n new UpdateCommand({\n TableName: this.table,\n Key: { pk: `APP#${u.app}#ISSUE`, sk: u.fingerprint },\n UpdateExpression:\n \"ADD #count :by SET app = :app, fingerprint = :fp, title = :title, flow = :flow, culprit = :culprit, firstSeen = if_not_exists(firstSeen, :first)\",\n ExpressionAttributeNames: { \"#count\": \"count\" },\n ExpressionAttributeValues: {\n \":by\": u.by,\n \":app\": u.app,\n \":fp\": u.fingerprint,\n \":title\": u.title,\n \":flow\": u.flow ?? null,\n \":culprit\": u.culprit ?? null,\n \":first\": u.firstTs,\n },\n }),\n );\n // lastSeen / lastEvent only move forward.\n await this.doc\n .send(\n new UpdateCommand({\n TableName: this.table,\n Key: { pk: `APP#${u.app}#ISSUE`, sk: u.fingerprint },\n UpdateExpression: \"SET lastSeen = :ts, lastEvent = :ev\",\n ConditionExpression: \"attribute_not_exists(lastSeen) OR lastSeen < :ts\",\n ExpressionAttributeValues: { \":ts\": u.ts, \":ev\": u.lastEvent },\n }),\n )\n .catch((e: { name?: string }) => {\n if (e.name !== \"ConditionalCheckFailedException\") throw e;\n });\n // firstSeen can also move backwards if an older event arrives late.\n await this.doc\n .send(\n new UpdateCommand({\n TableName: this.table,\n Key: { pk: `APP#${u.app}#ISSUE`, sk: u.fingerprint },\n UpdateExpression: \"SET firstSeen = :first\",\n ConditionExpression: \"firstSeen > :first\",\n ExpressionAttributeValues: { \":first\": u.firstTs },\n }),\n )\n .catch((e: { name?: string }) => {\n if (e.name !== \"ConditionalCheckFailedException\") throw e;\n });\n }),\n );\n }\n\n async incrementRollups(incs: RollupIncrement[]) {\n await Promise.all(\n coalesceRollups(incs).map((r) =>\n this.doc.send(\n new UpdateCommand({\n TableName: this.table,\n Key: { pk: `APP#${r.app}#ROLLUP#${r.dimension}`, sk: `${r.day}#${r.key}` },\n UpdateExpression: \"ADD errors :e, events :v SET app = :app, #day = :day, dimension = :dim, #key = :key\",\n ExpressionAttributeNames: { \"#day\": \"day\", \"#key\": \"key\" },\n ExpressionAttributeValues: { \":e\": r.errors, \":v\": r.events, \":app\": r.app, \":day\": r.day, \":dim\": r.dimension, \":key\": r.key },\n }),\n ),\n ),\n );\n }\n\n async listApps() {\n return (await this.queryAll({ pk: \"APPS\" })).map((i) => i.sk as string).sort();\n }\n async listIssues(app: string, { limit = 200 } = {}) {\n const items = (await this.queryAll({ pk: `APP#${app}#ISSUE` })) as unknown as Issue[];\n return items.sort((a, b) => b.lastSeen.localeCompare(a.lastSeen)).slice(0, limit).map(clean);\n }\n async getIssue(app: string, fp: string) {\n const out = await this.doc.send(new GetCommand({ TableName: this.table, Key: { pk: `APP#${app}#ISSUE`, sk: fp } }));\n return out.Item ? clean(out.Item as Issue) : null;\n }\n async getRollups(app: string, dimension: RollupDimension, fromDay: string, toDay: string) {\n const items = await this.queryAll({ pk: `APP#${app}#ROLLUP#${dimension}`, between: [`${fromDay}#`, `${toDay}#`] });\n return items.map((i) => clean(i as unknown as RollupRow));\n }\n async listIssueEvents(app: string, fp: string, { limit = 50 } = {}) {\n const out = await this.doc.send(\n new QueryCommand({\n TableName: this.table,\n KeyConditionExpression: \"pk = :pk\",\n ExpressionAttributeValues: { \":pk\": `APP#${app}#ISSUEEV#${fp}` },\n ScanIndexForward: false,\n Limit: limit,\n }),\n );\n return (out.Items ?? []).map((i) => clean(i as StoredEvent));\n }\n\n private async queryAll({ pk, between }: { pk: string; between?: [string, string] }) {\n const items: Record<string, unknown>[] = [];\n let ExclusiveStartKey: Record<string, unknown> | undefined;\n do {\n const out = await this.doc.send(\n new QueryCommand({\n TableName: this.table,\n KeyConditionExpression: between ? \"pk = :pk AND sk BETWEEN :a AND :b\" : \"pk = :pk\",\n ExpressionAttributeValues: between ? { \":pk\": pk, \":a\": between[0], \":b\": between[1] } : { \":pk\": pk },\n ExclusiveStartKey,\n }),\n );\n items.push(...(out.Items ?? []));\n ExclusiveStartKey = out.LastEvaluatedKey;\n } while (ExclusiveStartKey);\n return items;\n }\n}\n\nfunction clean<T>(item: T): T {\n const { pk: _p, sk: _s, ttl: _t, ...rest } = item as Record<string, unknown>;\n return rest as T;\n}\n"]}
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
import { DynamoDBDocumentClient } from '@aws-sdk/lib-dynamodb';
|
|
2
|
+
import { StoredEvent, RollupIncrement, Issue, RollupDimension, RollupRow } from '@debugkit/schema';
|
|
3
|
+
import { Store, IssueUpsert } from './index.cjs';
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* Single-table layout. Table needs: partition key `pk` (S), sort key `sk` (S),
|
|
7
|
+
* and TTL enabled on attribute `ttl`.
|
|
8
|
+
*
|
|
9
|
+
* APPS | {app} app marker
|
|
10
|
+
* APP#{app}#ISSUE | {fingerprint} Issue
|
|
11
|
+
* APP#{app}#ROLLUP#{dimension} | {day}#{key} RollupRow (ADD counters)
|
|
12
|
+
* APP#{app}#ISSUEEV#{fp} | {ts}#{id} StoredEvent (error events by issue)
|
|
13
|
+
* APP#{app}#EV#{day} | {ts}#{id} StoredEvent (all events by day)
|
|
14
|
+
*/
|
|
15
|
+
declare class DynamoStore implements Store {
|
|
16
|
+
private doc;
|
|
17
|
+
private opts;
|
|
18
|
+
constructor(doc: DynamoDBDocumentClient, opts: {
|
|
19
|
+
table: string;
|
|
20
|
+
retentionDays?: number;
|
|
21
|
+
});
|
|
22
|
+
private get table();
|
|
23
|
+
registerApp(app: string): Promise<void>;
|
|
24
|
+
writeEvents(events: StoredEvent[]): Promise<void>;
|
|
25
|
+
upsertIssues(updates: IssueUpsert[]): Promise<void>;
|
|
26
|
+
incrementRollups(incs: RollupIncrement[]): Promise<void>;
|
|
27
|
+
listApps(): Promise<string[]>;
|
|
28
|
+
listIssues(app: string, { limit }?: {
|
|
29
|
+
limit?: number | undefined;
|
|
30
|
+
}): Promise<Issue[]>;
|
|
31
|
+
getIssue(app: string, fp: string): Promise<Issue | null>;
|
|
32
|
+
getRollups(app: string, dimension: RollupDimension, fromDay: string, toDay: string): Promise<RollupRow[]>;
|
|
33
|
+
listIssueEvents(app: string, fp: string, { limit }?: {
|
|
34
|
+
limit?: number | undefined;
|
|
35
|
+
}): Promise<StoredEvent[]>;
|
|
36
|
+
private queryAll;
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
export { DynamoStore };
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
import { DynamoDBDocumentClient } from '@aws-sdk/lib-dynamodb';
|
|
2
|
+
import { StoredEvent, RollupIncrement, Issue, RollupDimension, RollupRow } from '@debugkit/schema';
|
|
3
|
+
import { Store, IssueUpsert } from './index.js';
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* Single-table layout. Table needs: partition key `pk` (S), sort key `sk` (S),
|
|
7
|
+
* and TTL enabled on attribute `ttl`.
|
|
8
|
+
*
|
|
9
|
+
* APPS | {app} app marker
|
|
10
|
+
* APP#{app}#ISSUE | {fingerprint} Issue
|
|
11
|
+
* APP#{app}#ROLLUP#{dimension} | {day}#{key} RollupRow (ADD counters)
|
|
12
|
+
* APP#{app}#ISSUEEV#{fp} | {ts}#{id} StoredEvent (error events by issue)
|
|
13
|
+
* APP#{app}#EV#{day} | {ts}#{id} StoredEvent (all events by day)
|
|
14
|
+
*/
|
|
15
|
+
declare class DynamoStore implements Store {
|
|
16
|
+
private doc;
|
|
17
|
+
private opts;
|
|
18
|
+
constructor(doc: DynamoDBDocumentClient, opts: {
|
|
19
|
+
table: string;
|
|
20
|
+
retentionDays?: number;
|
|
21
|
+
});
|
|
22
|
+
private get table();
|
|
23
|
+
registerApp(app: string): Promise<void>;
|
|
24
|
+
writeEvents(events: StoredEvent[]): Promise<void>;
|
|
25
|
+
upsertIssues(updates: IssueUpsert[]): Promise<void>;
|
|
26
|
+
incrementRollups(incs: RollupIncrement[]): Promise<void>;
|
|
27
|
+
listApps(): Promise<string[]>;
|
|
28
|
+
listIssues(app: string, { limit }?: {
|
|
29
|
+
limit?: number | undefined;
|
|
30
|
+
}): Promise<Issue[]>;
|
|
31
|
+
getIssue(app: string, fp: string): Promise<Issue | null>;
|
|
32
|
+
getRollups(app: string, dimension: RollupDimension, fromDay: string, toDay: string): Promise<RollupRow[]>;
|
|
33
|
+
listIssueEvents(app: string, fp: string, { limit }?: {
|
|
34
|
+
limit?: number | undefined;
|
|
35
|
+
}): Promise<StoredEvent[]>;
|
|
36
|
+
private queryAll;
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
export { DynamoStore };
|
package/dist/dynamodb.js
ADDED
|
@@ -0,0 +1,152 @@
|
|
|
1
|
+
import {
|
|
2
|
+
coalesceRollups
|
|
3
|
+
} from "./chunk-IUP4NDEQ.js";
|
|
4
|
+
|
|
5
|
+
// src/dynamodb.ts
|
|
6
|
+
import { BatchWriteCommand, GetCommand, PutCommand, QueryCommand, UpdateCommand } from "@aws-sdk/lib-dynamodb";
|
|
7
|
+
var DynamoStore = class {
|
|
8
|
+
constructor(doc, opts) {
|
|
9
|
+
this.doc = doc;
|
|
10
|
+
this.opts = opts;
|
|
11
|
+
}
|
|
12
|
+
doc;
|
|
13
|
+
opts;
|
|
14
|
+
get table() {
|
|
15
|
+
return this.opts.table;
|
|
16
|
+
}
|
|
17
|
+
async registerApp(app) {
|
|
18
|
+
await this.doc.send(new PutCommand({ TableName: this.table, Item: { pk: "APPS", sk: app } }));
|
|
19
|
+
}
|
|
20
|
+
async writeEvents(events) {
|
|
21
|
+
const ttl = (e) => Math.floor(Date.parse(e.receivedAt) / 1e3) + (this.opts.retentionDays ?? 30) * 86400;
|
|
22
|
+
const items = events.flatMap((e) => {
|
|
23
|
+
const sk = `${e.event.ts}#${e.event.id}`;
|
|
24
|
+
const base = { ...e, ttl: ttl(e) };
|
|
25
|
+
const rows = [{ pk: `APP#${e.app}#EV#${e.day}`, sk, ...base }];
|
|
26
|
+
if (e.fingerprint) rows.push({ pk: `APP#${e.app}#ISSUEEV#${e.fingerprint}`, sk, ...base });
|
|
27
|
+
return rows;
|
|
28
|
+
});
|
|
29
|
+
for (let i = 0; i < items.length; i += 25) {
|
|
30
|
+
let req = {
|
|
31
|
+
[this.table]: items.slice(i, i + 25).map((Item) => ({ PutRequest: { Item } }))
|
|
32
|
+
};
|
|
33
|
+
for (let attempt = 0; req && Object.keys(req).length && attempt < 5; attempt++) {
|
|
34
|
+
const out = await this.doc.send(new BatchWriteCommand({ RequestItems: req }));
|
|
35
|
+
req = out.UnprocessedItems;
|
|
36
|
+
if (req && Object.keys(req).length) await new Promise((r) => setTimeout(r, 50 * 2 ** attempt));
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
async upsertIssues(updates) {
|
|
41
|
+
await Promise.all(
|
|
42
|
+
updates.map(async (u) => {
|
|
43
|
+
await this.doc.send(
|
|
44
|
+
new UpdateCommand({
|
|
45
|
+
TableName: this.table,
|
|
46
|
+
Key: { pk: `APP#${u.app}#ISSUE`, sk: u.fingerprint },
|
|
47
|
+
UpdateExpression: "ADD #count :by SET app = :app, fingerprint = :fp, title = :title, flow = :flow, culprit = :culprit, firstSeen = if_not_exists(firstSeen, :first)",
|
|
48
|
+
ExpressionAttributeNames: { "#count": "count" },
|
|
49
|
+
ExpressionAttributeValues: {
|
|
50
|
+
":by": u.by,
|
|
51
|
+
":app": u.app,
|
|
52
|
+
":fp": u.fingerprint,
|
|
53
|
+
":title": u.title,
|
|
54
|
+
":flow": u.flow ?? null,
|
|
55
|
+
":culprit": u.culprit ?? null,
|
|
56
|
+
":first": u.firstTs
|
|
57
|
+
}
|
|
58
|
+
})
|
|
59
|
+
);
|
|
60
|
+
await this.doc.send(
|
|
61
|
+
new UpdateCommand({
|
|
62
|
+
TableName: this.table,
|
|
63
|
+
Key: { pk: `APP#${u.app}#ISSUE`, sk: u.fingerprint },
|
|
64
|
+
UpdateExpression: "SET lastSeen = :ts, lastEvent = :ev",
|
|
65
|
+
ConditionExpression: "attribute_not_exists(lastSeen) OR lastSeen < :ts",
|
|
66
|
+
ExpressionAttributeValues: { ":ts": u.ts, ":ev": u.lastEvent }
|
|
67
|
+
})
|
|
68
|
+
).catch((e) => {
|
|
69
|
+
if (e.name !== "ConditionalCheckFailedException") throw e;
|
|
70
|
+
});
|
|
71
|
+
await this.doc.send(
|
|
72
|
+
new UpdateCommand({
|
|
73
|
+
TableName: this.table,
|
|
74
|
+
Key: { pk: `APP#${u.app}#ISSUE`, sk: u.fingerprint },
|
|
75
|
+
UpdateExpression: "SET firstSeen = :first",
|
|
76
|
+
ConditionExpression: "firstSeen > :first",
|
|
77
|
+
ExpressionAttributeValues: { ":first": u.firstTs }
|
|
78
|
+
})
|
|
79
|
+
).catch((e) => {
|
|
80
|
+
if (e.name !== "ConditionalCheckFailedException") throw e;
|
|
81
|
+
});
|
|
82
|
+
})
|
|
83
|
+
);
|
|
84
|
+
}
|
|
85
|
+
async incrementRollups(incs) {
|
|
86
|
+
await Promise.all(
|
|
87
|
+
coalesceRollups(incs).map(
|
|
88
|
+
(r) => this.doc.send(
|
|
89
|
+
new UpdateCommand({
|
|
90
|
+
TableName: this.table,
|
|
91
|
+
Key: { pk: `APP#${r.app}#ROLLUP#${r.dimension}`, sk: `${r.day}#${r.key}` },
|
|
92
|
+
UpdateExpression: "ADD errors :e, events :v SET app = :app, #day = :day, dimension = :dim, #key = :key",
|
|
93
|
+
ExpressionAttributeNames: { "#day": "day", "#key": "key" },
|
|
94
|
+
ExpressionAttributeValues: { ":e": r.errors, ":v": r.events, ":app": r.app, ":day": r.day, ":dim": r.dimension, ":key": r.key }
|
|
95
|
+
})
|
|
96
|
+
)
|
|
97
|
+
)
|
|
98
|
+
);
|
|
99
|
+
}
|
|
100
|
+
async listApps() {
|
|
101
|
+
return (await this.queryAll({ pk: "APPS" })).map((i) => i.sk).sort();
|
|
102
|
+
}
|
|
103
|
+
async listIssues(app, { limit = 200 } = {}) {
|
|
104
|
+
const items = await this.queryAll({ pk: `APP#${app}#ISSUE` });
|
|
105
|
+
return items.sort((a, b) => b.lastSeen.localeCompare(a.lastSeen)).slice(0, limit).map(clean);
|
|
106
|
+
}
|
|
107
|
+
async getIssue(app, fp) {
|
|
108
|
+
const out = await this.doc.send(new GetCommand({ TableName: this.table, Key: { pk: `APP#${app}#ISSUE`, sk: fp } }));
|
|
109
|
+
return out.Item ? clean(out.Item) : null;
|
|
110
|
+
}
|
|
111
|
+
async getRollups(app, dimension, fromDay, toDay) {
|
|
112
|
+
const items = await this.queryAll({ pk: `APP#${app}#ROLLUP#${dimension}`, between: [`${fromDay}#`, `${toDay}#\uFFFF`] });
|
|
113
|
+
return items.map((i) => clean(i));
|
|
114
|
+
}
|
|
115
|
+
async listIssueEvents(app, fp, { limit = 50 } = {}) {
|
|
116
|
+
const out = await this.doc.send(
|
|
117
|
+
new QueryCommand({
|
|
118
|
+
TableName: this.table,
|
|
119
|
+
KeyConditionExpression: "pk = :pk",
|
|
120
|
+
ExpressionAttributeValues: { ":pk": `APP#${app}#ISSUEEV#${fp}` },
|
|
121
|
+
ScanIndexForward: false,
|
|
122
|
+
Limit: limit
|
|
123
|
+
})
|
|
124
|
+
);
|
|
125
|
+
return (out.Items ?? []).map((i) => clean(i));
|
|
126
|
+
}
|
|
127
|
+
async queryAll({ pk, between }) {
|
|
128
|
+
const items = [];
|
|
129
|
+
let ExclusiveStartKey;
|
|
130
|
+
do {
|
|
131
|
+
const out = await this.doc.send(
|
|
132
|
+
new QueryCommand({
|
|
133
|
+
TableName: this.table,
|
|
134
|
+
KeyConditionExpression: between ? "pk = :pk AND sk BETWEEN :a AND :b" : "pk = :pk",
|
|
135
|
+
ExpressionAttributeValues: between ? { ":pk": pk, ":a": between[0], ":b": between[1] } : { ":pk": pk },
|
|
136
|
+
ExclusiveStartKey
|
|
137
|
+
})
|
|
138
|
+
);
|
|
139
|
+
items.push(...out.Items ?? []);
|
|
140
|
+
ExclusiveStartKey = out.LastEvaluatedKey;
|
|
141
|
+
} while (ExclusiveStartKey);
|
|
142
|
+
return items;
|
|
143
|
+
}
|
|
144
|
+
};
|
|
145
|
+
function clean(item) {
|
|
146
|
+
const { pk: _p, sk: _s, ttl: _t, ...rest } = item;
|
|
147
|
+
return rest;
|
|
148
|
+
}
|
|
149
|
+
export {
|
|
150
|
+
DynamoStore
|
|
151
|
+
};
|
|
152
|
+
//# sourceMappingURL=dynamodb.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/dynamodb.ts"],"sourcesContent":["import type { DynamoDBDocumentClient } from \"@aws-sdk/lib-dynamodb\";\nimport { BatchWriteCommand, type BatchWriteCommandOutput, GetCommand, PutCommand, QueryCommand, UpdateCommand } from \"@aws-sdk/lib-dynamodb\";\nimport type { Issue, RollupDimension, RollupIncrement, RollupRow, StoredEvent } from \"@debugkit/schema\";\nimport { coalesceRollups } from \"./rollups\";\nimport type { IssueUpsert, Store } from \"./index\";\n\n/**\n * Single-table layout. Table needs: partition key `pk` (S), sort key `sk` (S),\n * and TTL enabled on attribute `ttl`.\n *\n * APPS | {app} app marker\n * APP#{app}#ISSUE | {fingerprint} Issue\n * APP#{app}#ROLLUP#{dimension} | {day}#{key} RollupRow (ADD counters)\n * APP#{app}#ISSUEEV#{fp} | {ts}#{id} StoredEvent (error events by issue)\n * APP#{app}#EV#{day} | {ts}#{id} StoredEvent (all events by day)\n */\nexport class DynamoStore implements Store {\n constructor(\n private doc: DynamoDBDocumentClient,\n private opts: { table: string; retentionDays?: number },\n ) {}\n\n private get table() {\n return this.opts.table;\n }\n\n async registerApp(app: string) {\n await this.doc.send(new PutCommand({ TableName: this.table, Item: { pk: \"APPS\", sk: app } }));\n }\n\n async writeEvents(events: StoredEvent[]) {\n const ttl = (e: StoredEvent) => Math.floor(Date.parse(e.receivedAt) / 1000) + (this.opts.retentionDays ?? 30) * 86_400;\n const items = events.flatMap((e) => {\n const sk = `${e.event.ts}#${e.event.id}`;\n const base = { ...e, ttl: ttl(e) };\n const rows = [{ pk: `APP#${e.app}#EV#${e.day}`, sk, ...base }];\n if (e.fingerprint) rows.push({ pk: `APP#${e.app}#ISSUEEV#${e.fingerprint}`, sk, ...base });\n return rows;\n });\n for (let i = 0; i < items.length; i += 25) {\n let req: Record<string, any> | undefined = {\n [this.table]: items.slice(i, i + 25).map((Item) => ({ PutRequest: { Item } })),\n };\n for (let attempt = 0; req && Object.keys(req).length && attempt < 5; attempt++) {\n const out: BatchWriteCommandOutput = await this.doc.send(new BatchWriteCommand({ RequestItems: req }));\n req = out.UnprocessedItems;\n if (req && Object.keys(req).length) await new Promise((r) => setTimeout(r, 50 * 2 ** attempt));\n }\n }\n }\n\n async upsertIssues(updates: IssueUpsert[]) {\n await Promise.all(\n updates.map(async (u) => {\n // Counters + firstSeen in one atomic update.\n await this.doc.send(\n new UpdateCommand({\n TableName: this.table,\n Key: { pk: `APP#${u.app}#ISSUE`, sk: u.fingerprint },\n UpdateExpression:\n \"ADD #count :by SET app = :app, fingerprint = :fp, title = :title, flow = :flow, culprit = :culprit, firstSeen = if_not_exists(firstSeen, :first)\",\n ExpressionAttributeNames: { \"#count\": \"count\" },\n ExpressionAttributeValues: {\n \":by\": u.by,\n \":app\": u.app,\n \":fp\": u.fingerprint,\n \":title\": u.title,\n \":flow\": u.flow ?? null,\n \":culprit\": u.culprit ?? null,\n \":first\": u.firstTs,\n },\n }),\n );\n // lastSeen / lastEvent only move forward.\n await this.doc\n .send(\n new UpdateCommand({\n TableName: this.table,\n Key: { pk: `APP#${u.app}#ISSUE`, sk: u.fingerprint },\n UpdateExpression: \"SET lastSeen = :ts, lastEvent = :ev\",\n ConditionExpression: \"attribute_not_exists(lastSeen) OR lastSeen < :ts\",\n ExpressionAttributeValues: { \":ts\": u.ts, \":ev\": u.lastEvent },\n }),\n )\n .catch((e: { name?: string }) => {\n if (e.name !== \"ConditionalCheckFailedException\") throw e;\n });\n // firstSeen can also move backwards if an older event arrives late.\n await this.doc\n .send(\n new UpdateCommand({\n TableName: this.table,\n Key: { pk: `APP#${u.app}#ISSUE`, sk: u.fingerprint },\n UpdateExpression: \"SET firstSeen = :first\",\n ConditionExpression: \"firstSeen > :first\",\n ExpressionAttributeValues: { \":first\": u.firstTs },\n }),\n )\n .catch((e: { name?: string }) => {\n if (e.name !== \"ConditionalCheckFailedException\") throw e;\n });\n }),\n );\n }\n\n async incrementRollups(incs: RollupIncrement[]) {\n await Promise.all(\n coalesceRollups(incs).map((r) =>\n this.doc.send(\n new UpdateCommand({\n TableName: this.table,\n Key: { pk: `APP#${r.app}#ROLLUP#${r.dimension}`, sk: `${r.day}#${r.key}` },\n UpdateExpression: \"ADD errors :e, events :v SET app = :app, #day = :day, dimension = :dim, #key = :key\",\n ExpressionAttributeNames: { \"#day\": \"day\", \"#key\": \"key\" },\n ExpressionAttributeValues: { \":e\": r.errors, \":v\": r.events, \":app\": r.app, \":day\": r.day, \":dim\": r.dimension, \":key\": r.key },\n }),\n ),\n ),\n );\n }\n\n async listApps() {\n return (await this.queryAll({ pk: \"APPS\" })).map((i) => i.sk as string).sort();\n }\n async listIssues(app: string, { limit = 200 } = {}) {\n const items = (await this.queryAll({ pk: `APP#${app}#ISSUE` })) as unknown as Issue[];\n return items.sort((a, b) => b.lastSeen.localeCompare(a.lastSeen)).slice(0, limit).map(clean);\n }\n async getIssue(app: string, fp: string) {\n const out = await this.doc.send(new GetCommand({ TableName: this.table, Key: { pk: `APP#${app}#ISSUE`, sk: fp } }));\n return out.Item ? clean(out.Item as Issue) : null;\n }\n async getRollups(app: string, dimension: RollupDimension, fromDay: string, toDay: string) {\n const items = await this.queryAll({ pk: `APP#${app}#ROLLUP#${dimension}`, between: [`${fromDay}#`, `${toDay}#`] });\n return items.map((i) => clean(i as unknown as RollupRow));\n }\n async listIssueEvents(app: string, fp: string, { limit = 50 } = {}) {\n const out = await this.doc.send(\n new QueryCommand({\n TableName: this.table,\n KeyConditionExpression: \"pk = :pk\",\n ExpressionAttributeValues: { \":pk\": `APP#${app}#ISSUEEV#${fp}` },\n ScanIndexForward: false,\n Limit: limit,\n }),\n );\n return (out.Items ?? []).map((i) => clean(i as StoredEvent));\n }\n\n private async queryAll({ pk, between }: { pk: string; between?: [string, string] }) {\n const items: Record<string, unknown>[] = [];\n let ExclusiveStartKey: Record<string, unknown> | undefined;\n do {\n const out = await this.doc.send(\n new QueryCommand({\n TableName: this.table,\n KeyConditionExpression: between ? \"pk = :pk AND sk BETWEEN :a AND :b\" : \"pk = :pk\",\n ExpressionAttributeValues: between ? { \":pk\": pk, \":a\": between[0], \":b\": between[1] } : { \":pk\": pk },\n ExclusiveStartKey,\n }),\n );\n items.push(...(out.Items ?? []));\n ExclusiveStartKey = out.LastEvaluatedKey;\n } while (ExclusiveStartKey);\n return items;\n }\n}\n\nfunction clean<T>(item: T): T {\n const { pk: _p, sk: _s, ttl: _t, ...rest } = item as Record<string, unknown>;\n return rest as T;\n}\n"],"mappings":"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","names":[]}
|