@cnstra/pg-boss 0.2.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 +2 -2
- package/dist/index.d.mts +21 -26
- package/dist/index.d.ts +21 -26
- package/dist/index.js +31 -77
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +32 -78
- package/dist/index.mjs.map +1 -1
- package/package.json +12 -9
package/README.md
CHANGED
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
> **⚠️ Experimental**: API may change in future versions.
|
|
4
4
|
|
|
5
5
|
[pg-boss](https://github.com/timgit/pg-boss) (Postgres-backed job queue) integration for
|
|
6
|
-
[CNStra](https://cnstra.org). Run each stimulation as a
|
|
6
|
+
[CNStra](https://cnstra.org). Run each stimulation as a resumable job, get Postgres-native
|
|
7
7
|
retry/backoff/scheduling/singleton, and stream **per-hop progress** into any store for a
|
|
8
8
|
live admin/history view.
|
|
9
9
|
|
|
@@ -102,7 +102,7 @@ await store.deleteStale(24 * 60 * 60 * 1000);
|
|
|
102
102
|
## What this package does / doesn't
|
|
103
103
|
|
|
104
104
|
- ✅ Job execution, retry/backoff, scheduling, singleton, concurrency → **pg-boss**.
|
|
105
|
-
- ✅
|
|
105
|
+
- ✅ Resumable frontier **resume** across retries → a pluggable `resume.repository` (in-package
|
|
106
106
|
Postgres, `@cnstra/progress-redis`, or your own); omit it for plain re-run-from-entry.
|
|
107
107
|
- ✅ Name-based run/attempt/task **observability** → an optional `observe` store (in-memory to
|
|
108
108
|
just see it, Postgres to keep it — the same model the DevTools render).
|
package/dist/index.d.mts
CHANGED
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
import { CNSPersistOptionsRegistry, ICNSStimulationRepository, ICNSProgressRepository, ICNSProgressRecordingStrategy } from '@cnstra/persist';
|
|
2
2
|
import { ICNS } from '@cnstra/types';
|
|
3
3
|
import { CNSCollateral } from '@cnstra/core';
|
|
4
|
+
import { TCNSStimulationJobData } from '@cnstra/broker';
|
|
5
|
+
export { ICNSResponseLike, TCNSStimulationJobData } from '@cnstra/broker';
|
|
4
6
|
|
|
5
7
|
/**
|
|
6
8
|
* Minimal, structural view of the pg-boss surface this package touches.
|
|
@@ -22,29 +24,6 @@ interface IPgBossLike {
|
|
|
22
24
|
work<TData extends object>(name: string, handler: (jobs: IPgBossJob<TData>[]) => Promise<unknown>): Promise<string>;
|
|
23
25
|
work<TData extends object>(name: string, options: Record<string, unknown>, handler: (jobs: IPgBossJob<TData>[]) => Promise<unknown>): Promise<string>;
|
|
24
26
|
}
|
|
25
|
-
/**
|
|
26
|
-
* The payload placed on the queue. It carries the collateral **name** and a
|
|
27
|
-
* JSON-serializable payload — never live neuron/collateral object references,
|
|
28
|
-
* which are not portable across processes.
|
|
29
|
-
*/
|
|
30
|
-
interface TCNSStimulationJobData {
|
|
31
|
-
collateralName: string;
|
|
32
|
-
payload: unknown;
|
|
33
|
-
/** Optional per-run cap on neuron hops. */
|
|
34
|
-
maxNeuronHops?: number;
|
|
35
|
-
}
|
|
36
|
-
/** Structural view of the response passed to `onResponse` (not exported by core). */
|
|
37
|
-
interface ICNSResponseLike {
|
|
38
|
-
inputSignal?: {
|
|
39
|
-
collateral?: unknown;
|
|
40
|
-
payload?: unknown;
|
|
41
|
-
};
|
|
42
|
-
outputSignal?: {
|
|
43
|
-
collateral?: unknown;
|
|
44
|
-
payload?: unknown;
|
|
45
|
-
};
|
|
46
|
-
error?: unknown;
|
|
47
|
-
}
|
|
48
27
|
|
|
49
28
|
interface TCNSWorkerOptions {
|
|
50
29
|
/** Your pg-boss instance (already started). */
|
|
@@ -66,7 +45,7 @@ interface TCNSWorkerOptions {
|
|
|
66
45
|
*/
|
|
67
46
|
observe?: ICNSStimulationRepository;
|
|
68
47
|
/**
|
|
69
|
-
* Optional
|
|
48
|
+
* Optional resumable **resume**. When set, the worker records the run's
|
|
70
49
|
* outstanding frontier (by name) into `repository` as it advances, and a
|
|
71
50
|
* retried job (same job id) resumes from that frontier via `cns.activate(...)`
|
|
72
51
|
* instead of re-running from the entry signal. The record's lifecycle matches
|
|
@@ -94,7 +73,23 @@ interface TCNSWorkerOptions {
|
|
|
94
73
|
* `waitUntilComplete()` rejects and the error propagates so pg-boss applies its
|
|
95
74
|
* own retry/backoff policy.
|
|
96
75
|
*/
|
|
97
|
-
|
|
76
|
+
/** A registered worker + a graceful `shutdown` (see {@link createCNSWorker}). */
|
|
77
|
+
interface TCNSPgBossWorkerHandle {
|
|
78
|
+
/** The pg-boss `work()` registration id. */
|
|
79
|
+
workId: string;
|
|
80
|
+
/**
|
|
81
|
+
* Graceful shutdown of THIS worker's processing: stop taking new jobs, let
|
|
82
|
+
* in-flight stimulations finish for up to `drainMs` (default 10s), then **abort**
|
|
83
|
+
* whatever is still running — an aborted stimulation's remaining tasks become its
|
|
84
|
+
* frontier, which the resume checkpoint records, so the job's retry resumes there.
|
|
85
|
+
* It does NOT stop or close your `boss` (you own it) or your stores — call
|
|
86
|
+
* `boss.stop(...)` and close your stores after this resolves.
|
|
87
|
+
*/
|
|
88
|
+
shutdown(opts?: {
|
|
89
|
+
drainMs?: number;
|
|
90
|
+
}): Promise<void>;
|
|
91
|
+
}
|
|
92
|
+
declare function createCNSWorker(options: TCNSWorkerOptions): Promise<TCNSPgBossWorkerHandle>;
|
|
98
93
|
|
|
99
94
|
/** Commonly used pg-boss send options (a superset is passed through). */
|
|
100
95
|
interface TCNSSendOptions {
|
|
@@ -116,4 +111,4 @@ declare function stimulationJob(registry: CNSPersistOptionsRegistry, collateral:
|
|
|
116
111
|
/** Send a stimulation job to a pg-boss queue. Returns the job id. */
|
|
117
112
|
declare function enqueueStimulation(boss: IPgBossLike, queue: string, job: TCNSStimulationJobData, options?: TCNSSendOptions): Promise<string | null>;
|
|
118
113
|
|
|
119
|
-
export { type
|
|
114
|
+
export { type IPgBossJob, type IPgBossLike, type TCNSPgBossWorkerHandle, type TCNSSendOptions, type TCNSWorkerOptions, createCNSWorker, enqueueStimulation, stimulationJob };
|
package/dist/index.d.ts
CHANGED
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
import { CNSPersistOptionsRegistry, ICNSStimulationRepository, ICNSProgressRepository, ICNSProgressRecordingStrategy } from '@cnstra/persist';
|
|
2
2
|
import { ICNS } from '@cnstra/types';
|
|
3
3
|
import { CNSCollateral } from '@cnstra/core';
|
|
4
|
+
import { TCNSStimulationJobData } from '@cnstra/broker';
|
|
5
|
+
export { ICNSResponseLike, TCNSStimulationJobData } from '@cnstra/broker';
|
|
4
6
|
|
|
5
7
|
/**
|
|
6
8
|
* Minimal, structural view of the pg-boss surface this package touches.
|
|
@@ -22,29 +24,6 @@ interface IPgBossLike {
|
|
|
22
24
|
work<TData extends object>(name: string, handler: (jobs: IPgBossJob<TData>[]) => Promise<unknown>): Promise<string>;
|
|
23
25
|
work<TData extends object>(name: string, options: Record<string, unknown>, handler: (jobs: IPgBossJob<TData>[]) => Promise<unknown>): Promise<string>;
|
|
24
26
|
}
|
|
25
|
-
/**
|
|
26
|
-
* The payload placed on the queue. It carries the collateral **name** and a
|
|
27
|
-
* JSON-serializable payload — never live neuron/collateral object references,
|
|
28
|
-
* which are not portable across processes.
|
|
29
|
-
*/
|
|
30
|
-
interface TCNSStimulationJobData {
|
|
31
|
-
collateralName: string;
|
|
32
|
-
payload: unknown;
|
|
33
|
-
/** Optional per-run cap on neuron hops. */
|
|
34
|
-
maxNeuronHops?: number;
|
|
35
|
-
}
|
|
36
|
-
/** Structural view of the response passed to `onResponse` (not exported by core). */
|
|
37
|
-
interface ICNSResponseLike {
|
|
38
|
-
inputSignal?: {
|
|
39
|
-
collateral?: unknown;
|
|
40
|
-
payload?: unknown;
|
|
41
|
-
};
|
|
42
|
-
outputSignal?: {
|
|
43
|
-
collateral?: unknown;
|
|
44
|
-
payload?: unknown;
|
|
45
|
-
};
|
|
46
|
-
error?: unknown;
|
|
47
|
-
}
|
|
48
27
|
|
|
49
28
|
interface TCNSWorkerOptions {
|
|
50
29
|
/** Your pg-boss instance (already started). */
|
|
@@ -66,7 +45,7 @@ interface TCNSWorkerOptions {
|
|
|
66
45
|
*/
|
|
67
46
|
observe?: ICNSStimulationRepository;
|
|
68
47
|
/**
|
|
69
|
-
* Optional
|
|
48
|
+
* Optional resumable **resume**. When set, the worker records the run's
|
|
70
49
|
* outstanding frontier (by name) into `repository` as it advances, and a
|
|
71
50
|
* retried job (same job id) resumes from that frontier via `cns.activate(...)`
|
|
72
51
|
* instead of re-running from the entry signal. The record's lifecycle matches
|
|
@@ -94,7 +73,23 @@ interface TCNSWorkerOptions {
|
|
|
94
73
|
* `waitUntilComplete()` rejects and the error propagates so pg-boss applies its
|
|
95
74
|
* own retry/backoff policy.
|
|
96
75
|
*/
|
|
97
|
-
|
|
76
|
+
/** A registered worker + a graceful `shutdown` (see {@link createCNSWorker}). */
|
|
77
|
+
interface TCNSPgBossWorkerHandle {
|
|
78
|
+
/** The pg-boss `work()` registration id. */
|
|
79
|
+
workId: string;
|
|
80
|
+
/**
|
|
81
|
+
* Graceful shutdown of THIS worker's processing: stop taking new jobs, let
|
|
82
|
+
* in-flight stimulations finish for up to `drainMs` (default 10s), then **abort**
|
|
83
|
+
* whatever is still running — an aborted stimulation's remaining tasks become its
|
|
84
|
+
* frontier, which the resume checkpoint records, so the job's retry resumes there.
|
|
85
|
+
* It does NOT stop or close your `boss` (you own it) or your stores — call
|
|
86
|
+
* `boss.stop(...)` and close your stores after this resolves.
|
|
87
|
+
*/
|
|
88
|
+
shutdown(opts?: {
|
|
89
|
+
drainMs?: number;
|
|
90
|
+
}): Promise<void>;
|
|
91
|
+
}
|
|
92
|
+
declare function createCNSWorker(options: TCNSWorkerOptions): Promise<TCNSPgBossWorkerHandle>;
|
|
98
93
|
|
|
99
94
|
/** Commonly used pg-boss send options (a superset is passed through). */
|
|
100
95
|
interface TCNSSendOptions {
|
|
@@ -116,4 +111,4 @@ declare function stimulationJob(registry: CNSPersistOptionsRegistry, collateral:
|
|
|
116
111
|
/** Send a stimulation job to a pg-boss queue. Returns the job id. */
|
|
117
112
|
declare function enqueueStimulation(boss: IPgBossLike, queue: string, job: TCNSStimulationJobData, options?: TCNSSendOptions): Promise<string | null>;
|
|
118
113
|
|
|
119
|
-
export { type
|
|
114
|
+
export { type IPgBossJob, type IPgBossLike, type TCNSPgBossWorkerHandle, type TCNSSendOptions, type TCNSWorkerOptions, createCNSWorker, enqueueStimulation, stimulationJob };
|
package/dist/index.js
CHANGED
|
@@ -28,99 +28,53 @@ module.exports = __toCommonJS(index_exports);
|
|
|
28
28
|
|
|
29
29
|
// src/worker.ts
|
|
30
30
|
var import_persist = require("@cnstra/persist");
|
|
31
|
-
|
|
31
|
+
var import_broker = require("@cnstra/broker");
|
|
32
|
+
async function createCNSWorker(options) {
|
|
32
33
|
const { boss, cns, registry, queue, observe, resume, workOptions } = options;
|
|
33
34
|
const serializer = resume ? new import_persist.CNSProgressSerializer(registry) : void 0;
|
|
35
|
+
const drain = (0, import_broker.createDrainState)();
|
|
34
36
|
const runJob = /* @__PURE__ */ __name(async (job) => {
|
|
35
37
|
var _a;
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
}
|
|
41
|
-
const recorder = resume && serializer ? new import_persist.CNSProgressRecorder({
|
|
42
|
-
serializer,
|
|
43
|
-
repository: resume.repository,
|
|
44
|
-
key: job.id,
|
|
45
|
-
strategy: resume.strategy
|
|
46
|
-
}) : void 0;
|
|
47
|
-
const attemptNumber = ((_a = job.retryCount) != null ? _a : 0) + 1;
|
|
48
|
-
const persistor = observe ? new import_persist.CNSStimulationPersistor({
|
|
49
|
-
repository: observe,
|
|
38
|
+
await (0, import_broker.runStimulationJob)({
|
|
39
|
+
pkgName: "@cnstra/pg-boss",
|
|
40
|
+
stopping: drain.isStopping(),
|
|
41
|
+
cns,
|
|
50
42
|
registry,
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
recorder == null ? void 0 : recorder.onResponse(r);
|
|
65
|
-
persistor == null ? void 0 : persistor.onResponse(r);
|
|
66
|
-
}, "onResponse");
|
|
67
|
-
const stimulationContext = {
|
|
68
|
-
stimulationId: job.id,
|
|
69
|
-
attemptNumber
|
|
70
|
-
};
|
|
71
|
-
let stimulation;
|
|
72
|
-
if (resume && serializer) {
|
|
73
|
-
const saved = await resume.repository.load(job.id);
|
|
74
|
-
if (saved) {
|
|
75
|
-
const { tasks, ctx } = serializer.hydrate(saved);
|
|
76
|
-
stimulation = cns.activate(tasks, {
|
|
77
|
-
maxNeuronHops,
|
|
78
|
-
ctx,
|
|
79
|
-
onResponse,
|
|
80
|
-
stimulationContext
|
|
81
|
-
});
|
|
82
|
-
}
|
|
83
|
-
}
|
|
84
|
-
if (!stimulation) {
|
|
85
|
-
stimulation = cns.stimulate(collateral.createSignal(payload), {
|
|
86
|
-
maxNeuronHops,
|
|
87
|
-
onResponse,
|
|
88
|
-
stimulationContext
|
|
89
|
-
});
|
|
90
|
-
}
|
|
91
|
-
try {
|
|
92
|
-
await stimulation.waitUntilComplete();
|
|
93
|
-
if (resume) await resume.repository.delete(job.id);
|
|
94
|
-
} catch (err) {
|
|
95
|
-
if (resume && serializer) {
|
|
96
|
-
await resume.repository.save(job.id, serializer.serialize(stimulation));
|
|
97
|
-
}
|
|
98
|
-
throw err;
|
|
99
|
-
} finally {
|
|
100
|
-
recorder == null ? void 0 : recorder.dispose();
|
|
101
|
-
persistor == null ? void 0 : persistor.dispose();
|
|
102
|
-
}
|
|
43
|
+
jobId: job.id,
|
|
44
|
+
collateralName: job.data.collateralName,
|
|
45
|
+
payload: job.data.payload,
|
|
46
|
+
maxNeuronHops: job.data.maxNeuronHops,
|
|
47
|
+
// Each pg-boss retry is a new attempt (retryCount is 0-based).
|
|
48
|
+
attemptNumber: ((_a = job.retryCount) != null ? _a : 0) + 1,
|
|
49
|
+
observe,
|
|
50
|
+
serializer,
|
|
51
|
+
resumeRepo: resume == null ? void 0 : resume.repository,
|
|
52
|
+
resumeStrategy: resume == null ? void 0 : resume.strategy,
|
|
53
|
+
abort: drain.abort,
|
|
54
|
+
active: drain.active
|
|
55
|
+
});
|
|
103
56
|
}, "runJob");
|
|
104
57
|
const handler = /* @__PURE__ */ __name(async (jobs) => {
|
|
105
58
|
for (const job of jobs) {
|
|
106
59
|
await runJob(job);
|
|
107
60
|
}
|
|
108
61
|
}, "handler");
|
|
109
|
-
|
|
62
|
+
const workId = await (workOptions ? boss.work(queue, workOptions, handler) : boss.work(queue, handler));
|
|
63
|
+
const shutdown = /* @__PURE__ */ __name(async (opts) => {
|
|
64
|
+
var _a;
|
|
65
|
+
await drain.drainAndAbort((_a = opts == null ? void 0 : opts.drainMs) != null ? _a : 1e4);
|
|
66
|
+
}, "shutdown");
|
|
67
|
+
return {
|
|
68
|
+
workId,
|
|
69
|
+
shutdown
|
|
70
|
+
};
|
|
110
71
|
}
|
|
111
72
|
__name(createCNSWorker, "createCNSWorker");
|
|
112
73
|
|
|
113
74
|
// src/enqueue.ts
|
|
75
|
+
var import_broker2 = require("@cnstra/broker");
|
|
114
76
|
function stimulationJob(registry, collateral, payload, extra) {
|
|
115
|
-
|
|
116
|
-
if (!collateralName) {
|
|
117
|
-
throw new Error("[@cnstra/pg-boss] Collateral is not registered in the CNSPersistOptionsRegistry; cannot enqueue it by name.");
|
|
118
|
-
}
|
|
119
|
-
return {
|
|
120
|
-
collateralName,
|
|
121
|
-
payload,
|
|
122
|
-
...extra
|
|
123
|
-
};
|
|
77
|
+
return (0, import_broker2.stimulationJob)(registry, collateral, payload, extra, "@cnstra/pg-boss");
|
|
124
78
|
}
|
|
125
79
|
__name(stimulationJob, "stimulationJob");
|
|
126
80
|
function enqueueStimulation(boss, queue, job, options) {
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/index.ts","../src/worker.ts","../src/enqueue.ts"],"sourcesContent":["export { createCNSWorker } from './worker';\nexport type { TCNSWorkerOptions } from './worker';\n\nexport { enqueueStimulation, stimulationJob } from './enqueue';\nexport type { TCNSSendOptions } from './enqueue';\n\nexport type {\n IPgBossLike,\n IPgBossJob,\n TCNSStimulationJobData,\n ICNSResponseLike,\n} from './types';\n","import {\n CNSProgressRecorder,\n CNSProgressSerializer,\n CNSStimulationPersistor,\n} from '@cnstra/persist';\nimport type {\n CNSPersistOptionsRegistry,\n ICNSProgressRepository,\n ICNSProgressRecordingStrategy,\n ICNSStimulationRepository,\n} from '@cnstra/persist';\nimport type { ICNS, TCNSStimulationResponse } from '@cnstra/types';\nimport type {\n IPgBossLike,\n IPgBossJob,\n ICNSResponseLike,\n TCNSStimulationJobData,\n} from './types';\n\nexport interface TCNSWorkerOptions {\n /** Your pg-boss instance (already started). */\n boss: IPgBossLike;\n /** The CNS the jobs run against. */\n cns: ICNS<any, any>;\n /** Name ↔ object registry used to reconstruct signals from job data. */\n registry: CNSPersistOptionsRegistry;\n /** Queue name to consume. */\n queue: string;\n /**\n * Optional **observability** store: the canonical name-based\n * Stimulation → Attempt → Task history (the SAME model the devtools use),\n * written per hop via a {@link CNSStimulationPersistor}. Pass a\n * `CNSInMemoryStimulationRepository` to just *see* progress (ephemeral), or a\n * Postgres store to keep it. It records history; it does not drive resume —\n * that is `resume` below. One job = one stimulation (`stimulationId` = job id),\n * each pg-boss retry = a new attempt.\n */\n observe?: ICNSStimulationRepository;\n /**\n * Optional durable **resume**. When set, the worker records the run's\n * outstanding frontier (by name) into `repository` as it advances, and a\n * retried job (same job id) resumes from that frontier via `cns.activate(...)`\n * instead of re-running from the entry signal. The record's lifecycle matches\n * the pg-boss job row: dropped on success, kept on failure for the retry.\n *\n * Requires every neuron/collateral that can appear in the frontier to be in\n * the `registry` (not just the entry collaterals). Neurons should still be\n * idempotent — resume narrows re-execution to the unfinished branch, it does\n * not make individual hops exactly-once.\n */\n resume?: {\n repository: ICNSProgressRepository;\n /** When to persist during the run. Defaults to debounce + max-staleness. */\n strategy?: ICNSProgressRecordingStrategy;\n };\n /** pg-boss `work` options (batchSize, pollingIntervalSeconds, …). */\n workOptions?: Record<string, unknown>;\n}\n\n/**\n * Register a pg-boss worker that turns each job into a CNStra stimulation.\n *\n * One job = one stimulation. The job carries `{ collateralName, payload }`; the\n * worker reconstructs the entry signal by name, stimulates, optionally streams\n * per-hop progress, and awaits completion. If the stimulation fails or aborts,\n * `waitUntilComplete()` rejects and the error propagates so pg-boss applies its\n * own retry/backoff policy.\n */\nexport function createCNSWorker(options: TCNSWorkerOptions): Promise<string> {\n const { boss, cns, registry, queue, observe, resume, workOptions } =\n options;\n\n // One serializer per worker; the recorder is per-job (holds job-specific key).\n const serializer = resume\n ? new CNSProgressSerializer(registry)\n : undefined;\n\n const runJob = async (\n job: IPgBossJob<TCNSStimulationJobData>\n ): Promise<void> => {\n const { collateralName, payload, maxNeuronHops } = job.data;\n const collateral = registry.getCollateral(collateralName);\n if (!collateral) {\n throw new Error(\n `[@cnstra/pg-boss] Unknown collateral \"${collateralName}\". ` +\n `Register it in the CNSPersistOptionsRegistry.`\n );\n }\n\n // Records the outstanding frontier as the run advances (crash-safety); the\n // explicit save on failure below guarantees the exact final frontier.\n const recorder =\n resume && serializer\n ? new CNSProgressRecorder({\n serializer,\n repository: resume.repository,\n key: job.id,\n strategy: resume.strategy,\n })\n : undefined;\n\n // Observability: stream the canonical name-based Stimulation → Attempt →\n // Task model to `observe`. One job = one stimulation; each pg-boss retry is\n // a new attempt (retryCount is 0-based).\n const attemptNumber = (job.retryCount ?? 0) + 1;\n const persistor = observe\n ? new CNSStimulationPersistor({\n repository: observe,\n registry,\n stimulationId: job.id,\n stimulationAttemptId: `${job.id}#${attemptNumber}`,\n attemptNumber,\n entry: [{ collateralName, payload }],\n volume: 'full',\n })\n : undefined;\n\n const onResponse = (resp: ICNSResponseLike) => {\n const r = resp as unknown as TCNSStimulationResponse;\n // recorder → resume checkpoint; persistor → observability history.\n recorder?.onResponse(r);\n persistor?.onResponse(r);\n };\n\n // Correlate logs to this run: every handler reads `ctx.stimulationContext`\n // (same object on every hop, sync or async) — no wiring on the user's side.\n const stimulationContext = { stimulationId: job.id, attemptNumber };\n\n // Resume from a persisted frontier if this job already has one (a retry of\n // the same job id); otherwise start fresh from the entry signal.\n let stimulation;\n if (resume && serializer) {\n const saved = await resume.repository.load(job.id);\n if (saved) {\n const { tasks, ctx } = serializer.hydrate(saved);\n stimulation = cns.activate(tasks, {\n maxNeuronHops,\n ctx,\n onResponse,\n stimulationContext,\n });\n }\n }\n if (!stimulation) {\n stimulation = cns.stimulate(collateral.createSignal(payload), {\n maxNeuronHops,\n onResponse,\n stimulationContext,\n });\n }\n\n try {\n await stimulation.waitUntilComplete();\n // Job about to complete → its resume checkpoint can go (lifecycle = job row).\n if (resume) await resume.repository.delete(job.id);\n } catch (err) {\n // Persist the exact final frontier before throwing so the retried job\n // resumes from it (the recorder's cadence may still be debouncing).\n if (resume && serializer) {\n await resume.repository.save(\n job.id,\n serializer.serialize(stimulation)\n );\n }\n // Let pg-boss decide about retries/backoff.\n throw err;\n } finally {\n recorder?.dispose();\n persistor?.dispose();\n }\n };\n\n const handler = async (\n jobs: IPgBossJob<TCNSStimulationJobData>[]\n ): Promise<void> => {\n for (const job of jobs) {\n await runJob(job);\n }\n };\n\n return workOptions\n ? boss.work<TCNSStimulationJobData>(queue, workOptions, handler)\n : boss.work<TCNSStimulationJobData>(queue, handler);\n}\n","import type { CNSCollateral } from '@cnstra/core';\nimport type { CNSPersistOptionsRegistry } from '@cnstra/persist';\nimport type { IPgBossLike, TCNSStimulationJobData } from './types';\n\n/** Commonly used pg-boss send options (a superset is passed through). */\nexport interface TCNSSendOptions {\n retryLimit?: number;\n retryBackoff?: boolean;\n retryDelay?: number;\n /** One in-flight job per key — e.g. one stimulation per entity id. */\n singletonKey?: string;\n startAfter?: number | string | Date;\n expireInSeconds?: number;\n priority?: number;\n [key: string]: unknown;\n}\n\n/**\n * Build a queue job from a **registered** collateral. Throws if the collateral\n * is not in the registry, so a typo can't reach the queue.\n */\nexport function stimulationJob(\n registry: CNSPersistOptionsRegistry,\n collateral: CNSCollateral<unknown>,\n payload: unknown,\n extra?: Pick<TCNSStimulationJobData, 'maxNeuronHops'>\n): TCNSStimulationJobData {\n const collateralName = registry.getCollateralName(collateral);\n if (!collateralName) {\n throw new Error(\n '[@cnstra/pg-boss] Collateral is not registered in the ' +\n 'CNSPersistOptionsRegistry; cannot enqueue it by name.'\n );\n }\n return { collateralName, payload, ...extra };\n}\n\n/** Send a stimulation job to a pg-boss queue. Returns the job id. */\nexport function enqueueStimulation(\n boss: IPgBossLike,\n queue: string,\n job: TCNSStimulationJobData,\n options?: TCNSSendOptions\n): Promise<string | null> {\n return boss.send(queue, job as unknown as object, options);\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;AAAA;;;;;;;;;ACAA,qBAIO;AAgEA,SAASA,gBAAgBC,SAA0B;AACtD,QAAM,EAAEC,MAAMC,KAAKC,UAAUC,OAAOC,SAASC,QAAQC,YAAW,IAC5DP;AAGJ,QAAMQ,aAAaF,SACb,IAAIG,qCAAsBN,QAAAA,IAC1BO;AAEN,QAAMC,SAAS,8BACXC,QAAAA;AA9ER;AAgFQ,UAAM,EAAEC,gBAAgBC,SAASC,cAAa,IAAKH,IAAII;AACvD,UAAMC,aAAad,SAASe,cAAcL,cAAAA;AAC1C,QAAI,CAACI,YAAY;AACb,YAAM,IAAIE,MACN,yCAAyCN,cAAAA,kDACU;IAE3D;AAIA,UAAMO,WACFd,UAAUE,aACJ,IAAIa,mCAAoB;MACpBb;MACAc,YAAYhB,OAAOgB;MACnBC,KAAKX,IAAIY;MACTC,UAAUnB,OAAOmB;IACrB,CAAA,IACAf;AAKV,UAAMgB,kBAAiBd,SAAIe,eAAJf,YAAkB,KAAK;AAC9C,UAAMgB,YAAYvB,UACZ,IAAIwB,uCAAwB;MACxBP,YAAYjB;MACZF;MACA2B,eAAelB,IAAIY;MACnBO,sBAAsB,GAAGnB,IAAIY,EAAE,IAAIE,aAAAA;MACnCA;MACAM,OAAO;QAAC;UAAEnB;UAAgBC;QAAQ;;MAClCmB,QAAQ;IACZ,CAAA,IACAvB;AAEN,UAAMwB,aAAa,wBAACC,SAAAA;AAChB,YAAMC,IAAID;AAEVf,2CAAUc,WAAWE;AACrBR,6CAAWM,WAAWE;IAC1B,GALmB;AASnB,UAAMC,qBAAqB;MAAEP,eAAelB,IAAIY;MAAIE;IAAc;AAIlE,QAAIY;AACJ,QAAIhC,UAAUE,YAAY;AACtB,YAAM+B,QAAQ,MAAMjC,OAAOgB,WAAWkB,KAAK5B,IAAIY,EAAE;AACjD,UAAIe,OAAO;AACP,cAAM,EAAEE,OAAOC,IAAG,IAAKlC,WAAWmC,QAAQJ,KAAAA;AAC1CD,sBAAcpC,IAAI0C,SAASH,OAAO;UAC9B1B;UACA2B;UACAR;UACAG;QACJ,CAAA;MACJ;IACJ;AACA,QAAI,CAACC,aAAa;AACdA,oBAAcpC,IAAI2C,UAAU5B,WAAW6B,aAAahC,OAAAA,GAAU;QAC1DC;QACAmB;QACAG;MACJ,CAAA;IACJ;AAEA,QAAI;AACA,YAAMC,YAAYS,kBAAiB;AAEnC,UAAIzC,OAAQ,OAAMA,OAAOgB,WAAW0B,OAAOpC,IAAIY,EAAE;IACrD,SAASyB,KAAK;AAGV,UAAI3C,UAAUE,YAAY;AACtB,cAAMF,OAAOgB,WAAW4B,KACpBtC,IAAIY,IACJhB,WAAW2C,UAAUb,WAAAA,CAAAA;MAE7B;AAEA,YAAMW;IACV,UAAA;AACI7B,2CAAUgC;AACVxB,6CAAWwB;IACf;EACJ,GA7Fe;AA+Ff,QAAMC,UAAU,8BACZC,SAAAA;AAEA,eAAW1C,OAAO0C,MAAM;AACpB,YAAM3C,OAAOC,GAAAA;IACjB;EACJ,GANgB;AAQhB,SAAOL,cACDN,KAAKsD,KAA6BnD,OAAOG,aAAa8C,OAAAA,IACtDpD,KAAKsD,KAA6BnD,OAAOiD,OAAAA;AACnD;AAnHgBtD;;;AC/CT,SAASyD,eACZC,UACAC,YACAC,SACAC,OAAqD;AAErD,QAAMC,iBAAiBJ,SAASK,kBAAkBJ,UAAAA;AAClD,MAAI,CAACG,gBAAgB;AACjB,UAAM,IAAIE,MACN,6GACI;EAEZ;AACA,SAAO;IAAEF;IAAgBF;IAAS,GAAGC;EAAM;AAC/C;AAdgBJ;AAiBT,SAASQ,mBACZC,MACAC,OACAC,KACAC,SAAyB;AAEzB,SAAOH,KAAKI,KAAKH,OAAOC,KAA0BC,OAAAA;AACtD;AAPgBJ;","names":["createCNSWorker","options","boss","cns","registry","queue","observe","resume","workOptions","serializer","CNSProgressSerializer","undefined","runJob","job","collateralName","payload","maxNeuronHops","data","collateral","getCollateral","Error","recorder","CNSProgressRecorder","repository","key","id","strategy","attemptNumber","retryCount","persistor","CNSStimulationPersistor","stimulationId","stimulationAttemptId","entry","volume","onResponse","resp","r","stimulationContext","stimulation","saved","load","tasks","ctx","hydrate","activate","stimulate","createSignal","waitUntilComplete","delete","err","save","serialize","dispose","handler","jobs","work","stimulationJob","registry","collateral","payload","extra","collateralName","getCollateralName","Error","enqueueStimulation","boss","queue","job","options","send"]}
|
|
1
|
+
{"version":3,"sources":["../src/index.ts","../src/worker.ts","../src/enqueue.ts"],"sourcesContent":["export { createCNSWorker } from './worker';\nexport type { TCNSWorkerOptions, TCNSPgBossWorkerHandle } from './worker';\n\nexport { enqueueStimulation, stimulationJob } from './enqueue';\nexport type { TCNSSendOptions } from './enqueue';\n\nexport type {\n IPgBossLike,\n IPgBossJob,\n TCNSStimulationJobData,\n ICNSResponseLike,\n} from './types';\n","import { CNSProgressSerializer } from '@cnstra/persist';\nimport type {\n CNSPersistOptionsRegistry,\n ICNSProgressRepository,\n ICNSProgressRecordingStrategy,\n ICNSStimulationRepository,\n} from '@cnstra/persist';\nimport type { ICNS } from '@cnstra/types';\nimport { runStimulationJob, createDrainState } from '@cnstra/broker';\nimport type {\n IPgBossLike,\n IPgBossJob,\n TCNSStimulationJobData,\n} from './types';\n\nexport interface TCNSWorkerOptions {\n /** Your pg-boss instance (already started). */\n boss: IPgBossLike;\n /** The CNS the jobs run against. */\n cns: ICNS<any, any>;\n /** Name ↔ object registry used to reconstruct signals from job data. */\n registry: CNSPersistOptionsRegistry;\n /** Queue name to consume. */\n queue: string;\n /**\n * Optional **observability** store: the canonical name-based\n * Stimulation → Attempt → Task history (the SAME model the devtools use),\n * written per hop via a {@link CNSStimulationPersistor}. Pass a\n * `CNSInMemoryStimulationRepository` to just *see* progress (ephemeral), or a\n * Postgres store to keep it. It records history; it does not drive resume —\n * that is `resume` below. One job = one stimulation (`stimulationId` = job id),\n * each pg-boss retry = a new attempt.\n */\n observe?: ICNSStimulationRepository;\n /**\n * Optional resumable **resume**. When set, the worker records the run's\n * outstanding frontier (by name) into `repository` as it advances, and a\n * retried job (same job id) resumes from that frontier via `cns.activate(...)`\n * instead of re-running from the entry signal. The record's lifecycle matches\n * the pg-boss job row: dropped on success, kept on failure for the retry.\n *\n * Requires every neuron/collateral that can appear in the frontier to be in\n * the `registry` (not just the entry collaterals). Neurons should still be\n * idempotent — resume narrows re-execution to the unfinished branch, it does\n * not make individual hops exactly-once.\n */\n resume?: {\n repository: ICNSProgressRepository;\n /** When to persist during the run. Defaults to debounce + max-staleness. */\n strategy?: ICNSProgressRecordingStrategy;\n };\n /** pg-boss `work` options (batchSize, pollingIntervalSeconds, …). */\n workOptions?: Record<string, unknown>;\n}\n\n/**\n * Register a pg-boss worker that turns each job into a CNStra stimulation.\n *\n * One job = one stimulation. The job carries `{ collateralName, payload }`; the\n * worker reconstructs the entry signal by name, stimulates, optionally streams\n * per-hop progress, and awaits completion. If the stimulation fails or aborts,\n * `waitUntilComplete()` rejects and the error propagates so pg-boss applies its\n * own retry/backoff policy.\n */\n/** A registered worker + a graceful `shutdown` (see {@link createCNSWorker}). */\nexport interface TCNSPgBossWorkerHandle {\n /** The pg-boss `work()` registration id. */\n workId: string;\n /**\n * Graceful shutdown of THIS worker's processing: stop taking new jobs, let\n * in-flight stimulations finish for up to `drainMs` (default 10s), then **abort**\n * whatever is still running — an aborted stimulation's remaining tasks become its\n * frontier, which the resume checkpoint records, so the job's retry resumes there.\n * It does NOT stop or close your `boss` (you own it) or your stores — call\n * `boss.stop(...)` and close your stores after this resolves.\n */\n shutdown(opts?: { drainMs?: number }): Promise<void>;\n}\n\nexport async function createCNSWorker(\n options: TCNSWorkerOptions\n): Promise<TCNSPgBossWorkerHandle> {\n const { boss, cns, registry, queue, observe, resume, workOptions } =\n options;\n\n // One serializer per worker; the per-job pipeline lives in @cnstra/broker.\n const serializer = resume ? new CNSProgressSerializer(registry) : undefined;\n const drain = createDrainState();\n\n const runJob = async (\n job: IPgBossJob<TCNSStimulationJobData>\n ): Promise<void> => {\n await runStimulationJob({\n pkgName: '@cnstra/pg-boss',\n stopping: drain.isStopping(),\n cns,\n registry,\n jobId: job.id,\n collateralName: job.data.collateralName,\n payload: job.data.payload,\n maxNeuronHops: job.data.maxNeuronHops,\n // Each pg-boss retry is a new attempt (retryCount is 0-based).\n attemptNumber: (job.retryCount ?? 0) + 1,\n observe,\n serializer,\n resumeRepo: resume?.repository,\n resumeStrategy: resume?.strategy,\n abort: drain.abort,\n active: drain.active,\n });\n };\n\n const handler = async (\n jobs: IPgBossJob<TCNSStimulationJobData>[]\n ): Promise<void> => {\n for (const job of jobs) {\n await runJob(job);\n }\n };\n\n const workId = await (workOptions\n ? boss.work<TCNSStimulationJobData>(queue, workOptions, handler)\n : boss.work<TCNSStimulationJobData>(queue, handler));\n\n const shutdown = async (opts?: { drainMs?: number }): Promise<void> => {\n await drain.drainAndAbort(opts?.drainMs ?? 10_000);\n };\n\n return { workId, shutdown };\n}\n","import type { CNSCollateral } from '@cnstra/core';\nimport type { CNSPersistOptionsRegistry } from '@cnstra/persist';\nimport { stimulationJob as brokerStimulationJob } from '@cnstra/broker';\nimport type { IPgBossLike, TCNSStimulationJobData } from './types';\n\n/** Commonly used pg-boss send options (a superset is passed through). */\nexport interface TCNSSendOptions {\n retryLimit?: number;\n retryBackoff?: boolean;\n retryDelay?: number;\n /** One in-flight job per key — e.g. one stimulation per entity id. */\n singletonKey?: string;\n startAfter?: number | string | Date;\n expireInSeconds?: number;\n priority?: number;\n [key: string]: unknown;\n}\n\n/**\n * Build a queue job from a **registered** collateral. Throws if the collateral\n * is not in the registry, so a typo can't reach the queue.\n */\nexport function stimulationJob(\n registry: CNSPersistOptionsRegistry,\n collateral: CNSCollateral<unknown>,\n payload: unknown,\n extra?: Pick<TCNSStimulationJobData, 'maxNeuronHops'>\n): TCNSStimulationJobData {\n return brokerStimulationJob(registry, collateral, payload, extra, '@cnstra/pg-boss');\n}\n\n/** Send a stimulation job to a pg-boss queue. Returns the job id. */\nexport function enqueueStimulation(\n boss: IPgBossLike,\n queue: string,\n job: TCNSStimulationJobData,\n options?: TCNSSendOptions\n): Promise<string | null> {\n return boss.send(queue, job as unknown as object, options);\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;AAAA;;;;;;;;;ACAA,qBAAsC;AAQtC,oBAAoD;AAuEpD,eAAsBA,gBAClBC,SAA0B;AAE1B,QAAM,EAAEC,MAAMC,KAAKC,UAAUC,OAAOC,SAASC,QAAQC,YAAW,IAC5DP;AAGJ,QAAMQ,aAAaF,SAAS,IAAIG,qCAAsBN,QAAAA,IAAYO;AAClE,QAAMC,YAAQC,gCAAAA;AAEd,QAAMC,SAAS,8BACXC,QAAAA;AA1FR;AA4FQ,cAAMC,iCAAkB;MACpBC,SAAS;MACTC,UAAUN,MAAMO,WAAU;MAC1BhB;MACAC;MACAgB,OAAOL,IAAIM;MACXC,gBAAgBP,IAAIQ,KAAKD;MACzBE,SAAST,IAAIQ,KAAKC;MAClBC,eAAeV,IAAIQ,KAAKE;;MAExBC,iBAAgBX,SAAIY,eAAJZ,YAAkB,KAAK;MACvCT;MACAG;MACAmB,YAAYrB,iCAAQsB;MACpBC,gBAAgBvB,iCAAQwB;MACxBC,OAAOpB,MAAMoB;MACbC,QAAQrB,MAAMqB;IAClB,CAAA;EACJ,GArBe;AAuBf,QAAMC,UAAU,8BACZC,SAAAA;AAEA,eAAWpB,OAAOoB,MAAM;AACpB,YAAMrB,OAAOC,GAAAA;IACjB;EACJ,GANgB;AAQhB,QAAMqB,SAAS,OAAO5B,cAChBN,KAAKmC,KAA6BhC,OAAOG,aAAa0B,OAAAA,IACtDhC,KAAKmC,KAA6BhC,OAAO6B,OAAAA;AAE/C,QAAMI,WAAW,8BAAOC,SAAAA;AA5H5B;AA6HQ,UAAM3B,MAAM4B,eAAcD,kCAAME,YAANF,YAAiB,GAAA;EAC/C,GAFiB;AAIjB,SAAO;IAAEH;IAAQE;EAAS;AAC9B;AAlDsBtC;;;AC7EtB,IAAA0C,iBAAuD;AAoBhD,SAASC,eACZC,UACAC,YACAC,SACAC,OAAqD;AAErD,aAAOC,eAAAA,gBAAqBJ,UAAUC,YAAYC,SAASC,OAAO,iBAAA;AACtE;AAPgBJ;AAUT,SAASM,mBACZC,MACAC,OACAC,KACAC,SAAyB;AAEzB,SAAOH,KAAKI,KAAKH,OAAOC,KAA0BC,OAAAA;AACtD;AAPgBJ;","names":["createCNSWorker","options","boss","cns","registry","queue","observe","resume","workOptions","serializer","CNSProgressSerializer","undefined","drain","createDrainState","runJob","job","runStimulationJob","pkgName","stopping","isStopping","jobId","id","collateralName","data","payload","maxNeuronHops","attemptNumber","retryCount","resumeRepo","repository","resumeStrategy","strategy","abort","active","handler","jobs","workId","work","shutdown","opts","drainAndAbort","drainMs","import_broker","stimulationJob","registry","collateral","payload","extra","brokerStimulationJob","enqueueStimulation","boss","queue","job","options","send"]}
|
package/dist/index.mjs
CHANGED
|
@@ -3,100 +3,54 @@ import {
|
|
|
3
3
|
} from "./chunk-2JTKI4GS.mjs";
|
|
4
4
|
|
|
5
5
|
// src/worker.ts
|
|
6
|
-
import {
|
|
7
|
-
|
|
6
|
+
import { CNSProgressSerializer } from "@cnstra/persist";
|
|
7
|
+
import { runStimulationJob, createDrainState } from "@cnstra/broker";
|
|
8
|
+
async function createCNSWorker(options) {
|
|
8
9
|
const { boss, cns, registry, queue, observe, resume, workOptions } = options;
|
|
9
10
|
const serializer = resume ? new CNSProgressSerializer(registry) : void 0;
|
|
11
|
+
const drain = createDrainState();
|
|
10
12
|
const runJob = /* @__PURE__ */ __name(async (job) => {
|
|
11
13
|
var _a;
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
}
|
|
17
|
-
const recorder = resume && serializer ? new CNSProgressRecorder({
|
|
18
|
-
serializer,
|
|
19
|
-
repository: resume.repository,
|
|
20
|
-
key: job.id,
|
|
21
|
-
strategy: resume.strategy
|
|
22
|
-
}) : void 0;
|
|
23
|
-
const attemptNumber = ((_a = job.retryCount) != null ? _a : 0) + 1;
|
|
24
|
-
const persistor = observe ? new CNSStimulationPersistor({
|
|
25
|
-
repository: observe,
|
|
14
|
+
await runStimulationJob({
|
|
15
|
+
pkgName: "@cnstra/pg-boss",
|
|
16
|
+
stopping: drain.isStopping(),
|
|
17
|
+
cns,
|
|
26
18
|
registry,
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
recorder == null ? void 0 : recorder.onResponse(r);
|
|
41
|
-
persistor == null ? void 0 : persistor.onResponse(r);
|
|
42
|
-
}, "onResponse");
|
|
43
|
-
const stimulationContext = {
|
|
44
|
-
stimulationId: job.id,
|
|
45
|
-
attemptNumber
|
|
46
|
-
};
|
|
47
|
-
let stimulation;
|
|
48
|
-
if (resume && serializer) {
|
|
49
|
-
const saved = await resume.repository.load(job.id);
|
|
50
|
-
if (saved) {
|
|
51
|
-
const { tasks, ctx } = serializer.hydrate(saved);
|
|
52
|
-
stimulation = cns.activate(tasks, {
|
|
53
|
-
maxNeuronHops,
|
|
54
|
-
ctx,
|
|
55
|
-
onResponse,
|
|
56
|
-
stimulationContext
|
|
57
|
-
});
|
|
58
|
-
}
|
|
59
|
-
}
|
|
60
|
-
if (!stimulation) {
|
|
61
|
-
stimulation = cns.stimulate(collateral.createSignal(payload), {
|
|
62
|
-
maxNeuronHops,
|
|
63
|
-
onResponse,
|
|
64
|
-
stimulationContext
|
|
65
|
-
});
|
|
66
|
-
}
|
|
67
|
-
try {
|
|
68
|
-
await stimulation.waitUntilComplete();
|
|
69
|
-
if (resume) await resume.repository.delete(job.id);
|
|
70
|
-
} catch (err) {
|
|
71
|
-
if (resume && serializer) {
|
|
72
|
-
await resume.repository.save(job.id, serializer.serialize(stimulation));
|
|
73
|
-
}
|
|
74
|
-
throw err;
|
|
75
|
-
} finally {
|
|
76
|
-
recorder == null ? void 0 : recorder.dispose();
|
|
77
|
-
persistor == null ? void 0 : persistor.dispose();
|
|
78
|
-
}
|
|
19
|
+
jobId: job.id,
|
|
20
|
+
collateralName: job.data.collateralName,
|
|
21
|
+
payload: job.data.payload,
|
|
22
|
+
maxNeuronHops: job.data.maxNeuronHops,
|
|
23
|
+
// Each pg-boss retry is a new attempt (retryCount is 0-based).
|
|
24
|
+
attemptNumber: ((_a = job.retryCount) != null ? _a : 0) + 1,
|
|
25
|
+
observe,
|
|
26
|
+
serializer,
|
|
27
|
+
resumeRepo: resume == null ? void 0 : resume.repository,
|
|
28
|
+
resumeStrategy: resume == null ? void 0 : resume.strategy,
|
|
29
|
+
abort: drain.abort,
|
|
30
|
+
active: drain.active
|
|
31
|
+
});
|
|
79
32
|
}, "runJob");
|
|
80
33
|
const handler = /* @__PURE__ */ __name(async (jobs) => {
|
|
81
34
|
for (const job of jobs) {
|
|
82
35
|
await runJob(job);
|
|
83
36
|
}
|
|
84
37
|
}, "handler");
|
|
85
|
-
|
|
38
|
+
const workId = await (workOptions ? boss.work(queue, workOptions, handler) : boss.work(queue, handler));
|
|
39
|
+
const shutdown = /* @__PURE__ */ __name(async (opts) => {
|
|
40
|
+
var _a;
|
|
41
|
+
await drain.drainAndAbort((_a = opts == null ? void 0 : opts.drainMs) != null ? _a : 1e4);
|
|
42
|
+
}, "shutdown");
|
|
43
|
+
return {
|
|
44
|
+
workId,
|
|
45
|
+
shutdown
|
|
46
|
+
};
|
|
86
47
|
}
|
|
87
48
|
__name(createCNSWorker, "createCNSWorker");
|
|
88
49
|
|
|
89
50
|
// src/enqueue.ts
|
|
51
|
+
import { stimulationJob as brokerStimulationJob } from "@cnstra/broker";
|
|
90
52
|
function stimulationJob(registry, collateral, payload, extra) {
|
|
91
|
-
|
|
92
|
-
if (!collateralName) {
|
|
93
|
-
throw new Error("[@cnstra/pg-boss] Collateral is not registered in the CNSPersistOptionsRegistry; cannot enqueue it by name.");
|
|
94
|
-
}
|
|
95
|
-
return {
|
|
96
|
-
collateralName,
|
|
97
|
-
payload,
|
|
98
|
-
...extra
|
|
99
|
-
};
|
|
53
|
+
return brokerStimulationJob(registry, collateral, payload, extra, "@cnstra/pg-boss");
|
|
100
54
|
}
|
|
101
55
|
__name(stimulationJob, "stimulationJob");
|
|
102
56
|
function enqueueStimulation(boss, queue, job, options) {
|
package/dist/index.mjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/worker.ts","../src/enqueue.ts"],"sourcesContent":["import {\n CNSProgressRecorder,\n CNSProgressSerializer,\n CNSStimulationPersistor,\n} from '@cnstra/persist';\nimport type {\n CNSPersistOptionsRegistry,\n ICNSProgressRepository,\n ICNSProgressRecordingStrategy,\n ICNSStimulationRepository,\n} from '@cnstra/persist';\nimport type { ICNS, TCNSStimulationResponse } from '@cnstra/types';\nimport type {\n IPgBossLike,\n IPgBossJob,\n ICNSResponseLike,\n TCNSStimulationJobData,\n} from './types';\n\nexport interface TCNSWorkerOptions {\n /** Your pg-boss instance (already started). */\n boss: IPgBossLike;\n /** The CNS the jobs run against. */\n cns: ICNS<any, any>;\n /** Name ↔ object registry used to reconstruct signals from job data. */\n registry: CNSPersistOptionsRegistry;\n /** Queue name to consume. */\n queue: string;\n /**\n * Optional **observability** store: the canonical name-based\n * Stimulation → Attempt → Task history (the SAME model the devtools use),\n * written per hop via a {@link CNSStimulationPersistor}. Pass a\n * `CNSInMemoryStimulationRepository` to just *see* progress (ephemeral), or a\n * Postgres store to keep it. It records history; it does not drive resume —\n * that is `resume` below. One job = one stimulation (`stimulationId` = job id),\n * each pg-boss retry = a new attempt.\n */\n observe?: ICNSStimulationRepository;\n /**\n * Optional durable **resume**. When set, the worker records the run's\n * outstanding frontier (by name) into `repository` as it advances, and a\n * retried job (same job id) resumes from that frontier via `cns.activate(...)`\n * instead of re-running from the entry signal. The record's lifecycle matches\n * the pg-boss job row: dropped on success, kept on failure for the retry.\n *\n * Requires every neuron/collateral that can appear in the frontier to be in\n * the `registry` (not just the entry collaterals). Neurons should still be\n * idempotent — resume narrows re-execution to the unfinished branch, it does\n * not make individual hops exactly-once.\n */\n resume?: {\n repository: ICNSProgressRepository;\n /** When to persist during the run. Defaults to debounce + max-staleness. */\n strategy?: ICNSProgressRecordingStrategy;\n };\n /** pg-boss `work` options (batchSize, pollingIntervalSeconds, …). */\n workOptions?: Record<string, unknown>;\n}\n\n/**\n * Register a pg-boss worker that turns each job into a CNStra stimulation.\n *\n * One job = one stimulation. The job carries `{ collateralName, payload }`; the\n * worker reconstructs the entry signal by name, stimulates, optionally streams\n * per-hop progress, and awaits completion. If the stimulation fails or aborts,\n * `waitUntilComplete()` rejects and the error propagates so pg-boss applies its\n * own retry/backoff policy.\n */\nexport function createCNSWorker(options: TCNSWorkerOptions): Promise<string> {\n const { boss, cns, registry, queue, observe, resume, workOptions } =\n options;\n\n // One serializer per worker; the recorder is per-job (holds job-specific key).\n const serializer = resume\n ? new CNSProgressSerializer(registry)\n : undefined;\n\n const runJob = async (\n job: IPgBossJob<TCNSStimulationJobData>\n ): Promise<void> => {\n const { collateralName, payload, maxNeuronHops } = job.data;\n const collateral = registry.getCollateral(collateralName);\n if (!collateral) {\n throw new Error(\n `[@cnstra/pg-boss] Unknown collateral \"${collateralName}\". ` +\n `Register it in the CNSPersistOptionsRegistry.`\n );\n }\n\n // Records the outstanding frontier as the run advances (crash-safety); the\n // explicit save on failure below guarantees the exact final frontier.\n const recorder =\n resume && serializer\n ? new CNSProgressRecorder({\n serializer,\n repository: resume.repository,\n key: job.id,\n strategy: resume.strategy,\n })\n : undefined;\n\n // Observability: stream the canonical name-based Stimulation → Attempt →\n // Task model to `observe`. One job = one stimulation; each pg-boss retry is\n // a new attempt (retryCount is 0-based).\n const attemptNumber = (job.retryCount ?? 0) + 1;\n const persistor = observe\n ? new CNSStimulationPersistor({\n repository: observe,\n registry,\n stimulationId: job.id,\n stimulationAttemptId: `${job.id}#${attemptNumber}`,\n attemptNumber,\n entry: [{ collateralName, payload }],\n volume: 'full',\n })\n : undefined;\n\n const onResponse = (resp: ICNSResponseLike) => {\n const r = resp as unknown as TCNSStimulationResponse;\n // recorder → resume checkpoint; persistor → observability history.\n recorder?.onResponse(r);\n persistor?.onResponse(r);\n };\n\n // Correlate logs to this run: every handler reads `ctx.stimulationContext`\n // (same object on every hop, sync or async) — no wiring on the user's side.\n const stimulationContext = { stimulationId: job.id, attemptNumber };\n\n // Resume from a persisted frontier if this job already has one (a retry of\n // the same job id); otherwise start fresh from the entry signal.\n let stimulation;\n if (resume && serializer) {\n const saved = await resume.repository.load(job.id);\n if (saved) {\n const { tasks, ctx } = serializer.hydrate(saved);\n stimulation = cns.activate(tasks, {\n maxNeuronHops,\n ctx,\n onResponse,\n stimulationContext,\n });\n }\n }\n if (!stimulation) {\n stimulation = cns.stimulate(collateral.createSignal(payload), {\n maxNeuronHops,\n onResponse,\n stimulationContext,\n });\n }\n\n try {\n await stimulation.waitUntilComplete();\n // Job about to complete → its resume checkpoint can go (lifecycle = job row).\n if (resume) await resume.repository.delete(job.id);\n } catch (err) {\n // Persist the exact final frontier before throwing so the retried job\n // resumes from it (the recorder's cadence may still be debouncing).\n if (resume && serializer) {\n await resume.repository.save(\n job.id,\n serializer.serialize(stimulation)\n );\n }\n // Let pg-boss decide about retries/backoff.\n throw err;\n } finally {\n recorder?.dispose();\n persistor?.dispose();\n }\n };\n\n const handler = async (\n jobs: IPgBossJob<TCNSStimulationJobData>[]\n ): Promise<void> => {\n for (const job of jobs) {\n await runJob(job);\n }\n };\n\n return workOptions\n ? boss.work<TCNSStimulationJobData>(queue, workOptions, handler)\n : boss.work<TCNSStimulationJobData>(queue, handler);\n}\n","import type { CNSCollateral } from '@cnstra/core';\nimport type { CNSPersistOptionsRegistry } from '@cnstra/persist';\nimport type { IPgBossLike, TCNSStimulationJobData } from './types';\n\n/** Commonly used pg-boss send options (a superset is passed through). */\nexport interface TCNSSendOptions {\n retryLimit?: number;\n retryBackoff?: boolean;\n retryDelay?: number;\n /** One in-flight job per key — e.g. one stimulation per entity id. */\n singletonKey?: string;\n startAfter?: number | string | Date;\n expireInSeconds?: number;\n priority?: number;\n [key: string]: unknown;\n}\n\n/**\n * Build a queue job from a **registered** collateral. Throws if the collateral\n * is not in the registry, so a typo can't reach the queue.\n */\nexport function stimulationJob(\n registry: CNSPersistOptionsRegistry,\n collateral: CNSCollateral<unknown>,\n payload: unknown,\n extra?: Pick<TCNSStimulationJobData, 'maxNeuronHops'>\n): TCNSStimulationJobData {\n const collateralName = registry.getCollateralName(collateral);\n if (!collateralName) {\n throw new Error(\n '[@cnstra/pg-boss] Collateral is not registered in the ' +\n 'CNSPersistOptionsRegistry; cannot enqueue it by name.'\n );\n }\n return { collateralName, payload, ...extra };\n}\n\n/** Send a stimulation job to a pg-boss queue. Returns the job id. */\nexport function enqueueStimulation(\n boss: IPgBossLike,\n queue: string,\n job: TCNSStimulationJobData,\n options?: TCNSSendOptions\n): Promise<string | null> {\n return boss.send(queue, job as unknown as object, options);\n}\n"],"mappings":";;;;;AAAA,SACIA,qBACAC,uBACAC,+BACG;AAgEA,SAASC,gBAAgBC,SAA0B;AACtD,QAAM,EAAEC,MAAMC,KAAKC,UAAUC,OAAOC,SAASC,QAAQC,YAAW,IAC5DP;AAGJ,QAAMQ,aAAaF,SACb,IAAIG,sBAAsBN,QAAAA,IAC1BO;AAEN,QAAMC,SAAS,8BACXC,QAAAA;AA9ER;AAgFQ,UAAM,EAAEC,gBAAgBC,SAASC,cAAa,IAAKH,IAAII;AACvD,UAAMC,aAAad,SAASe,cAAcL,cAAAA;AAC1C,QAAI,CAACI,YAAY;AACb,YAAM,IAAIE,MACN,yCAAyCN,cAAAA,kDACU;IAE3D;AAIA,UAAMO,WACFd,UAAUE,aACJ,IAAIa,oBAAoB;MACpBb;MACAc,YAAYhB,OAAOgB;MACnBC,KAAKX,IAAIY;MACTC,UAAUnB,OAAOmB;IACrB,CAAA,IACAf;AAKV,UAAMgB,kBAAiBd,SAAIe,eAAJf,YAAkB,KAAK;AAC9C,UAAMgB,YAAYvB,UACZ,IAAIwB,wBAAwB;MACxBP,YAAYjB;MACZF;MACA2B,eAAelB,IAAIY;MACnBO,sBAAsB,GAAGnB,IAAIY,EAAE,IAAIE,aAAAA;MACnCA;MACAM,OAAO;QAAC;UAAEnB;UAAgBC;QAAQ;;MAClCmB,QAAQ;IACZ,CAAA,IACAvB;AAEN,UAAMwB,aAAa,wBAACC,SAAAA;AAChB,YAAMC,IAAID;AAEVf,2CAAUc,WAAWE;AACrBR,6CAAWM,WAAWE;IAC1B,GALmB;AASnB,UAAMC,qBAAqB;MAAEP,eAAelB,IAAIY;MAAIE;IAAc;AAIlE,QAAIY;AACJ,QAAIhC,UAAUE,YAAY;AACtB,YAAM+B,QAAQ,MAAMjC,OAAOgB,WAAWkB,KAAK5B,IAAIY,EAAE;AACjD,UAAIe,OAAO;AACP,cAAM,EAAEE,OAAOC,IAAG,IAAKlC,WAAWmC,QAAQJ,KAAAA;AAC1CD,sBAAcpC,IAAI0C,SAASH,OAAO;UAC9B1B;UACA2B;UACAR;UACAG;QACJ,CAAA;MACJ;IACJ;AACA,QAAI,CAACC,aAAa;AACdA,oBAAcpC,IAAI2C,UAAU5B,WAAW6B,aAAahC,OAAAA,GAAU;QAC1DC;QACAmB;QACAG;MACJ,CAAA;IACJ;AAEA,QAAI;AACA,YAAMC,YAAYS,kBAAiB;AAEnC,UAAIzC,OAAQ,OAAMA,OAAOgB,WAAW0B,OAAOpC,IAAIY,EAAE;IACrD,SAASyB,KAAK;AAGV,UAAI3C,UAAUE,YAAY;AACtB,cAAMF,OAAOgB,WAAW4B,KACpBtC,IAAIY,IACJhB,WAAW2C,UAAUb,WAAAA,CAAAA;MAE7B;AAEA,YAAMW;IACV,UAAA;AACI7B,2CAAUgC;AACVxB,6CAAWwB;IACf;EACJ,GA7Fe;AA+Ff,QAAMC,UAAU,8BACZC,SAAAA;AAEA,eAAW1C,OAAO0C,MAAM;AACpB,YAAM3C,OAAOC,GAAAA;IACjB;EACJ,GANgB;AAQhB,SAAOL,cACDN,KAAKsD,KAA6BnD,OAAOG,aAAa8C,OAAAA,IACtDpD,KAAKsD,KAA6BnD,OAAOiD,OAAAA;AACnD;AAnHgBtD;;;AC/CT,SAASyD,eACZC,UACAC,YACAC,SACAC,OAAqD;AAErD,QAAMC,iBAAiBJ,SAASK,kBAAkBJ,UAAAA;AAClD,MAAI,CAACG,gBAAgB;AACjB,UAAM,IAAIE,MACN,6GACI;EAEZ;AACA,SAAO;IAAEF;IAAgBF;IAAS,GAAGC;EAAM;AAC/C;AAdgBJ;AAiBT,SAASQ,mBACZC,MACAC,OACAC,KACAC,SAAyB;AAEzB,SAAOH,KAAKI,KAAKH,OAAOC,KAA0BC,OAAAA;AACtD;AAPgBJ;","names":["CNSProgressRecorder","CNSProgressSerializer","CNSStimulationPersistor","createCNSWorker","options","boss","cns","registry","queue","observe","resume","workOptions","serializer","CNSProgressSerializer","undefined","runJob","job","collateralName","payload","maxNeuronHops","data","collateral","getCollateral","Error","recorder","CNSProgressRecorder","repository","key","id","strategy","attemptNumber","retryCount","persistor","CNSStimulationPersistor","stimulationId","stimulationAttemptId","entry","volume","onResponse","resp","r","stimulationContext","stimulation","saved","load","tasks","ctx","hydrate","activate","stimulate","createSignal","waitUntilComplete","delete","err","save","serialize","dispose","handler","jobs","work","stimulationJob","registry","collateral","payload","extra","collateralName","getCollateralName","Error","enqueueStimulation","boss","queue","job","options","send"]}
|
|
1
|
+
{"version":3,"sources":["../src/worker.ts","../src/enqueue.ts"],"sourcesContent":["import { CNSProgressSerializer } from '@cnstra/persist';\nimport type {\n CNSPersistOptionsRegistry,\n ICNSProgressRepository,\n ICNSProgressRecordingStrategy,\n ICNSStimulationRepository,\n} from '@cnstra/persist';\nimport type { ICNS } from '@cnstra/types';\nimport { runStimulationJob, createDrainState } from '@cnstra/broker';\nimport type {\n IPgBossLike,\n IPgBossJob,\n TCNSStimulationJobData,\n} from './types';\n\nexport interface TCNSWorkerOptions {\n /** Your pg-boss instance (already started). */\n boss: IPgBossLike;\n /** The CNS the jobs run against. */\n cns: ICNS<any, any>;\n /** Name ↔ object registry used to reconstruct signals from job data. */\n registry: CNSPersistOptionsRegistry;\n /** Queue name to consume. */\n queue: string;\n /**\n * Optional **observability** store: the canonical name-based\n * Stimulation → Attempt → Task history (the SAME model the devtools use),\n * written per hop via a {@link CNSStimulationPersistor}. Pass a\n * `CNSInMemoryStimulationRepository` to just *see* progress (ephemeral), or a\n * Postgres store to keep it. It records history; it does not drive resume —\n * that is `resume` below. One job = one stimulation (`stimulationId` = job id),\n * each pg-boss retry = a new attempt.\n */\n observe?: ICNSStimulationRepository;\n /**\n * Optional resumable **resume**. When set, the worker records the run's\n * outstanding frontier (by name) into `repository` as it advances, and a\n * retried job (same job id) resumes from that frontier via `cns.activate(...)`\n * instead of re-running from the entry signal. The record's lifecycle matches\n * the pg-boss job row: dropped on success, kept on failure for the retry.\n *\n * Requires every neuron/collateral that can appear in the frontier to be in\n * the `registry` (not just the entry collaterals). Neurons should still be\n * idempotent — resume narrows re-execution to the unfinished branch, it does\n * not make individual hops exactly-once.\n */\n resume?: {\n repository: ICNSProgressRepository;\n /** When to persist during the run. Defaults to debounce + max-staleness. */\n strategy?: ICNSProgressRecordingStrategy;\n };\n /** pg-boss `work` options (batchSize, pollingIntervalSeconds, …). */\n workOptions?: Record<string, unknown>;\n}\n\n/**\n * Register a pg-boss worker that turns each job into a CNStra stimulation.\n *\n * One job = one stimulation. The job carries `{ collateralName, payload }`; the\n * worker reconstructs the entry signal by name, stimulates, optionally streams\n * per-hop progress, and awaits completion. If the stimulation fails or aborts,\n * `waitUntilComplete()` rejects and the error propagates so pg-boss applies its\n * own retry/backoff policy.\n */\n/** A registered worker + a graceful `shutdown` (see {@link createCNSWorker}). */\nexport interface TCNSPgBossWorkerHandle {\n /** The pg-boss `work()` registration id. */\n workId: string;\n /**\n * Graceful shutdown of THIS worker's processing: stop taking new jobs, let\n * in-flight stimulations finish for up to `drainMs` (default 10s), then **abort**\n * whatever is still running — an aborted stimulation's remaining tasks become its\n * frontier, which the resume checkpoint records, so the job's retry resumes there.\n * It does NOT stop or close your `boss` (you own it) or your stores — call\n * `boss.stop(...)` and close your stores after this resolves.\n */\n shutdown(opts?: { drainMs?: number }): Promise<void>;\n}\n\nexport async function createCNSWorker(\n options: TCNSWorkerOptions\n): Promise<TCNSPgBossWorkerHandle> {\n const { boss, cns, registry, queue, observe, resume, workOptions } =\n options;\n\n // One serializer per worker; the per-job pipeline lives in @cnstra/broker.\n const serializer = resume ? new CNSProgressSerializer(registry) : undefined;\n const drain = createDrainState();\n\n const runJob = async (\n job: IPgBossJob<TCNSStimulationJobData>\n ): Promise<void> => {\n await runStimulationJob({\n pkgName: '@cnstra/pg-boss',\n stopping: drain.isStopping(),\n cns,\n registry,\n jobId: job.id,\n collateralName: job.data.collateralName,\n payload: job.data.payload,\n maxNeuronHops: job.data.maxNeuronHops,\n // Each pg-boss retry is a new attempt (retryCount is 0-based).\n attemptNumber: (job.retryCount ?? 0) + 1,\n observe,\n serializer,\n resumeRepo: resume?.repository,\n resumeStrategy: resume?.strategy,\n abort: drain.abort,\n active: drain.active,\n });\n };\n\n const handler = async (\n jobs: IPgBossJob<TCNSStimulationJobData>[]\n ): Promise<void> => {\n for (const job of jobs) {\n await runJob(job);\n }\n };\n\n const workId = await (workOptions\n ? boss.work<TCNSStimulationJobData>(queue, workOptions, handler)\n : boss.work<TCNSStimulationJobData>(queue, handler));\n\n const shutdown = async (opts?: { drainMs?: number }): Promise<void> => {\n await drain.drainAndAbort(opts?.drainMs ?? 10_000);\n };\n\n return { workId, shutdown };\n}\n","import type { CNSCollateral } from '@cnstra/core';\nimport type { CNSPersistOptionsRegistry } from '@cnstra/persist';\nimport { stimulationJob as brokerStimulationJob } from '@cnstra/broker';\nimport type { IPgBossLike, TCNSStimulationJobData } from './types';\n\n/** Commonly used pg-boss send options (a superset is passed through). */\nexport interface TCNSSendOptions {\n retryLimit?: number;\n retryBackoff?: boolean;\n retryDelay?: number;\n /** One in-flight job per key — e.g. one stimulation per entity id. */\n singletonKey?: string;\n startAfter?: number | string | Date;\n expireInSeconds?: number;\n priority?: number;\n [key: string]: unknown;\n}\n\n/**\n * Build a queue job from a **registered** collateral. Throws if the collateral\n * is not in the registry, so a typo can't reach the queue.\n */\nexport function stimulationJob(\n registry: CNSPersistOptionsRegistry,\n collateral: CNSCollateral<unknown>,\n payload: unknown,\n extra?: Pick<TCNSStimulationJobData, 'maxNeuronHops'>\n): TCNSStimulationJobData {\n return brokerStimulationJob(registry, collateral, payload, extra, '@cnstra/pg-boss');\n}\n\n/** Send a stimulation job to a pg-boss queue. Returns the job id. */\nexport function enqueueStimulation(\n boss: IPgBossLike,\n queue: string,\n job: TCNSStimulationJobData,\n options?: TCNSSendOptions\n): Promise<string | null> {\n return boss.send(queue, job as unknown as object, options);\n}\n"],"mappings":";;;;;AAAA,SAASA,6BAA6B;AAQtC,SAASC,mBAAmBC,wBAAwB;AAuEpD,eAAsBC,gBAClBC,SAA0B;AAE1B,QAAM,EAAEC,MAAMC,KAAKC,UAAUC,OAAOC,SAASC,QAAQC,YAAW,IAC5DP;AAGJ,QAAMQ,aAAaF,SAAS,IAAIG,sBAAsBN,QAAAA,IAAYO;AAClE,QAAMC,QAAQC,iBAAAA;AAEd,QAAMC,SAAS,8BACXC,QAAAA;AA1FR;AA4FQ,UAAMC,kBAAkB;MACpBC,SAAS;MACTC,UAAUN,MAAMO,WAAU;MAC1BhB;MACAC;MACAgB,OAAOL,IAAIM;MACXC,gBAAgBP,IAAIQ,KAAKD;MACzBE,SAAST,IAAIQ,KAAKC;MAClBC,eAAeV,IAAIQ,KAAKE;;MAExBC,iBAAgBX,SAAIY,eAAJZ,YAAkB,KAAK;MACvCT;MACAG;MACAmB,YAAYrB,iCAAQsB;MACpBC,gBAAgBvB,iCAAQwB;MACxBC,OAAOpB,MAAMoB;MACbC,QAAQrB,MAAMqB;IAClB,CAAA;EACJ,GArBe;AAuBf,QAAMC,UAAU,8BACZC,SAAAA;AAEA,eAAWpB,OAAOoB,MAAM;AACpB,YAAMrB,OAAOC,GAAAA;IACjB;EACJ,GANgB;AAQhB,QAAMqB,SAAS,OAAO5B,cAChBN,KAAKmC,KAA6BhC,OAAOG,aAAa0B,OAAAA,IACtDhC,KAAKmC,KAA6BhC,OAAO6B,OAAAA;AAE/C,QAAMI,WAAW,8BAAOC,SAAAA;AA5H5B;AA6HQ,UAAM3B,MAAM4B,eAAcD,kCAAME,YAANF,YAAiB,GAAA;EAC/C,GAFiB;AAIjB,SAAO;IAAEH;IAAQE;EAAS;AAC9B;AAlDsBtC;;;AC7EtB,SAAS0C,kBAAkBC,4BAA4B;AAoBhD,SAASC,eACZC,UACAC,YACAC,SACAC,OAAqD;AAErD,SAAOC,qBAAqBJ,UAAUC,YAAYC,SAASC,OAAO,iBAAA;AACtE;AAPgBJ;AAUT,SAASM,mBACZC,MACAC,OACAC,KACAC,SAAyB;AAEzB,SAAOH,KAAKI,KAAKH,OAAOC,KAA0BC,OAAAA;AACtD;AAPgBJ;","names":["CNSProgressSerializer","runStimulationJob","createDrainState","createCNSWorker","options","boss","cns","registry","queue","observe","resume","workOptions","serializer","CNSProgressSerializer","undefined","drain","createDrainState","runJob","job","runStimulationJob","pkgName","stopping","isStopping","jobId","id","collateralName","data","payload","maxNeuronHops","attemptNumber","retryCount","resumeRepo","repository","resumeStrategy","strategy","abort","active","handler","jobs","workId","work","shutdown","opts","drainAndAbort","drainMs","stimulationJob","brokerStimulationJob","stimulationJob","registry","collateral","payload","extra","brokerStimulationJob","enqueueStimulation","boss","queue","job","options","send"]}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@cnstra/pg-boss",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.4.0",
|
|
4
4
|
"description": "pg-boss (Postgres) integration for CNStra: run stimulations as jobs and stream per-hop progress",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"cnstra",
|
|
@@ -33,32 +33,35 @@
|
|
|
33
33
|
"dist"
|
|
34
34
|
],
|
|
35
35
|
"scripts": {
|
|
36
|
+
"prepublishOnly": "node ../../scripts/check-drift.mjs --gate",
|
|
36
37
|
"build": "tsup",
|
|
37
38
|
"test": "jest",
|
|
38
39
|
"test:types": "tsc --noEmit",
|
|
39
40
|
"clean": "rm -rf dist"
|
|
40
41
|
},
|
|
41
42
|
"dependencies": {
|
|
42
|
-
"@cnstra/types": "^0.
|
|
43
|
+
"@cnstra/types": "^0.2.0",
|
|
44
|
+
"@cnstra/broker": "^0.2.0"
|
|
43
45
|
},
|
|
44
46
|
"peerDependencies": {
|
|
45
|
-
"@cnstra/core": "^
|
|
46
|
-
"@cnstra/persist": "^0.
|
|
47
|
-
"@cnstra/persist-dto": "^0.
|
|
47
|
+
"@cnstra/core": "^3.0.0",
|
|
48
|
+
"@cnstra/persist": "^0.2.0",
|
|
49
|
+
"@cnstra/persist-dto": "^0.2.0",
|
|
48
50
|
"pg-boss": ">=9",
|
|
49
51
|
"pg": ">=8"
|
|
50
52
|
},
|
|
51
53
|
"devDependencies": {
|
|
52
|
-
"@cnstra/core": "^
|
|
53
|
-
"@cnstra/persist": "^0.
|
|
54
|
-
"@cnstra/persist-dto": "^0.
|
|
54
|
+
"@cnstra/core": "^3.0.0",
|
|
55
|
+
"@cnstra/persist": "^0.2.0",
|
|
56
|
+
"@cnstra/persist-dto": "^0.2.0",
|
|
55
57
|
"@types/jest": "29.5.12",
|
|
56
58
|
"@types/pg": "^8.11.0",
|
|
57
59
|
"jest": "29.7.0",
|
|
58
60
|
"pg": "^8.11.0",
|
|
59
61
|
"ts-jest": "29.1.2",
|
|
60
62
|
"tsup": "8.5.1",
|
|
61
|
-
"typescript": "5.7.2"
|
|
63
|
+
"typescript": "5.7.2",
|
|
64
|
+
"@cnstra/broker": "^0.2.0"
|
|
62
65
|
},
|
|
63
66
|
"peerDependenciesMeta": {
|
|
64
67
|
"pg-boss": {
|