@vendoai/store 0.1.0 → 0.4.1
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/README.md +81 -0
- package/dist/blobs.d.ts +5 -0
- package/dist/blobs.d.ts.map +1 -0
- package/dist/blobs.js +62 -0
- package/dist/blobs.js.map +1 -0
- package/dist/crypto.d.ts +10 -0
- package/dist/crypto.d.ts.map +1 -0
- package/dist/crypto.js +44 -0
- package/dist/crypto.js.map +1 -0
- package/dist/db.d.ts +26 -26
- package/dist/db.d.ts.map +1 -0
- package/dist/db.js +335 -86
- package/dist/db.js.map +1 -0
- package/dist/erase.d.ts +33 -0
- package/dist/erase.d.ts.map +1 -0
- package/dist/erase.js +107 -0
- package/dist/erase.js.map +1 -0
- package/dist/helpers/approvals.d.ts +12 -0
- package/dist/helpers/approvals.d.ts.map +1 -0
- package/dist/helpers/approvals.js +37 -0
- package/dist/helpers/approvals.js.map +1 -0
- package/dist/helpers/apps.d.ts +15 -0
- package/dist/helpers/apps.d.ts.map +1 -0
- package/dist/helpers/apps.js +42 -0
- package/dist/helpers/apps.js.map +1 -0
- package/dist/helpers/audit.d.ts +24 -0
- package/dist/helpers/audit.d.ts.map +1 -0
- package/dist/helpers/audit.js +79 -0
- package/dist/helpers/audit.js.map +1 -0
- package/dist/helpers/grants.d.ts +14 -0
- package/dist/helpers/grants.d.ts.map +1 -0
- package/dist/helpers/grants.js +46 -0
- package/dist/helpers/grants.js.map +1 -0
- package/dist/helpers/rows.d.ts +30 -0
- package/dist/helpers/rows.d.ts.map +1 -0
- package/dist/helpers/rows.js +191 -0
- package/dist/helpers/rows.js.map +1 -0
- package/dist/helpers/runs.d.ts +19 -0
- package/dist/helpers/runs.d.ts.map +1 -0
- package/dist/helpers/runs.js +47 -0
- package/dist/helpers/runs.js.map +1 -0
- package/dist/helpers/state.d.ts +9 -0
- package/dist/helpers/state.d.ts.map +1 -0
- package/dist/helpers/state.js +22 -0
- package/dist/helpers/state.js.map +1 -0
- package/dist/helpers/subjects.d.ts +32 -0
- package/dist/helpers/subjects.d.ts.map +1 -0
- package/dist/helpers/subjects.js +78 -0
- package/dist/helpers/subjects.js.map +1 -0
- package/dist/helpers/threads.d.ts +19 -0
- package/dist/helpers/threads.d.ts.map +1 -0
- package/dist/helpers/threads.js +38 -0
- package/dist/helpers/threads.js.map +1 -0
- package/dist/helpers/types.d.ts +60 -0
- package/dist/helpers/types.d.ts.map +1 -0
- package/dist/helpers/types.js +2 -0
- package/dist/helpers/types.js.map +1 -0
- package/dist/helpers/utils.d.ts +37 -0
- package/dist/helpers/utils.d.ts.map +1 -0
- package/dist/helpers/utils.js +94 -0
- package/dist/helpers/utils.js.map +1 -0
- package/dist/index.d.ts +13 -11
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +17 -15
- package/dist/index.js.map +1 -0
- package/dist/records.d.ts +7 -0
- package/dist/records.d.ts.map +1 -0
- package/dist/records.js +208 -0
- package/dist/records.js.map +1 -0
- package/dist/routing.d.ts +7 -0
- package/dist/routing.d.ts.map +1 -0
- package/dist/routing.js +393 -0
- package/dist/routing.js.map +1 -0
- package/dist/schema.d.ts +18 -1420
- package/dist/schema.d.ts.map +1 -0
- package/dist/schema.js +206 -114
- package/dist/schema.js.map +1 -0
- package/dist/secrets.d.ts +13 -0
- package/dist/secrets.d.ts.map +1 -0
- package/dist/secrets.js +86 -0
- package/dist/secrets.js.map +1 -0
- package/dist/sessions.d.ts +53 -0
- package/dist/sessions.d.ts.map +1 -0
- package/dist/sessions.js +85 -0
- package/dist/sessions.js.map +1 -0
- package/dist/store.d.ts +24 -0
- package/dist/store.d.ts.map +1 -0
- package/dist/store.js +49 -0
- package/dist/store.js.map +1 -0
- package/dist/validate.d.ts +25 -0
- package/dist/validate.d.ts.map +1 -0
- package/dist/validate.js +144 -0
- package/dist/validate.js.map +1 -0
- package/package.json +15 -14
- package/dist/automation-store.d.ts +0 -62
- package/dist/automation-store.js +0 -675
- package/dist/connections-store.d.ts +0 -25
- package/dist/connections-store.js +0 -102
- package/dist/decision-store.d.ts +0 -8
- package/dist/decision-store.js +0 -39
- package/dist/meta.d.ts +0 -5
- package/dist/meta.js +0 -20
- package/dist/thread-store.d.ts +0 -5
- package/dist/thread-store.js +0 -209
- package/dist/vendo-registry.d.ts +0 -5
- package/dist/vendo-registry.js +0 -52
- package/migrations/.gitkeep +0 -0
- package/migrations/0000_living_kate_bishop.sql +0 -110
- package/migrations/0001_brainy_tinkerer.sql +0 -13
- package/migrations/meta/0000_snapshot.json +0 -757
- package/migrations/meta/0001_snapshot.json +0 -859
- package/migrations/meta/_journal.json +0 -20
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import type { AppId, Json, Principal } from "@vendoai/core";
|
|
2
|
+
import { type VendoStore } from "../store.js";
|
|
3
|
+
/** 02-store §3 */
|
|
4
|
+
export declare function stateStore(store: VendoStore): {
|
|
5
|
+
get(principal: Principal, appId: AppId): Promise<Json | null>;
|
|
6
|
+
put(principal: Principal, appId: AppId, data: Json): Promise<void>;
|
|
7
|
+
delete(principal: Principal, appId: AppId): Promise<void>;
|
|
8
|
+
};
|
|
9
|
+
//# sourceMappingURL=state.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"state.d.ts","sourceRoot":"","sources":["../../src/helpers/state.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,KAAK,EAAE,IAAI,EAAE,SAAS,EAAE,MAAM,eAAe,CAAC;AAE5D,OAAO,EAAS,KAAK,UAAU,EAAE,MAAM,aAAa,CAAC;AAGrD,kBAAkB;AAClB,wBAAgB,UAAU,CAAC,KAAK,EAAE,UAAU,GAAG;IAC7C,GAAG,CAAC,SAAS,EAAE,SAAS,EAAE,KAAK,EAAE,KAAK,GAAG,OAAO,CAAC,IAAI,GAAG,IAAI,CAAC,CAAC;IAC9D,GAAG,CAAC,SAAS,EAAE,SAAS,EAAE,KAAK,EAAE,KAAK,EAAE,IAAI,EAAE,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IACnE,MAAM,CAAC,SAAS,EAAE,SAAS,EAAE,KAAK,EAAE,KAAK,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;CAC3D,CAmBA"}
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import { putStateRow } from "./rows.js";
|
|
2
|
+
import { dbFor } from "../store.js";
|
|
3
|
+
import { requireJson } from "../validate.js";
|
|
4
|
+
/** 02-store §3 */
|
|
5
|
+
export function stateStore(store) {
|
|
6
|
+
const db = dbFor(store);
|
|
7
|
+
return {
|
|
8
|
+
async get(principal, appId) {
|
|
9
|
+
const result = await db.query("SELECT data FROM vendo_state WHERE app_id = $1 AND subject = $2", [appId, principal.subject]);
|
|
10
|
+
return result.rows[0]?.["data"] ?? null;
|
|
11
|
+
},
|
|
12
|
+
async put(principal, appId, data) {
|
|
13
|
+
const parsedData = requireJson(data, "state data");
|
|
14
|
+
// Shared write path with the routed seam (helpers/rows putStateRow).
|
|
15
|
+
await putStateRow(db, { appId, subject: principal.subject, data: parsedData });
|
|
16
|
+
},
|
|
17
|
+
async delete(principal, appId) {
|
|
18
|
+
await db.query("DELETE FROM vendo_state WHERE app_id = $1 AND subject = $2", [appId, principal.subject]);
|
|
19
|
+
},
|
|
20
|
+
};
|
|
21
|
+
}
|
|
22
|
+
//# sourceMappingURL=state.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"state.js","sourceRoot":"","sources":["../../src/helpers/state.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,WAAW,EAAE,MAAM,WAAW,CAAC;AACxC,OAAO,EAAE,KAAK,EAAmB,MAAM,aAAa,CAAC;AACrD,OAAO,EAAE,WAAW,EAAE,MAAM,gBAAgB,CAAC;AAE7C,kBAAkB;AAClB,MAAM,UAAU,UAAU,CAAC,KAAiB;IAK1C,MAAM,EAAE,GAAG,KAAK,CAAC,KAAK,CAAC,CAAC;IACxB,OAAO;QACL,KAAK,CAAC,GAAG,CAAC,SAAS,EAAE,KAAK;YACxB,MAAM,MAAM,GAAG,MAAM,EAAE,CAAC,KAAK,CAC3B,iEAAiE,EACjE,CAAC,KAAK,EAAE,SAAS,CAAC,OAAO,CAAC,CAC3B,CAAC;YACF,OAAO,MAAM,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,MAAM,CAAC,IAAI,IAAI,CAAC;QAC1C,CAAC;QACD,KAAK,CAAC,GAAG,CAAC,SAAS,EAAE,KAAK,EAAE,IAAI;YAC9B,MAAM,UAAU,GAAG,WAAW,CAAC,IAAI,EAAE,YAAY,CAAC,CAAC;YACnD,qEAAqE;YACrE,MAAM,WAAW,CAAC,EAAE,EAAE,EAAE,KAAK,EAAE,OAAO,EAAE,SAAS,CAAC,OAAO,EAAE,IAAI,EAAE,UAAU,EAAE,CAAC,CAAC;QACjF,CAAC;QACD,KAAK,CAAC,MAAM,CAAC,SAAS,EAAE,KAAK;YAC3B,MAAM,EAAE,CAAC,KAAK,CAAC,4DAA4D,EAAE,CAAC,KAAK,EAAE,SAAS,CAAC,OAAO,CAAC,CAAC,CAAC;QAC3G,CAAC;KACF,CAAC;AACJ,CAAC"}
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
import { type VendoStore } from "../store.js";
|
|
2
|
+
/** What an anonymous→signed-in merge moved (block-actions design §C). */
|
|
3
|
+
export interface SubjectMergeReport {
|
|
4
|
+
apps: number;
|
|
5
|
+
threads: number;
|
|
6
|
+
states: number;
|
|
7
|
+
/** Rows whose slot the signed-in subject already owned — NEVER overwritten
|
|
8
|
+
(a merge cannot replace the target's data); the anonymous copy is dropped. */
|
|
9
|
+
skipped: number;
|
|
10
|
+
}
|
|
11
|
+
/** Block-actions design §C — anonymous→signed-in auto-merge. One of the two
|
|
12
|
+
sanctioned doors through 02-store §2's "rows never cross subjects": the
|
|
13
|
+
first authenticated request carrying a valid anonymous cookie adopts the
|
|
14
|
+
anonymous session's threads, apps (with their per-app record/blob
|
|
15
|
+
collections, which travel with app ownership), and state into the
|
|
16
|
+
signed-in subject.
|
|
17
|
+
|
|
18
|
+
Deliberately NOT migrated — consent does not transfer identities:
|
|
19
|
+
- grants and approvals (users re-approve as themselves),
|
|
20
|
+
- connected accounts (Composio keys them by subject; users reconnect),
|
|
21
|
+
- audit and run history (history is a record of what the anonymous
|
|
22
|
+
principal did; it is not rewritten).
|
|
23
|
+
The dropped rows are DELETED with the session (kill-list B3: anonymous rows
|
|
24
|
+
are disk rows now).
|
|
25
|
+
|
|
26
|
+
Idempotent: an anonymous subject that was never registered (or was already
|
|
27
|
+
merged) returns null and moves nothing. Nothing is ever stolen: apps and
|
|
28
|
+
threads are keyed by unique ids the write doors already refuse to flip
|
|
29
|
+
across subjects, and a state row that collides with one the signed-in
|
|
30
|
+
subject already owns is skipped, never overwritten. */
|
|
31
|
+
export declare function adoptEphemeralSubject(store: VendoStore, from: string, to: string): Promise<SubjectMergeReport | null>;
|
|
32
|
+
//# sourceMappingURL=subjects.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"subjects.d.ts","sourceRoot":"","sources":["../../src/helpers/subjects.ts"],"names":[],"mappings":"AAEA,OAAO,EAAS,KAAK,UAAU,EAAE,MAAM,aAAa,CAAC;AAErD,yEAAyE;AACzE,MAAM,WAAW,kBAAkB;IACjC,IAAI,EAAE,MAAM,CAAC;IACb,OAAO,EAAE,MAAM,CAAC;IAChB,MAAM,EAAE,MAAM,CAAC;IACf;qFACiF;IACjF,OAAO,EAAE,MAAM,CAAC;CACjB;AAED;;;;;;;;;;;;;;;;;;;0DAmB0D;AAC1D,wBAAsB,qBAAqB,CACzC,KAAK,EAAE,UAAU,EACjB,IAAI,EAAE,MAAM,EACZ,EAAE,EAAE,MAAM,GACT,OAAO,CAAC,kBAAkB,GAAG,IAAI,CAAC,CAyEpC"}
|
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
import { isReservedSubject, VendoError } from "@vendoai/core";
|
|
2
|
+
import { isEphemeralSubject } from "../sessions.js";
|
|
3
|
+
import { dbFor } from "../store.js";
|
|
4
|
+
/** Block-actions design §C — anonymous→signed-in auto-merge. One of the two
|
|
5
|
+
sanctioned doors through 02-store §2's "rows never cross subjects": the
|
|
6
|
+
first authenticated request carrying a valid anonymous cookie adopts the
|
|
7
|
+
anonymous session's threads, apps (with their per-app record/blob
|
|
8
|
+
collections, which travel with app ownership), and state into the
|
|
9
|
+
signed-in subject.
|
|
10
|
+
|
|
11
|
+
Deliberately NOT migrated — consent does not transfer identities:
|
|
12
|
+
- grants and approvals (users re-approve as themselves),
|
|
13
|
+
- connected accounts (Composio keys them by subject; users reconnect),
|
|
14
|
+
- audit and run history (history is a record of what the anonymous
|
|
15
|
+
principal did; it is not rewritten).
|
|
16
|
+
The dropped rows are DELETED with the session (kill-list B3: anonymous rows
|
|
17
|
+
are disk rows now).
|
|
18
|
+
|
|
19
|
+
Idempotent: an anonymous subject that was never registered (or was already
|
|
20
|
+
merged) returns null and moves nothing. Nothing is ever stolen: apps and
|
|
21
|
+
threads are keyed by unique ids the write doors already refuse to flip
|
|
22
|
+
across subjects, and a state row that collides with one the signed-in
|
|
23
|
+
subject already owns is skipped, never overwritten. */
|
|
24
|
+
export async function adoptEphemeralSubject(store, from, to) {
|
|
25
|
+
if (from === to)
|
|
26
|
+
throw new VendoError("validation", "cannot merge a subject into itself");
|
|
27
|
+
if (isReservedSubject(to)) {
|
|
28
|
+
throw new VendoError("validation", "cannot merge an anonymous session into a reserved subject");
|
|
29
|
+
}
|
|
30
|
+
if (await isEphemeralSubject(store, to)) {
|
|
31
|
+
throw new VendoError("validation", "cannot merge an anonymous session into an ephemeral subject");
|
|
32
|
+
}
|
|
33
|
+
const db = dbFor(store);
|
|
34
|
+
// Claim-first serialization with the TTL sweep (sessions.ts): deleting the
|
|
35
|
+
// session row is the mutual-exclusion point. Losing the claim means the
|
|
36
|
+
// subject was never registered, was already merged, or a concurrent sweep
|
|
37
|
+
// owns it — this merge moves nothing. Winning it means no sweep can erase
|
|
38
|
+
// the subject's rows while they are moved. The trade: a transient failure
|
|
39
|
+
// MID-merge (after the claim) strands the unmoved remainder as unregistered
|
|
40
|
+
// rows a retry can no longer see — bounded by 02 §4's lingers-like-durable
|
|
41
|
+
// semantics (an explicit erase.bySubject still reaches them).
|
|
42
|
+
const claimed = await db.query("DELETE FROM vendo_sessions WHERE subject = $1 RETURNING 1", [from]);
|
|
43
|
+
if (claimed.rows[0] === undefined)
|
|
44
|
+
return null;
|
|
45
|
+
const report = { apps: 0, threads: 0, states: 0, skipped: 0 };
|
|
46
|
+
// Apps move by flipping the subject column. Ids are the vendo_apps PRIMARY
|
|
47
|
+
// KEY and the write doors refuse cross-subject flips, so `from`'s app ids
|
|
48
|
+
// can never collide with anyone else's rows. The app's record collections
|
|
49
|
+
// and blob namespaces (`app:<appId>:...`) are keyed by app id, not subject —
|
|
50
|
+
// they travel with the ownership flip untouched.
|
|
51
|
+
const movedApps = await db.query("UPDATE vendo_apps SET subject = $2 WHERE subject = $1 RETURNING id", [from, to]);
|
|
52
|
+
report.apps = movedApps.rows.length;
|
|
53
|
+
// Threads: same shape (unique PRIMARY KEY id, door-guarded).
|
|
54
|
+
const movedThreads = await db.query("UPDATE vendo_threads SET subject = $2 WHERE subject = $1 RETURNING id", [from, to]);
|
|
55
|
+
report.threads = movedThreads.rows.length;
|
|
56
|
+
// State is keyed (app_id, subject), so the signed-in subject may already
|
|
57
|
+
// hold a row for the same app — that existing row wins; the anonymous copy
|
|
58
|
+
// is skipped and dropped.
|
|
59
|
+
const movedStates = await db.query(`UPDATE vendo_state SET subject = $2 WHERE subject = $1
|
|
60
|
+
AND NOT EXISTS (
|
|
61
|
+
SELECT 1 FROM vendo_state existing
|
|
62
|
+
WHERE existing.app_id = vendo_state.app_id AND existing.subject = $2
|
|
63
|
+
)
|
|
64
|
+
RETURNING app_id`, [from, to]);
|
|
65
|
+
report.states = movedStates.rows.length;
|
|
66
|
+
const skippedStates = await db.query("DELETE FROM vendo_state WHERE subject = $1 RETURNING app_id", [from]);
|
|
67
|
+
report.skipped += skippedStates.rows.length;
|
|
68
|
+
// Everything else the anonymous subject accrued is deliberately dropped:
|
|
69
|
+
// grants, approvals, audit, and the run history of its (now adopted) apps.
|
|
70
|
+
await db.query("DELETE FROM vendo_grants WHERE subject = $1", [from]);
|
|
71
|
+
await db.query("DELETE FROM vendo_approvals WHERE subject = $1", [from]);
|
|
72
|
+
await db.query("DELETE FROM vendo_audit WHERE subject = $1", [from]);
|
|
73
|
+
if (report.apps > 0) {
|
|
74
|
+
await db.query("DELETE FROM vendo_runs WHERE app_id = ANY($1::text[])", [movedApps.rows.map((row) => String(row["id"]))]);
|
|
75
|
+
}
|
|
76
|
+
return report;
|
|
77
|
+
}
|
|
78
|
+
//# sourceMappingURL=subjects.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"subjects.js","sourceRoot":"","sources":["../../src/helpers/subjects.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import type { Json, Principal, ThreadId } from "@vendoai/core";
|
|
2
|
+
import { type VendoStore } from "../store.js";
|
|
3
|
+
import type { ThreadRow } from "./types.js";
|
|
4
|
+
/** 02-store §3 */
|
|
5
|
+
export declare function threadStore(store: VendoStore): {
|
|
6
|
+
put(principal: Principal, thread: {
|
|
7
|
+
id: ThreadId;
|
|
8
|
+
messages: Json[];
|
|
9
|
+
}): Promise<ThreadRow>;
|
|
10
|
+
get(principal: Principal, id: ThreadId): Promise<ThreadRow | null>;
|
|
11
|
+
list(principal: Principal): Promise<Array<{
|
|
12
|
+
id: ThreadId;
|
|
13
|
+
createdAt: string;
|
|
14
|
+
updatedAt: string;
|
|
15
|
+
}>>;
|
|
16
|
+
delete(principal: Principal, id: ThreadId): Promise<void>;
|
|
17
|
+
};
|
|
18
|
+
export type { ThreadRow } from "./types.js";
|
|
19
|
+
//# sourceMappingURL=threads.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"threads.d.ts","sourceRoot":"","sources":["../../src/helpers/threads.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,IAAI,EAAE,SAAS,EAAE,QAAQ,EAAE,MAAM,eAAe,CAAC;AAC/D,OAAO,EAAS,KAAK,UAAU,EAAE,MAAM,aAAa,CAAC;AACrD,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,YAAY,CAAC;AAK5C,kBAAkB;AAClB,wBAAgB,WAAW,CAAC,KAAK,EAAE,UAAU,GAAG;IAC9C,GAAG,CAAC,SAAS,EAAE,SAAS,EAAE,MAAM,EAAE;QAAE,EAAE,EAAE,QAAQ,CAAC;QAAC,QAAQ,EAAE,IAAI,EAAE,CAAA;KAAE,GAAG,OAAO,CAAC,SAAS,CAAC,CAAC;IAC1F,GAAG,CAAC,SAAS,EAAE,SAAS,EAAE,EAAE,EAAE,QAAQ,GAAG,OAAO,CAAC,SAAS,GAAG,IAAI,CAAC,CAAC;IACnE,IAAI,CAAC,SAAS,EAAE,SAAS,GAAG,OAAO,CAAC,KAAK,CAAC;QAAE,EAAE,EAAE,QAAQ,CAAC;QAAC,SAAS,EAAE,MAAM,CAAC;QAAC,SAAS,EAAE,MAAM,CAAA;KAAE,CAAC,CAAC,CAAC;IACnG,MAAM,CAAC,SAAS,EAAE,SAAS,EAAE,EAAE,EAAE,QAAQ,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;CAC3D,CAqCA;AAED,YAAY,EAAE,SAAS,EAAE,MAAM,YAAY,CAAC"}
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
import { dbFor } from "../store.js";
|
|
2
|
+
import { putThreadRow, threadFromRow } from "./rows.js";
|
|
3
|
+
import { iso, text } from "./utils.js";
|
|
4
|
+
import { parseThreadData } from "../validate.js";
|
|
5
|
+
/** 02-store §3 */
|
|
6
|
+
export function threadStore(store) {
|
|
7
|
+
const db = dbFor(store);
|
|
8
|
+
return {
|
|
9
|
+
async put(principal, thread) {
|
|
10
|
+
const parsed = parseThreadData({ subject: principal.subject, messages: thread.messages }, thread.id);
|
|
11
|
+
// Threads never cross subjects (03 §5): the guarded upsert refuses a
|
|
12
|
+
// foreign-owned id atomically.
|
|
13
|
+
return putThreadRow(db, {
|
|
14
|
+
id: thread.id,
|
|
15
|
+
subject: parsed.subject,
|
|
16
|
+
messages: parsed.messages,
|
|
17
|
+
});
|
|
18
|
+
},
|
|
19
|
+
async get(principal, id) {
|
|
20
|
+
const result = await db.query(`SELECT id, subject, messages, title, created_at, updated_at, revision FROM vendo_threads
|
|
21
|
+
WHERE id = $1 AND subject = $2`, [id, principal.subject]);
|
|
22
|
+
return result.rows[0] ? threadFromRow(result.rows[0]) : null;
|
|
23
|
+
},
|
|
24
|
+
async list(principal) {
|
|
25
|
+
const result = await db.query(`SELECT id, created_at, updated_at FROM vendo_threads WHERE subject = $1
|
|
26
|
+
ORDER BY updated_at DESC, id DESC`, [principal.subject]);
|
|
27
|
+
return result.rows.map((row) => ({
|
|
28
|
+
id: text(row["id"]),
|
|
29
|
+
createdAt: iso(row["created_at"]),
|
|
30
|
+
updatedAt: iso(row["updated_at"]),
|
|
31
|
+
}));
|
|
32
|
+
},
|
|
33
|
+
async delete(principal, id) {
|
|
34
|
+
await db.query("DELETE FROM vendo_threads WHERE id = $1 AND subject = $2", [id, principal.subject]);
|
|
35
|
+
},
|
|
36
|
+
};
|
|
37
|
+
}
|
|
38
|
+
//# sourceMappingURL=threads.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"threads.js","sourceRoot":"","sources":["../../src/helpers/threads.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,KAAK,EAAmB,MAAM,aAAa,CAAC;AAErD,OAAO,EAAE,YAAY,EAAE,aAAa,EAAE,MAAM,WAAW,CAAC;AACxD,OAAO,EAAE,GAAG,EAAE,IAAI,EAAE,MAAM,YAAY,CAAC;AACvC,OAAO,EAAE,eAAe,EAAE,MAAM,gBAAgB,CAAC;AAEjD,kBAAkB;AAClB,MAAM,UAAU,WAAW,CAAC,KAAiB;IAM3C,MAAM,EAAE,GAAG,KAAK,CAAC,KAAK,CAAC,CAAC;IACxB,OAAO;QACL,KAAK,CAAC,GAAG,CAAC,SAAS,EAAE,MAAM;YACzB,MAAM,MAAM,GAAG,eAAe,CAAC,EAAE,OAAO,EAAE,SAAS,CAAC,OAAO,EAAE,QAAQ,EAAE,MAAM,CAAC,QAAQ,EAAE,EAAE,MAAM,CAAC,EAAE,CAAC,CAAC;YACrG,qEAAqE;YACrE,+BAA+B;YAC/B,OAAO,YAAY,CAAC,EAAE,EAAE;gBACtB,EAAE,EAAE,MAAM,CAAC,EAAE;gBACb,OAAO,EAAE,MAAM,CAAC,OAAO;gBACvB,QAAQ,EAAE,MAAM,CAAC,QAAQ;aAC1B,CAAC,CAAC;QACL,CAAC;QACD,KAAK,CAAC,GAAG,CAAC,SAAS,EAAE,EAAE;YACrB,MAAM,MAAM,GAAG,MAAM,EAAE,CAAC,KAAK,CAC3B;wCACgC,EAChC,CAAC,EAAE,EAAE,SAAS,CAAC,OAAO,CAAC,CACxB,CAAC;YACF,OAAO,MAAM,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,aAAa,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC;QAC/D,CAAC;QACD,KAAK,CAAC,IAAI,CAAC,SAAS;YAClB,MAAM,MAAM,GAAG,MAAM,EAAE,CAAC,KAAK,CAC3B;2CACmC,EACnC,CAAC,SAAS,CAAC,OAAO,CAAC,CACpB,CAAC;YACF,OAAO,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,GAAG,EAAE,EAAE,CAAC,CAAC;gBAC/B,EAAE,EAAE,IAAI,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC;gBACnB,SAAS,EAAE,GAAG,CAAC,GAAG,CAAC,YAAY,CAAC,CAAC;gBACjC,SAAS,EAAE,GAAG,CAAC,GAAG,CAAC,YAAY,CAAC,CAAC;aAClC,CAAC,CAAC,CAAC;QACN,CAAC;QACD,KAAK,CAAC,MAAM,CAAC,SAAS,EAAE,EAAE;YACxB,MAAM,EAAE,CAAC,KAAK,CAAC,0DAA0D,EAAE,CAAC,EAAE,EAAE,SAAS,CAAC,OAAO,CAAC,CAAC,CAAC;QACtG,CAAC;KACF,CAAC;AACJ,CAAC"}
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
import type { AppDocument, AppId, ApprovalId, ApprovalRequest, IsoDateTime, Json, RunId, ThreadId, TriggerSource } from "@vendoai/core";
|
|
2
|
+
/** 02-store §3 */
|
|
3
|
+
export interface AppRow {
|
|
4
|
+
id: AppId;
|
|
5
|
+
subject: string;
|
|
6
|
+
enabled: boolean;
|
|
7
|
+
doc: AppDocument;
|
|
8
|
+
createdAt: IsoDateTime;
|
|
9
|
+
updatedAt: IsoDateTime;
|
|
10
|
+
/** Opaque write counter backing the routed atomic capability (01 §12); bumped
|
|
11
|
+
* on every write. Wave 7 — arbitration for the machine lifecycle and the
|
|
12
|
+
* schedule engine's fire claims (updateAppRow's read-mutate-CAS). */
|
|
13
|
+
revision?: string;
|
|
14
|
+
}
|
|
15
|
+
/** 02-store §3 */
|
|
16
|
+
export interface ThreadRow {
|
|
17
|
+
id: ThreadId;
|
|
18
|
+
subject: string;
|
|
19
|
+
messages: Json[];
|
|
20
|
+
/** Precomputed listing title (03 §5); lets `list` skip loading the messages array. */
|
|
21
|
+
title?: string;
|
|
22
|
+
createdAt: IsoDateTime;
|
|
23
|
+
updatedAt: IsoDateTime;
|
|
24
|
+
/** Opaque write counter backing the routed atomic capability (01 §12); bumped
|
|
25
|
+
* on every write. Absent only on projections that never carry it (listSelect). */
|
|
26
|
+
revision?: string;
|
|
27
|
+
}
|
|
28
|
+
/** 02-store §3 */
|
|
29
|
+
export interface ApprovalRow {
|
|
30
|
+
id: ApprovalId;
|
|
31
|
+
subject: string;
|
|
32
|
+
request: ApprovalRequest;
|
|
33
|
+
status: "pending" | "approved" | "denied";
|
|
34
|
+
decidedAt?: IsoDateTime;
|
|
35
|
+
sessionId?: string;
|
|
36
|
+
consumedAt?: IsoDateTime;
|
|
37
|
+
createdAt: IsoDateTime;
|
|
38
|
+
}
|
|
39
|
+
/** 02-store §3 */
|
|
40
|
+
export interface RunRow {
|
|
41
|
+
id: RunId;
|
|
42
|
+
appId: AppId;
|
|
43
|
+
trigger: {
|
|
44
|
+
kind: TriggerSource["kind"];
|
|
45
|
+
event?: string;
|
|
46
|
+
};
|
|
47
|
+
status: "running" | "ok" | "error" | "stopped" | "pending-approval";
|
|
48
|
+
record: Json;
|
|
49
|
+
startedAt: IsoDateTime;
|
|
50
|
+
finishedAt?: IsoDateTime;
|
|
51
|
+
}
|
|
52
|
+
/** 02-store §3 — one per-user-per-app state singleton row. */
|
|
53
|
+
export interface StateRow {
|
|
54
|
+
appId: AppId;
|
|
55
|
+
subject: string;
|
|
56
|
+
data: Json;
|
|
57
|
+
createdAt: IsoDateTime;
|
|
58
|
+
updatedAt: IsoDateTime;
|
|
59
|
+
}
|
|
60
|
+
//# sourceMappingURL=types.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../src/helpers/types.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EACV,WAAW,EACX,KAAK,EACL,UAAU,EACV,eAAe,EACf,WAAW,EACX,IAAI,EACJ,KAAK,EACL,QAAQ,EACR,aAAa,EACd,MAAM,eAAe,CAAC;AAEvB,kBAAkB;AAClB,MAAM,WAAW,MAAM;IACrB,EAAE,EAAE,KAAK,CAAC;IACV,OAAO,EAAE,MAAM,CAAC;IAChB,OAAO,EAAE,OAAO,CAAC;IACjB,GAAG,EAAE,WAAW,CAAC;IACjB,SAAS,EAAE,WAAW,CAAC;IACvB,SAAS,EAAE,WAAW,CAAC;IACvB;;0EAEsE;IACtE,QAAQ,CAAC,EAAE,MAAM,CAAC;CACnB;AAED,kBAAkB;AAClB,MAAM,WAAW,SAAS;IACxB,EAAE,EAAE,QAAQ,CAAC;IACb,OAAO,EAAE,MAAM,CAAC;IAChB,QAAQ,EAAE,IAAI,EAAE,CAAC;IACjB,sFAAsF;IACtF,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,SAAS,EAAE,WAAW,CAAC;IACvB,SAAS,EAAE,WAAW,CAAC;IACvB;uFACmF;IACnF,QAAQ,CAAC,EAAE,MAAM,CAAC;CACnB;AAED,kBAAkB;AAClB,MAAM,WAAW,WAAW;IAC1B,EAAE,EAAE,UAAU,CAAC;IACf,OAAO,EAAE,MAAM,CAAC;IAChB,OAAO,EAAE,eAAe,CAAC;IACzB,MAAM,EAAE,SAAS,GAAG,UAAU,GAAG,QAAQ,CAAC;IAC1C,SAAS,CAAC,EAAE,WAAW,CAAC;IACxB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,UAAU,CAAC,EAAE,WAAW,CAAC;IACzB,SAAS,EAAE,WAAW,CAAC;CACxB;AAED,kBAAkB;AAClB,MAAM,WAAW,MAAM;IACrB,EAAE,EAAE,KAAK,CAAC;IACV,KAAK,EAAE,KAAK,CAAC;IACb,OAAO,EAAE;QAAE,IAAI,EAAE,aAAa,CAAC,MAAM,CAAC,CAAC;QAAC,KAAK,CAAC,EAAE,MAAM,CAAA;KAAE,CAAC;IACzD,MAAM,EAAE,SAAS,GAAG,IAAI,GAAG,OAAO,GAAG,SAAS,GAAG,kBAAkB,CAAC;IACpE,MAAM,EAAE,IAAI,CAAC;IACb,SAAS,EAAE,WAAW,CAAC;IACvB,UAAU,CAAC,EAAE,WAAW,CAAC;CAC1B;AAED,8DAA8D;AAC9D,MAAM,WAAW,QAAQ;IACvB,KAAK,EAAE,KAAK,CAAC;IACb,OAAO,EAAE,MAAM,CAAC;IAChB,IAAI,EAAE,IAAI,CAAC;IACX,SAAS,EAAE,WAAW,CAAC;IACvB,SAAS,EAAE,WAAW,CAAC;CACxB"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"types.js","sourceRoot":"","sources":["../../src/helpers/types.ts"],"names":[],"mappings":""}
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
import { VendoError, type IsoDateTime } from "@vendoai/core";
|
|
2
|
+
import type { Db } from "../db.js";
|
|
3
|
+
export declare function iso(value: unknown): IsoDateTime;
|
|
4
|
+
export declare function optionalIso(value: unknown): IsoDateTime | undefined;
|
|
5
|
+
export declare function text(value: unknown): string;
|
|
6
|
+
/** Keyset timestamps compare at millisecond precision. The timestamptz cursor
|
|
7
|
+
* columns can hold microseconds — the §2 table map is public (direct host
|
|
8
|
+
* SQL, vendo_state's DEFAULT now()), and caller-supplied ISO timestamps
|
|
9
|
+
* (audit `at`, run `startedAt`) are z.string().datetime()-validated, which
|
|
10
|
+
* accepts sub-ms digits — while cursors round-trip through JS Dates (ms).
|
|
11
|
+
* Comparing at full precision against a truncated cursor silently skips rows
|
|
12
|
+
* (DESC lists) or repeats them (ASC export), so every keyset predicate and
|
|
13
|
+
* its ORDER BY route BOTH the column and the cursor parameter through this
|
|
14
|
+
* same truncated expression. */
|
|
15
|
+
export declare function cursorMs(expr: string): string;
|
|
16
|
+
export declare function encodeCursor(date: IsoDateTime, id: string): string;
|
|
17
|
+
export declare function decodeCursor(value: string): {
|
|
18
|
+
c: IsoDateTime;
|
|
19
|
+
i: string;
|
|
20
|
+
};
|
|
21
|
+
export declare function pageLimit(value: number | undefined): number;
|
|
22
|
+
export declare function jsonParam(value: unknown): string;
|
|
23
|
+
export declare function escapeLike(value: string): string;
|
|
24
|
+
/** The owning appId of an app-scoped record collection or blob namespace, or
|
|
25
|
+
* undefined for non-app-scoped scopes. */
|
|
26
|
+
export declare function appScopeId(scope: string): string | undefined;
|
|
27
|
+
/** 02-store §4 (STORE-1): the fail-closed refusal for app-scoped writes whose
|
|
28
|
+
* owning app has no `vendo_apps` row — never existed, or its ephemeral
|
|
29
|
+
* session was swept. */
|
|
30
|
+
export declare function unknownAppError(appId: string): VendoError;
|
|
31
|
+
/** Pre-check for non-row-creating verbs (delete, guarded mutations): a clean
|
|
32
|
+
* error signal, not the no-orphan guarantee. Row-CREATING statements embed
|
|
33
|
+
* the same condition in the statement itself (`WHERE EXISTS (SELECT 1 FROM
|
|
34
|
+
* vendo_apps WHERE id = ...)`) so a sweep racing the write can never leave an
|
|
35
|
+
* orphaned row — the guarantee is structural, not ordering care. */
|
|
36
|
+
export declare function requireKnownApp(db: Db, appId: string | undefined): Promise<void>;
|
|
37
|
+
//# sourceMappingURL=utils.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"utils.d.ts","sourceRoot":"","sources":["../../src/helpers/utils.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAqB,KAAK,WAAW,EAAE,MAAM,eAAe,CAAC;AAChF,OAAO,KAAK,EAAE,EAAE,EAAE,MAAM,UAAU,CAAC;AAEnC,wBAAgB,GAAG,CAAC,KAAK,EAAE,OAAO,GAAG,WAAW,CAK/C;AAED,wBAAgB,WAAW,CAAC,KAAK,EAAE,OAAO,GAAG,WAAW,GAAG,SAAS,CAEnE;AAED,wBAAgB,IAAI,CAAC,KAAK,EAAE,OAAO,GAAG,MAAM,CAG3C;AAED;;;;;;;;iCAQiC;AACjC,wBAAgB,QAAQ,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAE7C;AAED,wBAAgB,YAAY,CAAC,IAAI,EAAE,WAAW,EAAE,EAAE,EAAE,MAAM,GAAG,MAAM,CAElE;AAED,wBAAgB,YAAY,CAAC,KAAK,EAAE,MAAM,GAAG;IAAE,CAAC,EAAE,WAAW,CAAC;IAAC,CAAC,EAAE,MAAM,CAAA;CAAE,CAoBzE;AAED,wBAAgB,SAAS,CAAC,KAAK,EAAE,MAAM,GAAG,SAAS,GAAG,MAAM,CAM3D;AAED,wBAAgB,SAAS,CAAC,KAAK,EAAE,OAAO,GAAG,MAAM,CAEhD;AAED,wBAAgB,UAAU,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,CAEhD;AAMD;2CAC2C;AAC3C,wBAAgB,UAAU,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,GAAG,SAAS,CAE5D;AAED;;yBAEyB;AACzB,wBAAgB,eAAe,CAAC,KAAK,EAAE,MAAM,GAAG,UAAU,CAEzD;AAED;;;;qEAIqE;AACrE,wBAAsB,eAAe,CAAC,EAAE,EAAE,EAAE,EAAE,KAAK,EAAE,MAAM,GAAG,SAAS,GAAG,OAAO,CAAC,IAAI,CAAC,CAItF"}
|
|
@@ -0,0 +1,94 @@
|
|
|
1
|
+
import { VendoError, isoDateTimeSchema } from "@vendoai/core";
|
|
2
|
+
export function iso(value) {
|
|
3
|
+
if (!(typeof value === "string" || value instanceof Date)) {
|
|
4
|
+
throw new Error("Expected database timestamp");
|
|
5
|
+
}
|
|
6
|
+
return new Date(value).toISOString();
|
|
7
|
+
}
|
|
8
|
+
export function optionalIso(value) {
|
|
9
|
+
return value == null ? undefined : iso(value);
|
|
10
|
+
}
|
|
11
|
+
export function text(value) {
|
|
12
|
+
if (typeof value !== "string")
|
|
13
|
+
throw new Error("Expected database text");
|
|
14
|
+
return value;
|
|
15
|
+
}
|
|
16
|
+
/** Keyset timestamps compare at millisecond precision. The timestamptz cursor
|
|
17
|
+
* columns can hold microseconds — the §2 table map is public (direct host
|
|
18
|
+
* SQL, vendo_state's DEFAULT now()), and caller-supplied ISO timestamps
|
|
19
|
+
* (audit `at`, run `startedAt`) are z.string().datetime()-validated, which
|
|
20
|
+
* accepts sub-ms digits — while cursors round-trip through JS Dates (ms).
|
|
21
|
+
* Comparing at full precision against a truncated cursor silently skips rows
|
|
22
|
+
* (DESC lists) or repeats them (ASC export), so every keyset predicate and
|
|
23
|
+
* its ORDER BY route BOTH the column and the cursor parameter through this
|
|
24
|
+
* same truncated expression. */
|
|
25
|
+
export function cursorMs(expr) {
|
|
26
|
+
return `date_trunc('milliseconds', ${expr})`;
|
|
27
|
+
}
|
|
28
|
+
export function encodeCursor(date, id) {
|
|
29
|
+
return Buffer.from(JSON.stringify({ c: date, i: id }), "utf8").toString("base64url");
|
|
30
|
+
}
|
|
31
|
+
export function decodeCursor(value) {
|
|
32
|
+
try {
|
|
33
|
+
const decoded = Buffer.from(value, "base64url");
|
|
34
|
+
if (decoded.toString("base64url") !== value)
|
|
35
|
+
throw new Error("non-canonical cursor");
|
|
36
|
+
const parsed = JSON.parse(decoded.toString("utf8"));
|
|
37
|
+
if (typeof parsed !== "object" || parsed === null
|
|
38
|
+
|| Array.isArray(parsed)
|
|
39
|
+
|| Object.keys(parsed).length !== 2
|
|
40
|
+
|| !Object.prototype.hasOwnProperty.call(parsed, "c")
|
|
41
|
+
|| !Object.prototype.hasOwnProperty.call(parsed, "i")
|
|
42
|
+
|| typeof parsed.c !== "string"
|
|
43
|
+
|| typeof parsed.i !== "string")
|
|
44
|
+
throw new Error("invalid cursor");
|
|
45
|
+
const timestamp = isoDateTimeSchema.safeParse(parsed.c);
|
|
46
|
+
if (!timestamp.success)
|
|
47
|
+
throw new Error("invalid cursor timestamp");
|
|
48
|
+
return { c: timestamp.data, i: parsed.i };
|
|
49
|
+
}
|
|
50
|
+
catch {
|
|
51
|
+
throw new VendoError("validation", "malformed cursor");
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
export function pageLimit(value) {
|
|
55
|
+
if (value === undefined)
|
|
56
|
+
return 100;
|
|
57
|
+
if (!Number.isInteger(value) || value < 1) {
|
|
58
|
+
throw new VendoError("validation", "limit must be a positive integer");
|
|
59
|
+
}
|
|
60
|
+
return Math.min(value, 1000);
|
|
61
|
+
}
|
|
62
|
+
export function jsonParam(value) {
|
|
63
|
+
return JSON.stringify(value);
|
|
64
|
+
}
|
|
65
|
+
export function escapeLike(value) {
|
|
66
|
+
return value.replace(/[\\%_]/g, "\\$&");
|
|
67
|
+
}
|
|
68
|
+
/** app:<appId>:… is the collection/namespace grammar for app-scoped record and
|
|
69
|
+
* blob stores (01-core §12). The appId segment is colon-free by construction. */
|
|
70
|
+
const APP_SCOPE = /^app:([^:]+):/;
|
|
71
|
+
/** The owning appId of an app-scoped record collection or blob namespace, or
|
|
72
|
+
* undefined for non-app-scoped scopes. */
|
|
73
|
+
export function appScopeId(scope) {
|
|
74
|
+
return APP_SCOPE.exec(scope)?.[1];
|
|
75
|
+
}
|
|
76
|
+
/** 02-store §4 (STORE-1): the fail-closed refusal for app-scoped writes whose
|
|
77
|
+
* owning app has no `vendo_apps` row — never existed, or its ephemeral
|
|
78
|
+
* session was swept. */
|
|
79
|
+
export function unknownAppError(appId) {
|
|
80
|
+
return new VendoError("not-found", `app ${appId} does not exist (its session may have expired)`);
|
|
81
|
+
}
|
|
82
|
+
/** Pre-check for non-row-creating verbs (delete, guarded mutations): a clean
|
|
83
|
+
* error signal, not the no-orphan guarantee. Row-CREATING statements embed
|
|
84
|
+
* the same condition in the statement itself (`WHERE EXISTS (SELECT 1 FROM
|
|
85
|
+
* vendo_apps WHERE id = ...)`) so a sweep racing the write can never leave an
|
|
86
|
+
* orphaned row — the guarantee is structural, not ordering care. */
|
|
87
|
+
export async function requireKnownApp(db, appId) {
|
|
88
|
+
if (appId === undefined)
|
|
89
|
+
return;
|
|
90
|
+
const result = await db.query("SELECT 1 FROM vendo_apps WHERE id = $1", [appId]);
|
|
91
|
+
if (result.rows[0] === undefined)
|
|
92
|
+
throw unknownAppError(appId);
|
|
93
|
+
}
|
|
94
|
+
//# sourceMappingURL=utils.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"utils.js","sourceRoot":"","sources":["../../src/helpers/utils.ts"],"names":[],"mappings":"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"}
|
package/dist/index.d.ts
CHANGED
|
@@ -1,11 +1,13 @@
|
|
|
1
|
-
|
|
2
|
-
export {
|
|
3
|
-
export
|
|
4
|
-
export {
|
|
5
|
-
export {
|
|
6
|
-
export {
|
|
7
|
-
export {
|
|
8
|
-
export {
|
|
9
|
-
export {
|
|
10
|
-
export {
|
|
11
|
-
export
|
|
1
|
+
/** @vendoai/store — persistence under everything (docs/contracts/02-store.md). */
|
|
2
|
+
export { createStore, type VendoStore } from "./store.js";
|
|
3
|
+
export { DEDICATED_RECORD_COLLECTIONS, RESERVED_COLLECTIONS, type ReservedCollection, } from "./routing.js";
|
|
4
|
+
export { eraseStore, type EraseReport, type EraseTable } from "./erase.js";
|
|
5
|
+
export { claimEphemeralSubject, listStaleEphemeralSubjects, registerEphemeralSubject, sweepEphemeralSubjects, } from "./sessions.js";
|
|
6
|
+
export { envSecrets, secretStore, storeSecrets } from "./secrets.js";
|
|
7
|
+
export { appStore, type AppRow } from "./helpers/apps.js";
|
|
8
|
+
export { threadStore, type ThreadRow } from "./helpers/threads.js";
|
|
9
|
+
export { grantStore } from "./helpers/grants.js";
|
|
10
|
+
export { auditStore, type AuditQuery } from "./helpers/audit.js";
|
|
11
|
+
export { runStore, type RunRow } from "./helpers/runs.js";
|
|
12
|
+
export { adoptEphemeralSubject, type SubjectMergeReport, } from "./helpers/subjects.js";
|
|
13
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,kFAAkF;AAClF,OAAO,EAAE,WAAW,EAAE,KAAK,UAAU,EAAE,MAAM,YAAY,CAAC;AAK1D,OAAO,EACL,4BAA4B,EAC5B,oBAAoB,EACpB,KAAK,kBAAkB,GACxB,MAAM,cAAc,CAAC;AACtB,OAAO,EAAE,UAAU,EAAE,KAAK,WAAW,EAAE,KAAK,UAAU,EAAE,MAAM,YAAY,CAAC;AAC3E,OAAO,EACL,qBAAqB,EACrB,0BAA0B,EAC1B,wBAAwB,EACxB,sBAAsB,GACvB,MAAM,eAAe,CAAC;AACvB,OAAO,EAAE,UAAU,EAAE,WAAW,EAAE,YAAY,EAAE,MAAM,cAAc,CAAC;AACrE,OAAO,EAAE,QAAQ,EAAE,KAAK,MAAM,EAAE,MAAM,mBAAmB,CAAC;AAC1D,OAAO,EAAE,WAAW,EAAE,KAAK,SAAS,EAAE,MAAM,sBAAsB,CAAC;AACnE,OAAO,EAAE,UAAU,EAAE,MAAM,qBAAqB,CAAC;AACjD,OAAO,EAAE,UAAU,EAAE,KAAK,UAAU,EAAE,MAAM,oBAAoB,CAAC;AACjE,OAAO,EAAE,QAAQ,EAAE,KAAK,MAAM,EAAE,MAAM,mBAAmB,CAAC;AAC1D,OAAO,EACL,qBAAqB,EACrB,KAAK,kBAAkB,GACxB,MAAM,uBAAuB,CAAC"}
|
package/dist/index.js
CHANGED
|
@@ -1,15 +1,17 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
//
|
|
4
|
-
//
|
|
5
|
-
//
|
|
6
|
-
//
|
|
7
|
-
export {
|
|
8
|
-
export {
|
|
9
|
-
export {
|
|
10
|
-
export {
|
|
11
|
-
export {
|
|
12
|
-
export {
|
|
13
|
-
export {
|
|
14
|
-
export {
|
|
15
|
-
export {
|
|
1
|
+
/** @vendoai/store — persistence under everything (docs/contracts/02-store.md). */
|
|
2
|
+
export { createStore } from "./store.js";
|
|
3
|
+
// The reserved-collection map (02-store §2): exported so remote StoreAdapters
|
|
4
|
+
// (the umbrella's hostedStore) can mirror this engine's per-collection
|
|
5
|
+
// capability shape — claim on non-routed collections, atomic on generic
|
|
6
|
+
// collections and vendo_threads — without re-deriving the routing table.
|
|
7
|
+
export { DEDICATED_RECORD_COLLECTIONS, RESERVED_COLLECTIONS, } from "./routing.js";
|
|
8
|
+
export { eraseStore } from "./erase.js";
|
|
9
|
+
export { claimEphemeralSubject, listStaleEphemeralSubjects, registerEphemeralSubject, sweepEphemeralSubjects, } from "./sessions.js";
|
|
10
|
+
export { envSecrets, secretStore, storeSecrets } from "./secrets.js";
|
|
11
|
+
export { appStore } from "./helpers/apps.js";
|
|
12
|
+
export { threadStore } from "./helpers/threads.js";
|
|
13
|
+
export { grantStore } from "./helpers/grants.js";
|
|
14
|
+
export { auditStore } from "./helpers/audit.js";
|
|
15
|
+
export { runStore } from "./helpers/runs.js";
|
|
16
|
+
export { adoptEphemeralSubject, } from "./helpers/subjects.js";
|
|
17
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,kFAAkF;AAClF,OAAO,EAAE,WAAW,EAAmB,MAAM,YAAY,CAAC;AAC1D,8EAA8E;AAC9E,uEAAuE;AACvE,wEAAwE;AACxE,yEAAyE;AACzE,OAAO,EACL,4BAA4B,EAC5B,oBAAoB,GAErB,MAAM,cAAc,CAAC;AACtB,OAAO,EAAE,UAAU,EAAqC,MAAM,YAAY,CAAC;AAC3E,OAAO,EACL,qBAAqB,EACrB,0BAA0B,EAC1B,wBAAwB,EACxB,sBAAsB,GACvB,MAAM,eAAe,CAAC;AACvB,OAAO,EAAE,UAAU,EAAE,WAAW,EAAE,YAAY,EAAE,MAAM,cAAc,CAAC;AACrE,OAAO,EAAE,QAAQ,EAAe,MAAM,mBAAmB,CAAC;AAC1D,OAAO,EAAE,WAAW,EAAkB,MAAM,sBAAsB,CAAC;AACnE,OAAO,EAAE,UAAU,EAAE,MAAM,qBAAqB,CAAC;AACjD,OAAO,EAAE,UAAU,EAAmB,MAAM,oBAAoB,CAAC;AACjE,OAAO,EAAE,QAAQ,EAAe,MAAM,mBAAmB,CAAC;AAC1D,OAAO,EACL,qBAAqB,GAEtB,MAAM,uBAAuB,CAAC"}
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import { type RecordStore } from "@vendoai/core";
|
|
2
|
+
import type { Db } from "./db.js";
|
|
3
|
+
export type DedicatedRecordTable = "vendo_mcp_clients" | "vendo_mcp_grants";
|
|
4
|
+
export declare function requireRevision(value: string): void;
|
|
5
|
+
/** 01-core §12 */
|
|
6
|
+
export declare function createRecordStore(db: Db, collection: string, dedicatedTable?: DedicatedRecordTable): RecordStore;
|
|
7
|
+
//# sourceMappingURL=records.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"records.d.ts","sourceRoot":"","sources":["../src/records.ts"],"names":[],"mappings":"AAAA,OAAO,EAML,KAAK,WAAW,EAEjB,MAAM,eAAe,CAAC;AACvB,OAAO,KAAK,EAAE,EAAE,EAAE,MAAM,SAAS,CAAC;AAclC,MAAM,MAAM,oBAAoB,GAAG,mBAAmB,GAAG,kBAAkB,CAAC;AA0B5E,wBAAgB,eAAe,CAAC,KAAK,EAAE,MAAM,GAAG,IAAI,CAEnD;AAWD,kBAAkB;AAClB,wBAAgB,iBAAiB,CAC/B,EAAE,EAAE,EAAE,EACN,UAAU,EAAE,MAAM,EAClB,cAAc,CAAC,EAAE,oBAAoB,GACpC,WAAW,CAqNb"}
|