@cnstra/persist-postgres 0.1.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 +67 -0
- package/dist/chunk-U4IKAHS4.mjs +97 -0
- package/dist/chunk-U4IKAHS4.mjs.map +1 -0
- package/dist/cli.d.mts +1 -0
- package/dist/cli.d.ts +1 -0
- package/dist/cli.js +120 -0
- package/dist/cli.js.map +1 -0
- package/dist/cli.mjs +38 -0
- package/dist/cli.mjs.map +1 -0
- package/dist/index.d.mts +77 -0
- package/dist/index.d.ts +77 -0
- package/dist/index.js +435 -0
- package/dist/index.js.map +1 -0
- package/dist/index.mjs +326 -0
- package/dist/index.mjs.map +1 -0
- package/package.json +40 -0
package/README.md
ADDED
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
# @cnstra/persist-postgres
|
|
2
|
+
|
|
3
|
+
> **⚠️ Experimental**: API may change.
|
|
4
|
+
|
|
5
|
+
A **Postgres relational store** for CNStra's durable run/attempt/task model —
|
|
6
|
+
implements [`ICNSStimulationRepository`](../persist) (the run roster → attempt timeline →
|
|
7
|
+
task waterfall the retry admin renders). Makes the durable-execution history **survive a
|
|
8
|
+
process restart**, and a retried run resumes from the persisted frontier.
|
|
9
|
+
|
|
10
|
+
This is domain **#2** (run/attempt/task history), distinct from the resume **checkpoint**
|
|
11
|
+
progress store (domain #1 — `@cnstra/pg-boss/postgres-progress`, `@cnstra/progress-redis`).
|
|
12
|
+
|
|
13
|
+
## Install
|
|
14
|
+
|
|
15
|
+
```bash
|
|
16
|
+
npm install @cnstra/persist-postgres pg
|
|
17
|
+
```
|
|
18
|
+
|
|
19
|
+
## Use
|
|
20
|
+
|
|
21
|
+
```ts
|
|
22
|
+
import { CNSPostgresStimulationRepository } from '@cnstra/persist-postgres';
|
|
23
|
+
|
|
24
|
+
const repo = new CNSPostgresStimulationRepository({
|
|
25
|
+
connectionString: process.env.DATABASE_URL, // or { pool } / { queryable }
|
|
26
|
+
tablePrefix: 'cns_', // default
|
|
27
|
+
autoMigrate: true, // lazily create tables on first use
|
|
28
|
+
});
|
|
29
|
+
|
|
30
|
+
// Drives the persistor (write) and the retry UI (read):
|
|
31
|
+
// saveRun / saveStimulation / appendTask ; getRun / listRuns / getStimulations / getTasks / delete
|
|
32
|
+
```
|
|
33
|
+
|
|
34
|
+
Reuses the `IPgQueryable` seam, so `pg` is an optional peer — inject any pg client/pool,
|
|
35
|
+
or pass a `connectionString` for the repo to own a Pool (`close()` ends it).
|
|
36
|
+
|
|
37
|
+
## Schema
|
|
38
|
+
|
|
39
|
+
Five tables under the prefix (default `cns_`). The run's `progress` is **normalised**: the
|
|
40
|
+
outstanding frontier into `cns_run_frontier` (one row per task) and per-neuron context into
|
|
41
|
+
`cns_run_context`, so you can query the frontier — rebuilt into `TCNSProgress` on read.
|
|
42
|
+
|
|
43
|
+
```
|
|
44
|
+
cns_run(stimulation_run_id PK, entry jsonb, status, created_at, updated_at)
|
|
45
|
+
cns_run_frontier(run_id → cns_run, ord, neuron_name, dendrite_collateral_name, input jsonb)
|
|
46
|
+
cns_run_context(run_id → cns_run, neuron_name, value jsonb)
|
|
47
|
+
cns_stimulation(stimulation_id PK, run_id → cns_run, attempt_number, status, started_at,
|
|
48
|
+
completed_at, hop_count, has_error, replay_of, entry jsonb,
|
|
49
|
+
UNIQUE(run_id, attempt_number))
|
|
50
|
+
cns_task(stimulation_id, index, neuron_name, dendrite_collateral_name, input_index,
|
|
51
|
+
output jsonb, status, error, started_at, duration, PK(stimulation_id, index))
|
|
52
|
+
```
|
|
53
|
+
|
|
54
|
+
## Migrations
|
|
55
|
+
|
|
56
|
+
The package owns its schema. Auto-created on first use, or manage it yourself:
|
|
57
|
+
|
|
58
|
+
```bash
|
|
59
|
+
DATABASE_URL=postgres://... npx cnstra-persist-postgres migrate
|
|
60
|
+
# CNS_TABLE_PREFIX=my_ to override the prefix
|
|
61
|
+
```
|
|
62
|
+
|
|
63
|
+
or call `ensureSchema(pool, prefix)` from `@cnstra/persist-postgres`.
|
|
64
|
+
|
|
65
|
+
## License
|
|
66
|
+
|
|
67
|
+
MIT
|
|
@@ -0,0 +1,97 @@
|
|
|
1
|
+
var __defProp = Object.defineProperty;
|
|
2
|
+
var __defNormalProp = (obj, key, value) => key in obj ? __defProp(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value;
|
|
3
|
+
var __name = (target, value) => __defProp(target, "name", { value, configurable: true });
|
|
4
|
+
var __publicField = (obj, key, value) => __defNormalProp(obj, typeof key !== "symbol" ? key + "" : key, value);
|
|
5
|
+
|
|
6
|
+
// src/migrate.ts
|
|
7
|
+
var DEFAULT_PREFIX = "cns_";
|
|
8
|
+
function assertSafePrefix(prefix) {
|
|
9
|
+
if (!/^[A-Za-z_][A-Za-z0-9_]*$/.test(prefix)) {
|
|
10
|
+
throw new Error(`[@cnstra/persist-postgres] unsafe table prefix: ${JSON.stringify(prefix)}`);
|
|
11
|
+
}
|
|
12
|
+
return prefix;
|
|
13
|
+
}
|
|
14
|
+
__name(assertSafePrefix, "assertSafePrefix");
|
|
15
|
+
function persistPostgresSchemaSql(prefix = DEFAULT_PREFIX) {
|
|
16
|
+
const p = assertSafePrefix(prefix);
|
|
17
|
+
return `
|
|
18
|
+
CREATE TABLE IF NOT EXISTS ${p}stimulation (
|
|
19
|
+
stimulation_id text PRIMARY KEY,
|
|
20
|
+
entry jsonb NOT NULL,
|
|
21
|
+
status text NOT NULL,
|
|
22
|
+
scope_name text,
|
|
23
|
+
created_at timestamptz NOT NULL DEFAULT now(),
|
|
24
|
+
updated_at timestamptz NOT NULL DEFAULT now()
|
|
25
|
+
);
|
|
26
|
+
CREATE INDEX IF NOT EXISTS ${p}stimulation_updated_idx ON ${p}stimulation (updated_at DESC);
|
|
27
|
+
CREATE INDEX IF NOT EXISTS ${p}stimulation_scope_idx ON ${p}stimulation (scope_name);
|
|
28
|
+
|
|
29
|
+
CREATE TABLE IF NOT EXISTS ${p}stimulation_frontier (
|
|
30
|
+
stimulation_id text NOT NULL REFERENCES ${p}stimulation(stimulation_id) ON DELETE CASCADE,
|
|
31
|
+
ord int NOT NULL,
|
|
32
|
+
neuron_name text NOT NULL,
|
|
33
|
+
dendrite_collateral_name text NOT NULL,
|
|
34
|
+
input jsonb,
|
|
35
|
+
PRIMARY KEY (stimulation_id, ord)
|
|
36
|
+
);
|
|
37
|
+
|
|
38
|
+
CREATE TABLE IF NOT EXISTS ${p}stimulation_context (
|
|
39
|
+
stimulation_id text NOT NULL REFERENCES ${p}stimulation(stimulation_id) ON DELETE CASCADE,
|
|
40
|
+
neuron_name text NOT NULL,
|
|
41
|
+
value jsonb NOT NULL,
|
|
42
|
+
PRIMARY KEY (stimulation_id, neuron_name)
|
|
43
|
+
);
|
|
44
|
+
|
|
45
|
+
-- NOTE: no FK on stimulation_id \u2014 over the devtools wire the persistor's
|
|
46
|
+
-- saveStimulation and saveAttempt ride SEPARATE async batches processed
|
|
47
|
+
-- concurrently by the server, so an attempt insert can reach Postgres before its
|
|
48
|
+
-- stimulation row's transaction commits; a hard FK would reject it (23503). Same
|
|
49
|
+
-- exposure as ${p}task; delete() removes attempts explicitly instead of cascading.
|
|
50
|
+
CREATE TABLE IF NOT EXISTS ${p}stimulation_attempt (
|
|
51
|
+
stimulation_attempt_id text PRIMARY KEY,
|
|
52
|
+
stimulation_id text NOT NULL,
|
|
53
|
+
attempt_number int NOT NULL,
|
|
54
|
+
status text NOT NULL,
|
|
55
|
+
started_at bigint NOT NULL,
|
|
56
|
+
completed_at bigint,
|
|
57
|
+
hop_count int NOT NULL,
|
|
58
|
+
has_error boolean NOT NULL,
|
|
59
|
+
replay_of text,
|
|
60
|
+
entry jsonb NOT NULL,
|
|
61
|
+
UNIQUE (stimulation_id, attempt_number)
|
|
62
|
+
);
|
|
63
|
+
CREATE INDEX IF NOT EXISTS ${p}stimulation_attempt_idx ON ${p}stimulation_attempt (stimulation_id, attempt_number);
|
|
64
|
+
|
|
65
|
+
-- NOTE: no FK on stimulation_attempt_id \u2014 the persistor streams settled tasks
|
|
66
|
+
-- (appendTask) as hops complete, BEFORE the attempt marker is flushed (saveAttempt),
|
|
67
|
+
-- so a hard FK would reject the insert. delete() removes tasks explicitly instead.
|
|
68
|
+
CREATE TABLE IF NOT EXISTS ${p}task (
|
|
69
|
+
stimulation_attempt_id text NOT NULL,
|
|
70
|
+
"index" int NOT NULL,
|
|
71
|
+
neuron_name text NOT NULL,
|
|
72
|
+
dendrite_collateral_name text NOT NULL,
|
|
73
|
+
input_index int NOT NULL,
|
|
74
|
+
output jsonb,
|
|
75
|
+
status text NOT NULL,
|
|
76
|
+
error text,
|
|
77
|
+
started_at bigint NOT NULL,
|
|
78
|
+
duration int,
|
|
79
|
+
PRIMARY KEY (stimulation_attempt_id, "index")
|
|
80
|
+
);
|
|
81
|
+
`;
|
|
82
|
+
}
|
|
83
|
+
__name(persistPostgresSchemaSql, "persistPostgresSchemaSql");
|
|
84
|
+
async function ensureSchema(db, prefix = DEFAULT_PREFIX) {
|
|
85
|
+
await db.query(persistPostgresSchemaSql(prefix));
|
|
86
|
+
}
|
|
87
|
+
__name(ensureSchema, "ensureSchema");
|
|
88
|
+
|
|
89
|
+
export {
|
|
90
|
+
__name,
|
|
91
|
+
__publicField,
|
|
92
|
+
DEFAULT_PREFIX,
|
|
93
|
+
assertSafePrefix,
|
|
94
|
+
persistPostgresSchemaSql,
|
|
95
|
+
ensureSchema
|
|
96
|
+
};
|
|
97
|
+
//# sourceMappingURL=chunk-U4IKAHS4.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/migrate.ts"],"sourcesContent":["/**\n * Schema / migrations for the CNStra stimulation/attempt/task relational store.\n *\n * The package OWNS this schema — run it explicitly via `cnstra-persist-postgres migrate`\n * or `ensureSchema(db)`, or let the repository auto-create it on first use. Five tables\n * under a configurable prefix (default `cns_`); the stimulation's `progress` is\n * NORMALISED into `stimulation_frontier` (one row per outstanding task) +\n * `stimulation_context` (one row per neuron).\n */\n\n/** Minimal pg client surface (a `pg` Pool or Client satisfies it). */\nexport interface IPgQueryable {\n query(text: string, params?: unknown[]): Promise<{ rows: unknown[] }>;\n}\n\nexport const DEFAULT_PREFIX = 'cns_';\n\n/** Guard the table prefix (it is interpolated into DDL/DML, not parameterised). */\nexport function assertSafePrefix(prefix: string): string {\n if (!/^[A-Za-z_][A-Za-z0-9_]*$/.test(prefix)) {\n throw new Error(\n `[@cnstra/persist-postgres] unsafe table prefix: ${JSON.stringify(prefix)}`\n );\n }\n return prefix;\n}\n\n/** The full CREATE TABLE + CREATE INDEX DDL (idempotent). */\nexport function persistPostgresSchemaSql(prefix: string = DEFAULT_PREFIX): string {\n const p = assertSafePrefix(prefix);\n return `\nCREATE TABLE IF NOT EXISTS ${p}stimulation (\n stimulation_id text PRIMARY KEY,\n entry jsonb NOT NULL,\n status text NOT NULL,\n scope_name text,\n created_at timestamptz NOT NULL DEFAULT now(),\n updated_at timestamptz NOT NULL DEFAULT now()\n);\nCREATE INDEX IF NOT EXISTS ${p}stimulation_updated_idx ON ${p}stimulation (updated_at DESC);\nCREATE INDEX IF NOT EXISTS ${p}stimulation_scope_idx ON ${p}stimulation (scope_name);\n\nCREATE TABLE IF NOT EXISTS ${p}stimulation_frontier (\n stimulation_id text NOT NULL REFERENCES ${p}stimulation(stimulation_id) ON DELETE CASCADE,\n ord int NOT NULL,\n neuron_name text NOT NULL,\n dendrite_collateral_name text NOT NULL,\n input jsonb,\n PRIMARY KEY (stimulation_id, ord)\n);\n\nCREATE TABLE IF NOT EXISTS ${p}stimulation_context (\n stimulation_id text NOT NULL REFERENCES ${p}stimulation(stimulation_id) ON DELETE CASCADE,\n neuron_name text NOT NULL,\n value jsonb NOT NULL,\n PRIMARY KEY (stimulation_id, neuron_name)\n);\n\n-- NOTE: no FK on stimulation_id — over the devtools wire the persistor's\n-- saveStimulation and saveAttempt ride SEPARATE async batches processed\n-- concurrently by the server, so an attempt insert can reach Postgres before its\n-- stimulation row's transaction commits; a hard FK would reject it (23503). Same\n-- exposure as ${p}task; delete() removes attempts explicitly instead of cascading.\nCREATE TABLE IF NOT EXISTS ${p}stimulation_attempt (\n stimulation_attempt_id text PRIMARY KEY,\n stimulation_id text NOT NULL,\n attempt_number int NOT NULL,\n status text NOT NULL,\n started_at bigint NOT NULL,\n completed_at bigint,\n hop_count int NOT NULL,\n has_error boolean NOT NULL,\n replay_of text,\n entry jsonb NOT NULL,\n UNIQUE (stimulation_id, attempt_number)\n);\nCREATE INDEX IF NOT EXISTS ${p}stimulation_attempt_idx ON ${p}stimulation_attempt (stimulation_id, attempt_number);\n\n-- NOTE: no FK on stimulation_attempt_id — the persistor streams settled tasks\n-- (appendTask) as hops complete, BEFORE the attempt marker is flushed (saveAttempt),\n-- so a hard FK would reject the insert. delete() removes tasks explicitly instead.\nCREATE TABLE IF NOT EXISTS ${p}task (\n stimulation_attempt_id text NOT NULL,\n \"index\" int NOT NULL,\n neuron_name text NOT NULL,\n dendrite_collateral_name text NOT NULL,\n input_index int NOT NULL,\n output jsonb,\n status text NOT NULL,\n error text,\n started_at bigint NOT NULL,\n duration int,\n PRIMARY KEY (stimulation_attempt_id, \"index\")\n);\n`;\n}\n\n/** Idempotently create all tables + indexes. Safe to run repeatedly. */\nexport async function ensureSchema(\n db: IPgQueryable,\n prefix: string = DEFAULT_PREFIX\n): Promise<void> {\n await db.query(persistPostgresSchemaSql(prefix));\n}\n"],"mappings":";;;;;;AAeO,IAAMA,iBAAiB;AAGvB,SAASC,iBAAiBC,QAAc;AAC3C,MAAI,CAAC,2BAA2BC,KAAKD,MAAAA,GAAS;AAC1C,UAAM,IAAIE,MACN,mDAAmDC,KAAKC,UAAUJ,MAAAA,CAAAA,EAAS;EAEnF;AACA,SAAOA;AACX;AAPgBD;AAUT,SAASM,yBAAyBL,SAAiBF,gBAAc;AACpE,QAAMQ,IAAIP,iBAAiBC,MAAAA;AAC3B,SAAO;6BACkBM,CAAAA;;;;;;;;6BAQAA,CAAAA,8BAA+BA,CAAAA;6BAC/BA,CAAAA,4BAA6BA,CAAAA;;6BAE7BA,CAAAA;8CACiBA,CAAAA;;;;;;;;6BAQjBA,CAAAA;8CACiBA,CAAAA;;;;;;;;;;iBAU7BA,CAAAA;6BACYA,CAAAA;;;;;;;;;;;;;6BAaAA,CAAAA,8BAA+BA,CAAAA;;;;;6BAK/BA,CAAAA;;;;;;;;;;;;;;AAc7B;AAnEgBD;AAsEhB,eAAsBE,aAClBC,IACAR,SAAiBF,gBAAc;AAE/B,QAAMU,GAAGC,MAAMJ,yBAAyBL,MAAAA,CAAAA;AAC5C;AALsBO;","names":["DEFAULT_PREFIX","assertSafePrefix","prefix","test","Error","JSON","stringify","persistPostgresSchemaSql","p","ensureSchema","db","query"]}
|
package/dist/cli.d.mts
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
package/dist/cli.d.ts
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
package/dist/cli.js
ADDED
|
@@ -0,0 +1,120 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
var __defProp = Object.defineProperty;
|
|
3
|
+
var __name = (target, value) => __defProp(target, "name", { value, configurable: true });
|
|
4
|
+
|
|
5
|
+
// src/cli.ts
|
|
6
|
+
var import_pg = require("pg");
|
|
7
|
+
|
|
8
|
+
// src/migrate.ts
|
|
9
|
+
var DEFAULT_PREFIX = "cns_";
|
|
10
|
+
function assertSafePrefix(prefix) {
|
|
11
|
+
if (!/^[A-Za-z_][A-Za-z0-9_]*$/.test(prefix)) {
|
|
12
|
+
throw new Error(`[@cnstra/persist-postgres] unsafe table prefix: ${JSON.stringify(prefix)}`);
|
|
13
|
+
}
|
|
14
|
+
return prefix;
|
|
15
|
+
}
|
|
16
|
+
__name(assertSafePrefix, "assertSafePrefix");
|
|
17
|
+
function persistPostgresSchemaSql(prefix = DEFAULT_PREFIX) {
|
|
18
|
+
const p = assertSafePrefix(prefix);
|
|
19
|
+
return `
|
|
20
|
+
CREATE TABLE IF NOT EXISTS ${p}stimulation (
|
|
21
|
+
stimulation_id text PRIMARY KEY,
|
|
22
|
+
entry jsonb NOT NULL,
|
|
23
|
+
status text NOT NULL,
|
|
24
|
+
scope_name text,
|
|
25
|
+
created_at timestamptz NOT NULL DEFAULT now(),
|
|
26
|
+
updated_at timestamptz NOT NULL DEFAULT now()
|
|
27
|
+
);
|
|
28
|
+
CREATE INDEX IF NOT EXISTS ${p}stimulation_updated_idx ON ${p}stimulation (updated_at DESC);
|
|
29
|
+
CREATE INDEX IF NOT EXISTS ${p}stimulation_scope_idx ON ${p}stimulation (scope_name);
|
|
30
|
+
|
|
31
|
+
CREATE TABLE IF NOT EXISTS ${p}stimulation_frontier (
|
|
32
|
+
stimulation_id text NOT NULL REFERENCES ${p}stimulation(stimulation_id) ON DELETE CASCADE,
|
|
33
|
+
ord int NOT NULL,
|
|
34
|
+
neuron_name text NOT NULL,
|
|
35
|
+
dendrite_collateral_name text NOT NULL,
|
|
36
|
+
input jsonb,
|
|
37
|
+
PRIMARY KEY (stimulation_id, ord)
|
|
38
|
+
);
|
|
39
|
+
|
|
40
|
+
CREATE TABLE IF NOT EXISTS ${p}stimulation_context (
|
|
41
|
+
stimulation_id text NOT NULL REFERENCES ${p}stimulation(stimulation_id) ON DELETE CASCADE,
|
|
42
|
+
neuron_name text NOT NULL,
|
|
43
|
+
value jsonb NOT NULL,
|
|
44
|
+
PRIMARY KEY (stimulation_id, neuron_name)
|
|
45
|
+
);
|
|
46
|
+
|
|
47
|
+
-- NOTE: no FK on stimulation_id \u2014 over the devtools wire the persistor's
|
|
48
|
+
-- saveStimulation and saveAttempt ride SEPARATE async batches processed
|
|
49
|
+
-- concurrently by the server, so an attempt insert can reach Postgres before its
|
|
50
|
+
-- stimulation row's transaction commits; a hard FK would reject it (23503). Same
|
|
51
|
+
-- exposure as ${p}task; delete() removes attempts explicitly instead of cascading.
|
|
52
|
+
CREATE TABLE IF NOT EXISTS ${p}stimulation_attempt (
|
|
53
|
+
stimulation_attempt_id text PRIMARY KEY,
|
|
54
|
+
stimulation_id text NOT NULL,
|
|
55
|
+
attempt_number int NOT NULL,
|
|
56
|
+
status text NOT NULL,
|
|
57
|
+
started_at bigint NOT NULL,
|
|
58
|
+
completed_at bigint,
|
|
59
|
+
hop_count int NOT NULL,
|
|
60
|
+
has_error boolean NOT NULL,
|
|
61
|
+
replay_of text,
|
|
62
|
+
entry jsonb NOT NULL,
|
|
63
|
+
UNIQUE (stimulation_id, attempt_number)
|
|
64
|
+
);
|
|
65
|
+
CREATE INDEX IF NOT EXISTS ${p}stimulation_attempt_idx ON ${p}stimulation_attempt (stimulation_id, attempt_number);
|
|
66
|
+
|
|
67
|
+
-- NOTE: no FK on stimulation_attempt_id \u2014 the persistor streams settled tasks
|
|
68
|
+
-- (appendTask) as hops complete, BEFORE the attempt marker is flushed (saveAttempt),
|
|
69
|
+
-- so a hard FK would reject the insert. delete() removes tasks explicitly instead.
|
|
70
|
+
CREATE TABLE IF NOT EXISTS ${p}task (
|
|
71
|
+
stimulation_attempt_id text NOT NULL,
|
|
72
|
+
"index" int NOT NULL,
|
|
73
|
+
neuron_name text NOT NULL,
|
|
74
|
+
dendrite_collateral_name text NOT NULL,
|
|
75
|
+
input_index int NOT NULL,
|
|
76
|
+
output jsonb,
|
|
77
|
+
status text NOT NULL,
|
|
78
|
+
error text,
|
|
79
|
+
started_at bigint NOT NULL,
|
|
80
|
+
duration int,
|
|
81
|
+
PRIMARY KEY (stimulation_attempt_id, "index")
|
|
82
|
+
);
|
|
83
|
+
`;
|
|
84
|
+
}
|
|
85
|
+
__name(persistPostgresSchemaSql, "persistPostgresSchemaSql");
|
|
86
|
+
async function ensureSchema(db, prefix = DEFAULT_PREFIX) {
|
|
87
|
+
await db.query(persistPostgresSchemaSql(prefix));
|
|
88
|
+
}
|
|
89
|
+
__name(ensureSchema, "ensureSchema");
|
|
90
|
+
|
|
91
|
+
// src/cli.ts
|
|
92
|
+
async function main() {
|
|
93
|
+
var _a;
|
|
94
|
+
const cmd = process.argv[2];
|
|
95
|
+
if (cmd !== "migrate") {
|
|
96
|
+
console.error("usage: cnstra-persist-postgres migrate");
|
|
97
|
+
process.exit(1);
|
|
98
|
+
}
|
|
99
|
+
const url = process.env.DATABASE_URL;
|
|
100
|
+
if (!url) {
|
|
101
|
+
console.error("DATABASE_URL is required");
|
|
102
|
+
process.exit(1);
|
|
103
|
+
}
|
|
104
|
+
const prefix = (_a = process.env.CNS_TABLE_PREFIX) != null ? _a : DEFAULT_PREFIX;
|
|
105
|
+
const pool = new import_pg.Pool({
|
|
106
|
+
connectionString: url
|
|
107
|
+
});
|
|
108
|
+
try {
|
|
109
|
+
await ensureSchema(pool, prefix);
|
|
110
|
+
console.log(`\u2713 ensured stimulation/attempt/task tables (prefix "${prefix}")`);
|
|
111
|
+
} finally {
|
|
112
|
+
await pool.end();
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
__name(main, "main");
|
|
116
|
+
void main().catch((e) => {
|
|
117
|
+
console.error(e);
|
|
118
|
+
process.exit(1);
|
|
119
|
+
});
|
|
120
|
+
//# sourceMappingURL=cli.js.map
|
package/dist/cli.js.map
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/cli.ts","../src/migrate.ts"],"sourcesContent":["#!/usr/bin/env node\n/**\n * `cnstra-persist-postgres migrate` — create the stimulation/attempt/task tables.\n *\n * DATABASE_URL=postgres://... cnstra-persist-postgres migrate\n * CNS_TABLE_PREFIX=my_ (optional; default cns_)\n */\nimport { Pool } from 'pg';\nimport { ensureSchema, DEFAULT_PREFIX } from './migrate';\n\nasync function main(): Promise<void> {\n const cmd = process.argv[2];\n if (cmd !== 'migrate') {\n console.error('usage: cnstra-persist-postgres migrate'); // eslint-disable-line no-console\n process.exit(1);\n }\n const url = process.env.DATABASE_URL;\n if (!url) {\n console.error('DATABASE_URL is required'); // eslint-disable-line no-console\n process.exit(1);\n }\n const prefix = process.env.CNS_TABLE_PREFIX ?? DEFAULT_PREFIX;\n const pool = new Pool({ connectionString: url });\n try {\n await ensureSchema(pool, prefix);\n // eslint-disable-next-line no-console\n console.log(`✓ ensured stimulation/attempt/task tables (prefix \"${prefix}\")`);\n } finally {\n await pool.end();\n }\n}\n\nvoid main().catch(e => {\n console.error(e); // eslint-disable-line no-console\n process.exit(1);\n});\n","/**\n * Schema / migrations for the CNStra stimulation/attempt/task relational store.\n *\n * The package OWNS this schema — run it explicitly via `cnstra-persist-postgres migrate`\n * or `ensureSchema(db)`, or let the repository auto-create it on first use. Five tables\n * under a configurable prefix (default `cns_`); the stimulation's `progress` is\n * NORMALISED into `stimulation_frontier` (one row per outstanding task) +\n * `stimulation_context` (one row per neuron).\n */\n\n/** Minimal pg client surface (a `pg` Pool or Client satisfies it). */\nexport interface IPgQueryable {\n query(text: string, params?: unknown[]): Promise<{ rows: unknown[] }>;\n}\n\nexport const DEFAULT_PREFIX = 'cns_';\n\n/** Guard the table prefix (it is interpolated into DDL/DML, not parameterised). */\nexport function assertSafePrefix(prefix: string): string {\n if (!/^[A-Za-z_][A-Za-z0-9_]*$/.test(prefix)) {\n throw new Error(\n `[@cnstra/persist-postgres] unsafe table prefix: ${JSON.stringify(prefix)}`\n );\n }\n return prefix;\n}\n\n/** The full CREATE TABLE + CREATE INDEX DDL (idempotent). */\nexport function persistPostgresSchemaSql(prefix: string = DEFAULT_PREFIX): string {\n const p = assertSafePrefix(prefix);\n return `\nCREATE TABLE IF NOT EXISTS ${p}stimulation (\n stimulation_id text PRIMARY KEY,\n entry jsonb NOT NULL,\n status text NOT NULL,\n scope_name text,\n created_at timestamptz NOT NULL DEFAULT now(),\n updated_at timestamptz NOT NULL DEFAULT now()\n);\nCREATE INDEX IF NOT EXISTS ${p}stimulation_updated_idx ON ${p}stimulation (updated_at DESC);\nCREATE INDEX IF NOT EXISTS ${p}stimulation_scope_idx ON ${p}stimulation (scope_name);\n\nCREATE TABLE IF NOT EXISTS ${p}stimulation_frontier (\n stimulation_id text NOT NULL REFERENCES ${p}stimulation(stimulation_id) ON DELETE CASCADE,\n ord int NOT NULL,\n neuron_name text NOT NULL,\n dendrite_collateral_name text NOT NULL,\n input jsonb,\n PRIMARY KEY (stimulation_id, ord)\n);\n\nCREATE TABLE IF NOT EXISTS ${p}stimulation_context (\n stimulation_id text NOT NULL REFERENCES ${p}stimulation(stimulation_id) ON DELETE CASCADE,\n neuron_name text NOT NULL,\n value jsonb NOT NULL,\n PRIMARY KEY (stimulation_id, neuron_name)\n);\n\n-- NOTE: no FK on stimulation_id — over the devtools wire the persistor's\n-- saveStimulation and saveAttempt ride SEPARATE async batches processed\n-- concurrently by the server, so an attempt insert can reach Postgres before its\n-- stimulation row's transaction commits; a hard FK would reject it (23503). Same\n-- exposure as ${p}task; delete() removes attempts explicitly instead of cascading.\nCREATE TABLE IF NOT EXISTS ${p}stimulation_attempt (\n stimulation_attempt_id text PRIMARY KEY,\n stimulation_id text NOT NULL,\n attempt_number int NOT NULL,\n status text NOT NULL,\n started_at bigint NOT NULL,\n completed_at bigint,\n hop_count int NOT NULL,\n has_error boolean NOT NULL,\n replay_of text,\n entry jsonb NOT NULL,\n UNIQUE (stimulation_id, attempt_number)\n);\nCREATE INDEX IF NOT EXISTS ${p}stimulation_attempt_idx ON ${p}stimulation_attempt (stimulation_id, attempt_number);\n\n-- NOTE: no FK on stimulation_attempt_id — the persistor streams settled tasks\n-- (appendTask) as hops complete, BEFORE the attempt marker is flushed (saveAttempt),\n-- so a hard FK would reject the insert. delete() removes tasks explicitly instead.\nCREATE TABLE IF NOT EXISTS ${p}task (\n stimulation_attempt_id text NOT NULL,\n \"index\" int NOT NULL,\n neuron_name text NOT NULL,\n dendrite_collateral_name text NOT NULL,\n input_index int NOT NULL,\n output jsonb,\n status text NOT NULL,\n error text,\n started_at bigint NOT NULL,\n duration int,\n PRIMARY KEY (stimulation_attempt_id, \"index\")\n);\n`;\n}\n\n/** Idempotently create all tables + indexes. Safe to run repeatedly. */\nexport async function ensureSchema(\n db: IPgQueryable,\n prefix: string = DEFAULT_PREFIX\n): Promise<void> {\n await db.query(persistPostgresSchemaSql(prefix));\n}\n"],"mappings":";;;;;AAOA,gBAAqB;;;ACQd,IAAMA,iBAAiB;AAGvB,SAASC,iBAAiBC,QAAc;AAC3C,MAAI,CAAC,2BAA2BC,KAAKD,MAAAA,GAAS;AAC1C,UAAM,IAAIE,MACN,mDAAmDC,KAAKC,UAAUJ,MAAAA,CAAAA,EAAS;EAEnF;AACA,SAAOA;AACX;AAPgBD;AAUT,SAASM,yBAAyBL,SAAiBF,gBAAc;AACpE,QAAMQ,IAAIP,iBAAiBC,MAAAA;AAC3B,SAAO;6BACkBM,CAAAA;;;;;;;;6BAQAA,CAAAA,8BAA+BA,CAAAA;6BAC/BA,CAAAA,4BAA6BA,CAAAA;;6BAE7BA,CAAAA;8CACiBA,CAAAA;;;;;;;;6BAQjBA,CAAAA;8CACiBA,CAAAA;;;;;;;;;;iBAU7BA,CAAAA;6BACYA,CAAAA;;;;;;;;;;;;;6BAaAA,CAAAA,8BAA+BA,CAAAA;;;;;6BAK/BA,CAAAA;;;;;;;;;;;;;;AAc7B;AAnEgBD;AAsEhB,eAAsBE,aAClBC,IACAR,SAAiBF,gBAAc;AAE/B,QAAMU,GAAGC,MAAMJ,yBAAyBL,MAAAA,CAAAA;AAC5C;AALsBO;;;ADxFtB,eAAeG,OAAAA;;AACX,QAAMC,MAAMC,QAAQC,KAAK,CAAA;AACzB,MAAIF,QAAQ,WAAW;AACnBG,YAAQC,MAAM,wCAAA;AACdH,YAAQI,KAAK,CAAA;EACjB;AACA,QAAMC,MAAML,QAAQM,IAAIC;AACxB,MAAI,CAACF,KAAK;AACNH,YAAQC,MAAM,0BAAA;AACdH,YAAQI,KAAK,CAAA;EACjB;AACA,QAAMI,UAASR,aAAQM,IAAIG,qBAAZT,YAAgCU;AAC/C,QAAMC,OAAO,IAAIC,eAAK;IAAEC,kBAAkBR;EAAI,CAAA;AAC9C,MAAI;AACA,UAAMS,aAAaH,MAAMH,MAAAA;AAEzBN,YAAQa,IAAI,2DAAsDP,MAAAA,IAAU;EAChF,UAAA;AACI,UAAMG,KAAKK,IAAG;EAClB;AACJ;AApBelB;AAsBf,KAAKA,KAAAA,EAAOmB,MAAMC,CAAAA,MAAAA;AACdhB,UAAQC,MAAMe,CAAAA;AACdlB,UAAQI,KAAK,CAAA;AACjB,CAAA;","names":["DEFAULT_PREFIX","assertSafePrefix","prefix","test","Error","JSON","stringify","persistPostgresSchemaSql","p","ensureSchema","db","query","main","cmd","process","argv","console","error","exit","url","env","DATABASE_URL","prefix","CNS_TABLE_PREFIX","DEFAULT_PREFIX","pool","Pool","connectionString","ensureSchema","log","end","catch","e"]}
|
package/dist/cli.mjs
ADDED
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import {
|
|
3
|
+
DEFAULT_PREFIX,
|
|
4
|
+
__name,
|
|
5
|
+
ensureSchema
|
|
6
|
+
} from "./chunk-U4IKAHS4.mjs";
|
|
7
|
+
|
|
8
|
+
// src/cli.ts
|
|
9
|
+
import { Pool } from "pg";
|
|
10
|
+
async function main() {
|
|
11
|
+
var _a;
|
|
12
|
+
const cmd = process.argv[2];
|
|
13
|
+
if (cmd !== "migrate") {
|
|
14
|
+
console.error("usage: cnstra-persist-postgres migrate");
|
|
15
|
+
process.exit(1);
|
|
16
|
+
}
|
|
17
|
+
const url = process.env.DATABASE_URL;
|
|
18
|
+
if (!url) {
|
|
19
|
+
console.error("DATABASE_URL is required");
|
|
20
|
+
process.exit(1);
|
|
21
|
+
}
|
|
22
|
+
const prefix = (_a = process.env.CNS_TABLE_PREFIX) != null ? _a : DEFAULT_PREFIX;
|
|
23
|
+
const pool = new Pool({
|
|
24
|
+
connectionString: url
|
|
25
|
+
});
|
|
26
|
+
try {
|
|
27
|
+
await ensureSchema(pool, prefix);
|
|
28
|
+
console.log(`\u2713 ensured stimulation/attempt/task tables (prefix "${prefix}")`);
|
|
29
|
+
} finally {
|
|
30
|
+
await pool.end();
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
__name(main, "main");
|
|
34
|
+
void main().catch((e) => {
|
|
35
|
+
console.error(e);
|
|
36
|
+
process.exit(1);
|
|
37
|
+
});
|
|
38
|
+
//# sourceMappingURL=cli.mjs.map
|
package/dist/cli.mjs.map
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/cli.ts"],"sourcesContent":["#!/usr/bin/env node\n/**\n * `cnstra-persist-postgres migrate` — create the stimulation/attempt/task tables.\n *\n * DATABASE_URL=postgres://... cnstra-persist-postgres migrate\n * CNS_TABLE_PREFIX=my_ (optional; default cns_)\n */\nimport { Pool } from 'pg';\nimport { ensureSchema, DEFAULT_PREFIX } from './migrate';\n\nasync function main(): Promise<void> {\n const cmd = process.argv[2];\n if (cmd !== 'migrate') {\n console.error('usage: cnstra-persist-postgres migrate'); // eslint-disable-line no-console\n process.exit(1);\n }\n const url = process.env.DATABASE_URL;\n if (!url) {\n console.error('DATABASE_URL is required'); // eslint-disable-line no-console\n process.exit(1);\n }\n const prefix = process.env.CNS_TABLE_PREFIX ?? DEFAULT_PREFIX;\n const pool = new Pool({ connectionString: url });\n try {\n await ensureSchema(pool, prefix);\n // eslint-disable-next-line no-console\n console.log(`✓ ensured stimulation/attempt/task tables (prefix \"${prefix}\")`);\n } finally {\n await pool.end();\n }\n}\n\nvoid main().catch(e => {\n console.error(e); // eslint-disable-line no-console\n process.exit(1);\n});\n"],"mappings":";;;;;;;;AAOA,SAASA,YAAY;AAGrB,eAAeC,OAAAA;;AACX,QAAMC,MAAMC,QAAQC,KAAK,CAAA;AACzB,MAAIF,QAAQ,WAAW;AACnBG,YAAQC,MAAM,wCAAA;AACdH,YAAQI,KAAK,CAAA;EACjB;AACA,QAAMC,MAAML,QAAQM,IAAIC;AACxB,MAAI,CAACF,KAAK;AACNH,YAAQC,MAAM,0BAAA;AACdH,YAAQI,KAAK,CAAA;EACjB;AACA,QAAMI,UAASR,aAAQM,IAAIG,qBAAZT,YAAgCU;AAC/C,QAAMC,OAAO,IAAIC,KAAK;IAAEC,kBAAkBR;EAAI,CAAA;AAC9C,MAAI;AACA,UAAMS,aAAaH,MAAMH,MAAAA;AAEzBN,YAAQa,IAAI,2DAAsDP,MAAAA,IAAU;EAChF,UAAA;AACI,UAAMG,KAAKK,IAAG;EAClB;AACJ;AApBelB;AAsBf,KAAKA,KAAAA,EAAOmB,MAAMC,CAAAA,MAAAA;AACdhB,UAAQC,MAAMe,CAAAA;AACdlB,UAAQI,KAAK,CAAA;AACjB,CAAA;","names":["Pool","main","cmd","process","argv","console","error","exit","url","env","DATABASE_URL","prefix","CNS_TABLE_PREFIX","DEFAULT_PREFIX","pool","Pool","connectionString","ensureSchema","log","end","catch","e"]}
|
package/dist/index.d.mts
ADDED
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
import { Pool } from 'pg';
|
|
2
|
+
import { ICNSStimulationRepository } from '@cnstra/persist';
|
|
3
|
+
import { TCNSStimulationPersisted, TCNSStimulationAttemptPersisted, TCNSStimulationTaskPersisted } from '@cnstra/persist-dto';
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* Schema / migrations for the CNStra stimulation/attempt/task relational store.
|
|
7
|
+
*
|
|
8
|
+
* The package OWNS this schema — run it explicitly via `cnstra-persist-postgres migrate`
|
|
9
|
+
* or `ensureSchema(db)`, or let the repository auto-create it on first use. Five tables
|
|
10
|
+
* under a configurable prefix (default `cns_`); the stimulation's `progress` is
|
|
11
|
+
* NORMALISED into `stimulation_frontier` (one row per outstanding task) +
|
|
12
|
+
* `stimulation_context` (one row per neuron).
|
|
13
|
+
*/
|
|
14
|
+
/** Minimal pg client surface (a `pg` Pool or Client satisfies it). */
|
|
15
|
+
interface IPgQueryable {
|
|
16
|
+
query(text: string, params?: unknown[]): Promise<{
|
|
17
|
+
rows: unknown[];
|
|
18
|
+
}>;
|
|
19
|
+
}
|
|
20
|
+
declare const DEFAULT_PREFIX = "cns_";
|
|
21
|
+
/** Guard the table prefix (it is interpolated into DDL/DML, not parameterised). */
|
|
22
|
+
declare function assertSafePrefix(prefix: string): string;
|
|
23
|
+
/** The full CREATE TABLE + CREATE INDEX DDL (idempotent). */
|
|
24
|
+
declare function persistPostgresSchemaSql(prefix?: string): string;
|
|
25
|
+
/** Idempotently create all tables + indexes. Safe to run repeatedly. */
|
|
26
|
+
declare function ensureSchema(db: IPgQueryable, prefix?: string): Promise<void>;
|
|
27
|
+
|
|
28
|
+
/**
|
|
29
|
+
* Postgres relational store for the CNStra stimulation/attempt/task durable model
|
|
30
|
+
* (`ICNSStimulationRepository`). The stimulation's `progress` is NORMALISED: the
|
|
31
|
+
* outstanding frontier into `<prefix>stimulation_frontier` (one row per task) and
|
|
32
|
+
* per-neuron context into `<prefix>stimulation_context` — rebuilt into `TCNSProgress`
|
|
33
|
+
* on read.
|
|
34
|
+
*
|
|
35
|
+
* Reuses the `IPgQueryable` seam so `pg` is an optional peer: inject any pg
|
|
36
|
+
* client/pool, or pass a `connectionString` for the repo to own a Pool. Behaviourally
|
|
37
|
+
* matches `CNSInMemoryStimulationRepository` (listStimulations newest-first, getAttempts
|
|
38
|
+
* by attemptNumber, getTasks by index, delete cascades).
|
|
39
|
+
*/
|
|
40
|
+
|
|
41
|
+
interface TCNSPostgresStimulationRepositoryOptions {
|
|
42
|
+
connectionString?: string;
|
|
43
|
+
/** Inject an existing pg Pool (transactions supported; not closed by `close()`). */
|
|
44
|
+
pool?: Pool;
|
|
45
|
+
/** Inject any queryable (reads + best-effort non-transactional writes). */
|
|
46
|
+
queryable?: IPgQueryable;
|
|
47
|
+
/** Table prefix (default `cns_`). */
|
|
48
|
+
tablePrefix?: string;
|
|
49
|
+
/** Lazily ensure the schema on first use (default true). */
|
|
50
|
+
autoMigrate?: boolean;
|
|
51
|
+
}
|
|
52
|
+
declare class CNSPostgresStimulationRepository implements ICNSStimulationRepository {
|
|
53
|
+
private readonly db;
|
|
54
|
+
private readonly pool?;
|
|
55
|
+
private readonly ownsPool;
|
|
56
|
+
private readonly p;
|
|
57
|
+
private readonly autoMigrate;
|
|
58
|
+
private ready;
|
|
59
|
+
constructor(opts: TCNSPostgresStimulationRepositoryOptions);
|
|
60
|
+
private ensure;
|
|
61
|
+
/** Run `fn` in a transaction when a Pool is available; else best-effort inline. */
|
|
62
|
+
private tx;
|
|
63
|
+
saveStimulation(s: TCNSStimulationPersisted): Promise<void>;
|
|
64
|
+
saveAttempt(a: TCNSStimulationAttemptPersisted): Promise<void>;
|
|
65
|
+
appendTask(t: TCNSStimulationTaskPersisted): Promise<void>;
|
|
66
|
+
getStimulation(stimulationId: string): Promise<TCNSStimulationPersisted | undefined>;
|
|
67
|
+
listStimulations(filter?: {
|
|
68
|
+
scopeName?: string;
|
|
69
|
+
}): Promise<TCNSStimulationPersisted[]>;
|
|
70
|
+
getAttempts(stimulationId: string): Promise<TCNSStimulationAttemptPersisted[]>;
|
|
71
|
+
getTasks(stimulationAttemptId: string): Promise<TCNSStimulationTaskPersisted[]>;
|
|
72
|
+
delete(stimulationId: string): Promise<void>;
|
|
73
|
+
close(): Promise<void>;
|
|
74
|
+
private buildProgress;
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
export { CNSPostgresStimulationRepository, DEFAULT_PREFIX, type IPgQueryable, type TCNSPostgresStimulationRepositoryOptions, assertSafePrefix, ensureSchema, persistPostgresSchemaSql };
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
import { Pool } from 'pg';
|
|
2
|
+
import { ICNSStimulationRepository } from '@cnstra/persist';
|
|
3
|
+
import { TCNSStimulationPersisted, TCNSStimulationAttemptPersisted, TCNSStimulationTaskPersisted } from '@cnstra/persist-dto';
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* Schema / migrations for the CNStra stimulation/attempt/task relational store.
|
|
7
|
+
*
|
|
8
|
+
* The package OWNS this schema — run it explicitly via `cnstra-persist-postgres migrate`
|
|
9
|
+
* or `ensureSchema(db)`, or let the repository auto-create it on first use. Five tables
|
|
10
|
+
* under a configurable prefix (default `cns_`); the stimulation's `progress` is
|
|
11
|
+
* NORMALISED into `stimulation_frontier` (one row per outstanding task) +
|
|
12
|
+
* `stimulation_context` (one row per neuron).
|
|
13
|
+
*/
|
|
14
|
+
/** Minimal pg client surface (a `pg` Pool or Client satisfies it). */
|
|
15
|
+
interface IPgQueryable {
|
|
16
|
+
query(text: string, params?: unknown[]): Promise<{
|
|
17
|
+
rows: unknown[];
|
|
18
|
+
}>;
|
|
19
|
+
}
|
|
20
|
+
declare const DEFAULT_PREFIX = "cns_";
|
|
21
|
+
/** Guard the table prefix (it is interpolated into DDL/DML, not parameterised). */
|
|
22
|
+
declare function assertSafePrefix(prefix: string): string;
|
|
23
|
+
/** The full CREATE TABLE + CREATE INDEX DDL (idempotent). */
|
|
24
|
+
declare function persistPostgresSchemaSql(prefix?: string): string;
|
|
25
|
+
/** Idempotently create all tables + indexes. Safe to run repeatedly. */
|
|
26
|
+
declare function ensureSchema(db: IPgQueryable, prefix?: string): Promise<void>;
|
|
27
|
+
|
|
28
|
+
/**
|
|
29
|
+
* Postgres relational store for the CNStra stimulation/attempt/task durable model
|
|
30
|
+
* (`ICNSStimulationRepository`). The stimulation's `progress` is NORMALISED: the
|
|
31
|
+
* outstanding frontier into `<prefix>stimulation_frontier` (one row per task) and
|
|
32
|
+
* per-neuron context into `<prefix>stimulation_context` — rebuilt into `TCNSProgress`
|
|
33
|
+
* on read.
|
|
34
|
+
*
|
|
35
|
+
* Reuses the `IPgQueryable` seam so `pg` is an optional peer: inject any pg
|
|
36
|
+
* client/pool, or pass a `connectionString` for the repo to own a Pool. Behaviourally
|
|
37
|
+
* matches `CNSInMemoryStimulationRepository` (listStimulations newest-first, getAttempts
|
|
38
|
+
* by attemptNumber, getTasks by index, delete cascades).
|
|
39
|
+
*/
|
|
40
|
+
|
|
41
|
+
interface TCNSPostgresStimulationRepositoryOptions {
|
|
42
|
+
connectionString?: string;
|
|
43
|
+
/** Inject an existing pg Pool (transactions supported; not closed by `close()`). */
|
|
44
|
+
pool?: Pool;
|
|
45
|
+
/** Inject any queryable (reads + best-effort non-transactional writes). */
|
|
46
|
+
queryable?: IPgQueryable;
|
|
47
|
+
/** Table prefix (default `cns_`). */
|
|
48
|
+
tablePrefix?: string;
|
|
49
|
+
/** Lazily ensure the schema on first use (default true). */
|
|
50
|
+
autoMigrate?: boolean;
|
|
51
|
+
}
|
|
52
|
+
declare class CNSPostgresStimulationRepository implements ICNSStimulationRepository {
|
|
53
|
+
private readonly db;
|
|
54
|
+
private readonly pool?;
|
|
55
|
+
private readonly ownsPool;
|
|
56
|
+
private readonly p;
|
|
57
|
+
private readonly autoMigrate;
|
|
58
|
+
private ready;
|
|
59
|
+
constructor(opts: TCNSPostgresStimulationRepositoryOptions);
|
|
60
|
+
private ensure;
|
|
61
|
+
/** Run `fn` in a transaction when a Pool is available; else best-effort inline. */
|
|
62
|
+
private tx;
|
|
63
|
+
saveStimulation(s: TCNSStimulationPersisted): Promise<void>;
|
|
64
|
+
saveAttempt(a: TCNSStimulationAttemptPersisted): Promise<void>;
|
|
65
|
+
appendTask(t: TCNSStimulationTaskPersisted): Promise<void>;
|
|
66
|
+
getStimulation(stimulationId: string): Promise<TCNSStimulationPersisted | undefined>;
|
|
67
|
+
listStimulations(filter?: {
|
|
68
|
+
scopeName?: string;
|
|
69
|
+
}): Promise<TCNSStimulationPersisted[]>;
|
|
70
|
+
getAttempts(stimulationId: string): Promise<TCNSStimulationAttemptPersisted[]>;
|
|
71
|
+
getTasks(stimulationAttemptId: string): Promise<TCNSStimulationTaskPersisted[]>;
|
|
72
|
+
delete(stimulationId: string): Promise<void>;
|
|
73
|
+
close(): Promise<void>;
|
|
74
|
+
private buildProgress;
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
export { CNSPostgresStimulationRepository, DEFAULT_PREFIX, type IPgQueryable, type TCNSPostgresStimulationRepositoryOptions, assertSafePrefix, ensureSchema, persistPostgresSchemaSql };
|