@x12i/memorix-service 3.9.2 → 3.10.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.
Files changed (61) hide show
  1. package/dist/analytics/definition.d.ts +158 -0
  2. package/dist/analytics/definition.d.ts.map +1 -0
  3. package/dist/analytics/definition.js +439 -0
  4. package/dist/analytics/definition.js.map +1 -0
  5. package/dist/analytics/engine.d.ts +63 -0
  6. package/dist/analytics/engine.d.ts.map +1 -0
  7. package/dist/analytics/engine.js +181 -0
  8. package/dist/analytics/engine.js.map +1 -0
  9. package/dist/analytics/host.d.ts +15 -0
  10. package/dist/analytics/host.d.ts.map +1 -0
  11. package/dist/analytics/host.js +129 -0
  12. package/dist/analytics/host.js.map +1 -0
  13. package/dist/analytics/materializer.d.ts +88 -0
  14. package/dist/analytics/materializer.d.ts.map +1 -0
  15. package/dist/analytics/materializer.js +358 -0
  16. package/dist/analytics/materializer.js.map +1 -0
  17. package/dist/analytics/pipeline-port.d.ts +10 -0
  18. package/dist/analytics/pipeline-port.d.ts.map +1 -0
  19. package/dist/analytics/pipeline-port.js +42 -0
  20. package/dist/analytics/pipeline-port.js.map +1 -0
  21. package/dist/analytics/projector.d.ts +60 -0
  22. package/dist/analytics/projector.d.ts.map +1 -0
  23. package/dist/analytics/projector.js +262 -0
  24. package/dist/analytics/projector.js.map +1 -0
  25. package/dist/analytics/state.d.ts +83 -0
  26. package/dist/analytics/state.d.ts.map +1 -0
  27. package/dist/analytics/state.js +273 -0
  28. package/dist/analytics/state.js.map +1 -0
  29. package/dist/analytics/tracker.d.ts +182 -0
  30. package/dist/analytics/tracker.d.ts.map +1 -0
  31. package/dist/analytics/tracker.js +1302 -0
  32. package/dist/analytics/tracker.js.map +1 -0
  33. package/dist/app.d.ts.map +1 -1
  34. package/dist/app.js +4 -0
  35. package/dist/app.js.map +1 -1
  36. package/dist/content-census-countex.d.ts +15 -0
  37. package/dist/content-census-countex.d.ts.map +1 -0
  38. package/dist/content-census-countex.js +18 -0
  39. package/dist/content-census-countex.js.map +1 -0
  40. package/dist/datasource-backfill.d.ts +2 -0
  41. package/dist/datasource-backfill.d.ts.map +1 -1
  42. package/dist/datasource-backfill.js +5 -1
  43. package/dist/datasource-backfill.js.map +1 -1
  44. package/dist/index.d.ts +4 -0
  45. package/dist/index.d.ts.map +1 -1
  46. package/dist/index.js +3 -0
  47. package/dist/index.js.map +1 -1
  48. package/dist/openapi/openapi.json +955 -210
  49. package/dist/platform.d.ts +6 -0
  50. package/dist/platform.d.ts.map +1 -1
  51. package/dist/platform.js +54 -2
  52. package/dist/platform.js.map +1 -1
  53. package/dist/routes/analytics-counters.d.ts +8 -0
  54. package/dist/routes/analytics-counters.d.ts.map +1 -0
  55. package/dist/routes/analytics-counters.js +100 -0
  56. package/dist/routes/analytics-counters.js.map +1 -0
  57. package/dist/routes/datasource-backfill.d.ts.map +1 -1
  58. package/dist/routes/datasource-backfill.js +8 -1
  59. package/dist/routes/datasource-backfill.js.map +1 -1
  60. package/openapi/openapi.json +955 -210
  61. package/package.json +5 -5
