@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.
Files changed (171) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +69 -0
  3. package/dist/audit-54XLVCWD.js +14 -0
  4. package/dist/audit-54XLVCWD.js.map +1 -0
  5. package/dist/auth.d.ts +640 -0
  6. package/dist/auth.js +94 -0
  7. package/dist/auth.js.map +1 -0
  8. package/dist/can-YLUHRJAB.js +19 -0
  9. package/dist/can-YLUHRJAB.js.map +1 -0
  10. package/dist/chunk-2G264RCD.js +68 -0
  11. package/dist/chunk-2G264RCD.js.map +1 -0
  12. package/dist/chunk-2YDGE7YX.js +92 -0
  13. package/dist/chunk-2YDGE7YX.js.map +1 -0
  14. package/dist/chunk-473S4TER.js +538 -0
  15. package/dist/chunk-473S4TER.js.map +1 -0
  16. package/dist/chunk-4ZLMEKFX.js +18 -0
  17. package/dist/chunk-4ZLMEKFX.js.map +1 -0
  18. package/dist/chunk-55FU6WED.js +179 -0
  19. package/dist/chunk-55FU6WED.js.map +1 -0
  20. package/dist/chunk-6YI5K2TI.js +1959 -0
  21. package/dist/chunk-6YI5K2TI.js.map +1 -0
  22. package/dist/chunk-BHK3AD3Q.js +41 -0
  23. package/dist/chunk-BHK3AD3Q.js.map +1 -0
  24. package/dist/chunk-CRUQBZUF.js +39 -0
  25. package/dist/chunk-CRUQBZUF.js.map +1 -0
  26. package/dist/chunk-CTSQ7BRI.js +175 -0
  27. package/dist/chunk-CTSQ7BRI.js.map +1 -0
  28. package/dist/chunk-DK2JBJH7.js +81 -0
  29. package/dist/chunk-DK2JBJH7.js.map +1 -0
  30. package/dist/chunk-DP2PREDU.js +597 -0
  31. package/dist/chunk-DP2PREDU.js.map +1 -0
  32. package/dist/chunk-EQ2Z3KMD.js +24 -0
  33. package/dist/chunk-EQ2Z3KMD.js.map +1 -0
  34. package/dist/chunk-FZ7O6DWI.js +305 -0
  35. package/dist/chunk-FZ7O6DWI.js.map +1 -0
  36. package/dist/chunk-ISLYFQWL.js +1270 -0
  37. package/dist/chunk-ISLYFQWL.js.map +1 -0
  38. package/dist/chunk-JJL74ZPK.js +68 -0
  39. package/dist/chunk-JJL74ZPK.js.map +1 -0
  40. package/dist/chunk-JKXAPSU4.js +24 -0
  41. package/dist/chunk-JKXAPSU4.js.map +1 -0
  42. package/dist/chunk-KU5M27ZC.js +24 -0
  43. package/dist/chunk-KU5M27ZC.js.map +1 -0
  44. package/dist/chunk-LSHHRDVR.js +34 -0
  45. package/dist/chunk-LSHHRDVR.js.map +1 -0
  46. package/dist/chunk-M43PGOQY.js +715 -0
  47. package/dist/chunk-M43PGOQY.js.map +1 -0
  48. package/dist/chunk-MEJAHXIO.js +150 -0
  49. package/dist/chunk-MEJAHXIO.js.map +1 -0
  50. package/dist/chunk-NUCGHWCF.js +101 -0
  51. package/dist/chunk-NUCGHWCF.js.map +1 -0
  52. package/dist/chunk-OK5HOCQI.js +845 -0
  53. package/dist/chunk-OK5HOCQI.js.map +1 -0
  54. package/dist/chunk-OROPGO65.js +13 -0
  55. package/dist/chunk-OROPGO65.js.map +1 -0
  56. package/dist/chunk-PPAS4SZR.js +176 -0
  57. package/dist/chunk-PPAS4SZR.js.map +1 -0
  58. package/dist/chunk-PPBWRKO2.js +171 -0
  59. package/dist/chunk-PPBWRKO2.js.map +1 -0
  60. package/dist/chunk-PZ5AY32C.js +10 -0
  61. package/dist/chunk-PZ5AY32C.js.map +1 -0
  62. package/dist/chunk-QO7LAQZH.js +321 -0
  63. package/dist/chunk-QO7LAQZH.js.map +1 -0
  64. package/dist/chunk-QVJ2HCAX.js +225 -0
  65. package/dist/chunk-QVJ2HCAX.js.map +1 -0
  66. package/dist/chunk-RIPHIRPP.js +68 -0
  67. package/dist/chunk-RIPHIRPP.js.map +1 -0
  68. package/dist/chunk-S27S42QY.js +134 -0
  69. package/dist/chunk-S27S42QY.js.map +1 -0
  70. package/dist/chunk-SBCVAC2Z.js +40 -0
  71. package/dist/chunk-SBCVAC2Z.js.map +1 -0
  72. package/dist/chunk-TFJ4MKPH.js +694 -0
  73. package/dist/chunk-TFJ4MKPH.js.map +1 -0
  74. package/dist/chunk-THX3SHYA.js +75 -0
  75. package/dist/chunk-THX3SHYA.js.map +1 -0
  76. package/dist/chunk-UGQSQO5B.js +222 -0
  77. package/dist/chunk-UGQSQO5B.js.map +1 -0
  78. package/dist/chunk-V2UNHGAP.js +26 -0
  79. package/dist/chunk-V2UNHGAP.js.map +1 -0
  80. package/dist/chunk-VGTPQXNQ.js +2790 -0
  81. package/dist/chunk-VGTPQXNQ.js.map +1 -0
  82. package/dist/chunk-VNIHXQ7W.js +194 -0
  83. package/dist/chunk-VNIHXQ7W.js.map +1 -0
  84. package/dist/chunk-WV272MPW.js +31 -0
  85. package/dist/chunk-WV272MPW.js.map +1 -0
  86. package/dist/chunk-X5KKBOUS.js +26 -0
  87. package/dist/chunk-X5KKBOUS.js.map +1 -0
  88. package/dist/chunk-XANPEOJC.js +17 -0
  89. package/dist/chunk-XANPEOJC.js.map +1 -0
  90. package/dist/chunk-XPVQIHAQ.js +83 -0
  91. package/dist/chunk-XPVQIHAQ.js.map +1 -0
  92. package/dist/chunk-ZCINJSS4.js +75 -0
  93. package/dist/chunk-ZCINJSS4.js.map +1 -0
  94. package/dist/community.d.ts +1425 -0
  95. package/dist/community.js +206 -0
  96. package/dist/community.js.map +1 -0
  97. package/dist/config-2GDU7PCK.js +32 -0
  98. package/dist/config-2GDU7PCK.js.map +1 -0
  99. package/dist/context-MNZ4QXPC.js +16 -0
  100. package/dist/context-MNZ4QXPC.js.map +1 -0
  101. package/dist/db-schema.d.ts +4 -0
  102. package/dist/db-schema.js +102 -0
  103. package/dist/db-schema.js.map +1 -0
  104. package/dist/db.d.ts +7 -0
  105. package/dist/db.js +117 -0
  106. package/dist/db.js.map +1 -0
  107. package/dist/digest-SY42GQSU.js +17 -0
  108. package/dist/digest-SY42GQSU.js.map +1 -0
  109. package/dist/errors-5OS3S2J3.js +22 -0
  110. package/dist/errors-5OS3S2J3.js.map +1 -0
  111. package/dist/host-OBOI4MJK.js +51 -0
  112. package/dist/host-OBOI4MJK.js.map +1 -0
  113. package/dist/i18n.d.ts +301 -0
  114. package/dist/i18n.js +68 -0
  115. package/dist/i18n.js.map +1 -0
  116. package/dist/index-B6-_vr_m.d.ts +590 -0
  117. package/dist/index-CY55LC0u.d.ts +4722 -0
  118. package/dist/index-CeiTvwbp.d.ts +168 -0
  119. package/dist/index-XwP1ET8b.d.ts +61 -0
  120. package/dist/index.d.ts +2037 -0
  121. package/dist/index.js +2205 -0
  122. package/dist/index.js.map +1 -0
  123. package/dist/job-log-VZXWQUDK.js +24 -0
  124. package/dist/job-log-VZXWQUDK.js.map +1 -0
  125. package/dist/jobs.d.ts +4 -0
  126. package/dist/jobs.js +76 -0
  127. package/dist/jobs.js.map +1 -0
  128. package/dist/logger-DqGaOU_j.d.ts +29 -0
  129. package/dist/logger-S7REWDNE.js +16 -0
  130. package/dist/logger-S7REWDNE.js.map +1 -0
  131. package/dist/media.d.ts +5 -0
  132. package/dist/media.js +41 -0
  133. package/dist/media.js.map +1 -0
  134. package/dist/mentions-2IHFVSHW.js +23 -0
  135. package/dist/mentions-2IHFVSHW.js.map +1 -0
  136. package/dist/mutes-EWAE5FZR.js +21 -0
  137. package/dist/mutes-EWAE5FZR.js.map +1 -0
  138. package/dist/notification-prefs-VPJDU7I6.js +21 -0
  139. package/dist/notification-prefs-VPJDU7I6.js.map +1 -0
  140. package/dist/observability.d.ts +156 -0
  141. package/dist/observability.js +32 -0
  142. package/dist/observability.js.map +1 -0
  143. package/dist/profanity-adapter-NU2JQSLX.js +12 -0
  144. package/dist/profanity-adapter-NU2JQSLX.js.map +1 -0
  145. package/dist/queue-XE5BC75T.js +14 -0
  146. package/dist/queue-XE5BC75T.js.map +1 -0
  147. package/dist/rate-limit.d.ts +99 -0
  148. package/dist/rate-limit.js +14 -0
  149. package/dist/rate-limit.js.map +1 -0
  150. package/dist/registry-XIXDEPVI.js +31 -0
  151. package/dist/registry-XIXDEPVI.js.map +1 -0
  152. package/dist/reputation-JRL2YQHM.js +11 -0
  153. package/dist/reputation-JRL2YQHM.js.map +1 -0
  154. package/dist/routes.d.ts +43 -0
  155. package/dist/routes.js +12 -0
  156. package/dist/routes.js.map +1 -0
  157. package/dist/scheduled-CIQM57HT.js +20 -0
  158. package/dist/scheduled-CIQM57HT.js.map +1 -0
  159. package/dist/seo.d.ts +410 -0
  160. package/dist/seo.js +44 -0
  161. package/dist/seo.js.map +1 -0
  162. package/dist/settings-FOBIESPB.js +17 -0
  163. package/dist/settings-FOBIESPB.js.map +1 -0
  164. package/dist/spam-adapter-XX3G737Z.js +12 -0
  165. package/dist/spam-adapter-XX3G737Z.js.map +1 -0
  166. package/dist/strings-VAE47B2C.js +29 -0
  167. package/dist/strings-VAE47B2C.js.map +1 -0
  168. package/dist/templates-IFVJMCJ6.js +12 -0
  169. package/dist/templates-IFVJMCJ6.js.map +1 -0
  170. package/dist/types-TlsbXS0T.d.ts +871 -0
  171. 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 };