@vendoai/store 0.1.0 → 0.4.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/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
package/dist/db.js
CHANGED
|
@@ -1,105 +1,354 @@
|
|
|
1
1
|
import { PGlite } from "@electric-sql/pglite";
|
|
2
|
-
import { drizzle as drizzlePglite } from "drizzle-orm/pglite";
|
|
3
|
-
import { migrate as migratePglite } from "drizzle-orm/pglite/migrator";
|
|
4
|
-
import { drizzle as drizzlePg } from "drizzle-orm/node-postgres";
|
|
5
|
-
import { migrate as migratePg } from "drizzle-orm/node-postgres/migrator";
|
|
6
|
-
import { sql } from "drizzle-orm";
|
|
7
2
|
import { Client, Pool } from "pg";
|
|
8
|
-
import { fileURLToPath } from "node:url";
|
|
9
3
|
import fs from "node:fs";
|
|
10
|
-
import
|
|
4
|
+
import { join, resolve } from "node:path";
|
|
11
5
|
const SERVERLESS_ENVS = ["VERCEL", "CF_PAGES", "AWS_LAMBDA_FUNCTION_NAME"];
|
|
12
|
-
const
|
|
13
|
-
const
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
const pool = new Pool({ connectionString });
|
|
26
|
-
pool.on("error", (err) => console.error("[vendo] postgres pool: idle connection error (recovering)", err));
|
|
27
|
-
return pool;
|
|
6
|
+
const ADVISORY_LOCK_KEY = 7_461_001;
|
|
7
|
+
const pgUrls = new WeakMap();
|
|
8
|
+
function preparePgliteDir(dataDir) {
|
|
9
|
+
if (dataDir.startsWith("memory://"))
|
|
10
|
+
return;
|
|
11
|
+
try {
|
|
12
|
+
fs.mkdirSync(dataDir, { recursive: true });
|
|
13
|
+
fs.accessSync(dataDir, fs.constants.W_OK);
|
|
14
|
+
}
|
|
15
|
+
catch (error) {
|
|
16
|
+
const cause = error instanceof Error ? error.message : String(error);
|
|
17
|
+
throw new Error(`[vendo] PGlite data directory "${dataDir}" is not writable — fix its permissions or choose another dataDir. Cause: ${cause}`);
|
|
18
|
+
}
|
|
28
19
|
}
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
const
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
20
|
+
/** ENG-350 — a dev server killed uncleanly leaves postmaster.pid in the data
|
|
21
|
+
dir, and PGlite.create can then hard-abort with a raw `Aborted()`
|
|
22
|
+
(electric-sql/pglite#2, #794). PGlite's wasm postgres always records
|
|
23
|
+
Emscripten's fake pid (-42), so a non-positive or unparseable pid can never
|
|
24
|
+
belong to a live owner — stale by definition. A real positive pid (a native
|
|
25
|
+
postgres dir, or a custom writer) is only stale once that process is gone. */
|
|
26
|
+
function readPgliteLock(dataDir) {
|
|
27
|
+
if (dataDir.startsWith("memory://"))
|
|
28
|
+
return undefined;
|
|
29
|
+
const lockPath = join(dataDir, "postmaster.pid");
|
|
30
|
+
let firstLine;
|
|
31
|
+
try {
|
|
32
|
+
firstLine = fs.readFileSync(lockPath, "utf8").split("\n", 1)[0] ?? "";
|
|
33
|
+
}
|
|
34
|
+
catch {
|
|
35
|
+
return undefined; // no lock file — nothing to heal
|
|
36
|
+
}
|
|
37
|
+
const pid = Number.parseInt(firstLine.trim(), 10);
|
|
38
|
+
if (Number.isFinite(pid) && pid > 0 && isPidAlive(pid))
|
|
39
|
+
return { path: lockPath, livePid: pid };
|
|
40
|
+
return { path: lockPath };
|
|
41
|
+
}
|
|
42
|
+
function isPidAlive(pid) {
|
|
43
|
+
try {
|
|
44
|
+
process.kill(pid, 0);
|
|
45
|
+
return true;
|
|
46
|
+
}
|
|
47
|
+
catch (error) {
|
|
48
|
+
// EPERM = exists but owned by someone else — still alive.
|
|
49
|
+
return error.code === "EPERM";
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
const errorMessage = (error) => (error instanceof Error ? error.message : String(error));
|
|
53
|
+
/* ENG-351 — single-writer discipline for the PGlite default.
|
|
54
|
+
|
|
55
|
+
Under `next dev` the host's server module graph is evaluated by more than
|
|
56
|
+
one OS process (Turbopack spawns transient evaluator workers) and, after an
|
|
57
|
+
HMR edit, more than once within one process. Each evaluation runs
|
|
58
|
+
`createVendo` → `createStore` → PGlite on the SAME data dir. PGlite has no
|
|
59
|
+
cross-process locking, and even a second instance's STARTUP (WAL recovery,
|
|
60
|
+
pg_control writes) tears the live writer's state — the dir later reopens
|
|
61
|
+
with a raw `Aborted()` and the last turns are gone (ENG-351's field trace).
|
|
62
|
+
|
|
63
|
+
Two layers, both keyed by the resolved data dir:
|
|
64
|
+
- In-process: a globalThis registry shares ONE live driver across every
|
|
65
|
+
handle (and every HMR copy of this module), refcounted so closing one
|
|
66
|
+
handle never tears the driver out from under another.
|
|
67
|
+
- Cross-process: a `<dataDir>/.vendo-writer.lock` pidfile taken BEFORE
|
|
68
|
+
PGlite.create. A second process WAITS (it never touches postgres files)
|
|
69
|
+
and takes over only once the holder is provably gone — recorded pid dead,
|
|
70
|
+
or the holder's mtime refresh stopped long ago (pid-reuse fallback). Lock
|
|
71
|
+
release rides close(); a SIGKILLed holder self-heals via the dead-pid
|
|
72
|
+
check, which is also what makes the ENG-350 postmaster.pid heal safe:
|
|
73
|
+
only the lock holder ever reaches it. */
|
|
74
|
+
const LOCK_TOUCH_MS = 5_000;
|
|
75
|
+
const LOCK_STALE_MTIME_MS = 30_000;
|
|
76
|
+
const LOCK_POLL_MS = 250;
|
|
77
|
+
const LOCK_WARN_AFTER_MS = 5_000;
|
|
78
|
+
const LOCK_REWARN_EVERY_MS = 60_000;
|
|
79
|
+
function readLockPid(lockPath) {
|
|
80
|
+
try {
|
|
81
|
+
const pid = Number.parseInt(fs.readFileSync(lockPath, "utf8").split("\n", 1)[0] ?? "", 10);
|
|
82
|
+
return Number.isFinite(pid) ? pid : undefined;
|
|
83
|
+
}
|
|
84
|
+
catch {
|
|
85
|
+
return undefined;
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
/** The holder is provably gone: its recorded pid is dead, it recorded THIS
|
|
89
|
+
process (a crashed predecessor with our reused pid — a live foreign holder
|
|
90
|
+
can never be us), or it stopped refreshing the lock's mtime long ago. */
|
|
91
|
+
function lockIsStale(lockPath) {
|
|
92
|
+
const pid = readLockPid(lockPath);
|
|
93
|
+
if (pid !== undefined && (pid === process.pid || !isPidAlive(pid)))
|
|
94
|
+
return true;
|
|
95
|
+
try {
|
|
96
|
+
return Date.now() - fs.statSync(lockPath).mtimeMs > LOCK_STALE_MTIME_MS;
|
|
97
|
+
}
|
|
98
|
+
catch {
|
|
99
|
+
return false; // vanished — the acquire loop just retries
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
async function acquirePgliteDirLock(dataDir) {
|
|
103
|
+
const lockPath = join(resolve(dataDir), ".vendo-writer.lock");
|
|
104
|
+
const startedAt = Date.now();
|
|
105
|
+
let warnedAt;
|
|
106
|
+
for (;;) {
|
|
107
|
+
try {
|
|
108
|
+
fs.writeFileSync(lockPath, `${process.pid}\n`, { flag: "wx" });
|
|
109
|
+
break;
|
|
110
|
+
}
|
|
111
|
+
catch (error) {
|
|
112
|
+
if (error.code !== "EEXIST") {
|
|
113
|
+
throw new Error(`[vendo] cannot create the PGlite lock file "${lockPath}" — fix permissions on its directory or choose another dataDir. Cause: ${errorMessage(error)}`);
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
if (lockIsStale(lockPath)) {
|
|
117
|
+
fs.rmSync(lockPath, { force: true });
|
|
118
|
+
continue; // re-race the wx create; exactly one contender wins
|
|
48
119
|
}
|
|
49
|
-
|
|
120
|
+
const waitedMs = Date.now() - startedAt;
|
|
121
|
+
if (waitedMs >= LOCK_WARN_AFTER_MS && (warnedAt === undefined || Date.now() - warnedAt >= LOCK_REWARN_EVERY_MS)) {
|
|
122
|
+
warnedAt = Date.now();
|
|
123
|
+
console.warn(`[vendo] PGlite data directory "${dataDir}" is held by another process (pid ${readLockPid(lockPath) ?? "unknown"}) — waiting for it to release the lock. If a second server runs on this dataDir, stop it or point it at its own dataDir / a Postgres url.`);
|
|
124
|
+
}
|
|
125
|
+
await wait(LOCK_POLL_MS);
|
|
126
|
+
}
|
|
127
|
+
// Refresh mtime while held so waiters can tell a live holder from a
|
|
128
|
+
// pid-reused ghost; unref'd so the lock never keeps the process alive.
|
|
129
|
+
const touch = setInterval(() => {
|
|
130
|
+
try {
|
|
131
|
+
const now = new Date();
|
|
132
|
+
fs.utimesSync(lockPath, now, now);
|
|
133
|
+
}
|
|
134
|
+
catch {
|
|
135
|
+
// Lock stolen or dir gone — nothing to refresh; release() still guards on pid.
|
|
136
|
+
}
|
|
137
|
+
}, LOCK_TOUCH_MS);
|
|
138
|
+
touch.unref?.();
|
|
139
|
+
let released = false;
|
|
140
|
+
return {
|
|
141
|
+
release() {
|
|
142
|
+
if (released)
|
|
143
|
+
return;
|
|
144
|
+
released = true;
|
|
145
|
+
clearInterval(touch);
|
|
50
146
|
try {
|
|
51
|
-
|
|
52
|
-
|
|
147
|
+
if (readLockPid(lockPath) === process.pid)
|
|
148
|
+
fs.rmSync(lockPath, { force: true });
|
|
53
149
|
}
|
|
54
|
-
catch
|
|
55
|
-
//
|
|
56
|
-
throw new Error(`[vendo] PGlite data directory "${dataDir}" is not writable — ` +
|
|
57
|
-
`fix its permissions or point VENDO_DATA_DIR elsewhere. Cause: ${err instanceof Error ? err.message : String(err)}`);
|
|
150
|
+
catch {
|
|
151
|
+
// Best-effort: a leftover lock self-heals via the dead-pid check.
|
|
58
152
|
}
|
|
59
|
-
}
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
153
|
+
},
|
|
154
|
+
};
|
|
155
|
+
}
|
|
156
|
+
const wait = (ms) => new Promise((done) => setTimeout(done, ms));
|
|
157
|
+
const PGLITE_REGISTRY_KEY = Symbol.for("vendoai.store.pglite-registry@1");
|
|
158
|
+
function pgliteRegistry() {
|
|
159
|
+
const holder = globalThis;
|
|
160
|
+
return (holder[PGLITE_REGISTRY_KEY] ??= new Map());
|
|
66
161
|
}
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
const
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
162
|
+
function acquireSharedPglite(dataDir) {
|
|
163
|
+
const registry = pgliteRegistry();
|
|
164
|
+
const key = resolve(dataDir);
|
|
165
|
+
let entry = registry.get(key);
|
|
166
|
+
if (!entry) {
|
|
167
|
+
const created = { refs: 0, driver: Promise.resolve(undefined) };
|
|
168
|
+
created.driver = (async () => {
|
|
169
|
+
const lock = await acquirePgliteDirLock(dataDir);
|
|
170
|
+
created.lock = lock;
|
|
171
|
+
try {
|
|
172
|
+
return await createPgliteHealingStaleLock(dataDir);
|
|
173
|
+
}
|
|
174
|
+
catch (error) {
|
|
175
|
+
lock.release();
|
|
176
|
+
throw error;
|
|
177
|
+
}
|
|
178
|
+
})();
|
|
179
|
+
// A failed open must not poison the dir for later attempts.
|
|
180
|
+
created.driver.catch(() => {
|
|
181
|
+
if (registry.get(key) === created)
|
|
182
|
+
registry.delete(key);
|
|
183
|
+
});
|
|
184
|
+
registry.set(key, created);
|
|
185
|
+
entry = created;
|
|
186
|
+
}
|
|
187
|
+
entry.refs += 1;
|
|
188
|
+
return entry;
|
|
189
|
+
}
|
|
190
|
+
async function releaseSharedPglite(dataDir, entry) {
|
|
191
|
+
entry.refs -= 1;
|
|
192
|
+
if (entry.refs > 0)
|
|
193
|
+
return;
|
|
194
|
+
const registry = pgliteRegistry();
|
|
195
|
+
const key = resolve(dataDir);
|
|
196
|
+
if (registry.get(key) === entry)
|
|
197
|
+
registry.delete(key);
|
|
198
|
+
const driver = await entry.driver.catch(() => undefined);
|
|
199
|
+
try {
|
|
200
|
+
await driver?.close();
|
|
201
|
+
}
|
|
202
|
+
finally {
|
|
203
|
+
entry.lock?.release();
|
|
204
|
+
}
|
|
205
|
+
}
|
|
206
|
+
/** ENG-350 — self-heal a stale lock instead of hard-aborting boot. */
|
|
207
|
+
async function createPgliteHealingStaleLock(dataDir) {
|
|
208
|
+
try {
|
|
209
|
+
return await PGlite.create(dataDir);
|
|
210
|
+
}
|
|
211
|
+
catch (error) {
|
|
212
|
+
const lock = readPgliteLock(dataDir);
|
|
213
|
+
if (!lock)
|
|
214
|
+
throw error;
|
|
215
|
+
if (lock.livePid !== undefined) {
|
|
216
|
+
throw new Error(`[vendo] PGlite data directory "${dataDir}" is locked by a live process (pid ${lock.livePid}) — stop that process or point dataDir elsewhere. Cause: ${errorMessage(error)}`);
|
|
76
217
|
}
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
// so the lock would protect nothing (and the unlock could target a session
|
|
80
|
-
// that never held it, leaking the lock on the one that did). Run the whole
|
|
81
|
-
// lock → migrate → unlock sequence on ONE dedicated connection instead.
|
|
82
|
-
// For the pg kind, cacheKey IS the connection string (see createVendoDatabase).
|
|
83
|
-
const client = new Client({ connectionString: handle.cacheKey });
|
|
84
|
-
await client.connect();
|
|
218
|
+
console.warn(`[vendo] PGlite data directory "${dataDir}" failed to open with a stale postmaster.pid left by an unclean shutdown — removing the stale lock and retrying.`);
|
|
219
|
+
fs.rmSync(lock.path, { force: true });
|
|
85
220
|
try {
|
|
86
|
-
|
|
87
|
-
|
|
221
|
+
return await PGlite.create(dataDir);
|
|
222
|
+
}
|
|
223
|
+
catch (retryError) {
|
|
224
|
+
throw new Error(`[vendo] PGlite data directory "${dataDir}" failed to open even after clearing its stale lock — the store is likely corrupt. Back up and delete the directory to start fresh, or set a Postgres url. Cause: ${errorMessage(retryError)}`);
|
|
225
|
+
}
|
|
226
|
+
}
|
|
227
|
+
}
|
|
228
|
+
/** 02-store §4 */
|
|
229
|
+
export function createDb(config = {}) {
|
|
230
|
+
const kind = config.url ? "pg" : "pglite";
|
|
231
|
+
const dataDir = config.dataDir ?? ".vendo/data";
|
|
232
|
+
let driver;
|
|
233
|
+
let opening;
|
|
234
|
+
let shared;
|
|
235
|
+
let closed = false;
|
|
236
|
+
if (config.url) {
|
|
237
|
+
const pool = new Pool({ connectionString: config.url });
|
|
238
|
+
pool.on("error", (error) => {
|
|
239
|
+
console.error("[vendo] postgres pool: idle connection error (recovering)", error);
|
|
240
|
+
});
|
|
241
|
+
driver = pool;
|
|
242
|
+
}
|
|
243
|
+
const open = () => {
|
|
244
|
+
if (closed)
|
|
245
|
+
return Promise.reject(new Error("[vendo] store is closed"));
|
|
246
|
+
if (driver)
|
|
247
|
+
return Promise.resolve(driver);
|
|
248
|
+
if (opening)
|
|
249
|
+
return opening;
|
|
250
|
+
opening = (async () => {
|
|
251
|
+
const serverlessEnv = SERVERLESS_ENVS.find((name) => process.env[name]);
|
|
252
|
+
if (serverlessEnv) {
|
|
253
|
+
throw new Error(`[vendo] PGlite cannot run on ${serverlessEnv} because its filesystem is ephemeral. Set a Postgres url instead.`);
|
|
254
|
+
}
|
|
255
|
+
preparePgliteDir(dataDir);
|
|
256
|
+
// memory:// dirs are private to this handle; a real dir goes through the
|
|
257
|
+
// shared single-writer registry (ENG-351).
|
|
258
|
+
if (dataDir.startsWith("memory://")) {
|
|
259
|
+
const pglite = await createPgliteHealingStaleLock(dataDir);
|
|
260
|
+
driver = pglite;
|
|
261
|
+
return pglite;
|
|
262
|
+
}
|
|
263
|
+
shared = acquireSharedPglite(dataDir);
|
|
88
264
|
try {
|
|
89
|
-
|
|
265
|
+
const pglite = await shared.driver;
|
|
266
|
+
driver = pglite;
|
|
267
|
+
return pglite;
|
|
268
|
+
}
|
|
269
|
+
catch (error) {
|
|
270
|
+
const failed = shared;
|
|
271
|
+
shared = undefined;
|
|
272
|
+
await releaseSharedPglite(dataDir, failed);
|
|
273
|
+
throw error;
|
|
274
|
+
}
|
|
275
|
+
})();
|
|
276
|
+
opening.catch(() => {
|
|
277
|
+
if (!closed)
|
|
278
|
+
opening = undefined;
|
|
279
|
+
});
|
|
280
|
+
return opening;
|
|
281
|
+
};
|
|
282
|
+
const db = {
|
|
283
|
+
kind,
|
|
284
|
+
async query(text, params = []) {
|
|
285
|
+
const active = await open();
|
|
286
|
+
const result = active instanceof Pool
|
|
287
|
+
? await active.query(text, params)
|
|
288
|
+
: await active.query(text, params);
|
|
289
|
+
return { rows: result.rows };
|
|
290
|
+
},
|
|
291
|
+
async close() {
|
|
292
|
+
if (closed)
|
|
293
|
+
return;
|
|
294
|
+
closed = true;
|
|
295
|
+
if (!opening && !driver)
|
|
296
|
+
return;
|
|
297
|
+
const active = driver ?? (opening ? await opening.catch(() => undefined) : undefined);
|
|
298
|
+
driver = undefined;
|
|
299
|
+
if (shared) {
|
|
300
|
+
// Shared PGlite: drop this handle's ref; the last one out closes the
|
|
301
|
+
// wasm instance and releases the dir lock (ENG-351).
|
|
302
|
+
const held = shared;
|
|
303
|
+
shared = undefined;
|
|
304
|
+
await releaseSharedPglite(dataDir, held);
|
|
305
|
+
return;
|
|
90
306
|
}
|
|
91
|
-
|
|
92
|
-
|
|
307
|
+
if (!active)
|
|
308
|
+
return;
|
|
309
|
+
if (active instanceof Pool)
|
|
310
|
+
await active.end();
|
|
311
|
+
else
|
|
312
|
+
await active.close();
|
|
313
|
+
},
|
|
314
|
+
raw() {
|
|
315
|
+
if (!driver) {
|
|
316
|
+
throw new Error("[vendo] store not opened yet — run ensureSchema() or any query first");
|
|
93
317
|
}
|
|
318
|
+
return driver;
|
|
319
|
+
},
|
|
320
|
+
};
|
|
321
|
+
if (config.url)
|
|
322
|
+
pgUrls.set(db, config.url);
|
|
323
|
+
return db;
|
|
324
|
+
}
|
|
325
|
+
/** 02-store §4 */
|
|
326
|
+
export async function withSchemaLock(db, work) {
|
|
327
|
+
return withAdvisoryLock(db, ADVISORY_LOCK_KEY, work);
|
|
328
|
+
}
|
|
329
|
+
async function withAdvisoryLock(db, key, work) {
|
|
330
|
+
if (db.kind === "pglite")
|
|
331
|
+
return work(db.query.bind(db));
|
|
332
|
+
const url = pgUrls.get(db);
|
|
333
|
+
if (!url)
|
|
334
|
+
throw new Error("[vendo] missing Postgres connection string");
|
|
335
|
+
const client = new Client({ connectionString: url });
|
|
336
|
+
await client.connect();
|
|
337
|
+
const query = async (text, params = []) => {
|
|
338
|
+
const result = await client.query(text, params);
|
|
339
|
+
return { rows: result.rows };
|
|
340
|
+
};
|
|
341
|
+
try {
|
|
342
|
+
await query("SELECT pg_advisory_lock($1)", [key]);
|
|
343
|
+
try {
|
|
344
|
+
return await work(query);
|
|
94
345
|
}
|
|
95
346
|
finally {
|
|
96
|
-
await
|
|
347
|
+
await query("SELECT pg_advisory_unlock($1)", [key]);
|
|
97
348
|
}
|
|
98
|
-
}
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
}
|
|
102
|
-
run.catch(() => registry.migrated.delete(handle.cacheKey));
|
|
103
|
-
registry.migrated.set(handle.cacheKey, run);
|
|
104
|
-
return run;
|
|
349
|
+
}
|
|
350
|
+
finally {
|
|
351
|
+
await client.end();
|
|
352
|
+
}
|
|
105
353
|
}
|
|
354
|
+
//# sourceMappingURL=db.js.map
|
package/dist/db.js.map
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"db.js","sourceRoot":"","sources":["../src/db.ts"],"names":[],"mappings":"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"}
|
package/dist/erase.d.ts
ADDED
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
import { type VendoStore } from "./store.js";
|
|
2
|
+
/** 02-store §5 — every table in §2's public map. The erase API cascades the
|
|
3
|
+
* matching data across all of them; `vendo_meta` holds schema metadata (schema
|
|
4
|
+
* version, boot id), never user data, so no selector ever matches it and its
|
|
5
|
+
* count stays 0 — as does `vendo_secrets` (name-keyed host config, likewise
|
|
6
|
+
* never matched by a subject or app selector). Listed so the report provably
|
|
7
|
+
* covers the whole map. */
|
|
8
|
+
export declare const ERASE_TABLES: readonly ["vendo_meta", "vendo_apps", "vendo_records", "vendo_blobs", "vendo_state", "vendo_threads", "vendo_grants", "vendo_approvals", "vendo_audit", "vendo_runs", "vendo_secrets", "vendo_mcp_clients", "vendo_mcp_grants", "vendo_sessions"];
|
|
9
|
+
export type EraseTable = typeof ERASE_TABLES[number];
|
|
10
|
+
/** Rows deleted per table. */
|
|
11
|
+
export type EraseReport = Record<EraseTable, number>;
|
|
12
|
+
/**
|
|
13
|
+
* 02-store §5 — the store-level erase API: by subject (full erasure) or by
|
|
14
|
+
* app, cascading the matching data across all 14 tables of §2's map. It is
|
|
15
|
+
* the ONLY sanctioned deletion path for `vendo_audit` rows — the routed door
|
|
16
|
+
* refuses audit deletion (§2); this API reaches the tables directly.
|
|
17
|
+
* Ephemeral subjects are erased the same way (their rows are ordinary disk
|
|
18
|
+
* rows — kill-list B3); the TTL sweep (sessions.ts) is built on this cascade.
|
|
19
|
+
* Policy engines and schedulers stay out of scope: hosts call this from their
|
|
20
|
+
* own jobs, and host SQL remains available for everything else.
|
|
21
|
+
*/
|
|
22
|
+
export declare function eraseStore(store: VendoStore): {
|
|
23
|
+
/** Full erasure of one subject: their apps (and each app's records, blobs,
|
|
24
|
+
state, and runs), plus every subject-keyed or subject-ref'd row and the
|
|
25
|
+
subject's session registration (§4). */
|
|
26
|
+
bySubject(subject: string): Promise<EraseReport>;
|
|
27
|
+
/** Erase one app: its row, record collections, blob namespaces, state, runs,
|
|
28
|
+
app-scoped grants and audit rows, and app-ref'd generic/door rows.
|
|
29
|
+
(Threads and approvals have no app axis in §2 — the subject selector
|
|
30
|
+
covers them.) */
|
|
31
|
+
byApp(appId: string): Promise<EraseReport>;
|
|
32
|
+
};
|
|
33
|
+
//# sourceMappingURL=erase.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"erase.d.ts","sourceRoot":"","sources":["../src/erase.ts"],"names":[],"mappings":"AACA,OAAO,EAAS,KAAK,UAAU,EAAE,MAAM,YAAY,CAAC;AAGpD;;;;;4BAK4B;AAC5B,eAAO,MAAM,YAAY,mPAef,CAAC;AAEX,MAAM,MAAM,UAAU,GAAG,OAAO,YAAY,CAAC,MAAM,CAAC,CAAC;AAErD,8BAA8B;AAC9B,MAAM,MAAM,WAAW,GAAG,MAAM,CAAC,UAAU,EAAE,MAAM,CAAC,CAAC;AAMrD;;;;;;;;;GASG;AACH,wBAAgB,UAAU,CAAC,KAAK,EAAE,UAAU,GAAG;IAC7C;;+CAE2C;IAC3C,SAAS,CAAC,OAAO,EAAE,MAAM,GAAG,OAAO,CAAC,WAAW,CAAC,CAAC;IACjD;;;wBAGoB;IACpB,KAAK,CAAC,KAAK,EAAE,MAAM,GAAG,OAAO,CAAC,WAAW,CAAC,CAAC;CAC5C,CA8EA"}
|
package/dist/erase.js
ADDED
|
@@ -0,0 +1,107 @@
|
|
|
1
|
+
import { escapeLike } from "./helpers/utils.js";
|
|
2
|
+
import { dbFor } from "./store.js";
|
|
3
|
+
import { invalid } from "./validate.js";
|
|
4
|
+
/** 02-store §5 — every table in §2's public map. The erase API cascades the
|
|
5
|
+
* matching data across all of them; `vendo_meta` holds schema metadata (schema
|
|
6
|
+
* version, boot id), never user data, so no selector ever matches it and its
|
|
7
|
+
* count stays 0 — as does `vendo_secrets` (name-keyed host config, likewise
|
|
8
|
+
* never matched by a subject or app selector). Listed so the report provably
|
|
9
|
+
* covers the whole map. */
|
|
10
|
+
export const ERASE_TABLES = [
|
|
11
|
+
"vendo_meta",
|
|
12
|
+
"vendo_apps",
|
|
13
|
+
"vendo_records",
|
|
14
|
+
"vendo_blobs",
|
|
15
|
+
"vendo_state",
|
|
16
|
+
"vendo_threads",
|
|
17
|
+
"vendo_grants",
|
|
18
|
+
"vendo_approvals",
|
|
19
|
+
"vendo_audit",
|
|
20
|
+
"vendo_runs",
|
|
21
|
+
"vendo_secrets",
|
|
22
|
+
"vendo_mcp_clients",
|
|
23
|
+
"vendo_mcp_grants",
|
|
24
|
+
"vendo_sessions",
|
|
25
|
+
];
|
|
26
|
+
function emptyReport() {
|
|
27
|
+
return Object.fromEntries(ERASE_TABLES.map((table) => [table, 0]));
|
|
28
|
+
}
|
|
29
|
+
/**
|
|
30
|
+
* 02-store §5 — the store-level erase API: by subject (full erasure) or by
|
|
31
|
+
* app, cascading the matching data across all 14 tables of §2's map. It is
|
|
32
|
+
* the ONLY sanctioned deletion path for `vendo_audit` rows — the routed door
|
|
33
|
+
* refuses audit deletion (§2); this API reaches the tables directly.
|
|
34
|
+
* Ephemeral subjects are erased the same way (their rows are ordinary disk
|
|
35
|
+
* rows — kill-list B3); the TTL sweep (sessions.ts) is built on this cascade.
|
|
36
|
+
* Policy engines and schedulers stay out of scope: hosts call this from their
|
|
37
|
+
* own jobs, and host SQL remains available for everything else.
|
|
38
|
+
*/
|
|
39
|
+
export function eraseStore(store) {
|
|
40
|
+
const db = dbFor(store);
|
|
41
|
+
const del = async (report, table, where, params) => {
|
|
42
|
+
const result = await db.query(`DELETE FROM ${table} WHERE ${where} RETURNING 1`, params);
|
|
43
|
+
report[table] += result.rows.length;
|
|
44
|
+
};
|
|
45
|
+
/** App-scoped data shared by the subject and app cascades: the app's record
|
|
46
|
+
collections and blob namespaces (`app:<appId>:...` — §3's naming
|
|
47
|
+
convention), its per-user state, and its run records. */
|
|
48
|
+
const eraseAppData = async (report, appId) => {
|
|
49
|
+
const prefix = `app:${escapeLike(appId)}:%`;
|
|
50
|
+
await del(report, "vendo_records", "collection LIKE $1 ESCAPE '\\'", [prefix]);
|
|
51
|
+
await del(report, "vendo_blobs", "namespace LIKE $1 ESCAPE '\\'", [prefix]);
|
|
52
|
+
await del(report, "vendo_state", "app_id = $1", [appId]);
|
|
53
|
+
await del(report, "vendo_runs", "app_id = $1", [appId]);
|
|
54
|
+
};
|
|
55
|
+
return {
|
|
56
|
+
async bySubject(subject) {
|
|
57
|
+
if (typeof subject !== "string" || subject === "") {
|
|
58
|
+
invalid("erase subject must be a non-empty string");
|
|
59
|
+
}
|
|
60
|
+
const report = emptyReport();
|
|
61
|
+
const subjectRef = JSON.stringify({ subject });
|
|
62
|
+
// The subject's apps drive the app-scoped cascade (records/blobs/state/runs
|
|
63
|
+
// carry the app id, not the subject). The app ROWS are deleted FIRST:
|
|
64
|
+
// once they are gone, no new gated write (records/blobs WHERE EXISTS)
|
|
65
|
+
// can land, so the data deletes below collect any stragglers — the
|
|
66
|
+
// remaining race residue is a write statement already in flight.
|
|
67
|
+
const owned = (await db.query("SELECT id FROM vendo_apps WHERE subject = $1", [subject])).rows
|
|
68
|
+
.map((row) => String(row["id"]));
|
|
69
|
+
await del(report, "vendo_apps", "subject = $1", [subject]);
|
|
70
|
+
for (const appId of owned)
|
|
71
|
+
await eraseAppData(report, appId);
|
|
72
|
+
// Ordering matters for accurate counts: the app cascade above already
|
|
73
|
+
// removed the subject's own state/run rows, so the subject-level deletes
|
|
74
|
+
// below only count rows the cascade did not reach (e.g. this subject's
|
|
75
|
+
// state under ANOTHER owner's app).
|
|
76
|
+
await del(report, "vendo_state", "subject = $1", [subject]);
|
|
77
|
+
await del(report, "vendo_threads", "subject = $1", [subject]);
|
|
78
|
+
await del(report, "vendo_grants", "subject = $1", [subject]);
|
|
79
|
+
await del(report, "vendo_approvals", "subject = $1", [subject]);
|
|
80
|
+
await del(report, "vendo_audit", "subject = $1", [subject]);
|
|
81
|
+
// Generic and door-owned rows carry the subject only as a ref (§2/§3).
|
|
82
|
+
await del(report, "vendo_records", "refs @> $1::jsonb", [subjectRef]);
|
|
83
|
+
await del(report, "vendo_mcp_clients", "refs @> $1::jsonb", [subjectRef]);
|
|
84
|
+
await del(report, "vendo_mcp_grants", "refs @> $1::jsonb", [subjectRef]);
|
|
85
|
+
// The session registration (if any) is retired with the data (§4).
|
|
86
|
+
await del(report, "vendo_sessions", "subject = $1", [subject]);
|
|
87
|
+
return report;
|
|
88
|
+
},
|
|
89
|
+
async byApp(appId) {
|
|
90
|
+
if (typeof appId !== "string" || appId === "") {
|
|
91
|
+
invalid("erase appId must be a non-empty string");
|
|
92
|
+
}
|
|
93
|
+
const report = emptyReport();
|
|
94
|
+
const appRef = JSON.stringify({ app_id: appId });
|
|
95
|
+
// App row first (same gate-closing order as bySubject), then its data.
|
|
96
|
+
await del(report, "vendo_apps", "id = $1", [appId]);
|
|
97
|
+
await eraseAppData(report, appId);
|
|
98
|
+
await del(report, "vendo_grants", "app_id = $1", [appId]);
|
|
99
|
+
await del(report, "vendo_audit", "app_id = $1", [appId]);
|
|
100
|
+
await del(report, "vendo_records", "refs @> $1::jsonb", [appRef]);
|
|
101
|
+
await del(report, "vendo_mcp_clients", "refs @> $1::jsonb", [appRef]);
|
|
102
|
+
await del(report, "vendo_mcp_grants", "refs @> $1::jsonb", [appRef]);
|
|
103
|
+
return report;
|
|
104
|
+
},
|
|
105
|
+
};
|
|
106
|
+
}
|
|
107
|
+
//# sourceMappingURL=erase.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"erase.js","sourceRoot":"","sources":["../src/erase.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import type { ApprovalId, ApprovalRequest, IsoDateTime, Principal } from "@vendoai/core";
|
|
2
|
+
import { type VendoStore } from "../store.js";
|
|
3
|
+
import type { ApprovalRow } from "./types.js";
|
|
4
|
+
/** 02-store §3 */
|
|
5
|
+
export declare function approvalStore(store: VendoStore): {
|
|
6
|
+
create(request: ApprovalRequest): Promise<void>;
|
|
7
|
+
get(id: ApprovalId): Promise<ApprovalRow | null>;
|
|
8
|
+
pending(principal: Principal): Promise<ApprovalRequest[]>;
|
|
9
|
+
decide(ids: ApprovalId | ApprovalId[], status: "approved" | "denied", decidedAt: IsoDateTime): Promise<ApprovalId[]>;
|
|
10
|
+
};
|
|
11
|
+
export type { ApprovalRow } from "./types.js";
|
|
12
|
+
//# sourceMappingURL=approvals.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"approvals.d.ts","sourceRoot":"","sources":["../../src/helpers/approvals.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,UAAU,EAAE,eAAe,EAAE,WAAW,EAAE,SAAS,EAAE,MAAM,eAAe,CAAC;AACzF,OAAO,EAAS,KAAK,UAAU,EAAE,MAAM,aAAa,CAAC;AACrD,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,YAAY,CAAC;AAK9C,kBAAkB;AAClB,wBAAgB,aAAa,CAAC,KAAK,EAAE,UAAU,GAAG;IAChD,MAAM,CAAC,OAAO,EAAE,eAAe,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IAChD,GAAG,CAAC,EAAE,EAAE,UAAU,GAAG,OAAO,CAAC,WAAW,GAAG,IAAI,CAAC,CAAC;IACjD,OAAO,CAAC,SAAS,EAAE,SAAS,GAAG,OAAO,CAAC,eAAe,EAAE,CAAC,CAAC;IAC1D,MAAM,CAAC,GAAG,EAAE,UAAU,GAAG,UAAU,EAAE,EAAE,MAAM,EAAE,UAAU,GAAG,QAAQ,EAAE,SAAS,EAAE,WAAW,GAAG,OAAO,CAAC,UAAU,EAAE,CAAC,CAAC;CACtH,CAoCA;AAED,YAAY,EAAE,WAAW,EAAE,MAAM,YAAY,CAAC"}
|