@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/dist/index.mjs
ADDED
|
@@ -0,0 +1,326 @@
|
|
|
1
|
+
import {
|
|
2
|
+
DEFAULT_PREFIX,
|
|
3
|
+
__name,
|
|
4
|
+
__publicField,
|
|
5
|
+
assertSafePrefix,
|
|
6
|
+
ensureSchema,
|
|
7
|
+
persistPostgresSchemaSql
|
|
8
|
+
} from "./chunk-U4IKAHS4.mjs";
|
|
9
|
+
|
|
10
|
+
// src/CNSPostgresStimulationRepository.ts
|
|
11
|
+
import { Pool } from "pg";
|
|
12
|
+
var j = /* @__PURE__ */ __name((v) => JSON.stringify(v != null ? v : null), "j");
|
|
13
|
+
var num = /* @__PURE__ */ __name((v) => Number(v), "num");
|
|
14
|
+
var numOrNull = /* @__PURE__ */ __name((v) => v == null ? null : Number(v), "numOrNull");
|
|
15
|
+
var _CNSPostgresStimulationRepository = class _CNSPostgresStimulationRepository {
|
|
16
|
+
constructor(opts) {
|
|
17
|
+
__publicField(this, "db");
|
|
18
|
+
__publicField(this, "pool");
|
|
19
|
+
__publicField(this, "ownsPool");
|
|
20
|
+
__publicField(this, "p");
|
|
21
|
+
__publicField(this, "autoMigrate");
|
|
22
|
+
__publicField(this, "ready");
|
|
23
|
+
var _a, _b;
|
|
24
|
+
if (opts.pool) {
|
|
25
|
+
this.pool = opts.pool;
|
|
26
|
+
this.db = opts.pool;
|
|
27
|
+
this.ownsPool = false;
|
|
28
|
+
} else if (opts.connectionString) {
|
|
29
|
+
this.pool = new Pool({
|
|
30
|
+
connectionString: opts.connectionString
|
|
31
|
+
});
|
|
32
|
+
this.db = this.pool;
|
|
33
|
+
this.ownsPool = true;
|
|
34
|
+
} else if (opts.queryable) {
|
|
35
|
+
this.db = opts.queryable;
|
|
36
|
+
this.ownsPool = false;
|
|
37
|
+
} else {
|
|
38
|
+
throw new Error("[@cnstra/persist-postgres] needs `connectionString`, `pool`, or `queryable`");
|
|
39
|
+
}
|
|
40
|
+
this.p = assertSafePrefix((_a = opts.tablePrefix) != null ? _a : DEFAULT_PREFIX);
|
|
41
|
+
this.autoMigrate = (_b = opts.autoMigrate) != null ? _b : true;
|
|
42
|
+
}
|
|
43
|
+
ensure() {
|
|
44
|
+
var _a;
|
|
45
|
+
if (!this.autoMigrate) return Promise.resolve();
|
|
46
|
+
return (_a = this.ready) != null ? _a : this.ready = ensureSchema(this.db, this.p);
|
|
47
|
+
}
|
|
48
|
+
/** Run `fn` in a transaction when a Pool is available; else best-effort inline. */
|
|
49
|
+
async tx(fn) {
|
|
50
|
+
if (!this.pool) return fn(this.db);
|
|
51
|
+
const client = await this.pool.connect();
|
|
52
|
+
try {
|
|
53
|
+
await client.query("BEGIN");
|
|
54
|
+
const out = await fn(client);
|
|
55
|
+
await client.query("COMMIT");
|
|
56
|
+
return out;
|
|
57
|
+
} catch (e) {
|
|
58
|
+
await client.query("ROLLBACK");
|
|
59
|
+
throw e;
|
|
60
|
+
} finally {
|
|
61
|
+
client.release();
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
// ── write ──
|
|
65
|
+
async saveStimulation(s) {
|
|
66
|
+
await this.ensure();
|
|
67
|
+
const { p } = this;
|
|
68
|
+
await this.tx(async (db) => {
|
|
69
|
+
var _a;
|
|
70
|
+
await db.query(`INSERT INTO ${p}stimulation (stimulation_id, entry, status, scope_name, updated_at)
|
|
71
|
+
VALUES ($1, $2::jsonb, $3, $4, now())
|
|
72
|
+
ON CONFLICT (stimulation_id)
|
|
73
|
+
DO UPDATE SET entry = EXCLUDED.entry, status = EXCLUDED.status,
|
|
74
|
+
scope_name = EXCLUDED.scope_name, updated_at = now()`, [
|
|
75
|
+
s.stimulationId,
|
|
76
|
+
j(s.entry),
|
|
77
|
+
s.status,
|
|
78
|
+
(_a = s.scopeName) != null ? _a : null
|
|
79
|
+
]);
|
|
80
|
+
await db.query(`DELETE FROM ${p}stimulation_frontier WHERE stimulation_id = $1`, [
|
|
81
|
+
s.stimulationId
|
|
82
|
+
]);
|
|
83
|
+
for (let i = 0; i < s.progress.tasks.length; i++) {
|
|
84
|
+
const t = s.progress.tasks[i];
|
|
85
|
+
await db.query(`INSERT INTO ${p}stimulation_frontier
|
|
86
|
+
(stimulation_id, ord, neuron_name, dendrite_collateral_name, input)
|
|
87
|
+
VALUES ($1, $2, $3, $4, $5::jsonb)`, [
|
|
88
|
+
s.stimulationId,
|
|
89
|
+
i,
|
|
90
|
+
t.neuronName,
|
|
91
|
+
t.dendriteCollateralName,
|
|
92
|
+
t.input ? j(t.input) : null
|
|
93
|
+
]);
|
|
94
|
+
}
|
|
95
|
+
await db.query(`DELETE FROM ${p}stimulation_context WHERE stimulation_id = $1`, [
|
|
96
|
+
s.stimulationId
|
|
97
|
+
]);
|
|
98
|
+
for (const [neuronName, value] of Object.entries(s.progress.context)) {
|
|
99
|
+
await db.query(`INSERT INTO ${p}stimulation_context (stimulation_id, neuron_name, value)
|
|
100
|
+
VALUES ($1, $2, $3::jsonb)`, [
|
|
101
|
+
s.stimulationId,
|
|
102
|
+
neuronName,
|
|
103
|
+
j(value)
|
|
104
|
+
]);
|
|
105
|
+
}
|
|
106
|
+
});
|
|
107
|
+
}
|
|
108
|
+
async saveAttempt(a) {
|
|
109
|
+
await this.ensure();
|
|
110
|
+
const { p } = this;
|
|
111
|
+
await this.db.query(`INSERT INTO ${p}stimulation_attempt
|
|
112
|
+
(stimulation_attempt_id, stimulation_id, attempt_number, status, started_at,
|
|
113
|
+
completed_at, hop_count, has_error, replay_of, entry)
|
|
114
|
+
VALUES ($1,$2,$3,$4,$5,$6,$7,$8,$9,$10::jsonb)
|
|
115
|
+
ON CONFLICT (stimulation_attempt_id) DO UPDATE SET
|
|
116
|
+
status = EXCLUDED.status, completed_at = EXCLUDED.completed_at,
|
|
117
|
+
hop_count = EXCLUDED.hop_count, has_error = EXCLUDED.has_error,
|
|
118
|
+
replay_of = EXCLUDED.replay_of, entry = EXCLUDED.entry`, [
|
|
119
|
+
a.stimulationAttemptId,
|
|
120
|
+
a.stimulationId,
|
|
121
|
+
a.attemptNumber,
|
|
122
|
+
a.status,
|
|
123
|
+
a.startedAt,
|
|
124
|
+
a.completedAt,
|
|
125
|
+
a.hopCount,
|
|
126
|
+
a.hasError,
|
|
127
|
+
a.replayOf,
|
|
128
|
+
j(a.entry)
|
|
129
|
+
]);
|
|
130
|
+
}
|
|
131
|
+
async appendTask(t) {
|
|
132
|
+
await this.ensure();
|
|
133
|
+
const { p } = this;
|
|
134
|
+
await this.db.query(`INSERT INTO ${p}task
|
|
135
|
+
(stimulation_attempt_id, "index", neuron_name, dendrite_collateral_name,
|
|
136
|
+
input_index, output, status, error, started_at, duration)
|
|
137
|
+
VALUES ($1,$2,$3,$4,$5,$6::jsonb,$7,$8,$9,$10)
|
|
138
|
+
ON CONFLICT (stimulation_attempt_id, "index") DO NOTHING`, [
|
|
139
|
+
t.stimulationAttemptId,
|
|
140
|
+
t.index,
|
|
141
|
+
t.neuronName,
|
|
142
|
+
t.dendriteCollateralName,
|
|
143
|
+
t.inputIndex,
|
|
144
|
+
t.output ? j(t.output) : null,
|
|
145
|
+
t.status,
|
|
146
|
+
t.error,
|
|
147
|
+
t.startedAt,
|
|
148
|
+
t.duration
|
|
149
|
+
]);
|
|
150
|
+
}
|
|
151
|
+
// ── read ──
|
|
152
|
+
async getStimulation(stimulationId) {
|
|
153
|
+
await this.ensure();
|
|
154
|
+
const { p } = this;
|
|
155
|
+
const stimRes = await this.db.query(`SELECT entry, status, scope_name FROM ${p}stimulation WHERE stimulation_id = $1`, [
|
|
156
|
+
stimulationId
|
|
157
|
+
]);
|
|
158
|
+
const row = stimRes.rows[0];
|
|
159
|
+
if (!row) return void 0;
|
|
160
|
+
const [frontier, context] = await Promise.all([
|
|
161
|
+
this.db.query(`SELECT ord, neuron_name, dendrite_collateral_name, input
|
|
162
|
+
FROM ${p}stimulation_frontier WHERE stimulation_id = $1 ORDER BY ord`, [
|
|
163
|
+
stimulationId
|
|
164
|
+
]),
|
|
165
|
+
this.db.query(`SELECT neuron_name, value FROM ${p}stimulation_context WHERE stimulation_id = $1`, [
|
|
166
|
+
stimulationId
|
|
167
|
+
])
|
|
168
|
+
]);
|
|
169
|
+
return {
|
|
170
|
+
stimulationId,
|
|
171
|
+
entry: row.entry,
|
|
172
|
+
status: row.status,
|
|
173
|
+
progress: this.buildProgress(frontier.rows, context.rows),
|
|
174
|
+
...row.scope_name != null ? {
|
|
175
|
+
scopeName: row.scope_name
|
|
176
|
+
} : {}
|
|
177
|
+
};
|
|
178
|
+
}
|
|
179
|
+
async listStimulations(filter) {
|
|
180
|
+
var _a;
|
|
181
|
+
await this.ensure();
|
|
182
|
+
const { p } = this;
|
|
183
|
+
const scope = (_a = filter == null ? void 0 : filter.scopeName) != null ? _a : null;
|
|
184
|
+
const stimsRes = await this.db.query(`SELECT stimulation_id, entry, status, scope_name FROM ${p}stimulation
|
|
185
|
+
WHERE ($1::text IS NULL OR scope_name = $1)
|
|
186
|
+
ORDER BY updated_at DESC`, [
|
|
187
|
+
scope
|
|
188
|
+
]);
|
|
189
|
+
const stims = stimsRes.rows;
|
|
190
|
+
if (stims.length === 0) return [];
|
|
191
|
+
const ids = stims.map((r) => r.stimulation_id);
|
|
192
|
+
const [frontierRes, contextRes] = await Promise.all([
|
|
193
|
+
this.db.query(`SELECT stimulation_id, ord, neuron_name, dendrite_collateral_name, input
|
|
194
|
+
FROM ${p}stimulation_frontier WHERE stimulation_id = ANY($1) ORDER BY stimulation_id, ord`, [
|
|
195
|
+
ids
|
|
196
|
+
]),
|
|
197
|
+
this.db.query(`SELECT stimulation_id, neuron_name, value
|
|
198
|
+
FROM ${p}stimulation_context WHERE stimulation_id = ANY($1)`, [
|
|
199
|
+
ids
|
|
200
|
+
])
|
|
201
|
+
]);
|
|
202
|
+
const frontierBy = groupBy(frontierRes.rows, (r) => r.stimulation_id);
|
|
203
|
+
const contextBy = groupBy(contextRes.rows, (r) => r.stimulation_id);
|
|
204
|
+
return stims.map((r) => {
|
|
205
|
+
var _a2, _b;
|
|
206
|
+
return {
|
|
207
|
+
stimulationId: r.stimulation_id,
|
|
208
|
+
entry: r.entry,
|
|
209
|
+
status: r.status,
|
|
210
|
+
progress: this.buildProgress((_a2 = frontierBy.get(r.stimulation_id)) != null ? _a2 : [], (_b = contextBy.get(r.stimulation_id)) != null ? _b : []),
|
|
211
|
+
...r.scope_name != null ? {
|
|
212
|
+
scopeName: r.scope_name
|
|
213
|
+
} : {}
|
|
214
|
+
};
|
|
215
|
+
});
|
|
216
|
+
}
|
|
217
|
+
async getAttempts(stimulationId) {
|
|
218
|
+
await this.ensure();
|
|
219
|
+
const { p } = this;
|
|
220
|
+
const res = await this.db.query(`SELECT * FROM ${p}stimulation_attempt WHERE stimulation_id = $1 ORDER BY attempt_number`, [
|
|
221
|
+
stimulationId
|
|
222
|
+
]);
|
|
223
|
+
return res.rows.map(rowToAttempt);
|
|
224
|
+
}
|
|
225
|
+
async getTasks(stimulationAttemptId) {
|
|
226
|
+
await this.ensure();
|
|
227
|
+
const { p } = this;
|
|
228
|
+
const res = await this.db.query(`SELECT * FROM ${p}task WHERE stimulation_attempt_id = $1 ORDER BY "index"`, [
|
|
229
|
+
stimulationAttemptId
|
|
230
|
+
]);
|
|
231
|
+
return res.rows.map(rowToTask);
|
|
232
|
+
}
|
|
233
|
+
// ── lifecycle ──
|
|
234
|
+
async delete(stimulationId) {
|
|
235
|
+
await this.ensure();
|
|
236
|
+
const { p } = this;
|
|
237
|
+
await this.tx(async (db) => {
|
|
238
|
+
await db.query(`DELETE FROM ${p}task WHERE stimulation_attempt_id IN
|
|
239
|
+
(SELECT stimulation_attempt_id FROM ${p}stimulation_attempt WHERE stimulation_id = $1)`, [
|
|
240
|
+
stimulationId
|
|
241
|
+
]);
|
|
242
|
+
await db.query(`DELETE FROM ${p}stimulation_attempt WHERE stimulation_id = $1`, [
|
|
243
|
+
stimulationId
|
|
244
|
+
]);
|
|
245
|
+
await db.query(`DELETE FROM ${p}stimulation WHERE stimulation_id = $1`, [
|
|
246
|
+
stimulationId
|
|
247
|
+
]);
|
|
248
|
+
});
|
|
249
|
+
}
|
|
250
|
+
async close() {
|
|
251
|
+
if (this.ownsPool && this.pool) await this.pool.end();
|
|
252
|
+
}
|
|
253
|
+
// ── helpers ──
|
|
254
|
+
buildProgress(frontier, context) {
|
|
255
|
+
const tasks = [
|
|
256
|
+
...frontier
|
|
257
|
+
].sort((a, b) => a.ord - b.ord).map((f) => ({
|
|
258
|
+
neuronName: f.neuron_name,
|
|
259
|
+
dendriteCollateralName: f.dendrite_collateral_name,
|
|
260
|
+
...f.input != null ? {
|
|
261
|
+
input: f.input
|
|
262
|
+
} : {}
|
|
263
|
+
}));
|
|
264
|
+
const ctx = {};
|
|
265
|
+
for (const c of context) ctx[c.neuron_name] = c.value;
|
|
266
|
+
return {
|
|
267
|
+
tasks,
|
|
268
|
+
context: ctx
|
|
269
|
+
};
|
|
270
|
+
}
|
|
271
|
+
};
|
|
272
|
+
__name(_CNSPostgresStimulationRepository, "CNSPostgresStimulationRepository");
|
|
273
|
+
var CNSPostgresStimulationRepository = _CNSPostgresStimulationRepository;
|
|
274
|
+
function rowToAttempt(a) {
|
|
275
|
+
var _a;
|
|
276
|
+
return {
|
|
277
|
+
stimulationAttemptId: a.stimulation_attempt_id,
|
|
278
|
+
stimulationId: a.stimulation_id,
|
|
279
|
+
attemptNumber: num(a.attempt_number),
|
|
280
|
+
status: a.status,
|
|
281
|
+
startedAt: num(a.started_at),
|
|
282
|
+
completedAt: numOrNull(a.completed_at),
|
|
283
|
+
hopCount: num(a.hop_count),
|
|
284
|
+
hasError: Boolean(a.has_error),
|
|
285
|
+
replayOf: (_a = a.replay_of) != null ? _a : null,
|
|
286
|
+
entry: a.entry
|
|
287
|
+
};
|
|
288
|
+
}
|
|
289
|
+
__name(rowToAttempt, "rowToAttempt");
|
|
290
|
+
function rowToTask(t) {
|
|
291
|
+
var _a, _b;
|
|
292
|
+
return {
|
|
293
|
+
stimulationAttemptId: t.stimulation_attempt_id,
|
|
294
|
+
index: num(t.index),
|
|
295
|
+
neuronName: t.neuron_name,
|
|
296
|
+
dendriteCollateralName: t.dendrite_collateral_name,
|
|
297
|
+
inputIndex: num(t.input_index),
|
|
298
|
+
output: (_a = t.output) != null ? _a : null,
|
|
299
|
+
status: t.status,
|
|
300
|
+
error: (_b = t.error) != null ? _b : null,
|
|
301
|
+
startedAt: num(t.started_at),
|
|
302
|
+
duration: numOrNull(t.duration)
|
|
303
|
+
};
|
|
304
|
+
}
|
|
305
|
+
__name(rowToTask, "rowToTask");
|
|
306
|
+
function groupBy(rows, key) {
|
|
307
|
+
const m = /* @__PURE__ */ new Map();
|
|
308
|
+
for (const r of rows) {
|
|
309
|
+
const k = key(r);
|
|
310
|
+
const b = m.get(k);
|
|
311
|
+
if (b) b.push(r);
|
|
312
|
+
else m.set(k, [
|
|
313
|
+
r
|
|
314
|
+
]);
|
|
315
|
+
}
|
|
316
|
+
return m;
|
|
317
|
+
}
|
|
318
|
+
__name(groupBy, "groupBy");
|
|
319
|
+
export {
|
|
320
|
+
CNSPostgresStimulationRepository,
|
|
321
|
+
DEFAULT_PREFIX,
|
|
322
|
+
assertSafePrefix,
|
|
323
|
+
ensureSchema,
|
|
324
|
+
persistPostgresSchemaSql
|
|
325
|
+
};
|
|
326
|
+
//# sourceMappingURL=index.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/CNSPostgresStimulationRepository.ts"],"sourcesContent":["/**\n * Postgres relational store for the CNStra stimulation/attempt/task durable model\n * (`ICNSStimulationRepository`). The stimulation's `progress` is NORMALISED: the\n * outstanding frontier into `<prefix>stimulation_frontier` (one row per task) and\n * per-neuron context into `<prefix>stimulation_context` — rebuilt into `TCNSProgress`\n * on read.\n *\n * Reuses the `IPgQueryable` seam so `pg` is an optional peer: inject any pg\n * client/pool, or pass a `connectionString` for the repo to own a Pool. Behaviourally\n * matches `CNSInMemoryStimulationRepository` (listStimulations newest-first, getAttempts\n * by attemptNumber, getTasks by index, delete cascades).\n */\nimport { Pool } from 'pg';\nimport type { ICNSStimulationRepository } from '@cnstra/persist';\nimport type {\n TCNSStimulationPersisted,\n TCNSStimulationAttemptPersisted,\n TCNSStimulationTaskPersisted,\n TCNSProgress,\n TCNSSerializedTask,\n TCNSSignalRef,\n TCNSStimulationStatus,\n TCNSStimulationTaskStatus,\n} from '@cnstra/persist-dto';\nimport {\n DEFAULT_PREFIX,\n assertSafePrefix,\n ensureSchema,\n type IPgQueryable,\n} from './migrate';\n\nexport interface TCNSPostgresStimulationRepositoryOptions {\n connectionString?: string;\n /** Inject an existing pg Pool (transactions supported; not closed by `close()`). */\n pool?: Pool;\n /** Inject any queryable (reads + best-effort non-transactional writes). */\n queryable?: IPgQueryable;\n /** Table prefix (default `cns_`). */\n tablePrefix?: string;\n /** Lazily ensure the schema on first use (default true). */\n autoMigrate?: boolean;\n}\n\nconst j = (v: unknown): string => JSON.stringify(v ?? null);\nconst num = (v: unknown): number => Number(v);\nconst numOrNull = (v: unknown): number | null => (v == null ? null : Number(v));\n\nexport class CNSPostgresStimulationRepository\n implements ICNSStimulationRepository\n{\n private readonly db: IPgQueryable;\n private readonly pool?: Pool;\n private readonly ownsPool: boolean;\n private readonly p: string;\n private readonly autoMigrate: boolean;\n private ready: Promise<void> | undefined;\n\n constructor(opts: TCNSPostgresStimulationRepositoryOptions) {\n if (opts.pool) {\n this.pool = opts.pool;\n this.db = opts.pool;\n this.ownsPool = false;\n } else if (opts.connectionString) {\n this.pool = new Pool({ connectionString: opts.connectionString });\n this.db = this.pool;\n this.ownsPool = true;\n } else if (opts.queryable) {\n this.db = opts.queryable;\n this.ownsPool = false;\n } else {\n throw new Error(\n '[@cnstra/persist-postgres] needs `connectionString`, `pool`, or `queryable`'\n );\n }\n this.p = assertSafePrefix(opts.tablePrefix ?? DEFAULT_PREFIX);\n this.autoMigrate = opts.autoMigrate ?? true;\n }\n\n private ensure(): Promise<void> {\n if (!this.autoMigrate) return Promise.resolve();\n return (this.ready ??= ensureSchema(this.db, this.p));\n }\n\n /** Run `fn` in a transaction when a Pool is available; else best-effort inline. */\n private async tx<T>(fn: (db: IPgQueryable) => Promise<T>): Promise<T> {\n if (!this.pool) return fn(this.db);\n const client = await this.pool.connect();\n try {\n await client.query('BEGIN');\n const out = await fn(client as unknown as IPgQueryable);\n await client.query('COMMIT');\n return out;\n } catch (e) {\n await client.query('ROLLBACK');\n throw e;\n } finally {\n client.release();\n }\n }\n\n // ── write ──\n\n async saveStimulation(s: TCNSStimulationPersisted): Promise<void> {\n await this.ensure();\n const { p } = this;\n await this.tx(async db => {\n await db.query(\n `INSERT INTO ${p}stimulation (stimulation_id, entry, status, scope_name, updated_at)\n VALUES ($1, $2::jsonb, $3, $4, now())\n ON CONFLICT (stimulation_id)\n DO UPDATE SET entry = EXCLUDED.entry, status = EXCLUDED.status,\n scope_name = EXCLUDED.scope_name, updated_at = now()`,\n [s.stimulationId, j(s.entry), s.status, s.scopeName ?? null]\n );\n // Replace the normalised frontier.\n await db.query(\n `DELETE FROM ${p}stimulation_frontier WHERE stimulation_id = $1`,\n [s.stimulationId]\n );\n for (let i = 0; i < s.progress.tasks.length; i++) {\n const t = s.progress.tasks[i];\n await db.query(\n `INSERT INTO ${p}stimulation_frontier\n (stimulation_id, ord, neuron_name, dendrite_collateral_name, input)\n VALUES ($1, $2, $3, $4, $5::jsonb)`,\n [\n s.stimulationId,\n i,\n t.neuronName,\n t.dendriteCollateralName,\n t.input ? j(t.input) : null,\n ]\n );\n }\n // Replace the normalised context.\n await db.query(\n `DELETE FROM ${p}stimulation_context WHERE stimulation_id = $1`,\n [s.stimulationId]\n );\n for (const [neuronName, value] of Object.entries(\n s.progress.context\n )) {\n await db.query(\n `INSERT INTO ${p}stimulation_context (stimulation_id, neuron_name, value)\n VALUES ($1, $2, $3::jsonb)`,\n [s.stimulationId, neuronName, j(value)]\n );\n }\n });\n }\n\n async saveAttempt(a: TCNSStimulationAttemptPersisted): Promise<void> {\n await this.ensure();\n const { p } = this;\n await this.db.query(\n `INSERT INTO ${p}stimulation_attempt\n (stimulation_attempt_id, stimulation_id, attempt_number, status, started_at,\n completed_at, hop_count, has_error, replay_of, entry)\n VALUES ($1,$2,$3,$4,$5,$6,$7,$8,$9,$10::jsonb)\n ON CONFLICT (stimulation_attempt_id) DO UPDATE SET\n status = EXCLUDED.status, completed_at = EXCLUDED.completed_at,\n hop_count = EXCLUDED.hop_count, has_error = EXCLUDED.has_error,\n replay_of = EXCLUDED.replay_of, entry = EXCLUDED.entry`,\n [\n a.stimulationAttemptId,\n a.stimulationId,\n a.attemptNumber,\n a.status,\n a.startedAt,\n a.completedAt,\n a.hopCount,\n a.hasError,\n a.replayOf,\n j(a.entry),\n ]\n );\n }\n\n async appendTask(t: TCNSStimulationTaskPersisted): Promise<void> {\n await this.ensure();\n const { p } = this;\n await this.db.query(\n `INSERT INTO ${p}task\n (stimulation_attempt_id, \"index\", neuron_name, dendrite_collateral_name,\n input_index, output, status, error, started_at, duration)\n VALUES ($1,$2,$3,$4,$5,$6::jsonb,$7,$8,$9,$10)\n ON CONFLICT (stimulation_attempt_id, \"index\") DO NOTHING`,\n [\n t.stimulationAttemptId,\n t.index,\n t.neuronName,\n t.dendriteCollateralName,\n t.inputIndex,\n t.output ? j(t.output) : null,\n t.status,\n t.error,\n t.startedAt,\n t.duration,\n ]\n );\n }\n\n // ── read ──\n\n async getStimulation(\n stimulationId: string\n ): Promise<TCNSStimulationPersisted | undefined> {\n await this.ensure();\n const { p } = this;\n const stimRes = await this.db.query(\n `SELECT entry, status, scope_name FROM ${p}stimulation WHERE stimulation_id = $1`,\n [stimulationId]\n );\n const row = stimRes.rows[0] as\n | {\n entry: TCNSSignalRef;\n status: TCNSStimulationStatus;\n scope_name: string | null;\n }\n | undefined;\n if (!row) return undefined;\n const [frontier, context] = await Promise.all([\n this.db.query(\n `SELECT ord, neuron_name, dendrite_collateral_name, input\n FROM ${p}stimulation_frontier WHERE stimulation_id = $1 ORDER BY ord`,\n [stimulationId]\n ),\n this.db.query(\n `SELECT neuron_name, value FROM ${p}stimulation_context WHERE stimulation_id = $1`,\n [stimulationId]\n ),\n ]);\n return {\n stimulationId,\n entry: row.entry,\n status: row.status,\n progress: this.buildProgress(\n frontier.rows as TFrontierRow[],\n context.rows as TContextRow[]\n ),\n ...(row.scope_name != null ? { scopeName: row.scope_name } : {}),\n };\n }\n\n async listStimulations(filter?: {\n scopeName?: string;\n }): Promise<TCNSStimulationPersisted[]> {\n await this.ensure();\n const { p } = this;\n const scope = filter?.scopeName ?? null;\n const stimsRes = await this.db.query(\n `SELECT stimulation_id, entry, status, scope_name FROM ${p}stimulation\n WHERE ($1::text IS NULL OR scope_name = $1)\n ORDER BY updated_at DESC`,\n [scope]\n );\n const stims = stimsRes.rows as TStimulationRow[];\n if (stims.length === 0) return [];\n const ids = stims.map(r => r.stimulation_id);\n const [frontierRes, contextRes] = await Promise.all([\n this.db.query(\n `SELECT stimulation_id, ord, neuron_name, dendrite_collateral_name, input\n FROM ${p}stimulation_frontier WHERE stimulation_id = ANY($1) ORDER BY stimulation_id, ord`,\n [ids]\n ),\n this.db.query(\n `SELECT stimulation_id, neuron_name, value\n FROM ${p}stimulation_context WHERE stimulation_id = ANY($1)`,\n [ids]\n ),\n ]);\n const frontierBy = groupBy(\n frontierRes.rows as TFrontierRow[],\n r => r.stimulation_id\n );\n const contextBy = groupBy(\n contextRes.rows as TContextRow[],\n r => r.stimulation_id\n );\n return stims.map(r => ({\n stimulationId: r.stimulation_id,\n entry: r.entry,\n status: r.status,\n progress: this.buildProgress(\n frontierBy.get(r.stimulation_id) ?? [],\n contextBy.get(r.stimulation_id) ?? []\n ),\n ...(r.scope_name != null ? { scopeName: r.scope_name } : {}),\n }));\n }\n\n async getAttempts(\n stimulationId: string\n ): Promise<TCNSStimulationAttemptPersisted[]> {\n await this.ensure();\n const { p } = this;\n const res = await this.db.query(\n `SELECT * FROM ${p}stimulation_attempt WHERE stimulation_id = $1 ORDER BY attempt_number`,\n [stimulationId]\n );\n return (res.rows as TAttemptRow[]).map(rowToAttempt);\n }\n\n async getTasks(\n stimulationAttemptId: string\n ): Promise<TCNSStimulationTaskPersisted[]> {\n await this.ensure();\n const { p } = this;\n const res = await this.db.query(\n `SELECT * FROM ${p}task WHERE stimulation_attempt_id = $1 ORDER BY \"index\"`,\n [stimulationAttemptId]\n );\n return (res.rows as TTaskRow[]).map(rowToTask);\n }\n\n // ── lifecycle ──\n\n async delete(stimulationId: string): Promise<void> {\n await this.ensure();\n const { p } = this;\n await this.tx(async db => {\n // tasks + attempts have no FK (see migrate.ts) → delete them explicitly,\n // tasks first (they reference attempt ids), then the attempts.\n await db.query(\n `DELETE FROM ${p}task WHERE stimulation_attempt_id IN\n (SELECT stimulation_attempt_id FROM ${p}stimulation_attempt WHERE stimulation_id = $1)`,\n [stimulationId]\n );\n await db.query(\n `DELETE FROM ${p}stimulation_attempt WHERE stimulation_id = $1`,\n [stimulationId]\n );\n // FK ON DELETE CASCADE removes frontier/context.\n await db.query(\n `DELETE FROM ${p}stimulation WHERE stimulation_id = $1`,\n [stimulationId]\n );\n });\n }\n\n async close(): Promise<void> {\n if (this.ownsPool && this.pool) await this.pool.end();\n }\n\n // ── helpers ──\n\n private buildProgress(\n frontier: TFrontierRow[],\n context: TContextRow[]\n ): TCNSProgress {\n const tasks: TCNSSerializedTask[] = [...frontier]\n .sort((a, b) => a.ord - b.ord)\n .map(f => ({\n neuronName: f.neuron_name,\n dendriteCollateralName: f.dendrite_collateral_name,\n ...(f.input != null ? { input: f.input } : {}),\n }));\n const ctx: Record<string, unknown> = {};\n for (const c of context) ctx[c.neuron_name] = c.value;\n return { tasks, context: ctx };\n }\n}\n\n// ── row shapes (jsonb columns come back already parsed by node-pg) ──\ntype TStimulationRow = {\n stimulation_id: string;\n entry: TCNSSignalRef;\n status: TCNSStimulationStatus;\n scope_name: string | null;\n};\ntype TFrontierRow = {\n stimulation_id: string;\n ord: number;\n neuron_name: string;\n dendrite_collateral_name: string;\n input: { collateralName: string; payload?: unknown } | null;\n};\ntype TContextRow = {\n stimulation_id: string;\n neuron_name: string;\n value: unknown;\n};\ntype TAttemptRow = {\n stimulation_attempt_id: string;\n stimulation_id: string;\n attempt_number: number;\n status: TCNSStimulationStatus;\n started_at: string | number;\n completed_at: string | number | null;\n hop_count: number;\n has_error: boolean;\n replay_of: string | null;\n entry: TCNSSignalRef[];\n};\ntype TTaskRow = {\n stimulation_attempt_id: string;\n index: number;\n neuron_name: string;\n dendrite_collateral_name: string;\n input_index: number;\n output: TCNSSignalRef | null;\n status: TCNSStimulationTaskStatus;\n error: string | null;\n started_at: string | number;\n duration: number | null;\n};\n\nfunction rowToAttempt(a: TAttemptRow): TCNSStimulationAttemptPersisted {\n return {\n stimulationAttemptId: a.stimulation_attempt_id,\n stimulationId: a.stimulation_id,\n attemptNumber: num(a.attempt_number),\n status: a.status,\n startedAt: num(a.started_at),\n completedAt: numOrNull(a.completed_at),\n hopCount: num(a.hop_count),\n hasError: Boolean(a.has_error),\n replayOf: a.replay_of ?? null,\n entry: a.entry,\n };\n}\n\nfunction rowToTask(t: TTaskRow): TCNSStimulationTaskPersisted {\n return {\n stimulationAttemptId: t.stimulation_attempt_id,\n index: num(t.index),\n neuronName: t.neuron_name,\n dendriteCollateralName: t.dendrite_collateral_name,\n inputIndex: num(t.input_index),\n output: t.output ?? null,\n status: t.status,\n error: t.error ?? null,\n startedAt: num(t.started_at),\n duration: numOrNull(t.duration),\n };\n}\n\nfunction groupBy<T, K>(rows: T[], key: (r: T) => K): Map<K, T[]> {\n const m = new Map<K, T[]>();\n for (const r of rows) {\n const k = key(r);\n const b = m.get(k);\n if (b) b.push(r);\n else m.set(k, [r]);\n }\n return m;\n}\n"],"mappings":"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","names":["Pool","j","v","JSON","stringify","num","Number","numOrNull","CNSPostgresStimulationRepository","opts","db","pool","ownsPool","p","autoMigrate","ready","connectionString","Pool","queryable","Error","assertSafePrefix","tablePrefix","DEFAULT_PREFIX","ensure","Promise","resolve","ensureSchema","tx","fn","client","connect","query","out","e","release","saveStimulation","s","stimulationId","entry","status","scopeName","i","progress","tasks","length","t","neuronName","dendriteCollateralName","input","value","Object","entries","context","saveAttempt","a","stimulationAttemptId","attemptNumber","startedAt","completedAt","hopCount","hasError","replayOf","appendTask","index","inputIndex","output","error","duration","getStimulation","stimRes","row","rows","undefined","frontier","all","buildProgress","scope_name","listStimulations","filter","scope","stimsRes","stims","ids","map","r","stimulation_id","frontierRes","contextRes","frontierBy","groupBy","contextBy","_a","get","getAttempts","res","rowToAttempt","getTasks","rowToTask","delete","close","end","sort","b","ord","f","neuron_name","dendrite_collateral_name","ctx","c","stimulation_attempt_id","attempt_number","started_at","completed_at","hop_count","Boolean","has_error","replay_of","input_index","key","m","Map","k","push","set"]}
|
package/package.json
ADDED
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@cnstra/persist-postgres",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"description": "Postgres relational store for CNStra durable execution (ICNSStimulationRepository: run/attempt/task) — owns its schema + migrations",
|
|
5
|
+
"keywords": ["cnstra", "postgres", "durable", "run", "attempt", "task", "persist"],
|
|
6
|
+
"main": "dist/index.js",
|
|
7
|
+
"module": "dist/index.mjs",
|
|
8
|
+
"types": "dist/index.d.ts",
|
|
9
|
+
"exports": { ".": { "types": "./dist/index.d.ts", "import": "./dist/index.mjs", "require": "./dist/index.js" } },
|
|
10
|
+
"bin": { "cnstra-persist-postgres": "./dist/cli.js" },
|
|
11
|
+
"sideEffects": false,
|
|
12
|
+
"files": ["dist"],
|
|
13
|
+
"scripts": {
|
|
14
|
+
"build": "tsup",
|
|
15
|
+
"test": "jest",
|
|
16
|
+
"test:types": "tsc --noEmit",
|
|
17
|
+
"clean": "rm -rf dist"
|
|
18
|
+
},
|
|
19
|
+
"peerDependencies": {
|
|
20
|
+
"@cnstra/persist": "^0.1.0",
|
|
21
|
+
"@cnstra/persist-dto": "^0.1.0",
|
|
22
|
+
"pg": ">=8"
|
|
23
|
+
},
|
|
24
|
+
"peerDependenciesMeta": { "pg": { "optional": true } },
|
|
25
|
+
"devDependencies": {
|
|
26
|
+
"@cnstra/persist": "^0.1.0",
|
|
27
|
+
"@cnstra/persist-dto": "^0.1.0",
|
|
28
|
+
"@types/jest": "29.5.12",
|
|
29
|
+
"@types/node": "^20.0.0",
|
|
30
|
+
"@types/pg": "^8.11.0",
|
|
31
|
+
"jest": "29.7.0",
|
|
32
|
+
"pg": "^8.11.0",
|
|
33
|
+
"ts-jest": "29.1.2",
|
|
34
|
+
"tsup": "8.5.1",
|
|
35
|
+
"typescript": "5.7.2"
|
|
36
|
+
},
|
|
37
|
+
"author": "Andrei Baikov <abaikov23@gmail.com>",
|
|
38
|
+
"license": "MIT",
|
|
39
|
+
"publishConfig": { "access": "public" }
|
|
40
|
+
}
|