@nexpress/core 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/LICENSE +21 -0
- package/README.md +69 -0
- package/dist/audit-54XLVCWD.js +14 -0
- package/dist/audit-54XLVCWD.js.map +1 -0
- package/dist/auth.d.ts +640 -0
- package/dist/auth.js +94 -0
- package/dist/auth.js.map +1 -0
- package/dist/can-YLUHRJAB.js +19 -0
- package/dist/can-YLUHRJAB.js.map +1 -0
- package/dist/chunk-2G264RCD.js +68 -0
- package/dist/chunk-2G264RCD.js.map +1 -0
- package/dist/chunk-2YDGE7YX.js +92 -0
- package/dist/chunk-2YDGE7YX.js.map +1 -0
- package/dist/chunk-473S4TER.js +538 -0
- package/dist/chunk-473S4TER.js.map +1 -0
- package/dist/chunk-4ZLMEKFX.js +18 -0
- package/dist/chunk-4ZLMEKFX.js.map +1 -0
- package/dist/chunk-55FU6WED.js +179 -0
- package/dist/chunk-55FU6WED.js.map +1 -0
- package/dist/chunk-6YI5K2TI.js +1959 -0
- package/dist/chunk-6YI5K2TI.js.map +1 -0
- package/dist/chunk-BHK3AD3Q.js +41 -0
- package/dist/chunk-BHK3AD3Q.js.map +1 -0
- package/dist/chunk-CRUQBZUF.js +39 -0
- package/dist/chunk-CRUQBZUF.js.map +1 -0
- package/dist/chunk-CTSQ7BRI.js +175 -0
- package/dist/chunk-CTSQ7BRI.js.map +1 -0
- package/dist/chunk-DK2JBJH7.js +81 -0
- package/dist/chunk-DK2JBJH7.js.map +1 -0
- package/dist/chunk-DP2PREDU.js +597 -0
- package/dist/chunk-DP2PREDU.js.map +1 -0
- package/dist/chunk-EQ2Z3KMD.js +24 -0
- package/dist/chunk-EQ2Z3KMD.js.map +1 -0
- package/dist/chunk-FZ7O6DWI.js +305 -0
- package/dist/chunk-FZ7O6DWI.js.map +1 -0
- package/dist/chunk-ISLYFQWL.js +1270 -0
- package/dist/chunk-ISLYFQWL.js.map +1 -0
- package/dist/chunk-JJL74ZPK.js +68 -0
- package/dist/chunk-JJL74ZPK.js.map +1 -0
- package/dist/chunk-JKXAPSU4.js +24 -0
- package/dist/chunk-JKXAPSU4.js.map +1 -0
- package/dist/chunk-KU5M27ZC.js +24 -0
- package/dist/chunk-KU5M27ZC.js.map +1 -0
- package/dist/chunk-LSHHRDVR.js +34 -0
- package/dist/chunk-LSHHRDVR.js.map +1 -0
- package/dist/chunk-M43PGOQY.js +715 -0
- package/dist/chunk-M43PGOQY.js.map +1 -0
- package/dist/chunk-MEJAHXIO.js +150 -0
- package/dist/chunk-MEJAHXIO.js.map +1 -0
- package/dist/chunk-NUCGHWCF.js +101 -0
- package/dist/chunk-NUCGHWCF.js.map +1 -0
- package/dist/chunk-OK5HOCQI.js +845 -0
- package/dist/chunk-OK5HOCQI.js.map +1 -0
- package/dist/chunk-OROPGO65.js +13 -0
- package/dist/chunk-OROPGO65.js.map +1 -0
- package/dist/chunk-PPAS4SZR.js +176 -0
- package/dist/chunk-PPAS4SZR.js.map +1 -0
- package/dist/chunk-PPBWRKO2.js +171 -0
- package/dist/chunk-PPBWRKO2.js.map +1 -0
- package/dist/chunk-PZ5AY32C.js +10 -0
- package/dist/chunk-PZ5AY32C.js.map +1 -0
- package/dist/chunk-QO7LAQZH.js +321 -0
- package/dist/chunk-QO7LAQZH.js.map +1 -0
- package/dist/chunk-QVJ2HCAX.js +225 -0
- package/dist/chunk-QVJ2HCAX.js.map +1 -0
- package/dist/chunk-RIPHIRPP.js +68 -0
- package/dist/chunk-RIPHIRPP.js.map +1 -0
- package/dist/chunk-S27S42QY.js +134 -0
- package/dist/chunk-S27S42QY.js.map +1 -0
- package/dist/chunk-SBCVAC2Z.js +40 -0
- package/dist/chunk-SBCVAC2Z.js.map +1 -0
- package/dist/chunk-TFJ4MKPH.js +694 -0
- package/dist/chunk-TFJ4MKPH.js.map +1 -0
- package/dist/chunk-THX3SHYA.js +75 -0
- package/dist/chunk-THX3SHYA.js.map +1 -0
- package/dist/chunk-UGQSQO5B.js +222 -0
- package/dist/chunk-UGQSQO5B.js.map +1 -0
- package/dist/chunk-V2UNHGAP.js +26 -0
- package/dist/chunk-V2UNHGAP.js.map +1 -0
- package/dist/chunk-VGTPQXNQ.js +2790 -0
- package/dist/chunk-VGTPQXNQ.js.map +1 -0
- package/dist/chunk-VNIHXQ7W.js +194 -0
- package/dist/chunk-VNIHXQ7W.js.map +1 -0
- package/dist/chunk-WV272MPW.js +31 -0
- package/dist/chunk-WV272MPW.js.map +1 -0
- package/dist/chunk-X5KKBOUS.js +26 -0
- package/dist/chunk-X5KKBOUS.js.map +1 -0
- package/dist/chunk-XANPEOJC.js +17 -0
- package/dist/chunk-XANPEOJC.js.map +1 -0
- package/dist/chunk-XPVQIHAQ.js +83 -0
- package/dist/chunk-XPVQIHAQ.js.map +1 -0
- package/dist/chunk-ZCINJSS4.js +75 -0
- package/dist/chunk-ZCINJSS4.js.map +1 -0
- package/dist/community.d.ts +1425 -0
- package/dist/community.js +206 -0
- package/dist/community.js.map +1 -0
- package/dist/config-2GDU7PCK.js +32 -0
- package/dist/config-2GDU7PCK.js.map +1 -0
- package/dist/context-MNZ4QXPC.js +16 -0
- package/dist/context-MNZ4QXPC.js.map +1 -0
- package/dist/db-schema.d.ts +4 -0
- package/dist/db-schema.js +102 -0
- package/dist/db-schema.js.map +1 -0
- package/dist/db.d.ts +7 -0
- package/dist/db.js +117 -0
- package/dist/db.js.map +1 -0
- package/dist/digest-SY42GQSU.js +17 -0
- package/dist/digest-SY42GQSU.js.map +1 -0
- package/dist/errors-5OS3S2J3.js +22 -0
- package/dist/errors-5OS3S2J3.js.map +1 -0
- package/dist/host-OBOI4MJK.js +51 -0
- package/dist/host-OBOI4MJK.js.map +1 -0
- package/dist/i18n.d.ts +301 -0
- package/dist/i18n.js +68 -0
- package/dist/i18n.js.map +1 -0
- package/dist/index-B6-_vr_m.d.ts +590 -0
- package/dist/index-CY55LC0u.d.ts +4722 -0
- package/dist/index-CeiTvwbp.d.ts +168 -0
- package/dist/index-XwP1ET8b.d.ts +61 -0
- package/dist/index.d.ts +2037 -0
- package/dist/index.js +2205 -0
- package/dist/index.js.map +1 -0
- package/dist/job-log-VZXWQUDK.js +24 -0
- package/dist/job-log-VZXWQUDK.js.map +1 -0
- package/dist/jobs.d.ts +4 -0
- package/dist/jobs.js +76 -0
- package/dist/jobs.js.map +1 -0
- package/dist/logger-DqGaOU_j.d.ts +29 -0
- package/dist/logger-S7REWDNE.js +16 -0
- package/dist/logger-S7REWDNE.js.map +1 -0
- package/dist/media.d.ts +5 -0
- package/dist/media.js +41 -0
- package/dist/media.js.map +1 -0
- package/dist/mentions-2IHFVSHW.js +23 -0
- package/dist/mentions-2IHFVSHW.js.map +1 -0
- package/dist/mutes-EWAE5FZR.js +21 -0
- package/dist/mutes-EWAE5FZR.js.map +1 -0
- package/dist/notification-prefs-VPJDU7I6.js +21 -0
- package/dist/notification-prefs-VPJDU7I6.js.map +1 -0
- package/dist/observability.d.ts +156 -0
- package/dist/observability.js +32 -0
- package/dist/observability.js.map +1 -0
- package/dist/profanity-adapter-NU2JQSLX.js +12 -0
- package/dist/profanity-adapter-NU2JQSLX.js.map +1 -0
- package/dist/queue-XE5BC75T.js +14 -0
- package/dist/queue-XE5BC75T.js.map +1 -0
- package/dist/rate-limit.d.ts +99 -0
- package/dist/rate-limit.js +14 -0
- package/dist/rate-limit.js.map +1 -0
- package/dist/registry-XIXDEPVI.js +31 -0
- package/dist/registry-XIXDEPVI.js.map +1 -0
- package/dist/reputation-JRL2YQHM.js +11 -0
- package/dist/reputation-JRL2YQHM.js.map +1 -0
- package/dist/routes.d.ts +43 -0
- package/dist/routes.js +12 -0
- package/dist/routes.js.map +1 -0
- package/dist/scheduled-CIQM57HT.js +20 -0
- package/dist/scheduled-CIQM57HT.js.map +1 -0
- package/dist/seo.d.ts +410 -0
- package/dist/seo.js +44 -0
- package/dist/seo.js.map +1 -0
- package/dist/settings-FOBIESPB.js +17 -0
- package/dist/settings-FOBIESPB.js.map +1 -0
- package/dist/spam-adapter-XX3G737Z.js +12 -0
- package/dist/spam-adapter-XX3G737Z.js.map +1 -0
- package/dist/strings-VAE47B2C.js +29 -0
- package/dist/strings-VAE47B2C.js.map +1 -0
- package/dist/templates-IFVJMCJ6.js +12 -0
- package/dist/templates-IFVJMCJ6.js.map +1 -0
- package/dist/types-TlsbXS0T.d.ts +871 -0
- package/package.json +129 -0
|
@@ -0,0 +1,590 @@
|
|
|
1
|
+
import { I as NpJobType, d as NpCollectionConfig, e as NpAuthUser, F as NpPrincipal } from './types-TlsbXS0T.js';
|
|
2
|
+
import { ConstructorOptions, PgBoss } from 'pg-boss';
|
|
3
|
+
import { N as NpLogLevel } from './logger-DqGaOU_j.js';
|
|
4
|
+
|
|
5
|
+
type NpJobHandler = (data: unknown) => Promise<void>;
|
|
6
|
+
declare function registerJobHandler(type: NpJobType, handler: NpJobHandler): void;
|
|
7
|
+
declare function getJobHandler(type: NpJobType): NpJobHandler | undefined;
|
|
8
|
+
declare function getAllJobHandlers(): ReadonlyMap<NpJobType, NpJobHandler>;
|
|
9
|
+
|
|
10
|
+
/**
|
|
11
|
+
* Phase 13 — admin-side job introspection. pg-boss tracks jobs
|
|
12
|
+
* across two tables (`pgboss.job` for active/scheduled,
|
|
13
|
+
* `pgboss.archive` for completed/failed); the framework
|
|
14
|
+
* surfaces a unified shape so the admin UI doesn't have to
|
|
15
|
+
* know the storage split.
|
|
16
|
+
*/
|
|
17
|
+
type NpJobState = "created" | "active" | "completed" | "failed" | "retry" | "cancelled" | "expired";
|
|
18
|
+
interface NpJobSummary {
|
|
19
|
+
id: string;
|
|
20
|
+
/** pg-boss queue name (after `:` → `.` translation). */
|
|
21
|
+
name: string;
|
|
22
|
+
state: NpJobState;
|
|
23
|
+
data: unknown;
|
|
24
|
+
/** Number of retries pg-boss has attempted so far. */
|
|
25
|
+
retryCount?: number;
|
|
26
|
+
/** Last failure message, if any. */
|
|
27
|
+
output?: string | null;
|
|
28
|
+
createdOn: string;
|
|
29
|
+
startedOn?: string | null;
|
|
30
|
+
completedOn?: string | null;
|
|
31
|
+
/**
|
|
32
|
+
* Phase 20.4 — which pg-boss table the row was read from.
|
|
33
|
+
* `"live"` = pgboss.job (still pending / active / retry),
|
|
34
|
+
* `"archive"` = pgboss.archive (rolled out by pg-boss after
|
|
35
|
+
* `keepUntil`). The admin Jobs view uses this to split the
|
|
36
|
+
* Failed tab into "live failures" (still actionable via
|
|
37
|
+
* `/api/admin/jobs/{id}/retry`) vs "archived" (kept for
|
|
38
|
+
* forensics; retry would re-create the row in `job`).
|
|
39
|
+
*/
|
|
40
|
+
source?: "live" | "archive";
|
|
41
|
+
}
|
|
42
|
+
interface NpJobListOptions {
|
|
43
|
+
/** Filter to one queue name (e.g. `"media.processImage"`). */
|
|
44
|
+
name?: string;
|
|
45
|
+
/** Filter to one state. Defaults to all. */
|
|
46
|
+
state?: NpJobState;
|
|
47
|
+
/** Page size. Default 50, capped at 200. */
|
|
48
|
+
limit?: number;
|
|
49
|
+
/** Skip count for pagination. */
|
|
50
|
+
offset?: number;
|
|
51
|
+
/**
|
|
52
|
+
* Phase 13.2 — only include jobs whose `created_on` is at or
|
|
53
|
+
* after this timestamp. Common operational query: "jobs from
|
|
54
|
+
* the last 24 hours" without paging through history.
|
|
55
|
+
*/
|
|
56
|
+
since?: Date;
|
|
57
|
+
/**
|
|
58
|
+
* Phase 20.4 — partition the result by pg-boss table:
|
|
59
|
+
* - `"live"` — pending / active / retry rows still in
|
|
60
|
+
* `pgboss.job`. Retryable.
|
|
61
|
+
* - `"archive"` — rolled rows in `pgboss.archive`. Read-only
|
|
62
|
+
* (pg-boss won't pick them up; retry routes refuse to
|
|
63
|
+
* touch archive rows).
|
|
64
|
+
* Default (undefined) keeps the historical UNION behavior.
|
|
65
|
+
*/
|
|
66
|
+
source?: "live" | "archive";
|
|
67
|
+
}
|
|
68
|
+
interface NpJobListResult {
|
|
69
|
+
jobs: NpJobSummary[];
|
|
70
|
+
total: number;
|
|
71
|
+
}
|
|
72
|
+
/**
|
|
73
|
+
* Phase 23.5 — counts per terminal-and-transient state across the
|
|
74
|
+
* union of `pgboss.job` and `pgboss.archive`. Drives the stuck-job
|
|
75
|
+
* widget in `/admin/jobs` and is the building block plugin authors
|
|
76
|
+
* use to roll their own monitoring without taking a hard dep on
|
|
77
|
+
* pg-boss schema knowledge.
|
|
78
|
+
*
|
|
79
|
+
* Every state key is always present (defaulting to 0) so the
|
|
80
|
+
* caller can index without optional chaining and the UI can render
|
|
81
|
+
* a stable row order.
|
|
82
|
+
*/
|
|
83
|
+
interface NpJobStateCounts {
|
|
84
|
+
created: number;
|
|
85
|
+
active: number;
|
|
86
|
+
completed: number;
|
|
87
|
+
failed: number;
|
|
88
|
+
retry: number;
|
|
89
|
+
cancelled: number;
|
|
90
|
+
expired: number;
|
|
91
|
+
}
|
|
92
|
+
interface NpJobCountOptions {
|
|
93
|
+
/**
|
|
94
|
+
* Time-bounded query: include only jobs whose `created_on` is at
|
|
95
|
+
* or after this timestamp. Useful for "failures in the last 24
|
|
96
|
+
* hours" without paging through history.
|
|
97
|
+
*/
|
|
98
|
+
since?: Date;
|
|
99
|
+
}
|
|
100
|
+
/**
|
|
101
|
+
* Phase 13.2 — registered cron schedule (one row per
|
|
102
|
+
* `boss.schedule()` call). Surfaces in the admin so
|
|
103
|
+
* operators can confirm `system:revisionPrune` and friends
|
|
104
|
+
* are actually registered, not just declared in code.
|
|
105
|
+
*/
|
|
106
|
+
interface NpScheduleSummary {
|
|
107
|
+
/** pg-boss queue name (after `:` → `.` translation). */
|
|
108
|
+
name: string;
|
|
109
|
+
/**
|
|
110
|
+
* Issue #217 — the second half of `pgboss.schedule`'s primary
|
|
111
|
+
* key. Empty string for single-cadence schedules; `"daily"` /
|
|
112
|
+
* `"weekly"` (etc.) for jobs that need multiple cadences under
|
|
113
|
+
* one queue name. The admin UI uses `(name, key)` as a stable
|
|
114
|
+
* React key so duplicate-name rows render cleanly.
|
|
115
|
+
*/
|
|
116
|
+
key: string;
|
|
117
|
+
/** Cron expression as registered. */
|
|
118
|
+
cron: string;
|
|
119
|
+
/** Timezone the cron runs in (defaults to UTC in pg-boss). */
|
|
120
|
+
timezone: string | null;
|
|
121
|
+
/** Default payload used when the cron fires. */
|
|
122
|
+
data: unknown;
|
|
123
|
+
createdOn: string;
|
|
124
|
+
updatedOn?: string | null;
|
|
125
|
+
}
|
|
126
|
+
interface NpJobQueue {
|
|
127
|
+
enqueue(type: NpJobType, data: unknown): Promise<string>;
|
|
128
|
+
start(): Promise<void>;
|
|
129
|
+
stop(): Promise<void>;
|
|
130
|
+
/**
|
|
131
|
+
* Phase 13 — admin introspection. Optional on the interface
|
|
132
|
+
* so test stubs / mock queues don't have to implement them;
|
|
133
|
+
* the admin endpoint returns 501 when the active queue
|
|
134
|
+
* doesn't support introspection.
|
|
135
|
+
*/
|
|
136
|
+
listJobs?(options: NpJobListOptions): Promise<NpJobListResult>;
|
|
137
|
+
/** Re-enqueue a failed/cancelled job's payload as a new job. Returns the new job id. */
|
|
138
|
+
retryJob?(id: string): Promise<string>;
|
|
139
|
+
/** Cancel a pending job (no-op for already-running / completed jobs). */
|
|
140
|
+
cancelJob?(id: string): Promise<void>;
|
|
141
|
+
/**
|
|
142
|
+
* Phase 13.2 — list every cron schedule registered with the
|
|
143
|
+
* queue. Surfaces in the admin so operators can confirm
|
|
144
|
+
* recurring jobs are actually registered, not just declared
|
|
145
|
+
* in code.
|
|
146
|
+
*/
|
|
147
|
+
listSchedules?(): Promise<NpScheduleSummary[]>;
|
|
148
|
+
/**
|
|
149
|
+
* Phase 20.2 — stop the worker from claiming new jobs without
|
|
150
|
+
* tearing down the queue. In-flight jobs run to completion;
|
|
151
|
+
* the producer keeps enqueueing. Optional on the interface
|
|
152
|
+
* because non-pg-boss test stubs don't implement it.
|
|
153
|
+
*/
|
|
154
|
+
pauseProcessing?(): Promise<void>;
|
|
155
|
+
/** Phase 20.2 — undo `pauseProcessing()`. Idempotent. */
|
|
156
|
+
resumeProcessing?(): Promise<void>;
|
|
157
|
+
/** Phase 20.2 — `true` when this adapter is currently paused. */
|
|
158
|
+
isProcessingPaused?(): boolean;
|
|
159
|
+
/**
|
|
160
|
+
* Phase 22.4 — readiness probe. Issues a cheap round-trip against
|
|
161
|
+
* the queue backing store and returns `true` when the connection
|
|
162
|
+
* is alive AND the queue's schema is installed. Adapters that
|
|
163
|
+
* can't tell return `true` (a missing answer is not a failure
|
|
164
|
+
* signal). Errors are caught and reported as `false` — the probe
|
|
165
|
+
* caller never sees an exception.
|
|
166
|
+
*/
|
|
167
|
+
isHealthy?(): Promise<boolean>;
|
|
168
|
+
/**
|
|
169
|
+
* Phase 23.5 — return job counts grouped by state across both
|
|
170
|
+
* pg-boss tables. Optional on the interface so test stubs that
|
|
171
|
+
* don't model state need not implement it; the admin endpoint
|
|
172
|
+
* omits the stuck-job widget when missing.
|
|
173
|
+
*/
|
|
174
|
+
countByState?(options?: NpJobCountOptions): Promise<NpJobStateCounts>;
|
|
175
|
+
/**
|
|
176
|
+
* Phase 4.2 — per-plugin schedule observability. Returns one row per
|
|
177
|
+
* `(pluginId, taskId)` aggregated over the plugin's history in
|
|
178
|
+
* `pgboss.job` + `pgboss.archive`: last completion, last failure, and
|
|
179
|
+
* counts split by state over the last `windowDays` (default 7). The
|
|
180
|
+
* registry-side cron / description is overlaid on top by the caller.
|
|
181
|
+
*
|
|
182
|
+
* Optional so test stubs that don't model job history can omit it; the
|
|
183
|
+
* admin surface degrades to "registered schedules only" without it.
|
|
184
|
+
*/
|
|
185
|
+
getPluginScheduleStats?(pluginId: string, options?: {
|
|
186
|
+
windowDays?: number;
|
|
187
|
+
}): Promise<NpPluginScheduleStats[]>;
|
|
188
|
+
/**
|
|
189
|
+
* Issue #461 — bring the queue's `pgboss.schedule` rows in sync with
|
|
190
|
+
* what `getRegisteredPluginSchedules()` reports today. Bootstrap
|
|
191
|
+
* registers schedules once at worker startup; without this method,
|
|
192
|
+
* `reloadPlugins()` could only update the in-memory registry, leaving
|
|
193
|
+
* pg-boss firing the *old* set of crons until the worker restarted.
|
|
194
|
+
*
|
|
195
|
+
* Behavior:
|
|
196
|
+
* - schedule entry in registry but missing from pg-boss → added
|
|
197
|
+
* - schedule entry in pg-boss but missing from registry → removed
|
|
198
|
+
* - same name + different cron expression → re-added (unschedule
|
|
199
|
+
* then schedule, since pg-boss has no in-place cron update)
|
|
200
|
+
*
|
|
201
|
+
* `boss.work()` registration is NOT touched: in production deploys
|
|
202
|
+
* the worker lives in a separate process from the admin web server,
|
|
203
|
+
* so the web process can't install / drop work loops on its boss
|
|
204
|
+
* instance for the worker process to pick up. Operators see their
|
|
205
|
+
* cron rows updated immediately; jobs that fire for newly-added
|
|
206
|
+
* schedules will still need a worker restart to be processed.
|
|
207
|
+
* Documented in the admin reload toast.
|
|
208
|
+
*
|
|
209
|
+
* Optional on the interface so test stubs / non-pg-boss adapters
|
|
210
|
+
* skip cleanly.
|
|
211
|
+
*/
|
|
212
|
+
reconcilePluginSchedules?(): Promise<NpReconcileSchedulesResult>;
|
|
213
|
+
}
|
|
214
|
+
interface NpReconcileSchedulesResult {
|
|
215
|
+
/** New schedule rows written to `pgboss.schedule`. */
|
|
216
|
+
added: number;
|
|
217
|
+
/** Existing rows whose cron expression changed (unschedule → reschedule). */
|
|
218
|
+
updated: number;
|
|
219
|
+
/** Stale rows removed (plugin was uninstalled / disabled / renamed). */
|
|
220
|
+
removed: number;
|
|
221
|
+
/**
|
|
222
|
+
* `true` when this process holds the worker `boss.work()` registrations
|
|
223
|
+
* for the affected schedules. When false, operators should restart the
|
|
224
|
+
* worker to pick up newly-added schedules. Adapters that can't tell
|
|
225
|
+
* (in-memory test queue, future adapters) return `null`.
|
|
226
|
+
*/
|
|
227
|
+
workerOwnsRegistrations: boolean | null;
|
|
228
|
+
}
|
|
229
|
+
interface NpPluginScheduleStats {
|
|
230
|
+
taskId: string;
|
|
231
|
+
/** Most recent run, regardless of state. ISO timestamp or null. */
|
|
232
|
+
lastRunAt: string | null;
|
|
233
|
+
/** Most recent successful run. ISO timestamp or null. */
|
|
234
|
+
lastSuccessAt: string | null;
|
|
235
|
+
/** Most recent failed run. ISO timestamp or null. */
|
|
236
|
+
lastFailureAt: string | null;
|
|
237
|
+
/** Count of successful runs inside the window. */
|
|
238
|
+
completedCount: number;
|
|
239
|
+
/** Count of failed runs inside the window. */
|
|
240
|
+
failedCount: number;
|
|
241
|
+
/** The window the counts cover, in days. Echoed for UI labels. */
|
|
242
|
+
windowDays: number;
|
|
243
|
+
}
|
|
244
|
+
declare function setJobQueue(queue: NpJobQueue | null): void;
|
|
245
|
+
declare function getJobQueue(): NpJobQueue;
|
|
246
|
+
declare function getOptionalJobQueue(): NpJobQueue | null;
|
|
247
|
+
/**
|
|
248
|
+
* Enqueues a job if the queue is wired up; otherwise no-ops so callers
|
|
249
|
+
* (content pipeline, media processing) can run without pg-boss during MVP
|
|
250
|
+
* blog-only workloads. Return value is an empty string in the no-op path.
|
|
251
|
+
*/
|
|
252
|
+
declare function enqueueJob(type: NpJobType, data: unknown): Promise<string>;
|
|
253
|
+
|
|
254
|
+
declare function startWorker(connectionString: string, options?: {
|
|
255
|
+
schema?: string;
|
|
256
|
+
heartbeat?: boolean | {
|
|
257
|
+
meta?: Record<string, unknown>;
|
|
258
|
+
};
|
|
259
|
+
/**
|
|
260
|
+
* When true (default), startWorker installs SIGINT / SIGTERM
|
|
261
|
+
* listeners that call `stopWorker()` and `process.exit(0)`.
|
|
262
|
+
* Set false in environments that manage their own shutdown
|
|
263
|
+
* sequencing (custom supervisors, embedded test harnesses).
|
|
264
|
+
*/
|
|
265
|
+
installSignalHandlers?: boolean;
|
|
266
|
+
}): Promise<void>;
|
|
267
|
+
/**
|
|
268
|
+
* Enqueue-only setup for the web/API process. Wires pg-boss as the job queue
|
|
269
|
+
* singleton without attaching any `boss.work()` loops — those belong in the
|
|
270
|
+
* dedicated worker process. Calling `enqueueJob` after this will actually
|
|
271
|
+
* send jobs instead of no-op'ing.
|
|
272
|
+
*/
|
|
273
|
+
declare function startProducer(connectionString: string, options?: {
|
|
274
|
+
schema?: string;
|
|
275
|
+
}): Promise<void>;
|
|
276
|
+
declare function stopWorker(): Promise<void>;
|
|
277
|
+
declare function stopProducer(): Promise<void>;
|
|
278
|
+
|
|
279
|
+
declare class PgBossAdapter implements NpJobQueue {
|
|
280
|
+
private readonly boss;
|
|
281
|
+
/**
|
|
282
|
+
* Phase 20.2 — every queue we've called `boss.work()` on, plus
|
|
283
|
+
* the function that re-registers it. We need both because
|
|
284
|
+
* `pauseProcessing()` calls `boss.offWork(name)` (drops the
|
|
285
|
+
* worker) and `resumeProcessing()` has to re-call the original
|
|
286
|
+
* `boss.work(...)` to bring it back. Order is preserved so
|
|
287
|
+
* resume registers in the same order as start did.
|
|
288
|
+
*/
|
|
289
|
+
private readonly workRegistrations;
|
|
290
|
+
private paused;
|
|
291
|
+
/**
|
|
292
|
+
* Flips `true` after `start()` runs (full worker mode). `startProducer()`
|
|
293
|
+
* doesn't set it. Used by `reconcilePluginSchedules()` to tell admins
|
|
294
|
+
* whether this process owns the `boss.work()` loops for plugin schedules
|
|
295
|
+
* — the same boss instance can act as producer-only in the web server
|
|
296
|
+
* and full worker in the worker process.
|
|
297
|
+
*/
|
|
298
|
+
private workerStarted;
|
|
299
|
+
constructor(connectionString: string, options?: ConstructorOptions);
|
|
300
|
+
enqueue(type: NpJobType, data: unknown): Promise<string>;
|
|
301
|
+
/**
|
|
302
|
+
* Opens the pg-boss connection and runs its migrations. Safe to call from a
|
|
303
|
+
* non-worker process (e.g. the Next.js server) so it can enqueue jobs.
|
|
304
|
+
*/
|
|
305
|
+
startProducer(): Promise<void>;
|
|
306
|
+
/**
|
|
307
|
+
* Full start: opens the connection (idempotent with startProducer) and
|
|
308
|
+
* registers `boss.work()` loops for every handler in the registry. Call
|
|
309
|
+
* this from the dedicated worker process.
|
|
310
|
+
*/
|
|
311
|
+
start(): Promise<void>;
|
|
312
|
+
/**
|
|
313
|
+
* Phase 20.2 — drop every registered worker so the boss stops
|
|
314
|
+
* claiming new jobs. The pg-boss connection stays open; the
|
|
315
|
+
* producer can keep enqueueing while paused. In-flight jobs
|
|
316
|
+
* picked up before pause finish normally because pg-boss only
|
|
317
|
+
* cancels the polling loop, not the fetch already in flight.
|
|
318
|
+
*/
|
|
319
|
+
pauseProcessing(): Promise<void>;
|
|
320
|
+
/** Phase 20.2 — re-run every captured `boss.work()` registration. Idempotent. */
|
|
321
|
+
resumeProcessing(): Promise<void>;
|
|
322
|
+
isProcessingPaused(): boolean;
|
|
323
|
+
/**
|
|
324
|
+
* Phase 22.4 — readiness probe round-trip. `boss.isInstalled()`
|
|
325
|
+
* issues a single SELECT against `pgboss.version`, so a true
|
|
326
|
+
* answer proves both that the DB connection is alive AND that
|
|
327
|
+
* pg-boss's schema migrations have applied. Any throw — pool
|
|
328
|
+
* dead, schema missing, permissions revoked — is caught and
|
|
329
|
+
* reported as `false`; the readiness probe never sees an
|
|
330
|
+
* exception bubble out of the queue check.
|
|
331
|
+
*/
|
|
332
|
+
isHealthy(): Promise<boolean>;
|
|
333
|
+
stop(): Promise<void>;
|
|
334
|
+
scheduleRecurring(): Promise<void>;
|
|
335
|
+
getBoss(): PgBoss;
|
|
336
|
+
/**
|
|
337
|
+
* Phase 13 — admin job introspection. Joins pgboss.job
|
|
338
|
+
* (pending / active / retry) and pgboss.archive (completed
|
|
339
|
+
* / failed / expired) into one unified list.
|
|
340
|
+
*
|
|
341
|
+
* Phase 13.2 — `since` filter for time-bounded queries
|
|
342
|
+
* ("last 24 hours") and accurate `total` via a parallel
|
|
343
|
+
* COUNT(*) so the admin pagination shows the right count.
|
|
344
|
+
* The COUNT runs against the same UNION; the per-page
|
|
345
|
+
* SELECT still gets the row data.
|
|
346
|
+
*/
|
|
347
|
+
listJobs(options: NpJobListOptions): Promise<NpJobListResult>;
|
|
348
|
+
/**
|
|
349
|
+
* Phase 13.2 — list every cron schedule registered in the
|
|
350
|
+
* queue. Reads from `pgboss.schedule`, which is the table
|
|
351
|
+
* pg-boss writes to on each `boss.schedule()` call. Sorted
|
|
352
|
+
* by name for stable display.
|
|
353
|
+
*/
|
|
354
|
+
listSchedules(): Promise<NpScheduleSummary[]>;
|
|
355
|
+
/**
|
|
356
|
+
* Phase 4.2 — pulls per-(pluginId, taskId) execution stats from the
|
|
357
|
+
* union of `pgboss.job` (in-flight + recently-completed) and
|
|
358
|
+
* `pgboss.archive` (rolled-over history). One row per taskId so the
|
|
359
|
+
* caller can index without a second pass.
|
|
360
|
+
*
|
|
361
|
+
* The window default is 7 days because longer windows force the
|
|
362
|
+
* archive table into the hot path and admins typically want recent
|
|
363
|
+
* health, not lifetime totals. Increase via `windowDays` if surfacing
|
|
364
|
+
* a "30-day reliability" widget.
|
|
365
|
+
*/
|
|
366
|
+
getPluginScheduleStats(pluginId: string, options?: {
|
|
367
|
+
windowDays?: number;
|
|
368
|
+
}): Promise<NpPluginScheduleStats[]>;
|
|
369
|
+
/**
|
|
370
|
+
* Issue #461 — diff the in-memory plugin schedule registry against the
|
|
371
|
+
* `pgboss.schedule` rows whose name starts with `plugin.scheduledTask.*`
|
|
372
|
+
* and bring pg-boss in line. Without this, `reloadPlugins()` only
|
|
373
|
+
* rebuilt the in-process registry and pg-boss kept firing the old set
|
|
374
|
+
* of crons until the worker process restarted — the admin "Reload all"
|
|
375
|
+
* toast was promising behavior the system didn't deliver.
|
|
376
|
+
*
|
|
377
|
+
* Worker `boss.work()` registrations stay untouched. In production the
|
|
378
|
+
* worker is a separate process with its own boss instance; the web
|
|
379
|
+
* process can't add or drop work loops there. We surface that via
|
|
380
|
+
* `workerOwnsRegistrations` so the admin UI can warn the operator.
|
|
381
|
+
*/
|
|
382
|
+
reconcilePluginSchedules(): Promise<NpReconcileSchedulesResult>;
|
|
383
|
+
/**
|
|
384
|
+
* Phase 23.5 — `GROUP BY state` across the union of pgboss.job
|
|
385
|
+
* (live) and pgboss.archive (rolled). Returns a fully-populated
|
|
386
|
+
* record so callers can index without optional chaining.
|
|
387
|
+
*
|
|
388
|
+
* Uses `created_on` for the optional `since` filter. Both tables
|
|
389
|
+
* carry the same column, so the union pre-filter is a single
|
|
390
|
+
* predicate.
|
|
391
|
+
*/
|
|
392
|
+
countByState(options?: NpJobCountOptions): Promise<NpJobStateCounts>;
|
|
393
|
+
retryJob(id: string): Promise<string>;
|
|
394
|
+
cancelJob(id: string): Promise<void>;
|
|
395
|
+
}
|
|
396
|
+
|
|
397
|
+
interface ContentJobData {
|
|
398
|
+
collection: string;
|
|
399
|
+
documentId: string;
|
|
400
|
+
operation: "create" | "update";
|
|
401
|
+
userId: string;
|
|
402
|
+
}
|
|
403
|
+
interface ContentDeleteJobData {
|
|
404
|
+
collection: string;
|
|
405
|
+
documentId: string;
|
|
406
|
+
userId: string;
|
|
407
|
+
}
|
|
408
|
+
interface ResolvedHookContext {
|
|
409
|
+
collectionConfig: NpCollectionConfig;
|
|
410
|
+
data: Record<string, unknown>;
|
|
411
|
+
/**
|
|
412
|
+
* Resolved staff session, or `null` when the originating actor
|
|
413
|
+
* was a member (Phase 9.7o widened the hook surface so member
|
|
414
|
+
* writes also fire `afterCreate` / `afterUpdate`).
|
|
415
|
+
*/
|
|
416
|
+
user: NpAuthUser | null;
|
|
417
|
+
/**
|
|
418
|
+
* Polymorphic actor reference. Resolvers should derive this
|
|
419
|
+
* from whatever actor metadata they recorded with the job —
|
|
420
|
+
* e.g. by checking whether the saved `userId` is null
|
|
421
|
+
* (member-authored) and looking up the member id separately.
|
|
422
|
+
*/
|
|
423
|
+
principal: NpPrincipal;
|
|
424
|
+
originalDoc?: Record<string, unknown> | null;
|
|
425
|
+
}
|
|
426
|
+
interface ResolvedDeleteHookContext {
|
|
427
|
+
collectionConfig: NpCollectionConfig;
|
|
428
|
+
data: Record<string, unknown>;
|
|
429
|
+
user: NpAuthUser | null;
|
|
430
|
+
principal: NpPrincipal;
|
|
431
|
+
}
|
|
432
|
+
interface BuiltinJobContext {
|
|
433
|
+
resolveContentAfterSaveContext?: (data: ContentJobData) => Promise<ResolvedHookContext | null> | ResolvedHookContext | null;
|
|
434
|
+
resolveContentAfterDeleteContext?: (data: ContentDeleteJobData) => Promise<ResolvedDeleteHookContext | null> | ResolvedDeleteHookContext | null;
|
|
435
|
+
processImage?: (data: unknown) => Promise<void> | void;
|
|
436
|
+
cleanupMedia?: (data: unknown) => Promise<void> | void;
|
|
437
|
+
runScheduledPluginTask?: (data: unknown) => Promise<void> | void;
|
|
438
|
+
pruneRevisions?: () => Promise<void> | void;
|
|
439
|
+
cleanupSessions?: () => Promise<void> | void;
|
|
440
|
+
sendPasswordReset?: (data: unknown) => Promise<void> | void;
|
|
441
|
+
sendMemberVerifyEmail?: (data: unknown) => Promise<void> | void;
|
|
442
|
+
sendMemberPasswordReset?: (data: unknown) => Promise<void> | void;
|
|
443
|
+
}
|
|
444
|
+
declare function configureBuiltinJobContext(context: Partial<BuiltinJobContext>): void;
|
|
445
|
+
declare function registerBuiltinHandlers(): void;
|
|
446
|
+
|
|
447
|
+
/**
|
|
448
|
+
* Phase 19 — worker liveness signal.
|
|
449
|
+
*
|
|
450
|
+
* The worker process upserts its row every
|
|
451
|
+
* `WORKER_HEARTBEAT_INTERVAL_MS` so admins can tell whether the
|
|
452
|
+
* pg-boss queue actually has a draining process attached.
|
|
453
|
+
* Without this the only signal was "Pending stays high while
|
|
454
|
+
* Completed doesn't grow," which a stuck DB or a stopped
|
|
455
|
+
* worker look identical from outside.
|
|
456
|
+
*
|
|
457
|
+
* Stale rows (no heartbeat in `WORKER_STALE_THRESHOLD_MS`) are
|
|
458
|
+
* reported `unhealthy`; the row stays in place until an
|
|
459
|
+
* operator GCs it or a worker with the same id rejoins. The
|
|
460
|
+
* id is `hostname:pid` so a restarted process on the same host
|
|
461
|
+
* naturally reclaims its row instead of stacking duplicates.
|
|
462
|
+
*/
|
|
463
|
+
/**
|
|
464
|
+
* How often a running worker pings its row. Tightening lets
|
|
465
|
+
* `lastSeenAt` track wall-clock more closely; loosening cuts
|
|
466
|
+
* write traffic on idle workers. `NP_WORKER_HEARTBEAT_SECONDS`.
|
|
467
|
+
*/
|
|
468
|
+
declare const WORKER_HEARTBEAT_INTERVAL_MS: number;
|
|
469
|
+
/**
|
|
470
|
+
* After how long with no heartbeat a worker is treated as
|
|
471
|
+
* unhealthy in the admin UI / health check. Default 90s is
|
|
472
|
+
* `3 × HEARTBEAT_INTERVAL` so a single missed beat doesn't trip
|
|
473
|
+
* the alarm. `NP_WORKER_STALE_THRESHOLD_SECONDS`.
|
|
474
|
+
*/
|
|
475
|
+
declare const WORKER_STALE_THRESHOLD_MS: number;
|
|
476
|
+
interface NpWorkerHeartbeat {
|
|
477
|
+
id: string;
|
|
478
|
+
status: string;
|
|
479
|
+
startedAt: Date;
|
|
480
|
+
lastSeenAt: Date;
|
|
481
|
+
meta: Record<string, unknown>;
|
|
482
|
+
}
|
|
483
|
+
interface NpWorkerHealthSummary {
|
|
484
|
+
workers: Array<NpWorkerHeartbeat & {
|
|
485
|
+
alive: boolean;
|
|
486
|
+
lastSeenAgoMs: number;
|
|
487
|
+
}>;
|
|
488
|
+
aliveCount: number;
|
|
489
|
+
totalCount: number;
|
|
490
|
+
/** ISO timestamp of the most recent heartbeat across all workers. */
|
|
491
|
+
newestHeartbeat: string | null;
|
|
492
|
+
}
|
|
493
|
+
/**
|
|
494
|
+
* Stamp a single heartbeat row. Used by `startHeartbeatLoop`
|
|
495
|
+
* and exposed for tests so they can inject fake worker rows
|
|
496
|
+
* without spinning up a real interval.
|
|
497
|
+
*/
|
|
498
|
+
declare function recordHeartbeat(workerId: string, meta?: Record<string, unknown>): Promise<void>;
|
|
499
|
+
/**
|
|
500
|
+
* Mark the row as `stopped` so the admin sees a graceful
|
|
501
|
+
* shutdown rather than the row drifting into `unhealthy`.
|
|
502
|
+
*/
|
|
503
|
+
declare function markWorkerStopped(workerId: string): Promise<void>;
|
|
504
|
+
/**
|
|
505
|
+
* Read every worker row, decorate with `alive` + `lastSeenAgoMs`
|
|
506
|
+
* relative to `now`. Sorted with the most recent heartbeat
|
|
507
|
+
* first so the admin's first row is the freshest worker.
|
|
508
|
+
*/
|
|
509
|
+
declare function listWorkerHealth(now?: Date): Promise<NpWorkerHealthSummary>;
|
|
510
|
+
/**
|
|
511
|
+
* Manual GC hook — purge worker rows whose `last_seen_at` is
|
|
512
|
+
* older than `olderThan`. Operators can call this from a
|
|
513
|
+
* cron / admin action when the table accumulates ghosts.
|
|
514
|
+
*/
|
|
515
|
+
declare function purgeStaleWorkers(olderThan?: Date): Promise<number>;
|
|
516
|
+
/** Return only the rows currently considered alive. Cheap probe for boot health. */
|
|
517
|
+
declare function countAliveWorkers(now?: Date): Promise<number>;
|
|
518
|
+
|
|
519
|
+
interface NpJobsPauseState {
|
|
520
|
+
paused: boolean;
|
|
521
|
+
/** ISO timestamp captured the last time the flag flipped. */
|
|
522
|
+
changedAt: string;
|
|
523
|
+
/** User id (staff) who flipped the flag, when known. */
|
|
524
|
+
changedByUserId: string | null;
|
|
525
|
+
/** Optional note an operator can leave for the next person. */
|
|
526
|
+
reason: string | null;
|
|
527
|
+
}
|
|
528
|
+
declare function getJobsPauseState(): Promise<NpJobsPauseState>;
|
|
529
|
+
interface SetJobsPauseStateInput {
|
|
530
|
+
paused: boolean;
|
|
531
|
+
changedByUserId?: string | null;
|
|
532
|
+
reason?: string | null;
|
|
533
|
+
}
|
|
534
|
+
declare function setJobsPauseState(input: SetJobsPauseStateInput): Promise<NpJobsPauseState>;
|
|
535
|
+
declare const PAUSE_SYNC_INTERVAL_MS = 30000;
|
|
536
|
+
|
|
537
|
+
declare function runInJobContext<T>(jobId: string, fn: () => Promise<T> | T): Promise<T> | T;
|
|
538
|
+
declare function getCurrentJobId(): string | null;
|
|
539
|
+
/**
|
|
540
|
+
* Record one log entry for the currently-running job. Async because
|
|
541
|
+
* it writes to Postgres; callers can `void` the promise if they
|
|
542
|
+
* don't need to wait. No-ops outside a job context (returns
|
|
543
|
+
* immediately without touching the DB).
|
|
544
|
+
*
|
|
545
|
+
* Errors writing to the log table are swallowed via the framework
|
|
546
|
+
* logger at `warn` — a logging failure must never cascade into a
|
|
547
|
+
* job failure or shutdown loop.
|
|
548
|
+
*/
|
|
549
|
+
declare function recordJobLog(level: NpLogLevel, message: string, context?: Record<string, unknown>): Promise<void>;
|
|
550
|
+
interface NpJobLogEntry {
|
|
551
|
+
id: string;
|
|
552
|
+
jobId: string;
|
|
553
|
+
level: NpLogLevel;
|
|
554
|
+
message: string;
|
|
555
|
+
context: Record<string, unknown> | null;
|
|
556
|
+
createdAt: Date;
|
|
557
|
+
}
|
|
558
|
+
interface ListJobLogsOptions {
|
|
559
|
+
/** Cap on rows returned. Default 200, max 1000 to keep the admin UI snappy. */
|
|
560
|
+
limit?: number;
|
|
561
|
+
/** Skip this many rows for pagination. */
|
|
562
|
+
offset?: number;
|
|
563
|
+
}
|
|
564
|
+
/**
|
|
565
|
+
* Fetch log entries for one job in chronological order. Paged so
|
|
566
|
+
* a runaway handler doesn't blow up the admin UI.
|
|
567
|
+
*/
|
|
568
|
+
declare function listJobLogs(jobId: string, options?: ListJobLogsOptions): Promise<NpJobLogEntry[]>;
|
|
569
|
+
/**
|
|
570
|
+
* How long per-job log rows survive before the cleanup handler
|
|
571
|
+
* deletes them. Compliance regimes (GDPR, SOX) frequently dictate
|
|
572
|
+
* a specific window — override via `NP_JOB_LOG_RETENTION_DAYS`.
|
|
573
|
+
*/
|
|
574
|
+
declare const DEFAULT_JOB_LOG_RETENTION_MS: number;
|
|
575
|
+
/**
|
|
576
|
+
* Delete log rows older than the cutoff. Safe to call from a
|
|
577
|
+
* scheduled handler — does not touch logs for active or recent
|
|
578
|
+
* jobs unless they pre-date the cutoff.
|
|
579
|
+
*
|
|
580
|
+
* Returns the row count deleted so the cron handler can log a
|
|
581
|
+
* useful retention summary.
|
|
582
|
+
*/
|
|
583
|
+
declare function pruneJobLogsOlderThan(cutoff: Date): Promise<number>;
|
|
584
|
+
/**
|
|
585
|
+
* Count entries for a job — drives the admin badge "37 log lines"
|
|
586
|
+
* without paying for the page payload until the operator expands.
|
|
587
|
+
*/
|
|
588
|
+
declare function countJobLogs(jobId: string, sinceCreatedAt?: Date): Promise<number>;
|
|
589
|
+
|
|
590
|
+
export { listJobLogs as A, listWorkerHealth as B, markWorkerStopped as C, DEFAULT_JOB_LOG_RETENTION_MS as D, pruneJobLogsOlderThan as E, purgeStaleWorkers as F, recordHeartbeat as G, recordJobLog as H, registerBuiltinHandlers as I, registerJobHandler as J, runInJobContext as K, type ListJobLogsOptions as L, setJobQueue as M, type NpJobCountOptions as N, setJobsPauseState as O, PAUSE_SYNC_INTERVAL_MS as P, startProducer as Q, startWorker as R, type SetJobsPauseStateInput as S, stopProducer as T, stopWorker as U, WORKER_HEARTBEAT_INTERVAL_MS as W, type NpJobHandler as a, type NpJobListOptions as b, type NpJobListResult as c, type NpJobLogEntry as d, type NpJobQueue as e, type NpJobState as f, type NpJobStateCounts as g, type NpJobSummary as h, type NpJobsPauseState as i, type NpPluginScheduleStats as j, type NpReconcileSchedulesResult as k, type NpScheduleSummary as l, type NpWorkerHealthSummary as m, type NpWorkerHeartbeat as n, PgBossAdapter as o, WORKER_STALE_THRESHOLD_MS as p, configureBuiltinJobContext as q, countAliveWorkers as r, countJobLogs as s, enqueueJob as t, getAllJobHandlers as u, getCurrentJobId as v, getJobHandler as w, getJobQueue as x, getJobsPauseState as y, getOptionalJobQueue as z };
|