@@ -0,0 +1,262 @@
1
+ import { whereList } from "./definition.js";
2
+ export const SEP = "\u001f";
3
+ /** Countex subject for facts that are not keyed by a leader, row, or value. */
4
+ export const NO_SUBJECT = "_";
5
+ export const metricKey = {
6
+ presence: (abstractType) => `p${SEP}${abstractType}`,
7
+ measure: (key) => `m${SEP}${key}`,
8
+ measureValue: (key) => `mv${SEP}${key}`,
9
+ group: (key, bucket) => `g${SEP}${key}${SEP}${bucket}`,
10
+ groupLabel: (key, bucket) => `gl${SEP}${key}${SEP}${bucket}`,
11
+ split: (key, bucket) => `s${SEP}${key}${SEP}${bucket}`,
12
+ unwind: (key) => `u${SEP}${key}`,
13
+ unwindAgg: (key, agg) => `ua${SEP}${key}${SEP}${agg}`,
14
+ unwindValue: (key, agg) => `uv${SEP}${key}${SEP}${agg}`,
15
+ leader: (key) => `l${SEP}${key}`,
16
+ coverage: (key) => `c${SEP}${key}`,
17
+ transition: (key) => `t${SEP}${key}`,
18
+ };
19
+ export function readProperty(subject, property) {
20
+ if (property === "recordId")
21
+ return subject.recordId;
22
+ if (property === "@datasource")
23
+ return subject.datasource?.system ?? undefined;
24
+ return subject.properties[property];
25
+ }
26
+ function isAbsent(v) {
27
+ return v === undefined || v === null || v === "";
28
+ }
29
+ function matchesPredicate(subject, p) {
30
+ const v = readProperty(subject, p.property);
31
+ if ("exists" in p)
32
+ return p.exists ? !isAbsent(v) : isAbsent(v);
33
+ if ("in" in p)
34
+ return Array.isArray(p.in) && p.in.includes(v);
35
+ return v === p.equals;
36
+ }
37
+ export function matchesWhere(subject, where) {
38
+ return whereList(where).every((p) => matchesPredicate(subject, p));
39
+ }
40
+ function asNumber(value) {
41
+ if (typeof value === "number" && Number.isFinite(value))
42
+ return value;
43
+ if (typeof value === "string" && value.trim() && Number.isFinite(Number(value)))
44
+ return Number(value);
45
+ return null;
46
+ }
47
+ function scalarKey(value) {
48
+ if (typeof value === "string")
49
+ return value.trim() ? value : null;
50
+ if (typeof value === "number" && Number.isFinite(value))
51
+ return String(value);
52
+ if (typeof value === "boolean")
53
+ return String(value);
54
+ return null;
55
+ }
56
+ function readNested(root, path) {
57
+ if (path.startsWith("item."))
58
+ return readNested(root, path.slice(5));
59
+ let cur = root;
60
+ for (const p of path.split(".")) {
61
+ if (!cur || typeof cur !== "object" || Array.isArray(cur))
62
+ return undefined;
63
+ cur = cur[p];
64
+ }
65
+ return cur;
66
+ }
67
+ function clockValue(raw) {
68
+ if (typeof raw !== "string" && typeof raw !== "number")
69
+ return null;
70
+ const t = typeof raw === "number" ? raw : Date.parse(raw);
71
+ return Number.isFinite(t) ? new Date(t).toISOString() : null;
72
+ }
73
+ export function projectSubject(def, abstractType, subject) {
74
+ let pair = null;
75
+ if (def.scope) {
76
+ const a = scalarKey(subject.properties[def.scope[0].property]);
77
+ const b = scalarKey(subject.properties[def.scope[1].property]);
78
+ if (a === null || b === null) {
79
+ return { pair: null, inScope: false, clock: null, facts: [], track: {} };
80
+ }
81
+ pair = [a, b];
82
+ }
83
+ const clock = def.clock && def.clock.abstractType === abstractType
84
+ ? clockValue(readProperty(subject, def.clock.property))
85
+ : null;
86
+ const facts = [{ metric: metricKey.presence(abstractType), subject: NO_SUBJECT }];
87
+ const add = (metric, extra) => facts.push({ metric, subject: NO_SUBJECT, ...extra });
88
+ for (const m of def.measures ?? []) {
89
+ if (m.abstractType !== abstractType || !matchesWhere(subject, m.where))
90
+ continue;
91
+ if (m.kind === "count") {
92
+ add(metricKey.measure(m.key));
93
+ continue;
94
+ }
95
+ const raw = readProperty(subject, m.property);
96
+ if (m.kind === "sum" || m.kind === "mean") {
97
+ const n = asNumber(raw);
98
+ if (n !== null)
99
+ add(metricKey.measure(m.key), { value: n });
100
+ }
101
+ else if (m.kind === "max") {
102
+ const n = asNumber(raw);
103
+ if (n !== null)
104
+ add(metricKey.measureValue(m.key), { subject: String(n) });
105
+ }
106
+ else {
107
+ const values = Array.isArray(raw) ? raw : [raw];
108
+ const seen = new Set();
109
+ for (const v of values) {
110
+ const k = scalarKey(v);
111
+ if (k === null || seen.has(k))
112
+ continue;
113
+ seen.add(k);
114
+ add(metricKey.measureValue(m.key), { subject: k });
115
+ }
116
+ }
117
+ }
118
+ for (const g of def.groups ?? []) {
119
+ if (g.abstractType !== abstractType || !matchesWhere(subject, g.where))
120
+ continue;
121
+ const bucket = scalarKey(readProperty(subject, g.property));
122
+ if (bucket === null)
123
+ continue;
124
+ if (g.buckets && !g.buckets.includes(bucket))
125
+ continue;
126
+ let split;
127
+ if (g.split) {
128
+ const raw = readProperty(subject, g.split.property);
129
+ const sv = scalarKey(raw);
130
+ if (sv !== null && g.split.buckets.includes(sv))
131
+ split = sv;
132
+ else if (isAbsent(raw) && g.split.missing)
133
+ split = g.split.missing;
134
+ }
135
+ add(metricKey.group(g.key, bucket), split !== undefined ? { split } : undefined);
136
+ if (!g.buckets && g.property === "@datasource" && subject.datasource?.name) {
137
+ add(metricKey.groupLabel(g.key, bucket), { subject: subject.datasource.name });
138
+ }
139
+ }
140
+ for (const s of def.splits ?? []) {
141
+ if (s.abstractType !== abstractType)
142
+ continue;
143
+ const bucket = scalarKey(readProperty(subject, s.property));
144
+ if (bucket === null || !s.buckets.includes(bucket))
145
+ continue;
146
+ add(metricKey.split(s.key, bucket), { split: "total" });
147
+ if (matchesWhere(subject, s.subset))
148
+ add(metricKey.split(s.key, bucket), { split: "subset" });
149
+ }
150
+ for (const u of def.unwinds ?? []) {
151
+ if (u.abstractType !== abstractType)
152
+ continue;
153
+ const arr = readProperty(subject, u.arrayProperty);
154
+ if (!Array.isArray(arr))
155
+ continue;
156
+ for (const item of arr) {
157
+ const cols = {};
158
+ for (const c of u.columns)
159
+ cols[c.key] = readNested(item, c.path) ?? null;
160
+ const row = JSON.stringify(cols);
161
+ add(metricKey.unwind(u.key), { subject: row });
162
+ for (const agg of u.aggregates ?? []) {
163
+ if (agg.kind === "count" || !agg.path)
164
+ continue;
165
+ const raw = agg.path.startsWith("item.")
166
+ ? readNested(item, agg.path)
167
+ : readProperty(subject, agg.path);
168
+ if (agg.kind === "group-count") {
169
+ const b = scalarKey(raw);
170
+ if (b !== null && agg.buckets?.includes(b)) {
171
+ add(metricKey.unwindAgg(u.key, agg.key), { subject: row, split: b });
172
+ }
173
+ }
174
+ else if (agg.kind === "sum") {
175
+ const n = asNumber(raw);
176
+ if (n !== null)
177
+ add(metricKey.unwindAgg(u.key, agg.key), { subject: row, value: n });
178
+ }
179
+ else {
180
+ const k = agg.kind === "max" ? (asNumber(raw) === null ? null : String(asNumber(raw))) : scalarKey(raw);
181
+ if (k !== null)
182
+ add(metricKey.unwindValue(u.key, agg.key), { subject: `${row}${SEP}${k}` });
183
+ }
184
+ }
185
+ }
186
+ }
187
+ for (const l of def.leaders ?? []) {
188
+ if (!l.countOf || l.countOf.abstractType !== abstractType)
189
+ continue;
190
+ if (!matchesWhere(subject, l.countOf.where))
191
+ continue;
192
+ const raw = readProperty(subject, l.countOf.property);
193
+ const ids = new Set();
194
+ for (const v of Array.isArray(raw) ? raw : [raw]) {
195
+ const k = scalarKey(v);
196
+ if (k !== null)
197
+ ids.add(k);
198
+ }
199
+ for (const id of ids)
200
+ add(metricKey.leader(l.key), { subject: id });
201
+ }
202
+ for (const c of def.coverage ?? []) {
203
+ if (c.abstractType === abstractType && matchesWhere(subject, c.where))
204
+ add(metricKey.coverage(c.key));
205
+ }
206
+ const track = {};
207
+ for (const t of def.transitions ?? []) {
208
+ if (t.abstractType !== abstractType)
209
+ continue;
210
+ const v = readProperty(subject, t.property);
211
+ if (!isAbsent(v))
212
+ track[t.key] = v;
213
+ }
214
+ return { pair, inScope: true, clock, facts, track };
215
+ }
216
+ export function factId(f) {
217
+ return [f.metric, f.subject, f.split ?? "", f.value ?? ""].join(SEP);
218
+ }
219
+ /** Multiset difference: what to decrement and what to increment to move `prev` to `next`. */
220
+ export function diffFacts(prev, next) {
221
+ const counts = new Map();
222
+ for (const f of prev) {
223
+ const id = factId(f);
224
+ const e = counts.get(id);
225
+ if (e)
226
+ e.n -= 1;
227
+ else
228
+ counts.set(id, { fact: f, n: -1 });
229
+ }
230
+ for (const f of next) {
231
+ const id = factId(f);
232
+ const e = counts.get(id);
233
+ if (e)
234
+ e.n += 1;
235
+ else
236
+ counts.set(id, { fact: f, n: 1 });
237
+ }
238
+ const removed = [];
239
+ const added = [];
240
+ for (const { fact, n } of counts.values()) {
241
+ for (let i = 0; i < -n; i++)
242
+ removed.push(fact);
243
+ for (let i = 0; i < n; i++)
244
+ added.push(fact);
245
+ }
246
+ return { removed, added };
247
+ }
248
+ /** Transition events between two tracked states of the same subject. */
249
+ export function transitionFacts(def, prevTrack, nextTrack) {
250
+ if (!prevTrack)
251
+ return [];
252
+ const out = [];
253
+ for (const t of def.transitions ?? []) {
254
+ const from = prevTrack[t.key];
255
+ const to = nextTrack[t.key];
256
+ if (from === undefined || to === undefined || from === to)
257
+ continue;
258
+ out.push({ metric: metricKey.transition(t.key), subject: JSON.stringify([from, to]) });
259
+ }
260
+ return out;
261
+ }
262
+ //# sourceMappingURL=projector.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"projector.js","sourceRoot":"","sources":["../../src/analytics/projector.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,83 @@
1
+ /**
2
+ * Durable state behind the counters: the registry, the per-subject fact ledger, and the
3
+ * covering pairs seen per counter generation. Stored in `{orgId}_operational`.
4
+ */
5
+ import type { Db } from "mongodb";
6
+ import type { CounterDefinition } from "./definition.js";
7
+ import type { Fact } from "./projector.js";
8
+ export type CounterOrigin = "pack" | "api" | "builtin";
9
+ export type CounterStatus = "backfilling" | "live" | "error";
10
+ export type BackfillProgress = {
11
+ cursor: string | null;
12
+ done: boolean;
13
+ seen: number;
14
+ };
15
+ export type CounterRecord = {
16
+ id: string;
17
+ orgId: string;
18
+ origin: CounterOrigin;
19
+ agentIds: string[];
20
+ definition: CounterDefinition;
21
+ hash: string;
22
+ generation: number;
23
+ status: CounterStatus;
24
+ backfill: Record<string, BackfillProgress>;
25
+ registeredAt: string;
26
+ updatedAt: string;
27
+ registeredBy?: string | null;
28
+ liveAt?: string | null;
29
+ lastMaterializedAt?: string | null;
30
+ error?: string | null;
31
+ };
32
+ /** What a subject currently contributes to one counter generation. */
33
+ export type SubjectState = {
34
+ scope: string;
35
+ at: string;
36
+ facts: Fact[];
37
+ track: Record<string, unknown>;
38
+ };
39
+ export type LedgerRow = SubjectState & {
40
+ key: string;
41
+ seq: number;
42
+ pending?: (SubjectState | {
43
+ removed: true;
44
+ }) | null;
45
+ pendingAt?: string | null;
46
+ };
47
+ export type LedgerIntent = {
48
+ key: string;
49
+ /** Sequence read before projecting; `null` when the row did not exist. */
50
+ expectedSeq: number | null;
51
+ next: SubjectState | null;
52
+ };
53
+ export type PairRow = {
54
+ scope: string;
55
+ counterId: string;
56
+ generation: number;
57
+ pair: [string, string] | null;
58
+ firstSeenAt: string;
59
+ };
60
+ export interface AnalyticsStateStore {
61
+ listCounters(): Promise<CounterRecord[]>;
62
+ getCounter(id: string): Promise<CounterRecord | null>;
63
+ saveCounter(record: CounterRecord): Promise<void>;
64
+ /** Partial update; with `generation` set, applies only while the counter is still on it. */
65
+ patchCounter(id: string, patch: Partial<CounterRecord>, generation?: number): Promise<boolean>;
66
+ deleteCounter(id: string): Promise<void>;
67
+ readLedger(counterId: string, generation: number, keys: string[]): Promise<Map<string, LedgerRow>>;
68
+ /** Records intents stamped `at`; returns keys whose sequence moved since they were read. */
69
+ writeIntents(counterId: string, generation: number, intents: LedgerIntent[], at: string): Promise<string[]>;
70
+ commitIntents(counterId: string, generation: number, keys: string[]): Promise<void>;
71
+ /** Counter ids with intents recorded before `olderThan` and never committed. */
72
+ stalePending(olderThan: Date): Promise<string[]>;
73
+ /** Drops ledger rows and pairs of generations below `belowGeneration` (all when omitted). */
74
+ dropGenerations(counterId: string, belowGeneration?: number): Promise<void>;
75
+ /** Registers a covering pair (keeping the earliest `firstSeenAt`); true when it was not known yet. */
76
+ touchPair(row: PairRow): Promise<boolean>;
77
+ /** Pairs of a generation, most recently first-seen first. */
78
+ listPairs(counterId: string, generation: number, limit: number): Promise<PairRow[]>;
79
+ }
80
+ export declare function createMemoryAnalyticsState(): AnalyticsStateStore;
81
+ export declare function createMongoAnalyticsState(db: Db): AnalyticsStateStore;
82
+ export declare function mongoAnalyticsStateFactory(dbFor: (orgId: string) => Db): (orgId: string) => AnalyticsStateStore;
83
+ //# sourceMappingURL=state.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"state.d.ts","sourceRoot":"","sources":["../../src/analytics/state.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,273 @@
1
+ const clone = (v) => structuredClone(v);
2
+ export function createMemoryAnalyticsState() {
3
+ const counters = new Map();
4
+ const ledger = new Map();
5
+ const pairs = new Map();
6
+ const rowId = (counterId, generation, key) => `${counterId}#${generation}|${key}`;
7
+ return {
8
+ async listCounters() {
9
+ return [...counters.values()].map(clone);
10
+ },
11
+ async getCounter(id) {
12
+ const c = counters.get(id);
13
+ return c ? clone(c) : null;
14
+ },
15
+ async saveCounter(record) {
16
+ counters.set(record.id, clone(record));
17
+ },
18
+ async patchCounter(id, patch, generation) {
19
+ const c = counters.get(id);
20
+ if (!c || (generation !== undefined && c.generation !== generation))
21
+ return false;
22
+ Object.assign(c, clone(patch));
23
+ return true;
24
+ },
25
+ async deleteCounter(id) {
26
+ counters.delete(id);
27
+ },
28
+ async readLedger(counterId, generation, keys) {
29
+ const out = new Map();
30
+ for (const key of keys) {
31
+ const row = ledger.get(rowId(counterId, generation, key));
32
+ if (row)
33
+ out.set(key, clone(row));
34
+ }
35
+ return out;
36
+ },
37
+ async writeIntents(counterId, generation, intents, now) {
38
+ const conflicts = [];
39
+ for (const i of intents) {
40
+ const id = rowId(counterId, generation, i.key);
41
+ const row = ledger.get(id);
42
+ const seq = row ? row.seq : null;
43
+ if (seq !== i.expectedSeq) {
44
+ conflicts.push(i.key);
45
+ continue;
46
+ }
47
+ const pending = i.next ? clone(i.next) : { removed: true };
48
+ if (row) {
49
+ row.seq += 1;
50
+ row.pending = pending;
51
+ row.pendingAt = now;
52
+ }
53
+ else {
54
+ ledger.set(id, {
55
+ counterId,
56
+ generation,
57
+ key: i.key,
58
+ scope: "",
59
+ at: "",
60
+ facts: [],
61
+ track: {},
62
+ seq: 1,
63
+ pending,
64
+ pendingAt: now,
65
+ });
66
+ }
67
+ }
68
+ return conflicts;
69
+ },
70
+ async commitIntents(counterId, generation, keys) {
71
+ for (const key of keys) {
72
+ const id = rowId(counterId, generation, key);
73
+ const row = ledger.get(id);
74
+ if (!row?.pending)
75
+ continue;
76
+ if ("removed" in row.pending) {
77
+ ledger.delete(id);
78
+ continue;
79
+ }
80
+ Object.assign(row, row.pending, { pending: null, pendingAt: null });
81
+ }
82
+ },
83
+ async stalePending(olderThan) {
84
+ const out = new Set();
85
+ for (const row of ledger.values()) {
86
+ if (row.pending && row.pendingAt && Date.parse(row.pendingAt) < olderThan.getTime()) {
87
+ out.add(row.counterId);
88
+ }
89
+ }
90
+ return [...out];
91
+ },
92
+ async dropGenerations(counterId, belowGeneration) {
93
+ for (const [id, row] of ledger) {
94
+ if (row.counterId === counterId && (belowGeneration === undefined || row.generation < belowGeneration)) {
95
+ ledger.delete(id);
96
+ }
97
+ }
98
+ for (const [id, row] of pairs) {
99
+ if (row.counterId === counterId && (belowGeneration === undefined || row.generation < belowGeneration)) {
100
+ pairs.delete(id);
101
+ }
102
+ }
103
+ },
104
+ async touchPair(row) {
105
+ const known = pairs.get(row.scope);
106
+ if (known) {
107
+ if (row.firstSeenAt < known.firstSeenAt)
108
+ known.firstSeenAt = row.firstSeenAt;
109
+ return false;
110
+ }
111
+ pairs.set(row.scope, clone(row));
112
+ return true;
113
+ },
114
+ async listPairs(counterId, generation, limit) {
115
+ return [...pairs.values()]
116
+ .filter((p) => p.counterId === counterId && p.generation === generation)
117
+ .sort((a, b) => b.firstSeenAt.localeCompare(a.firstSeenAt) || b.scope.localeCompare(a.scope))
118
+ .slice(0, limit)
119
+ .map(clone);
120
+ },
121
+ };
122
+ }
123
+ function isDuplicateKey(err) {
124
+ return Boolean(err && typeof err === "object" && err.code === 11000);
125
+ }
126
+ export function createMongoAnalyticsState(db) {
127
+ const countersCol = db.collection("analytics_counters");
128
+ const ledgerCol = db.collection("analytics_subject_state");
129
+ const pairsCol = db.collection("analytics_pairs");
130
+ let indexed = null;
131
+ const ensureIndexes = () => (indexed ??= Promise.all([
132
+ ledgerCol.createIndex({ counterId: 1, generation: 1 }),
133
+ ledgerCol.createIndex({ pendingAt: 1 }, { sparse: true }),
134
+ pairsCol.createIndex({ counterId: 1, generation: 1, firstSeenAt: -1 }),
135
+ ]).then(() => undefined));
136
+ const rowId = (counterId, generation, key) => `${counterId}#${generation}|${key}`;
137
+ const strip = (doc) => {
138
+ const { _id, ...rest } = doc;
139
+ void _id;
140
+ return rest;
141
+ };
142
+ return {
143
+ async listCounters() {
144
+ return (await countersCol.find({}).toArray()).map((d) => strip(d));
145
+ },
146
+ async getCounter(id) {
147
+ const d = await countersCol.findOne({ _id: id });
148
+ return d ? strip(d) : null;
149
+ },
150
+ async saveCounter(record) {
151
+ await countersCol.replaceOne({ _id: record.id }, record, { upsert: true });
152
+ },
153
+ async patchCounter(id, patch, generation) {
154
+ const { id: _ignored, ...fields } = patch;
155
+ void _ignored;
156
+ const res = await countersCol.updateOne(generation === undefined ? { _id: id } : { _id: id, generation }, { $set: fields });
157
+ return res.matchedCount > 0;
158
+ },
159
+ async deleteCounter(id) {
160
+ await countersCol.deleteOne({ _id: id });
161
+ },
162
+ async readLedger(counterId, generation, keys) {
163
+ await ensureIndexes();
164
+ const out = new Map();
165
+ if (keys.length === 0)
166
+ return out;
167
+ const docs = await ledgerCol
168
+ .find({ _id: { $in: keys.map((k) => rowId(counterId, generation, k)) } })
169
+ .toArray();
170
+ for (const d of docs)
171
+ out.set(d.key, strip(d));
172
+ return out;
173
+ },
174
+ async writeIntents(counterId, generation, intents, now) {
175
+ await ensureIndexes();
176
+ const conflicts = [];
177
+ for (const i of intents) {
178
+ const _id = rowId(counterId, generation, i.key);
179
+ const pending = i.next ?? { removed: true };
180
+ if (i.expectedSeq === null) {
181
+ try {
182
+ await ledgerCol.insertOne({
183
+ _id,
184
+ counterId,
185
+ generation,
186
+ key: i.key,
187
+ scope: "",
188
+ at: "",
189
+ facts: [],
190
+ track: {},
191
+ seq: 1,
192
+ pending,
193
+ pendingAt: now,
194
+ });
195
+ }
196
+ catch (err) {
197
+ if (!isDuplicateKey(err))
198
+ throw err;
199
+ conflicts.push(i.key);
200
+ }
201
+ continue;
202
+ }
203
+ const res = await ledgerCol.updateOne({ _id, seq: i.expectedSeq }, { $set: { pending, pendingAt: now }, $inc: { seq: 1 } });
204
+ if (res.matchedCount === 0)
205
+ conflicts.push(i.key);
206
+ }
207
+ return conflicts;
208
+ },
209
+ async commitIntents(counterId, generation, keys) {
210
+ if (keys.length === 0)
211
+ return;
212
+ const ids = keys.map((k) => rowId(counterId, generation, k));
213
+ await ledgerCol.deleteMany({ _id: { $in: ids }, "pending.removed": true });
214
+ await ledgerCol.updateMany({ _id: { $in: ids }, pending: { $type: "object" } }, [
215
+ {
216
+ $set: {
217
+ scope: "$pending.scope",
218
+ at: "$pending.at",
219
+ facts: "$pending.facts",
220
+ track: "$pending.track",
221
+ },
222
+ },
223
+ { $unset: ["pending", "pendingAt"] },
224
+ ]);
225
+ },
226
+ async stalePending(olderThan) {
227
+ await ensureIndexes();
228
+ const ids = await ledgerCol.distinct("counterId", {
229
+ pendingAt: { $lt: olderThan.toISOString() },
230
+ });
231
+ return ids.map(String);
232
+ },
233
+ async dropGenerations(counterId, belowGeneration) {
234
+ const filter = belowGeneration === undefined ? { counterId } : { counterId, generation: { $lt: belowGeneration } };
235
+ await ledgerCol.deleteMany(filter);
236
+ await pairsCol.deleteMany(filter);
237
+ },
238
+ async touchPair(row) {
239
+ await ensureIndexes();
240
+ const { firstSeenAt, ...rest } = row;
241
+ try {
242
+ const res = await pairsCol.updateOne({ _id: row.scope }, { $min: { firstSeenAt }, $setOnInsert: rest }, { upsert: true });
243
+ return res.upsertedCount > 0;
244
+ }
245
+ catch (err) {
246
+ if (isDuplicateKey(err))
247
+ return false;
248
+ throw err;
249
+ }
250
+ },
251
+ async listPairs(counterId, generation, limit) {
252
+ await ensureIndexes();
253
+ const docs = await pairsCol
254
+ .find({ counterId, generation })
255
+ .sort({ firstSeenAt: -1, _id: -1 })
256
+ .limit(limit)
257
+ .toArray();
258
+ return docs.map((d) => strip(d));
259
+ },
260
+ };
261
+ }
262
+ export function mongoAnalyticsStateFactory(dbFor) {
263
+ const cache = new Map();
264
+ return (orgId) => {
265
+ let s = cache.get(orgId);
266
+ if (!s) {
267
+ s = createMongoAnalyticsState(dbFor(orgId));
268
+ cache.set(orgId, s);
269
+ }
270
+ return s;
271
+ };
272
+ }
273
+ //# sourceMappingURL=state.js.map