@shivaedev/effect-pg-boss 0.5.0 → 0.6.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/CHANGELOG.md +13 -0
- package/README.md +53 -12
- package/dist/server/client.d.ts +7 -1
- package/dist/server/client.d.ts.map +1 -1
- package/dist/server/client.js.map +1 -1
- package/dist/server/definition.d.ts +5 -5
- package/dist/server/definition.d.ts.map +1 -1
- package/dist/server/error.d.ts +10 -1
- package/dist/server/error.d.ts.map +1 -1
- package/dist/server/error.js +5 -0
- package/dist/server/error.js.map +1 -1
- package/dist/server/health.d.ts +6 -0
- package/dist/server/health.d.ts.map +1 -1
- package/dist/server/health.js.map +1 -1
- package/dist/server/internal/countsFor.d.ts +5 -0
- package/dist/server/internal/countsFor.d.ts.map +1 -0
- package/dist/server/internal/countsFor.js +27 -0
- package/dist/server/internal/countsFor.js.map +1 -0
- package/dist/server/internal/ensureQueue.d.ts +4 -0
- package/dist/server/internal/ensureQueue.d.ts.map +1 -0
- package/dist/server/internal/ensureQueue.js +27 -0
- package/dist/server/internal/ensureQueue.js.map +1 -0
- package/dist/server/internal/jobControl.d.ts +5 -0
- package/dist/server/internal/jobControl.d.ts.map +1 -0
- package/dist/server/internal/jobControl.js +34 -0
- package/dist/server/internal/jobControl.js.map +1 -0
- package/dist/server/internal/register.d.ts +2 -1
- package/dist/server/internal/register.d.ts.map +1 -1
- package/dist/server/internal/register.js +5 -22
- package/dist/server/internal/register.js.map +1 -1
- package/dist/server/internal/service.d.ts +1 -1
- package/dist/server/internal/service.d.ts.map +1 -1
- package/dist/server/internal/service.js +7 -2
- package/dist/server/internal/service.js.map +1 -1
- package/dist/server/service.d.ts +8 -5
- package/dist/server/service.d.ts.map +1 -1
- package/dist/server/service.js +1 -1
- package/dist/server/service.js.map +1 -1
- package/docs/north-star.md +1 -1
- package/docs/roadmap.md +9 -3
- package/package.json +2 -2
- package/src/server/client.ts +17 -1
- package/src/server/definition.ts +5 -5
- package/src/server/error.ts +10 -1
- package/src/server/health.ts +7 -0
- package/src/server/internal/countsFor.ts +33 -0
- package/src/server/internal/ensureQueue.ts +29 -0
- package/src/server/internal/jobControl.ts +46 -0
- package/src/server/internal/register.ts +6 -24
- package/src/server/internal/service.ts +7 -2
- package/src/server/service.ts +13 -7
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,18 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## 0.6.0 - 2026-10-17
|
|
4
|
+
|
|
5
|
+
### Added
|
|
6
|
+
|
|
7
|
+
- `PgBossService.counts(queue)` returns live `QueueCounts` without waiting for the pg-boss monitor; `health` keeps using cached counts.
|
|
8
|
+
- `PgBossService.getJob(queue, id)` returns optional job metadata with Schema-decoded payloads and decoding service requirements. `PgBossService.retry(queue, id)` reprocesses failed or retry-waiting jobs under their original ID.
|
|
9
|
+
- `PgBossQueueOptionsError` identifies immutable queue option conflicts during Layer acquisition. Custom `PgBossClient` factories can provide optional native `findJobs`, `retry`, `update`, `getDb` and `updateQueue` capabilities for the new operations.
|
|
10
|
+
|
|
11
|
+
### Fixed
|
|
12
|
+
|
|
13
|
+
- Queue and schedule definitions accept pg-boss's full `Queue` options except the owned name and dead-letter setup, including notification delivery, policy, partitioning, heartbeat and backlog warnings.
|
|
14
|
+
- Startup reconciles declared mutable queue settings through `updateQueue`, including expiry and retry limits, so they reach existing queues after restart.
|
|
15
|
+
|
|
3
16
|
## 0.5.0 - 2026-10-13
|
|
4
17
|
|
|
5
18
|
### Added
|
package/README.md
CHANGED
|
@@ -31,7 +31,7 @@ A **queue definition** names a pg-boss queue and the Schema of its payload. Appl
|
|
|
31
31
|
|
|
32
32
|
A **schedule definition** describes a queue with a cron trigger. `schedule.run(effect)` pairs it with an Effect that needs no payload. Queue workers and scheduled workers are **registrations**: the values you put in the jobs Layer's `jobs` list.
|
|
33
33
|
|
|
34
|
-
The application creates one jobs service with `makePgBoss(identifier)`. Its Layer starts a client, registers the job list and captures the services those workers need. The Layer's scope owns its client reference and releases it when the scope ends; the final reference closes the client. Callers use the jobs service to enqueue a decoded payload or read queue health.
|
|
34
|
+
The application creates one jobs service with `makePgBoss(identifier)`. Its Layer starts a client, registers the job list and captures the services those workers need. The Layer's scope owns its client reference and releases it when the scope ends; the final reference closes the client. Callers use the jobs service to enqueue a decoded payload, look up or retry a job, or read queue health and live counts.
|
|
35
35
|
|
|
36
36
|
Use a distinct literal identifier for each jobs service. The service and its Layer keep that identifier in their types, so providing a Layer for a different identifier leaves the required jobs service unprovided at compile time.
|
|
37
37
|
|
|
@@ -116,7 +116,7 @@ export const jobsLayer = (databaseUrl: string) =>
|
|
|
116
116
|
}).pipe(Layer.provide(MailLive));
|
|
117
117
|
```
|
|
118
118
|
|
|
119
|
-
The constructor options belong to pg-boss. `jobs` is the complete list of registrations for this Layer. The Layer captures `MailLive` for both the queue handler and the scheduled Effect. A startup or registration failure fails Layer acquisition with `PgBossError` and releases its acquired client reference. A shared client stays running while another scope still owns a reference.
|
|
119
|
+
The constructor options belong to pg-boss. `jobs` is the complete list of registrations for this Layer. The Layer captures `MailLive` for both the queue handler and the scheduled Effect. A startup or registration failure fails Layer acquisition with `PgBossError` or `PgBossQueueOptionsError` and releases its acquired client reference. A shared client stays running while another scope still owns a reference.
|
|
120
120
|
|
|
121
121
|
Keep the Layer in the application's worker runtime scope. A short scope is useful for an acquisition or health check; a worker process needs its scope to remain open while it handles jobs.
|
|
122
122
|
|
|
@@ -173,7 +173,35 @@ export const readJobCounts = Effect.gen(function* () {
|
|
|
173
173
|
});
|
|
174
174
|
```
|
|
175
175
|
|
|
176
|
-
`health` exposes pg-boss queue counts as data. Its total fields include the registered queue's active, failed, queued and ready counts, plus the dead-letter queue's queued count. Decide in the application which values affect an HTTP health response or an alert.
|
|
176
|
+
`health` exposes pg-boss cached queue counts as data. It stays cheap and follows the pg-boss monitor interval; it does not establish that a queue has just drained. Its total fields include the registered queue's active, failed, queued and ready counts, plus the dead-letter queue's queued count. Decide in the application which values affect an HTTP health response or an alert.
|
|
177
|
+
|
|
178
|
+
### Read live counts and retry a job
|
|
179
|
+
|
|
180
|
+
```ts
|
|
181
|
+
import { Effect, Option } from "effect";
|
|
182
|
+
import { SendEmail } from "./jobs.ts";
|
|
183
|
+
import { Jobs } from "./services.ts";
|
|
184
|
+
|
|
185
|
+
export const inspectEmail = (id: string) => Effect.gen(function* () {
|
|
186
|
+
const jobs = yield* Jobs;
|
|
187
|
+
const job = yield* jobs.getJob(SendEmail, id);
|
|
188
|
+
if (Option.isSome(job) && job.value.state === "failed") {
|
|
189
|
+
yield* jobs.retry(SendEmail, id);
|
|
190
|
+
}
|
|
191
|
+
const counts = yield* jobs.counts(SendEmail);
|
|
192
|
+
return counts.queuedCount === 0 && counts.activeCount === 0;
|
|
193
|
+
});
|
|
194
|
+
```
|
|
195
|
+
|
|
196
|
+
`counts(queue)` reads the job table directly on each call. `queuedCount` includes created and retry-waiting jobs, including future jobs; `activeCount` counts running jobs, `failedCount` counts retained failures, and `totalCount` includes retained terminal jobs. A queue has drained when queued and active counts are both zero. These counts are a snapshot, so a producer can enqueue more work after the query returns.
|
|
197
|
+
|
|
198
|
+
`getJob(queue, id)` returns `None` when the ID is absent, or `Some` pg-boss job metadata with `data` decoded by the queue's Schema. Invalid stored data fails with `PgBossPayloadError`, and the Effect requires the Schema's decoding services. `retry(queue, id)` reuses the original ID: it makes a failed job eligible for another attempt and pulls a retry-waiting job forward to run now. Missing IDs and jobs in other states are left unchanged. It does not remove an earlier copy from the dead-letter queue.
|
|
199
|
+
|
|
200
|
+
### Queue settings on startup
|
|
201
|
+
|
|
202
|
+
`queue` accepts pg-boss `Queue` options except `name` and `deadLetter`, which the definition and adapter own. For prompt delivery, set `queue: { notify: true }` and enable `useListenNotify: true` in the worker Layer's constructor options. The PostgreSQL notification test waits for an idle worker with a ten-second polling interval and verifies delivery within 1.5 seconds.
|
|
203
|
+
|
|
204
|
+
Startup creates missing queues and reconciles declared settings on existing queues through `updateQueue`. Unspecified settings keep their stored values; the adapter always supplies its default retry settings and dead-letter destination. Changing an explicitly declared `policy` or `partition` on an existing queue fails startup with `PgBossQueueOptionsError`; its `queue`, `options` and message identify the conflict. Queue setting changes apply to subsequently enqueued jobs. Schedule queues follow the same registration path.
|
|
177
205
|
|
|
178
206
|
### Share a client between Layer builds
|
|
179
207
|
|
|
@@ -195,7 +223,17 @@ Overlapping Layer builds with the same `clientCacheKey` share one started client
|
|
|
195
223
|
|
|
196
224
|
### Separate producers and workers
|
|
197
225
|
|
|
198
|
-
Pass a queue definition directly in
|
|
226
|
+
Pass a queue definition directly in the jobs Layer to create its queue and dead-letter queue without starting a consumer:
|
|
227
|
+
|
|
228
|
+
```ts
|
|
229
|
+
import { SendEmail } from "./jobs.ts";
|
|
230
|
+
import { Jobs } from "./services.ts";
|
|
231
|
+
|
|
232
|
+
export const producerLayer = (databaseUrl: string) =>
|
|
233
|
+
Jobs.layer({ connectionString: databaseUrl, jobs: [SendEmail] });
|
|
234
|
+
```
|
|
235
|
+
|
|
236
|
+
Provide this Layer to the request runtime and call `jobs.enqueue(SendEmail, { userId })` as above. A separate worker process registers `SendEmail.handle(...)` against the same database. Producer queues use the same retry options and appear in `health`; only worker registrations capture handler services. The PostgreSQL producer test creates both queues through this Layer, enqueues a transformed payload, and observes a failing worker exhaust two retries before the job reaches its dead-letter queue.
|
|
199
237
|
|
|
200
238
|
### API
|
|
201
239
|
|
|
@@ -205,18 +243,21 @@ Import the module that defines the name; the package has no root entry.
|
|
|
205
243
|
| --- | --- |
|
|
206
244
|
| `@shivaedev/effect-pg-boss/definition.ts` | `defineQueue`, `defineSchedule`, `jobContext`; `DefineQueueOptions`, `DefineScheduleOptions`, `JobContext`, `JobPayloadSchema`, `JobRegistration`, `QueueDefinition`, `QueueWorker`, `RegistrationRequirements`, `ScheduleDefinition`, `ScheduledWorker` |
|
|
207
245
|
| `@shivaedev/effect-pg-boss/service.ts` | `makePgBoss`; `PgBossDefinition`, `PgBossLayerOptions`, `PgBossService` |
|
|
208
|
-
| `@shivaedev/effect-pg-boss/health.ts` | `deadLetterQueueName`; `JobsHealth`, `QueueHealth` |
|
|
209
|
-
| `@shivaedev/effect-pg-boss/error.ts` | `PgBossError`, `PgBossPayloadError`, `toPgBossError`; `PgBossOperation` |
|
|
246
|
+
| `@shivaedev/effect-pg-boss/health.ts` | `deadLetterQueueName`; `JobsHealth`, `QueueCounts`, `QueueHealth` |
|
|
247
|
+
| `@shivaedev/effect-pg-boss/error.ts` | `PgBossError`, `PgBossPayloadError`, `PgBossQueueOptionsError`, `toPgBossError`; `PgBossOperation` |
|
|
210
248
|
| `@shivaedev/effect-pg-boss/client.ts` | `defaultClientFactory`; `PgBossClient`, `PgBossClientFactory` |
|
|
211
249
|
|
|
212
|
-
The definition options keep the pg-boss names: `queue` uses `
|
|
250
|
+
The definition options keep the pg-boss names: `queue` uses `Queue` except `name` and `deadLetter`, `worker` uses `WorkOptions`, and a schedule's `schedule` uses `ScheduleOptions`. A queue also requires `name` and `schema`; a schedule requires `name` and `cron`.
|
|
213
251
|
|
|
214
|
-
The application's service has
|
|
252
|
+
The application's service has these operations:
|
|
215
253
|
|
|
216
254
|
- `enqueue(queue, payload, options?)`: an Effect returning `Option.Option<string>`, with `PgBossError | PgBossPayloadError` in its error type. Its options use pg-boss `SendOptions`.
|
|
217
|
-
- `health`: an Effect returning `JobsHealth`, with `PgBossError` in its error type.
|
|
255
|
+
- `health`: an Effect returning cached `JobsHealth`, with `PgBossError` in its error type.
|
|
256
|
+
- `counts(queue)`: an Effect returning live `QueueCounts`, with `PgBossError` in its error type.
|
|
257
|
+
- `getJob(queue, id)`: an Effect returning `Option.Option<JobWithMetadata<Payload>>` with decoded payload data, `PgBossError | PgBossPayloadError` in its error type and the Schema's decoding services in its requirements.
|
|
258
|
+
- `retry(queue, id)`: an Effect returning `void`, with `PgBossError` in its error type.
|
|
218
259
|
|
|
219
|
-
`Jobs.layer(options)` accepts pg-boss `ConstructorOptions` plus `jobs`, optional `clientCacheKey`, optional `clientFactory`, optional `onError` and optional `stop`. The factory type accepts constructor options and returns `PgBossClient
|
|
260
|
+
`Jobs.layer(options)` accepts pg-boss `ConstructorOptions` plus `jobs`, optional `clientCacheKey`, optional `clientFactory`, optional `onError` and optional `stop`. The factory type accepts constructor options and returns `PgBossClient`. Custom clients can supply the optional native capabilities `findJobs`, `retry`, `update`, `getDb` and `updateQueue` for lookup, retry, live counts and reconciliation; a requested operation fails with `PgBossError` if its required capability is absent. `onError` has the type `(error: Error) => Effect.Effect<unknown, never, R>`; `stop` uses pg-boss `StopOptions`.
|
|
220
261
|
|
|
221
262
|
`onError` handles pg-boss client `error` events using the services captured by the Layer. The Layer's requirement type includes the workers' services, the workers' payload Schemas' `DecodingServices` and the callback's services. `enqueue` includes the payload Schema's `EncodingServices` in its requirement type.
|
|
222
263
|
|
|
@@ -227,12 +268,12 @@ The application's service has two operations:
|
|
|
227
268
|
### Install, setup and limits
|
|
228
269
|
|
|
229
270
|
```sh
|
|
230
|
-
pnpm add @shivaedev/effect-pg-boss effect@4.0.4 pg-boss@12.
|
|
271
|
+
pnpm add @shivaedev/effect-pg-boss effect@4.0.4 pg-boss@12.37.0
|
|
231
272
|
```
|
|
232
273
|
|
|
233
274
|
The package requires Node.js 24 or later and PostgreSQL. `effect` and `pg-boss` are peers; use versions that satisfy the package's published peer ranges.
|
|
234
275
|
|
|
235
|
-
Provide the database connection and job registrations at the application runtime boundary. Application services own the domain work and provider calls; pg-boss owns queue execution. Treat enqueueing and an application database transaction as separate operations, and
|
|
276
|
+
Provide the database connection and job registrations at the application runtime boundary. Application services own the domain work and provider calls; pg-boss owns queue execution. Treat enqueueing and an application database transaction as separate operations. Enqueue after the application write commits, and handle queue failures in the application so they cannot roll back the write. The starter logs enqueue failures and returns the saved note; a failed enqueue leaves its word count pending. The package supplies no application transaction adapter.
|
|
236
277
|
|
|
237
278
|
For package development, the real PostgreSQL test uses `PLATFORM_EFFECT_PG_BOSS_TEST_DATABASE_URL` and skips when the variable is absent. A skipped test does not establish durable queue behavior. Fake-client tests cover registration and lifecycle calls; they do not establish real cron execution, retry timing or deployment behavior. The package is built for ShivaeDev applications and may change without a deprecation period.
|
|
238
279
|
|
package/dist/server/client.d.ts
CHANGED
|
@@ -1,14 +1,20 @@
|
|
|
1
|
-
import type { ConstructorOptions, Job, Queue, QueueResult, ScheduleOptions, SendOptions, StopOptions, WorkOptions } from "pg-boss";
|
|
1
|
+
import type { ConstructorOptions, FindJobsOptions, Job, JobWithMetadata, Queue, QueueResult, ScheduleOptions, SendOptions, StopOptions, WorkOptions } from "pg-boss";
|
|
2
|
+
import { PgBoss } from "pg-boss";
|
|
2
3
|
export interface PgBossClient {
|
|
3
4
|
readonly createQueue: (name: string, options?: Omit<Queue, "name">) => Promise<void>;
|
|
5
|
+
readonly findJobs?: (name: string, options?: FindJobsOptions) => Promise<readonly JobWithMetadata<unknown>[]>;
|
|
6
|
+
readonly getDb?: PgBoss["getDb"];
|
|
4
7
|
readonly getQueue: (name: string) => Promise<QueueResult | null>;
|
|
5
8
|
readonly off: (event: "error", listener: (error: Error) => void) => unknown;
|
|
6
9
|
readonly offWork: (name: string) => Promise<void>;
|
|
7
10
|
readonly on: (event: "error", listener: (error: Error) => void) => unknown;
|
|
11
|
+
readonly retry?: PgBoss["retry"];
|
|
8
12
|
readonly schedule: (name: string, cron: string, data?: object | null, options?: ScheduleOptions) => Promise<void>;
|
|
9
13
|
readonly send: (name: string, data?: object | null, options?: SendOptions) => Promise<string | null>;
|
|
10
14
|
readonly start: () => Promise<unknown>;
|
|
11
15
|
readonly stop: (options?: StopOptions) => Promise<void>;
|
|
16
|
+
readonly update?: PgBoss["update"];
|
|
17
|
+
readonly updateQueue?: PgBoss["updateQueue"];
|
|
12
18
|
readonly work: (name: string, options: WorkOptions, handler: (jobs: readonly Job<unknown>[]) => Promise<unknown>) => Promise<string>;
|
|
13
19
|
}
|
|
14
20
|
export type PgBossClientFactory = (options: ConstructorOptions) => PgBossClient;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"client.d.ts","sourceRoot":"","sources":["../../src/server/client.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,
|
|
1
|
+
{"version":3,"file":"client.d.ts","sourceRoot":"","sources":["../../src/server/client.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EACX,kBAAkB,EAClB,eAAe,EACf,GAAG,EACH,eAAe,EACf,KAAK,EACL,WAAW,EACX,eAAe,EACf,WAAW,EACX,WAAW,EACX,WAAW,EACX,MAAM,SAAS,CAAC;AACjB,OAAO,EAAE,MAAM,EAAE,MAAM,SAAS,CAAC;AAEjC,MAAM,WAAW,YAAY;IAC5B,QAAQ,CAAC,WAAW,EAAE,CAAC,IAAI,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,IAAI,CAAC,KAAK,EAAE,MAAM,CAAC,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;IACrF,QAAQ,CAAC,QAAQ,CAAC,EAAE,CAAC,IAAI,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,eAAe,KAAK,OAAO,CAAC,SAAS,eAAe,CAAC,OAAO,CAAC,EAAE,CAAC,CAAC;IAC9G,QAAQ,CAAC,KAAK,CAAC,EAAE,MAAM,CAAC,OAAO,CAAC,CAAC;IACjC,QAAQ,CAAC,QAAQ,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,OAAO,CAAC,WAAW,GAAG,IAAI,CAAC,CAAC;IACjE,QAAQ,CAAC,GAAG,EAAE,CAAC,KAAK,EAAE,OAAO,EAAE,QAAQ,EAAE,CAAC,KAAK,EAAE,KAAK,KAAK,IAAI,KAAK,OAAO,CAAC;IAC5E,QAAQ,CAAC,OAAO,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;IAClD,QAAQ,CAAC,EAAE,EAAE,CAAC,KAAK,EAAE,OAAO,EAAE,QAAQ,EAAE,CAAC,KAAK,EAAE,KAAK,KAAK,IAAI,KAAK,OAAO,CAAC;IAC3E,QAAQ,CAAC,KAAK,CAAC,EAAE,MAAM,CAAC,OAAO,CAAC,CAAC;IACjC,QAAQ,CAAC,QAAQ,EAAE,CAAC,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,EAAE,IAAI,CAAC,EAAE,MAAM,GAAG,IAAI,EAAE,OAAO,CAAC,EAAE,eAAe,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;IAClH,QAAQ,CAAC,IAAI,EAAE,CAAC,IAAI,EAAE,MAAM,EAAE,IAAI,CAAC,EAAE,MAAM,GAAG,IAAI,EAAE,OAAO,CAAC,EAAE,WAAW,KAAK,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC,CAAC;IACrG,QAAQ,CAAC,KAAK,EAAE,MAAM,OAAO,CAAC,OAAO,CAAC,CAAC;IACvC,QAAQ,CAAC,IAAI,EAAE,CAAC,OAAO,CAAC,EAAE,WAAW,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;IACxD,QAAQ,CAAC,MAAM,CAAC,EAAE,MAAM,CAAC,QAAQ,CAAC,CAAC;IACnC,QAAQ,CAAC,WAAW,CAAC,EAAE,MAAM,CAAC,aAAa,CAAC,CAAC;IAC7C,QAAQ,CAAC,IAAI,EAAE,CAAC,IAAI,EAAE,MAAM,EAAE,OAAO,EAAE,WAAW,EAAE,OAAO,EAAE,CAAC,IAAI,EAAE,SAAS,GAAG,CAAC,OAAO,CAAC,EAAE,KAAK,OAAO,CAAC,OAAO,CAAC,KAAK,OAAO,CAAC,MAAM,CAAC,CAAC;CACrI;AAED,MAAM,MAAM,mBAAmB,GAAG,CAAC,OAAO,EAAE,kBAAkB,KAAK,YAAY,CAAC;AAEhF,eAAO,MAAM,oBAAoB,EAAE,mBAAsD,CAAC"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"client.js","sourceRoot":"","sources":["../../src/server/client.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"client.js","sourceRoot":"","sources":["../../src/server/client.ts"],"names":[],"mappings":"AAYA,OAAO,EAAE,MAAM,EAAE,MAAM,SAAS,CAAC;AAsBjC,MAAM,CAAC,MAAM,oBAAoB,GAAwB,CAAC,OAAO,EAAE,EAAE,CAAC,IAAI,MAAM,CAAC,OAAO,CAAC,CAAC"}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import type { Effect, Schema } from "effect";
|
|
2
|
-
import type { Job,
|
|
2
|
+
import type { Job, Queue, ScheduleOptions, WorkOptions } from "pg-boss";
|
|
3
3
|
export interface JobPayloadSchema extends Schema.Top {
|
|
4
4
|
readonly Encoded: object;
|
|
5
5
|
readonly Type: object;
|
|
@@ -15,7 +15,7 @@ export interface QueueDefinition<Name extends string = string, Payload extends J
|
|
|
15
15
|
readonly _tag: "QueueDefinition";
|
|
16
16
|
readonly handle: <E, R>(handler: (payload: Schema.Schema.Type<Payload>, job: JobContext) => Effect.Effect<unknown, E, R>) => QueueWorker<Payload, R>;
|
|
17
17
|
readonly name: Name;
|
|
18
|
-
readonly queueOptions: Omit<
|
|
18
|
+
readonly queueOptions: Omit<Queue, "name" | "deadLetter">;
|
|
19
19
|
readonly schema: Payload;
|
|
20
20
|
readonly workerOptions: WorkOptions;
|
|
21
21
|
}
|
|
@@ -31,7 +31,7 @@ export interface ScheduleDefinition<Name extends string = string> {
|
|
|
31
31
|
readonly _tag: "ScheduleDefinition";
|
|
32
32
|
readonly cron: string;
|
|
33
33
|
readonly name: Name;
|
|
34
|
-
readonly queueOptions: Omit<
|
|
34
|
+
readonly queueOptions: Omit<Queue, "name" | "deadLetter">;
|
|
35
35
|
readonly run: <E, R>(effect: Effect.Effect<unknown, E, R>) => ScheduledWorker<R>;
|
|
36
36
|
readonly scheduleOptions: ScheduleOptions;
|
|
37
37
|
readonly workerOptions: WorkOptions;
|
|
@@ -44,7 +44,7 @@ export interface ScheduledWorker<Requirements = unknown> {
|
|
|
44
44
|
export type RegistrationRequirements<Registrations extends readonly JobRegistration[]> = Registrations[number] extends infer Registration ? Registration extends QueueWorker<infer Payload, infer Requirements> ? Requirements | Payload["DecodingServices"] : Registration extends ScheduledWorker<infer Requirements> ? Requirements : never : never;
|
|
45
45
|
export interface DefineQueueOptions<Name extends string, Payload extends JobPayloadSchema> {
|
|
46
46
|
readonly name: Name;
|
|
47
|
-
readonly queue?: Omit<
|
|
47
|
+
readonly queue?: Omit<Queue, "name" | "deadLetter">;
|
|
48
48
|
readonly schema: Payload;
|
|
49
49
|
readonly worker?: WorkOptions;
|
|
50
50
|
}
|
|
@@ -52,7 +52,7 @@ export declare const defineQueue: <const Name extends string, const Payload exte
|
|
|
52
52
|
export interface DefineScheduleOptions<Name extends string> {
|
|
53
53
|
readonly cron: string;
|
|
54
54
|
readonly name: Name;
|
|
55
|
-
readonly queue?: Omit<
|
|
55
|
+
readonly queue?: Omit<Queue, "name" | "deadLetter">;
|
|
56
56
|
readonly schedule?: ScheduleOptions;
|
|
57
57
|
readonly worker?: WorkOptions;
|
|
58
58
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"definition.d.ts","sourceRoot":"","sources":["../../src/server/definition.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,QAAQ,CAAC;AAC7C,OAAO,KAAK,EAAE,GAAG,EAAE,
|
|
1
|
+
{"version":3,"file":"definition.d.ts","sourceRoot":"","sources":["../../src/server/definition.ts"],"names":[],"mappings":"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"}
|
package/dist/server/error.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { Redacted } from "effect";
|
|
2
|
-
export type PgBossOperation = "enqueue" | "health" | "register" | "start" | "stop";
|
|
2
|
+
export type PgBossOperation = "counts" | "enqueue" | "getJob" | "health" | "register" | "retry" | "start" | "stop";
|
|
3
3
|
declare const PgBossError_base: new <A extends Record<string, any> = {}>(args: import("effect/Types").VoidIfEmpty<{ readonly [P in keyof A as P extends "_tag" ? never : P]: A[P]; }>) => import("effect/Cause").YieldableError & {
|
|
4
4
|
readonly _tag: "PgBossError";
|
|
5
5
|
} & Readonly<A>;
|
|
@@ -9,6 +9,15 @@ export declare class PgBossError extends PgBossError_base<{
|
|
|
9
9
|
readonly original: Redacted.Redacted<unknown>;
|
|
10
10
|
}> {
|
|
11
11
|
}
|
|
12
|
+
declare const PgBossQueueOptionsError_base: new <A extends Record<string, any> = {}>(args: import("effect/Types").VoidIfEmpty<{ readonly [P in keyof A as P extends "_tag" ? never : P]: A[P]; }>) => import("effect/Cause").YieldableError & {
|
|
13
|
+
readonly _tag: "PgBossQueueOptionsError";
|
|
14
|
+
} & Readonly<A>;
|
|
15
|
+
export declare class PgBossQueueOptionsError extends PgBossQueueOptionsError_base<{
|
|
16
|
+
readonly queue: string;
|
|
17
|
+
readonly options: readonly ("partition" | "policy")[];
|
|
18
|
+
}> {
|
|
19
|
+
get message(): string;
|
|
20
|
+
}
|
|
12
21
|
declare const PgBossPayloadError_base: new <A extends Record<string, any> = {}>(args: import("effect/Types").VoidIfEmpty<{ readonly [P in keyof A as P extends "_tag" ? never : P]: A[P]; }>) => import("effect/Cause").YieldableError & {
|
|
13
22
|
readonly _tag: "PgBossPayloadError";
|
|
14
23
|
} & Readonly<A>;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"error.d.ts","sourceRoot":"","sources":["../../src/server/error.ts"],"names":[],"mappings":"AAAA,OAAO,EAAQ,QAAQ,EAAE,MAAM,QAAQ,CAAC;AAExC,MAAM,MAAM,eAAe,GAAG,SAAS,GAAG,QAAQ,GAAG,UAAU,GAAG,OAAO,GAAG,MAAM,CAAC;;;;
|
|
1
|
+
{"version":3,"file":"error.d.ts","sourceRoot":"","sources":["../../src/server/error.ts"],"names":[],"mappings":"AAAA,OAAO,EAAQ,QAAQ,EAAE,MAAM,QAAQ,CAAC;AAExC,MAAM,MAAM,eAAe,GAAG,QAAQ,GAAG,SAAS,GAAG,QAAQ,GAAG,QAAQ,GAAG,UAAU,GAAG,OAAO,GAAG,OAAO,GAAG,MAAM,CAAC;;;;AAEnH,qBAAa,WAAY,SAAQ,iBAAgC;IAChE,QAAQ,CAAC,SAAS,EAAE,eAAe,CAAC;IACpC,QAAQ,CAAC,KAAK,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;IACpC,QAAQ,CAAC,QAAQ,EAAE,QAAQ,CAAC,QAAQ,CAAC,OAAO,CAAC,CAAC;CAC9C,CAAC;CAAG;;;;AAEL,qBAAa,uBAAwB,SAAQ,6BAA4C;IACxF,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;IACvB,QAAQ,CAAC,OAAO,EAAE,SAAS,CAAC,WAAW,GAAG,QAAQ,CAAC,EAAE,CAAC;CACtD,CAAC;IACD,IAAa,OAAO,IAAI,MAAM,CAE7B;CACD;;;;AAED,qBAAa,kBAAmB,SAAQ,wBAAuC;IAC9E,QAAQ,CAAC,SAAS,EAAE,QAAQ,GAAG,QAAQ,CAAC;IACxC,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;IACvB,QAAQ,CAAC,QAAQ,EAAE,QAAQ,CAAC,QAAQ,CAAC,OAAO,CAAC,CAAC;CAC9C,CAAC;CAAG;AAEL,eAAO,MAAM,aAAa,cAAe,eAAe,SAAS,OAAO,UAAU,MAAM,KAAG,WAKxF,CAAC"}
|
package/dist/server/error.js
CHANGED
|
@@ -1,6 +1,11 @@
|
|
|
1
1
|
import { Data, Redacted } from "effect";
|
|
2
2
|
export class PgBossError extends Data.TaggedError("PgBossError") {
|
|
3
3
|
}
|
|
4
|
+
export class PgBossQueueOptionsError extends Data.TaggedError("PgBossQueueOptionsError") {
|
|
5
|
+
get message() {
|
|
6
|
+
return `Queue ${this.queue} cannot change options after creation: ${this.options.join(", ")}`;
|
|
7
|
+
}
|
|
8
|
+
}
|
|
4
9
|
export class PgBossPayloadError extends Data.TaggedError("PgBossPayloadError") {
|
|
5
10
|
}
|
|
6
11
|
export const toPgBossError = (operation, error, queue) => new PgBossError({
|
package/dist/server/error.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"error.js","sourceRoot":"","sources":["../../src/server/error.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,IAAI,EAAE,QAAQ,EAAE,MAAM,QAAQ,CAAC;AAIxC,MAAM,OAAO,WAAY,SAAQ,IAAI,CAAC,WAAW,CAAC,aAAa,CAI7D;CAAG;AAEL,MAAM,OAAO,kBAAmB,SAAQ,IAAI,CAAC,WAAW,CAAC,oBAAoB,CAI3E;CAAG;AAEL,MAAM,CAAC,MAAM,aAAa,GAAG,CAAC,SAA0B,EAAE,KAAc,EAAE,KAAc,EAAe,EAAE,CACxG,IAAI,WAAW,CAAC;IACf,SAAS;IACT,QAAQ,EAAE,QAAQ,CAAC,IAAI,CAAC,KAAK,CAAC;IAC9B,KAAK;CACL,CAAC,CAAC"}
|
|
1
|
+
{"version":3,"file":"error.js","sourceRoot":"","sources":["../../src/server/error.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,IAAI,EAAE,QAAQ,EAAE,MAAM,QAAQ,CAAC;AAIxC,MAAM,OAAO,WAAY,SAAQ,IAAI,CAAC,WAAW,CAAC,aAAa,CAI7D;CAAG;AAEL,MAAM,OAAO,uBAAwB,SAAQ,IAAI,CAAC,WAAW,CAAC,yBAAyB,CAGrF;IACD,IAAa,OAAO;QACnB,OAAO,SAAS,IAAI,CAAC,KAAK,0CAA0C,IAAI,CAAC,OAAO,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC;IAC/F,CAAC;CACD;AAED,MAAM,OAAO,kBAAmB,SAAQ,IAAI,CAAC,WAAW,CAAC,oBAAoB,CAI3E;CAAG;AAEL,MAAM,CAAC,MAAM,aAAa,GAAG,CAAC,SAA0B,EAAE,KAAc,EAAE,KAAc,EAAe,EAAE,CACxG,IAAI,WAAW,CAAC;IACf,SAAS;IACT,QAAQ,EAAE,QAAQ,CAAC,IAAI,CAAC,KAAK,CAAC;IAC9B,KAAK;CACL,CAAC,CAAC"}
|
package/dist/server/health.d.ts
CHANGED
|
@@ -1,3 +1,9 @@
|
|
|
1
|
+
export interface QueueCounts {
|
|
2
|
+
readonly activeCount: number;
|
|
3
|
+
readonly failedCount: number;
|
|
4
|
+
readonly queuedCount: number;
|
|
5
|
+
readonly totalCount: number;
|
|
6
|
+
}
|
|
1
7
|
export interface QueueHealth {
|
|
2
8
|
readonly activeCount: number;
|
|
3
9
|
readonly deadLetteredCount: number;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"health.d.ts","sourceRoot":"","sources":["../../src/server/health.ts"],"names":[],"mappings":"AAAA,MAAM,WAAW,WAAW;IAC3B,QAAQ,CAAC,WAAW,EAAE,MAAM,CAAC;IAC7B,QAAQ,CAAC,iBAAiB,EAAE,MAAM,CAAC;IACnC,QAAQ,CAAC,WAAW,EAAE,MAAM,CAAC;IAC7B,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,WAAW,EAAE,MAAM,CAAC;IAC7B,QAAQ,CAAC,UAAU,EAAE,MAAM,CAAC;CAC5B;AAED,MAAM,WAAW,UAAU;IAC1B,QAAQ,CAAC,WAAW,EAAE,MAAM,CAAC;IAC7B,QAAQ,CAAC,iBAAiB,EAAE,MAAM,CAAC;IACnC,QAAQ,CAAC,WAAW,EAAE,MAAM,CAAC;IAC7B,QAAQ,CAAC,IAAI,EAAE,SAAS,WAAW,EAAE,CAAC;IACtC,QAAQ,CAAC,WAAW,EAAE,MAAM,CAAC;IAC7B,QAAQ,CAAC,UAAU,EAAE,MAAM,CAAC;CAC5B;AAED,eAAO,MAAM,mBAAmB,cAAe,MAAM,KAAG,MAA4B,CAAC"}
|
|
1
|
+
{"version":3,"file":"health.d.ts","sourceRoot":"","sources":["../../src/server/health.ts"],"names":[],"mappings":"AAAA,MAAM,WAAW,WAAW;IAC3B,QAAQ,CAAC,WAAW,EAAE,MAAM,CAAC;IAC7B,QAAQ,CAAC,WAAW,EAAE,MAAM,CAAC;IAC7B,QAAQ,CAAC,WAAW,EAAE,MAAM,CAAC;IAC7B,QAAQ,CAAC,UAAU,EAAE,MAAM,CAAC;CAC5B;AAED,MAAM,WAAW,WAAW;IAC3B,QAAQ,CAAC,WAAW,EAAE,MAAM,CAAC;IAC7B,QAAQ,CAAC,iBAAiB,EAAE,MAAM,CAAC;IACnC,QAAQ,CAAC,WAAW,EAAE,MAAM,CAAC;IAC7B,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,WAAW,EAAE,MAAM,CAAC;IAC7B,QAAQ,CAAC,UAAU,EAAE,MAAM,CAAC;CAC5B;AAED,MAAM,WAAW,UAAU;IAC1B,QAAQ,CAAC,WAAW,EAAE,MAAM,CAAC;IAC7B,QAAQ,CAAC,iBAAiB,EAAE,MAAM,CAAC;IACnC,QAAQ,CAAC,WAAW,EAAE,MAAM,CAAC;IAC7B,QAAQ,CAAC,IAAI,EAAE,SAAS,WAAW,EAAE,CAAC;IACtC,QAAQ,CAAC,WAAW,EAAE,MAAM,CAAC;IAC7B,QAAQ,CAAC,UAAU,EAAE,MAAM,CAAC;CAC5B;AAED,eAAO,MAAM,mBAAmB,cAAe,MAAM,KAAG,MAA4B,CAAC"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"health.js","sourceRoot":"","sources":["../../src/server/health.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"health.js","sourceRoot":"","sources":["../../src/server/health.ts"],"names":[],"mappings":"AAyBA,MAAM,CAAC,MAAM,mBAAmB,GAAG,CAAC,SAAiB,EAAU,EAAE,CAAC,GAAG,SAAS,MAAM,CAAC"}
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
import { Effect } from "effect";
|
|
2
|
+
import type { PgBossClient } from "#client.ts";
|
|
3
|
+
import type { QueueCounts } from "#health.ts";
|
|
4
|
+
export declare function countsFor(client: PgBossClient, schema: string, name: string): Effect.Effect<QueueCounts, import("#error.ts").PgBossError>;
|
|
5
|
+
//# sourceMappingURL=countsFor.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"countsFor.d.ts","sourceRoot":"","sources":["../../../src/server/internal/countsFor.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAU,MAAM,QAAQ,CAAC;AACxC,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,YAAY,CAAC;AAE/C,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,YAAY,CAAC;AAS9C,wBAAgB,SAAS,CAAC,MAAM,EAAE,YAAY,EAAE,MAAM,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,GAAG,MAAM,CAAC,MAAM,CAAC,WAAW,EAAE,OAAO,WAAW,EAAE,WAAW,CAAC,CAoBzI"}
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import { Effect, Schema } from "effect";
|
|
2
|
+
import { toPgBossError } from "#error.ts";
|
|
3
|
+
const Counts = Schema.Struct({
|
|
4
|
+
activeCount: Schema.Number,
|
|
5
|
+
failedCount: Schema.Number,
|
|
6
|
+
queuedCount: Schema.Number,
|
|
7
|
+
totalCount: Schema.Number,
|
|
8
|
+
});
|
|
9
|
+
export function countsFor(client, schema, name) {
|
|
10
|
+
return Effect.tryPromise({
|
|
11
|
+
catch: (error) => toPgBossError("counts", error, name),
|
|
12
|
+
try: async () => {
|
|
13
|
+
if (client.getDb === undefined) {
|
|
14
|
+
throw new TypeError("This pg-boss client does not support getDb");
|
|
15
|
+
}
|
|
16
|
+
const quotedSchema = `"${schema.replaceAll('"', '""')}"`;
|
|
17
|
+
const { rows } = await client.getDb().executeSql(`SELECT
|
|
18
|
+
count(*) FILTER (WHERE state = 'active')::int AS "activeCount",
|
|
19
|
+
count(*) FILTER (WHERE state = 'failed')::int AS "failedCount",
|
|
20
|
+
count(*) FILTER (WHERE state IN ('created', 'retry'))::int AS "queuedCount",
|
|
21
|
+
count(*)::int AS "totalCount"
|
|
22
|
+
FROM ${quotedSchema}.job WHERE name = $1`, [name]);
|
|
23
|
+
return Schema.decodeUnknownPromise(Counts)(rows[0]);
|
|
24
|
+
},
|
|
25
|
+
});
|
|
26
|
+
}
|
|
27
|
+
//# sourceMappingURL=countsFor.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"countsFor.js","sourceRoot":"","sources":["../../../src/server/internal/countsFor.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,QAAQ,CAAC;AAExC,OAAO,EAAE,aAAa,EAAE,MAAM,WAAW,CAAC;AAG1C,MAAM,MAAM,GAAG,MAAM,CAAC,MAAM,CAAC;IAC5B,WAAW,EAAE,MAAM,CAAC,MAAM;IAC1B,WAAW,EAAE,MAAM,CAAC,MAAM;IAC1B,WAAW,EAAE,MAAM,CAAC,MAAM;IAC1B,UAAU,EAAE,MAAM,CAAC,MAAM;CACzB,CAAC,CAAC;AAEH,MAAM,UAAU,SAAS,CAAC,MAAoB,EAAE,MAAc,EAAE,IAAY;IAC3E,OAAO,MAAM,CAAC,UAAU,CAAC;QACxB,KAAK,EAAE,CAAC,KAAK,EAAE,EAAE,CAAC,aAAa,CAAC,QAAQ,EAAE,KAAK,EAAE,IAAI,CAAC;QACtD,GAAG,EAAE,KAAK,IAAI,EAAE;YACf,IAAI,MAAM,CAAC,KAAK,KAAK,SAAS,EAAE,CAAC;gBAChC,MAAM,IAAI,SAAS,CAAC,4CAA4C,CAAC,CAAC;YACnE,CAAC;YACD,MAAM,YAAY,GAAG,IAAI,MAAM,CAAC,UAAU,CAAC,GAAG,EAAE,IAAI,CAAC,GAAG,CAAC;YACzD,MAAM,EAAE,IAAI,EAAE,GAAG,MAAM,MAAM,CAAC,KAAK,EAAE,CAAC,UAAU,CAC/C;;;;;WAKO,YAAY,sBAAsB,EACzC,CAAC,IAAI,CAAC,CACN,CAAC;YACF,OAAO,MAAM,CAAC,oBAAoB,CAAC,MAAM,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,CAAC;QACrD,CAAC;KACD,CAAC,CAAC;AACJ,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ensureQueue.d.ts","sourceRoot":"","sources":["../../../src/server/internal/ensureQueue.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,KAAK,EAAE,MAAM,SAAS,CAAC;AACrC,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,YAAY,CAAC;AAI/C,wBAAsB,WAAW,CAAC,MAAM,EAAE,YAAY,EAAE,IAAI,EAAE,MAAM,EAAE,OAAO,EAAE,IAAI,CAAC,KAAK,EAAE,MAAM,GAAG,YAAY,CAAC,GAAG,OAAO,CAAC,IAAI,CAAC,CAuBhI"}
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import { PgBossQueueOptionsError } from "#error.ts";
|
|
2
|
+
import { deadLetterQueueName } from "#health.ts";
|
|
3
|
+
export async function ensureQueue(client, name, options) {
|
|
4
|
+
const deadLetter = deadLetterQueueName(name);
|
|
5
|
+
const desired = { retryBackoff: true, retryLimit: 3, ...options, deadLetter };
|
|
6
|
+
await client.createQueue(deadLetter);
|
|
7
|
+
await client.createQueue(name, desired);
|
|
8
|
+
const existing = await client.getQueue(name);
|
|
9
|
+
if (existing === null) {
|
|
10
|
+
return;
|
|
11
|
+
}
|
|
12
|
+
const immutable = ["partition", "policy"].filter((key) => desired[key] !== undefined && desired[key] !== existing[key]);
|
|
13
|
+
if (immutable.length > 0) {
|
|
14
|
+
throw new PgBossQueueOptionsError({ options: immutable, queue: name });
|
|
15
|
+
}
|
|
16
|
+
const { partition: _partition, policy: _policy, ...mutable } = desired;
|
|
17
|
+
const changed = Object.entries(mutable).some(([key, value]) => value !== undefined && value !== Reflect.get(existing, key));
|
|
18
|
+
if (!changed) {
|
|
19
|
+
return;
|
|
20
|
+
}
|
|
21
|
+
if (client.updateQueue === undefined) {
|
|
22
|
+
throw new TypeError("This pg-boss client does not support updateQueue");
|
|
23
|
+
}
|
|
24
|
+
// pg-boss validates retryDelayMax against retryBackoff in the same update.
|
|
25
|
+
await client.updateQueue(name, mutable);
|
|
26
|
+
}
|
|
27
|
+
//# sourceMappingURL=ensureQueue.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ensureQueue.js","sourceRoot":"","sources":["../../../src/server/internal/ensureQueue.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,uBAAuB,EAAE,MAAM,WAAW,CAAC;AACpD,OAAO,EAAE,mBAAmB,EAAE,MAAM,YAAY,CAAC;AAEjD,MAAM,CAAC,KAAK,UAAU,WAAW,CAAC,MAAoB,EAAE,IAAY,EAAE,OAA2C;IAChH,MAAM,UAAU,GAAG,mBAAmB,CAAC,IAAI,CAAC,CAAC;IAC7C,MAAM,OAAO,GAAG,EAAE,YAAY,EAAE,IAAI,EAAE,UAAU,EAAE,CAAC,EAAE,GAAG,OAAO,EAAE,UAAU,EAAE,CAAC;IAC9E,MAAM,MAAM,CAAC,WAAW,CAAC,UAAU,CAAC,CAAC;IACrC,MAAM,MAAM,CAAC,WAAW,CAAC,IAAI,EAAE,OAAO,CAAC,CAAC;IACxC,MAAM,QAAQ,GAAG,MAAM,MAAM,CAAC,QAAQ,CAAC,IAAI,CAAC,CAAC;IAC7C,IAAI,QAAQ,KAAK,IAAI,EAAE,CAAC;QACvB,OAAO;IACR,CAAC;IACD,MAAM,SAAS,GAAI,CAAC,WAAW,EAAE,QAAQ,CAAW,CAAC,MAAM,CAAC,CAAC,GAAG,EAAE,EAAE,CAAC,OAAO,CAAC,GAAG,CAAC,KAAK,SAAS,IAAI,OAAO,CAAC,GAAG,CAAC,KAAK,QAAQ,CAAC,GAAG,CAAC,CAAC,CAAC;IACnI,IAAI,SAAS,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;QAC1B,MAAM,IAAI,uBAAuB,CAAC,EAAE,OAAO,EAAE,SAAS,EAAE,KAAK,EAAE,IAAI,EAAE,CAAC,CAAC;IACxE,CAAC;IACD,MAAM,EAAE,SAAS,EAAE,UAAU,EAAE,MAAM,EAAE,OAAO,EAAE,GAAG,OAAO,EAAE,GAAG,OAAO,CAAC;IACvE,MAAM,OAAO,GAAG,MAAM,CAAC,OAAO,CAAC,OAAO,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,GAAG,EAAE,KAAK,CAAC,EAAE,EAAE,CAAC,KAAK,KAAK,SAAS,IAAI,KAAK,KAAK,OAAO,CAAC,GAAG,CAAC,QAAQ,EAAE,GAAG,CAAC,CAAC,CAAC;IAC5H,IAAI,CAAC,OAAO,EAAE,CAAC;QACd,OAAO;IACR,CAAC;IACD,IAAI,MAAM,CAAC,WAAW,KAAK,SAAS,EAAE,CAAC;QACtC,MAAM,IAAI,SAAS,CAAC,kDAAkD,CAAC,CAAC;IACzE,CAAC;IACD,2EAA2E;IAC3E,MAAM,MAAM,CAAC,WAAW,CAAC,IAAI,EAAE,OAAO,CAAC,CAAC;AACzC,CAAC"}
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
import type { PgBossClient } from "#client.ts";
|
|
2
|
+
import type { PgBossService } from "#service.ts";
|
|
3
|
+
export declare function getJobFor(client: PgBossClient): PgBossService["getJob"];
|
|
4
|
+
export declare function retryFor(client: PgBossClient): PgBossService["retry"];
|
|
5
|
+
//# sourceMappingURL=jobControl.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"jobControl.d.ts","sourceRoot":"","sources":["../../../src/server/internal/jobControl.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,YAAY,CAAC;AAE/C,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,aAAa,CAAC;AAEjD,wBAAgB,SAAS,CAAC,MAAM,EAAE,YAAY,GAAG,aAAa,CAAC,QAAQ,CAAC,CAoBvE;AAED,wBAAgB,QAAQ,CAAC,MAAM,EAAE,YAAY,GAAG,aAAa,CAAC,OAAO,CAAC,CAkBrE"}
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
import { Effect, Option, Redacted, Schema } from "effect";
|
|
2
|
+
import { PgBossPayloadError, toPgBossError } from "#error.ts";
|
|
3
|
+
export function getJobFor(client) {
|
|
4
|
+
return (queue, id) => Effect.tryPromise({
|
|
5
|
+
catch: (error) => toPgBossError("getJob", error, queue.name),
|
|
6
|
+
try: async () => {
|
|
7
|
+
if (client.findJobs === undefined) {
|
|
8
|
+
throw new TypeError("This pg-boss client does not support findJobs");
|
|
9
|
+
}
|
|
10
|
+
return (await client.findJobs(queue.name, { id }))[0];
|
|
11
|
+
},
|
|
12
|
+
}).pipe(Effect.flatMap((job) => job === undefined
|
|
13
|
+
? Effect.succeed(Option.none())
|
|
14
|
+
: Schema.decodeUnknownEffect(queue.schema)(job.data).pipe(Effect.mapError((error) => new PgBossPayloadError({ direction: "decode", original: Redacted.make(error), queue: queue.name })), Effect.map((data) => Option.some({ ...job, data })))));
|
|
15
|
+
}
|
|
16
|
+
export function retryFor(client) {
|
|
17
|
+
return (queue, id) => Effect.tryPromise({
|
|
18
|
+
catch: (error) => toPgBossError("retry", error, queue.name),
|
|
19
|
+
try: async () => {
|
|
20
|
+
if (client.findJobs === undefined || client.retry === undefined || client.update === undefined) {
|
|
21
|
+
throw new TypeError("This pg-boss client does not support findJobs, retry and update");
|
|
22
|
+
}
|
|
23
|
+
const job = (await client.findJobs(queue.name, { id }))[0];
|
|
24
|
+
if (job?.state !== "failed" && job?.state !== "retry") {
|
|
25
|
+
return;
|
|
26
|
+
}
|
|
27
|
+
if (job.state === "failed") {
|
|
28
|
+
await client.retry(queue.name, id);
|
|
29
|
+
}
|
|
30
|
+
await client.update(queue.name, undefined, { id, startAfter: 0 });
|
|
31
|
+
},
|
|
32
|
+
});
|
|
33
|
+
}
|
|
34
|
+
//# sourceMappingURL=jobControl.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"jobControl.js","sourceRoot":"","sources":["../../../src/server/internal/jobControl.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,EAAE,MAAM,QAAQ,CAAC;AAE1D,OAAO,EAAE,kBAAkB,EAAE,aAAa,EAAE,MAAM,WAAW,CAAC;AAG9D,MAAM,UAAU,SAAS,CAAC,MAAoB;IAC7C,OAAO,CAAC,KAAK,EAAE,EAAE,EAAE,EAAE,CACpB,MAAM,CAAC,UAAU,CAAC;QACjB,KAAK,EAAE,CAAC,KAAK,EAAE,EAAE,CAAC,aAAa,CAAC,QAAQ,EAAE,KAAK,EAAE,KAAK,CAAC,IAAI,CAAC;QAC5D,GAAG,EAAE,KAAK,IAAI,EAAE;YACf,IAAI,MAAM,CAAC,QAAQ,KAAK,SAAS,EAAE,CAAC;gBACnC,MAAM,IAAI,SAAS,CAAC,+CAA+C,CAAC,CAAC;YACtE,CAAC;YACD,OAAO,CAAC,MAAM,MAAM,CAAC,QAAQ,CAAC,KAAK,CAAC,IAAI,EAAE,EAAE,EAAE,EAAE,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;QACvD,CAAC;KACD,CAAC,CAAC,IAAI,CACN,MAAM,CAAC,OAAO,CAAC,CAAC,GAAG,EAAE,EAAE,CACtB,GAAG,KAAK,SAAS;QAChB,CAAC,CAAC,MAAM,CAAC,OAAO,CAAC,MAAM,CAAC,IAAI,EAAE,CAAC;QAC/B,CAAC,CAAC,MAAM,CAAC,mBAAmB,CAAC,KAAK,CAAC,MAAM,CAAC,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC,IAAI,CACvD,MAAM,CAAC,QAAQ,CAAC,CAAC,KAAK,EAAE,EAAE,CAAC,IAAI,kBAAkB,CAAC,EAAE,SAAS,EAAE,QAAQ,EAAE,QAAQ,EAAE,QAAQ,CAAC,IAAI,CAAC,KAAK,CAAC,EAAE,KAAK,EAAE,KAAK,CAAC,IAAI,EAAE,CAAC,CAAC,EAC9H,MAAM,CAAC,GAAG,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,MAAM,CAAC,IAAI,CAAC,EAAE,GAAG,GAAG,EAAE,IAAI,EAAE,CAAC,CAAC,CACnD,CACH,CACD,CAAC;AACJ,CAAC;AAED,MAAM,UAAU,QAAQ,CAAC,MAAoB;IAC5C,OAAO,CAAC,KAAK,EAAE,EAAE,EAAE,EAAE,CACpB,MAAM,CAAC,UAAU,CAAC;QACjB,KAAK,EAAE,CAAC,KAAK,EAAE,EAAE,CAAC,aAAa,CAAC,OAAO,EAAE,KAAK,EAAE,KAAK,CAAC,IAAI,CAAC;QAC3D,GAAG,EAAE,KAAK,IAAI,EAAE;YACf,IAAI,MAAM,CAAC,QAAQ,KAAK,SAAS,IAAI,MAAM,CAAC,KAAK,KAAK,SAAS,IAAI,MAAM,CAAC,MAAM,KAAK,SAAS,EAAE,CAAC;gBAChG,MAAM,IAAI,SAAS,CAAC,iEAAiE,CAAC,CAAC;YACxF,CAAC;YACD,MAAM,GAAG,GAAG,CAAC,MAAM,MAAM,CAAC,QAAQ,CAAC,KAAK,CAAC,IAAI,EAAE,EAAE,EAAE,EAAE,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;YAC3D,IAAI,GAAG,EAAE,KAAK,KAAK,QAAQ,IAAI,GAAG,EAAE,KAAK,KAAK,OAAO,EAAE,CAAC;gBACvD,OAAO;YACR,CAAC;YACD,IAAI,GAAG,CAAC,KAAK,KAAK,QAAQ,EAAE,CAAC;gBAC5B,MAAM,MAAM,CAAC,KAAK,CAAC,KAAK,CAAC,IAAI,EAAE,EAAE,CAAC,CAAC;YACpC,CAAC;YACD,MAAM,MAAM,CAAC,MAAM,CAAC,KAAK,CAAC,IAAI,EAAE,SAAS,EAAE,EAAE,EAAE,EAAE,UAAU,EAAE,CAAC,EAAE,CAAC,CAAC;QACnE,CAAC;KACD,CAAC,CAAC;AACL,CAAC"}
|
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
import { type Context, Effect } from "effect";
|
|
2
2
|
import type { PgBossClient } from "#client.ts";
|
|
3
3
|
import { type JobRegistration } from "#definition.ts";
|
|
4
|
+
import { PgBossQueueOptionsError } from "#error.ts";
|
|
4
5
|
export declare const registrationName: (registration: JobRegistration) => string;
|
|
5
6
|
export declare const registrationNames: (registrations: readonly JobRegistration[]) => readonly string[];
|
|
6
|
-
export declare const registerJobs: <R>(client: PgBossClient, registrations: readonly JobRegistration[], context: Context.Context<R>, replaceWorkers: boolean) => Effect.Effect<void, import("#error.ts").PgBossError>;
|
|
7
|
+
export declare const registerJobs: <R>(client: PgBossClient, registrations: readonly JobRegistration[], context: Context.Context<R>, replaceWorkers: boolean) => Effect.Effect<void, import("#error.ts").PgBossError | PgBossQueueOptionsError>;
|
|
7
8
|
//# sourceMappingURL=register.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"register.d.ts","sourceRoot":"","sources":["../../../src/server/internal/register.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,OAAO,EAAE,MAAM,EAAoB,MAAM,QAAQ,CAAC;AAEhE,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,YAAY,CAAC;AAC/C,OAAO,EAAE,KAAK,eAAe,EAA4E,MAAM,gBAAgB,CAAC;
|
|
1
|
+
{"version":3,"file":"register.d.ts","sourceRoot":"","sources":["../../../src/server/internal/register.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,OAAO,EAAE,MAAM,EAAoB,MAAM,QAAQ,CAAC;AAEhE,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,YAAY,CAAC;AAC/C,OAAO,EAAE,KAAK,eAAe,EAA4E,MAAM,gBAAgB,CAAC;AAChI,OAAO,EAAsB,uBAAuB,EAAiB,MAAM,WAAW,CAAC;AAevF,eAAO,MAAM,gBAAgB,iBAAkB,eAAe,KAAG,MAWhE,CAAC;AAqEF,eAAO,MAAM,iBAAiB,kBAAmB,SAAS,eAAe,EAAE,KAAG,SAAS,MAAM,EAM5F,CAAC;AAEF,eAAO,MAAM,YAAY,GAAI,CAAC,UACrB,YAAY,iBACL,SAAS,eAAe,EAAE,WAChC,OAAO,CAAC,OAAO,CAAC,CAAC,CAAC,kBACX,OAAO,KACrB,MAAM,CAAC,MAAM,CAAC,IAAI,EAAE,OAAO,WAAW,EAAE,WAAW,GAAG,uBAAuB,CAc7E,CAAC"}
|
|
@@ -1,14 +1,7 @@
|
|
|
1
1
|
import { Effect, Redacted, Schema } from "effect";
|
|
2
2
|
import { jobContext } from "#definition.ts";
|
|
3
|
-
import { PgBossPayloadError, toPgBossError } from "#error.ts";
|
|
4
|
-
import {
|
|
5
|
-
function queueOptions(options) {
|
|
6
|
-
return {
|
|
7
|
-
retryBackoff: true,
|
|
8
|
-
retryLimit: 3,
|
|
9
|
-
...options,
|
|
10
|
-
};
|
|
11
|
-
}
|
|
3
|
+
import { PgBossPayloadError, PgBossQueueOptionsError, toPgBossError } from "#error.ts";
|
|
4
|
+
import { ensureQueue } from "#internal/ensureQueue.ts";
|
|
12
5
|
function isQueueWorker(registration) {
|
|
13
6
|
return registration._tag === "QueueWorker";
|
|
14
7
|
}
|
|
@@ -43,15 +36,10 @@ function runQueueWorker(worker, job, context) {
|
|
|
43
36
|
async function registerQueue(client, registration, context, replaceWorker) {
|
|
44
37
|
const queue = registration._tag === "QueueWorker" ? registration.queue : registration;
|
|
45
38
|
const name = queue.name;
|
|
46
|
-
const deadLetter = deadLetterQueueName(name);
|
|
47
39
|
if (replaceWorker && registration._tag === "QueueWorker") {
|
|
48
40
|
await client.offWork(name);
|
|
49
41
|
}
|
|
50
|
-
await client.
|
|
51
|
-
await client.createQueue(name, {
|
|
52
|
-
...queueOptions(queue.queueOptions),
|
|
53
|
-
deadLetter,
|
|
54
|
-
});
|
|
42
|
+
await ensureQueue(client, name, queue.queueOptions);
|
|
55
43
|
if (registration._tag === "QueueDefinition") {
|
|
56
44
|
return;
|
|
57
45
|
}
|
|
@@ -63,15 +51,10 @@ async function registerQueue(client, registration, context, replaceWorker) {
|
|
|
63
51
|
}
|
|
64
52
|
async function registerSchedule(client, worker, context, replaceWorker) {
|
|
65
53
|
const { schedule } = worker;
|
|
66
|
-
const deadLetter = deadLetterQueueName(schedule.name);
|
|
67
54
|
if (replaceWorker) {
|
|
68
55
|
await client.offWork(schedule.name);
|
|
69
56
|
}
|
|
70
|
-
await client.
|
|
71
|
-
await client.createQueue(schedule.name, {
|
|
72
|
-
...queueOptions(schedule.queueOptions),
|
|
73
|
-
deadLetter,
|
|
74
|
-
});
|
|
57
|
+
await ensureQueue(client, schedule.name, schedule.queueOptions);
|
|
75
58
|
await client.work(schedule.name, schedule.workerOptions, async (jobs) => {
|
|
76
59
|
for (const job of jobs) {
|
|
77
60
|
await Effect.runPromise(Effect.provide(worker.effect, context), {
|
|
@@ -89,7 +72,7 @@ export const registrationNames = (registrations) => {
|
|
|
89
72
|
return names;
|
|
90
73
|
};
|
|
91
74
|
export const registerJobs = (client, registrations, context, replaceWorkers) => Effect.tryPromise({
|
|
92
|
-
catch: (error) => toPgBossError("register", error),
|
|
75
|
+
catch: (error) => (error instanceof PgBossQueueOptionsError ? error : toPgBossError("register", error)),
|
|
93
76
|
try: async () => {
|
|
94
77
|
registrationNames(registrations);
|
|
95
78
|
const provided = workerContext(context);
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"register.js","sourceRoot":"","sources":["../../../src/server/internal/register.ts"],"names":[],"mappings":"AAAA,OAAO,EAAgB,MAAM,EAAE,QAAQ,EAAE,MAAM,EAAE,MAAM,QAAQ,CAAC;AAGhE,OAAO,EAAwB,UAAU,EAAgE,MAAM,gBAAgB,CAAC;AAChI,OAAO,EAAE,kBAAkB,EAAE,aAAa,EAAE,MAAM,WAAW,CAAC;
|
|
1
|
+
{"version":3,"file":"register.js","sourceRoot":"","sources":["../../../src/server/internal/register.ts"],"names":[],"mappings":"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"}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
1
|
import type { PgBossClient } from "#client.ts";
|
|
2
2
|
import type { PgBossService } from "#service.ts";
|
|
3
|
-
export declare const makeService: (client: PgBossClient, names: readonly string[]) => PgBossService;
|
|
3
|
+
export declare const makeService: (client: PgBossClient, names: readonly string[], schema?: string) => PgBossService;
|
|
4
4
|
//# sourceMappingURL=service.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"service.d.ts","sourceRoot":"","sources":["../../../src/server/internal/service.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,YAAY,CAAC;
|
|
1
|
+
{"version":3,"file":"service.d.ts","sourceRoot":"","sources":["../../../src/server/internal/service.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,YAAY,CAAC;AAK/C,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,aAAa,CAAC;AAEjD,eAAO,MAAM,WAAW,WAAY,YAAY,SAAS,SAAS,MAAM,EAAE,sBAAsB,aA+B9F,CAAC"}
|
|
@@ -1,7 +1,10 @@
|
|
|
1
1
|
import { Effect, Option, Redacted, Schema } from "effect";
|
|
2
2
|
import { PgBossPayloadError, toPgBossError } from "#error.ts";
|
|
3
|
-
import {
|
|
4
|
-
|
|
3
|
+
import { countsFor } from "#internal/countsFor.ts";
|
|
4
|
+
import { healthFor } from "#internal/health.ts";
|
|
5
|
+
import { getJobFor, retryFor } from "#internal/jobControl.ts";
|
|
6
|
+
export const makeService = (client, names, schema = "pgboss") => ({
|
|
7
|
+
counts: (queue) => countsFor(client, schema, queue.name),
|
|
5
8
|
enqueue: (queue, payload, options) => Schema.encodeUnknownEffect(queue.schema)(payload).pipe(Effect.mapError((error) => new PgBossPayloadError({
|
|
6
9
|
direction: "encode",
|
|
7
10
|
original: Redacted.make(error),
|
|
@@ -19,6 +22,8 @@ export const makeService = (client, names) => ({
|
|
|
19
22
|
try: () => client.send(queue.name, encoded, options),
|
|
20
23
|
}).pipe(Effect.map(Option.fromNullishOr));
|
|
21
24
|
})),
|
|
25
|
+
getJob: getJobFor(client),
|
|
22
26
|
health: healthFor(client, names),
|
|
27
|
+
retry: retryFor(client),
|
|
23
28
|
});
|
|
24
29
|
//# sourceMappingURL=service.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"service.js","sourceRoot":"","sources":["../../../src/server/internal/service.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,EAAE,MAAM,QAAQ,CAAC;AAE1D,OAAO,EAAoB,kBAAkB,EAAE,aAAa,EAAE,MAAM,WAAW,CAAC;
|
|
1
|
+
{"version":3,"file":"service.js","sourceRoot":"","sources":["../../../src/server/internal/service.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,EAAE,MAAM,QAAQ,CAAC;AAE1D,OAAO,EAAoB,kBAAkB,EAAE,aAAa,EAAE,MAAM,WAAW,CAAC;AAChF,OAAO,EAAE,SAAS,EAAE,MAAM,wBAAwB,CAAC;AACnD,OAAO,EAAE,SAAS,EAAE,MAAM,qBAAqB,CAAC;AAChD,OAAO,EAAE,SAAS,EAAE,QAAQ,EAAE,MAAM,yBAAyB,CAAC;AAG9D,MAAM,CAAC,MAAM,WAAW,GAAG,CAAC,MAAoB,EAAE,KAAwB,EAAE,MAAM,GAAG,QAAQ,EAAiB,EAAE,CAAC,CAAC;IACjH,MAAM,EAAE,CAAC,KAAK,EAAE,EAAE,CAAC,SAAS,CAAC,MAAM,EAAE,MAAM,EAAE,KAAK,CAAC,IAAI,CAAC;IACxD,OAAO,EAAE,CAAC,KAAK,EAAE,OAAO,EAAE,OAAO,EAAE,EAAE,CACpC,MAAM,CAAC,mBAAmB,CAAC,KAAK,CAAC,MAAM,CAAC,CAAC,OAAO,CAAC,CAAC,IAAI,CACrD,MAAM,CAAC,QAAQ,CACd,CAAC,KAAK,EAAE,EAAE,CACT,IAAI,kBAAkB,CAAC;QACtB,SAAS,EAAE,QAAQ;QACnB,QAAQ,EAAE,QAAQ,CAAC,IAAI,CAAC,KAAK,CAAC;QAC9B,KAAK,EAAE,KAAK,CAAC,IAAI;KACjB,CAAC,CACH,EACD,MAAM,CAAC,OAAO,CAAC,CAAC,OAAO,EAA0E,EAAE;QAClG,IAAI,OAAO,OAAO,KAAK,QAAQ,IAAI,OAAO,KAAK,IAAI,EAAE,CAAC;YACrD,OAAO,MAAM,CAAC,IAAI,CACjB,IAAI,kBAAkB,CAAC;gBACtB,SAAS,EAAE,QAAQ;gBACnB,QAAQ,EAAE,QAAQ,CAAC,IAAI,CAAC,IAAI,SAAS,CAAC,yCAAyC,CAAC,CAAC;gBACjF,KAAK,EAAE,KAAK,CAAC,IAAI;aACjB,CAAC,CACF,CAAC;QACH,CAAC;QACD,OAAO,MAAM,CAAC,UAAU,CAAC;YACxB,KAAK,EAAE,CAAC,KAAK,EAAE,EAAE,CAAC,aAAa,CAAC,SAAS,EAAE,KAAK,EAAE,KAAK,CAAC,IAAI,CAAC;YAC7D,GAAG,EAAE,GAAG,EAAE,CAAC,MAAM,CAAC,IAAI,CAAC,KAAK,CAAC,IAAI,EAAE,OAAO,EAAE,OAAO,CAAC;SACpD,CAAC,CAAC,IAAI,CAAC,MAAM,CAAC,GAAG,CAAC,MAAM,CAAC,aAAa,CAAC,CAAC,CAAC;IAC3C,CAAC,CAAC,CACF;IACF,MAAM,EAAE,SAAS,CAAC,MAAM,CAAC;IACzB,MAAM,EAAE,SAAS,CAAC,MAAM,EAAE,KAAK,CAAC;IAChC,KAAK,EAAE,QAAQ,CAAC,MAAM,CAAC;CACvB,CAAC,CAAC"}
|
package/dist/server/service.d.ts
CHANGED
|
@@ -1,12 +1,15 @@
|
|
|
1
1
|
import { Context, Effect, Layer, type Option, type Schema } from "effect";
|
|
2
|
-
import type { ConstructorOptions, SendOptions, StopOptions } from "pg-boss";
|
|
2
|
+
import type { ConstructorOptions, JobWithMetadata, SendOptions, StopOptions } from "pg-boss";
|
|
3
3
|
import type { PgBossClientFactory } from "#client.ts";
|
|
4
|
-
import type { JobPayloadSchema, JobRegistration, QueueDefinition, RegistrationRequirements } from "
|
|
5
|
-
import type { PgBossError, PgBossPayloadError } from "
|
|
6
|
-
import type { JobsHealth } from "
|
|
4
|
+
import type { JobPayloadSchema, JobRegistration, QueueDefinition, RegistrationRequirements } from "#definition.ts";
|
|
5
|
+
import type { PgBossError, PgBossPayloadError, PgBossQueueOptionsError } from "#error.ts";
|
|
6
|
+
import type { JobsHealth, QueueCounts } from "#health.ts";
|
|
7
7
|
export interface PgBossService {
|
|
8
|
+
readonly counts: (queue: QueueDefinition) => Effect.Effect<QueueCounts, PgBossError>;
|
|
8
9
|
readonly enqueue: <const TName extends string, const TPayload extends JobPayloadSchema>(queue: QueueDefinition<TName, TPayload>, payload: Schema.Schema.Type<TPayload>, options?: SendOptions) => Effect.Effect<Option.Option<string>, PgBossError | PgBossPayloadError, TPayload["EncodingServices"]>;
|
|
10
|
+
readonly getJob: <const TName extends string, const TPayload extends JobPayloadSchema>(queue: QueueDefinition<TName, TPayload>, id: string) => Effect.Effect<Option.Option<JobWithMetadata<TPayload["Type"]>>, PgBossError | PgBossPayloadError, TPayload["DecodingServices"]>;
|
|
9
11
|
readonly health: Effect.Effect<JobsHealth, PgBossError>;
|
|
12
|
+
readonly retry: (queue: QueueDefinition, id: string) => Effect.Effect<void, PgBossError>;
|
|
10
13
|
}
|
|
11
14
|
export type PgBossLayerOptions<TRegistrations extends readonly JobRegistration[], TErrorRequirements> = ConstructorOptions & {
|
|
12
15
|
readonly clientFactory?: PgBossClientFactory;
|
|
@@ -16,7 +19,7 @@ export type PgBossLayerOptions<TRegistrations extends readonly JobRegistration[]
|
|
|
16
19
|
readonly stop?: StopOptions;
|
|
17
20
|
};
|
|
18
21
|
export interface PgBossDefinition<TIdentifier extends string = string> extends Context.Service<TIdentifier, PgBossService> {
|
|
19
|
-
readonly layer: <const TRegistrations extends readonly JobRegistration[], TErrorRequirements = never>(options: PgBossLayerOptions<TRegistrations, TErrorRequirements>) => Layer.Layer<TIdentifier, PgBossError, RegistrationRequirements<TRegistrations> | TErrorRequirements>;
|
|
22
|
+
readonly layer: <const TRegistrations extends readonly JobRegistration[], TErrorRequirements = never>(options: PgBossLayerOptions<TRegistrations, TErrorRequirements>) => Layer.Layer<TIdentifier, PgBossError | PgBossQueueOptionsError, RegistrationRequirements<TRegistrations> | TErrorRequirements>;
|
|
20
23
|
}
|
|
21
24
|
export declare const makePgBoss: <const TIdentifier extends string>(identifier: TIdentifier) => PgBossDefinition<TIdentifier>;
|
|
22
25
|
//# sourceMappingURL=service.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"service.d.ts","sourceRoot":"","sources":["../../src/server/service.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,MAAM,EAAE,KAAK,EAAE,KAAK,MAAM,EAAE,KAAK,MAAM,EAAE,MAAM,QAAQ,CAAC;AAC1E,OAAO,KAAK,EAAE,kBAAkB,EAAE,WAAW,EAAE,WAAW,EAAE,MAAM,SAAS,CAAC;
|
|
1
|
+
{"version":3,"file":"service.d.ts","sourceRoot":"","sources":["../../src/server/service.ts"],"names":[],"mappings":"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"}
|
package/dist/server/service.js
CHANGED
|
@@ -31,7 +31,7 @@ export const makePgBoss = (identifier) => {
|
|
|
31
31
|
resource.acquired.client.off("error", resource.errorListener);
|
|
32
32
|
return releaseClient(resource.acquired, stop);
|
|
33
33
|
});
|
|
34
|
-
return Layer.effect(Service, Effect.map(acquire, ({ acquired }) => makeService(acquired.client, names)));
|
|
34
|
+
return Layer.effect(Service, Effect.map(acquire, ({ acquired }) => makeService(acquired.client, names, constructorOptions.schema)));
|
|
35
35
|
};
|
|
36
36
|
return Object.assign(Service, { layer });
|
|
37
37
|
};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"service.js","sourceRoot":"","sources":["../../src/server/service.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,MAAM,EAAE,KAAK,EAA4B,MAAM,QAAQ,CAAC;
|
|
1
|
+
{"version":3,"file":"service.js","sourceRoot":"","sources":["../../src/server/service.ts"],"names":[],"mappings":"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"}
|
package/docs/north-star.md
CHANGED
|
@@ -43,6 +43,6 @@ An application that needs only queueing should not import a repository framework
|
|
|
43
43
|
- Domain workers, authorization rules, external-provider interfaces and application logging or health policy.
|
|
44
44
|
- Another general service declaration system; native Context and Layer stay available, and general declaration ergonomics belong to `effect-service`.
|
|
45
45
|
- Application repositories or migration ownership; those belong to `effect-sql`.
|
|
46
|
-
-
|
|
46
|
+
- Transactional enqueueing: jobs use a separate connection from application SQL transactions. Applications own any required outbox policy; after-commit changes have their own path through `effect-changes`.
|
|
47
47
|
- A promise of exactly-once domain side effects or provider idempotency. Those need application-specific decisions and evidence.
|
|
48
48
|
- Browser, device or offline synchronization behavior. A PostgreSQL queue boundary cannot establish those product contracts.
|
package/docs/roadmap.md
CHANGED
|
@@ -8,9 +8,13 @@ This file owns the package's implementation inventory and open API choices. The
|
|
|
8
8
|
- [x] Object payload Schema definitions, decoded enqueue types and queue workers. Type tests reject primitive schemas, missing fields and encoded values passed as decoded payloads.
|
|
9
9
|
- [x] Encoding at enqueue and decoding before the handler. The PostgreSQL test round-trips a transformed payload and checks that malformed stored data reaches the dead-letter queue without calling the domain handler.
|
|
10
10
|
- [x] Queue and schedule registration, with dead-letter queues, default retry settings and UTC schedule registration. Fake-client tests verify the supplied settings and a retry-limit override.
|
|
11
|
+
- [x] Producer-only queue definitions create queues and dead-letter queues through `Jobs.layer({ jobs: [Queue] })`, without starting a consumer. The [PostgreSQL producer test](../src/server/producerPostgres.spec.ts) enqueues a transformed payload and observes worker retry exhaustion and dead-letter delivery. The [starter producer](../../../examples/starter/src/server/noteProducer.ts) uses this API.
|
|
11
12
|
- [x] Worker and scheduled Effect service capture. Runtime and packed-consumer type tests preserve handler requirements.
|
|
12
13
|
- [x] Scoped startup and shutdown, including cleanup when startup or registration fails.
|
|
13
14
|
- [x] Explicit keyed client reuse and unkeyed per-build acquisition. Lifecycle tests cover two builds sharing one client and replacing the registered worker.
|
|
15
|
+
- [x] Full pg-boss queue creation options, excluding the adapter's name and dead-letter setup. [PostgreSQL evidence](../src/server/notifyPostgres.spec.ts) verifies notification delivery to a waiting worker and immediate live counts after completion; [type tests](../src/server/service.typecheck.test.ts) enforce option ownership.
|
|
16
|
+
- [x] Schema-decoded lookup by job ID and retry under the same ID for failed or retry-waiting jobs. [PostgreSQL evidence](../src/server/jobControlPostgres.spec.ts) verifies both states complete after a manual retry.
|
|
17
|
+
- [x] Startup reconciles declared mutable queue settings and rejects immutable policy or partition changes with a typed error. [PostgreSQL restart evidence](../src/server/queueOptionsPostgres.spec.ts) checks expiry and retry-limit changes on an existing queue.
|
|
14
18
|
- [x] Queue health totals for active, failed, queued, ready and dead-lettered counts, verified with a populated fake queue.
|
|
15
19
|
- [x] Public defining-module imports and an installed-package type fixture.
|
|
16
20
|
|
|
@@ -18,9 +22,11 @@ This file owns the package's implementation inventory and open API choices. The
|
|
|
18
22
|
|
|
19
23
|
No further package API expansion is selected. Let the framework's jobs integration work identify the next package change, and preserve the existing payload and lifecycle contracts while it does. That integration's status stays in the framework roadmap.
|
|
20
24
|
|
|
25
|
+
## Decisions
|
|
26
|
+
|
|
27
|
+
Enqueueing is separate from application SQL transactions. The package supplies no transactional enqueue adapter. Applications use transactions for database invariants and handle queue errors without failing those transactions. The starter enqueues after its note write transaction and logs queue failures; its [PostgreSQL queue-failure story](../../../examples/starter/src/server/queueFailure.spec.ts) proves that rejected enqueueing leaves create and edit writes saved. Its [rollback story](../../../examples/starter/src/server/committedJobs.spec.ts) also proves that an independently enqueued job survives an outer application rollback.
|
|
28
|
+
|
|
21
29
|
## Open questions
|
|
22
30
|
|
|
23
|
-
- Which application boundary should choose how a database write becomes a durable job: an explicit after-commit action, an outbox, or another demonstrated approach? The enqueue API is independent of application SQL; no transaction coupling is selected.
|
|
24
31
|
- Should a shared client key be checked against constructor settings and registration changes? The lifecycle fixture uses the same Layer twice; it does not settle incompatible configurations under one key.
|
|
25
|
-
- Which public hooks need dedicated regression coverage before the guide makes stronger claims? Error-event reporting, cancellation through a job's AbortSignal, a declined send result, multi-queue health and actual cron
|
|
26
|
-
- Should the package grow only when an application's integration needs a helper, or is a wider jobs API intended? The package has no in-repo application consumer to settle that question.
|
|
32
|
+
- Which public hooks need dedicated regression coverage before the guide makes stronger claims? Error-event reporting, cancellation through a job's AbortSignal, a declined send result, multi-queue health and actual cron execution are implemented boundaries or engine behaviors without focused evidence here.
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@shivaedev/effect-pg-boss",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.6.0",
|
|
4
4
|
"description": "Effect-native jobs and lifecycle management for pg-boss",
|
|
5
5
|
"homepage": "https://github.com/ShivaeDev/platform/tree/main/packages/effect-pg-boss#readme",
|
|
6
6
|
"bugs": {
|
|
@@ -43,7 +43,7 @@
|
|
|
43
43
|
"README.md"
|
|
44
44
|
],
|
|
45
45
|
"devDependencies": {
|
|
46
|
-
"@shivaedev/quality": "0.
|
|
46
|
+
"@shivaedev/quality": "0.22.0",
|
|
47
47
|
"@types/node": "24.19.1",
|
|
48
48
|
"@typescript/native": "npm:typescript@7.0.2",
|
|
49
49
|
"effect": "4.0.4",
|
package/src/server/client.ts
CHANGED
|
@@ -1,16 +1,32 @@
|
|
|
1
|
-
import type {
|
|
1
|
+
import type {
|
|
2
|
+
ConstructorOptions,
|
|
3
|
+
FindJobsOptions,
|
|
4
|
+
Job,
|
|
5
|
+
JobWithMetadata,
|
|
6
|
+
Queue,
|
|
7
|
+
QueueResult,
|
|
8
|
+
ScheduleOptions,
|
|
9
|
+
SendOptions,
|
|
10
|
+
StopOptions,
|
|
11
|
+
WorkOptions,
|
|
12
|
+
} from "pg-boss";
|
|
2
13
|
import { PgBoss } from "pg-boss";
|
|
3
14
|
|
|
4
15
|
export interface PgBossClient {
|
|
5
16
|
readonly createQueue: (name: string, options?: Omit<Queue, "name">) => Promise<void>;
|
|
17
|
+
readonly findJobs?: (name: string, options?: FindJobsOptions) => Promise<readonly JobWithMetadata<unknown>[]>;
|
|
18
|
+
readonly getDb?: PgBoss["getDb"];
|
|
6
19
|
readonly getQueue: (name: string) => Promise<QueueResult | null>;
|
|
7
20
|
readonly off: (event: "error", listener: (error: Error) => void) => unknown;
|
|
8
21
|
readonly offWork: (name: string) => Promise<void>;
|
|
9
22
|
readonly on: (event: "error", listener: (error: Error) => void) => unknown;
|
|
23
|
+
readonly retry?: PgBoss["retry"];
|
|
10
24
|
readonly schedule: (name: string, cron: string, data?: object | null, options?: ScheduleOptions) => Promise<void>;
|
|
11
25
|
readonly send: (name: string, data?: object | null, options?: SendOptions) => Promise<string | null>;
|
|
12
26
|
readonly start: () => Promise<unknown>;
|
|
13
27
|
readonly stop: (options?: StopOptions) => Promise<void>;
|
|
28
|
+
readonly update?: PgBoss["update"];
|
|
29
|
+
readonly updateQueue?: PgBoss["updateQueue"];
|
|
14
30
|
readonly work: (name: string, options: WorkOptions, handler: (jobs: readonly Job<unknown>[]) => Promise<unknown>) => Promise<string>;
|
|
15
31
|
}
|
|
16
32
|
|
package/src/server/definition.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import type { Effect, Schema } from "effect";
|
|
2
|
-
import type { Job,
|
|
2
|
+
import type { Job, Queue, ScheduleOptions, WorkOptions } from "pg-boss";
|
|
3
3
|
|
|
4
4
|
export interface JobPayloadSchema extends Schema.Top {
|
|
5
5
|
readonly Encoded: object;
|
|
@@ -20,7 +20,7 @@ export interface QueueDefinition<Name extends string = string, Payload extends J
|
|
|
20
20
|
handler: (payload: Schema.Schema.Type<Payload>, job: JobContext) => Effect.Effect<unknown, E, R>,
|
|
21
21
|
) => QueueWorker<Payload, R>;
|
|
22
22
|
readonly name: Name;
|
|
23
|
-
readonly queueOptions: Omit<
|
|
23
|
+
readonly queueOptions: Omit<Queue, "name" | "deadLetter">;
|
|
24
24
|
readonly schema: Payload;
|
|
25
25
|
readonly workerOptions: WorkOptions;
|
|
26
26
|
}
|
|
@@ -39,7 +39,7 @@ export interface ScheduleDefinition<Name extends string = string> {
|
|
|
39
39
|
readonly _tag: "ScheduleDefinition";
|
|
40
40
|
readonly cron: string;
|
|
41
41
|
readonly name: Name;
|
|
42
|
-
readonly queueOptions: Omit<
|
|
42
|
+
readonly queueOptions: Omit<Queue, "name" | "deadLetter">;
|
|
43
43
|
readonly run: <E, R>(effect: Effect.Effect<unknown, E, R>) => ScheduledWorker<R>;
|
|
44
44
|
readonly scheduleOptions: ScheduleOptions;
|
|
45
45
|
readonly workerOptions: WorkOptions;
|
|
@@ -61,7 +61,7 @@ export type RegistrationRequirements<Registrations extends readonly JobRegistrat
|
|
|
61
61
|
|
|
62
62
|
export interface DefineQueueOptions<Name extends string, Payload extends JobPayloadSchema> {
|
|
63
63
|
readonly name: Name;
|
|
64
|
-
readonly queue?: Omit<
|
|
64
|
+
readonly queue?: Omit<Queue, "name" | "deadLetter">;
|
|
65
65
|
readonly schema: Payload;
|
|
66
66
|
readonly worker?: WorkOptions;
|
|
67
67
|
}
|
|
@@ -88,7 +88,7 @@ export const defineQueue = <const Name extends string, const Payload extends Job
|
|
|
88
88
|
export interface DefineScheduleOptions<Name extends string> {
|
|
89
89
|
readonly cron: string;
|
|
90
90
|
readonly name: Name;
|
|
91
|
-
readonly queue?: Omit<
|
|
91
|
+
readonly queue?: Omit<Queue, "name" | "deadLetter">;
|
|
92
92
|
readonly schedule?: ScheduleOptions;
|
|
93
93
|
readonly worker?: WorkOptions;
|
|
94
94
|
}
|
package/src/server/error.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { Data, Redacted } from "effect";
|
|
2
2
|
|
|
3
|
-
export type PgBossOperation = "enqueue" | "health" | "register" | "start" | "stop";
|
|
3
|
+
export type PgBossOperation = "counts" | "enqueue" | "getJob" | "health" | "register" | "retry" | "start" | "stop";
|
|
4
4
|
|
|
5
5
|
export class PgBossError extends Data.TaggedError("PgBossError")<{
|
|
6
6
|
readonly operation: PgBossOperation;
|
|
@@ -8,6 +8,15 @@ export class PgBossError extends Data.TaggedError("PgBossError")<{
|
|
|
8
8
|
readonly original: Redacted.Redacted<unknown>;
|
|
9
9
|
}> {}
|
|
10
10
|
|
|
11
|
+
export class PgBossQueueOptionsError extends Data.TaggedError("PgBossQueueOptionsError")<{
|
|
12
|
+
readonly queue: string;
|
|
13
|
+
readonly options: readonly ("partition" | "policy")[];
|
|
14
|
+
}> {
|
|
15
|
+
override get message(): string {
|
|
16
|
+
return `Queue ${this.queue} cannot change options after creation: ${this.options.join(", ")}`;
|
|
17
|
+
}
|
|
18
|
+
}
|
|
19
|
+
|
|
11
20
|
export class PgBossPayloadError extends Data.TaggedError("PgBossPayloadError")<{
|
|
12
21
|
readonly direction: "decode" | "encode";
|
|
13
22
|
readonly queue: string;
|
package/src/server/health.ts
CHANGED
|
@@ -1,3 +1,10 @@
|
|
|
1
|
+
export interface QueueCounts {
|
|
2
|
+
readonly activeCount: number;
|
|
3
|
+
readonly failedCount: number;
|
|
4
|
+
readonly queuedCount: number;
|
|
5
|
+
readonly totalCount: number;
|
|
6
|
+
}
|
|
7
|
+
|
|
1
8
|
export interface QueueHealth {
|
|
2
9
|
readonly activeCount: number;
|
|
3
10
|
readonly deadLetteredCount: number;
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
import { Effect, Schema } from "effect";
|
|
2
|
+
import type { PgBossClient } from "#client.ts";
|
|
3
|
+
import { toPgBossError } from "#error.ts";
|
|
4
|
+
import type { QueueCounts } from "#health.ts";
|
|
5
|
+
|
|
6
|
+
const Counts = Schema.Struct({
|
|
7
|
+
activeCount: Schema.Number,
|
|
8
|
+
failedCount: Schema.Number,
|
|
9
|
+
queuedCount: Schema.Number,
|
|
10
|
+
totalCount: Schema.Number,
|
|
11
|
+
});
|
|
12
|
+
|
|
13
|
+
export function countsFor(client: PgBossClient, schema: string, name: string): Effect.Effect<QueueCounts, import("#error.ts").PgBossError> {
|
|
14
|
+
return Effect.tryPromise({
|
|
15
|
+
catch: (error) => toPgBossError("counts", error, name),
|
|
16
|
+
try: async () => {
|
|
17
|
+
if (client.getDb === undefined) {
|
|
18
|
+
throw new TypeError("This pg-boss client does not support getDb");
|
|
19
|
+
}
|
|
20
|
+
const quotedSchema = `"${schema.replaceAll('"', '""')}"`;
|
|
21
|
+
const { rows } = await client.getDb().executeSql(
|
|
22
|
+
`SELECT
|
|
23
|
+
count(*) FILTER (WHERE state = 'active')::int AS "activeCount",
|
|
24
|
+
count(*) FILTER (WHERE state = 'failed')::int AS "failedCount",
|
|
25
|
+
count(*) FILTER (WHERE state IN ('created', 'retry'))::int AS "queuedCount",
|
|
26
|
+
count(*)::int AS "totalCount"
|
|
27
|
+
FROM ${quotedSchema}.job WHERE name = $1`,
|
|
28
|
+
[name],
|
|
29
|
+
);
|
|
30
|
+
return Schema.decodeUnknownPromise(Counts)(rows[0]);
|
|
31
|
+
},
|
|
32
|
+
});
|
|
33
|
+
}
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import type { Queue } from "pg-boss";
|
|
2
|
+
import type { PgBossClient } from "#client.ts";
|
|
3
|
+
import { PgBossQueueOptionsError } from "#error.ts";
|
|
4
|
+
import { deadLetterQueueName } from "#health.ts";
|
|
5
|
+
|
|
6
|
+
export async function ensureQueue(client: PgBossClient, name: string, options: Omit<Queue, "name" | "deadLetter">): Promise<void> {
|
|
7
|
+
const deadLetter = deadLetterQueueName(name);
|
|
8
|
+
const desired = { retryBackoff: true, retryLimit: 3, ...options, deadLetter };
|
|
9
|
+
await client.createQueue(deadLetter);
|
|
10
|
+
await client.createQueue(name, desired);
|
|
11
|
+
const existing = await client.getQueue(name);
|
|
12
|
+
if (existing === null) {
|
|
13
|
+
return;
|
|
14
|
+
}
|
|
15
|
+
const immutable = (["partition", "policy"] as const).filter((key) => desired[key] !== undefined && desired[key] !== existing[key]);
|
|
16
|
+
if (immutable.length > 0) {
|
|
17
|
+
throw new PgBossQueueOptionsError({ options: immutable, queue: name });
|
|
18
|
+
}
|
|
19
|
+
const { partition: _partition, policy: _policy, ...mutable } = desired;
|
|
20
|
+
const changed = Object.entries(mutable).some(([key, value]) => value !== undefined && value !== Reflect.get(existing, key));
|
|
21
|
+
if (!changed) {
|
|
22
|
+
return;
|
|
23
|
+
}
|
|
24
|
+
if (client.updateQueue === undefined) {
|
|
25
|
+
throw new TypeError("This pg-boss client does not support updateQueue");
|
|
26
|
+
}
|
|
27
|
+
// pg-boss validates retryDelayMax against retryBackoff in the same update.
|
|
28
|
+
await client.updateQueue(name, mutable);
|
|
29
|
+
}
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
import { Effect, Option, Redacted, Schema } from "effect";
|
|
2
|
+
import type { PgBossClient } from "#client.ts";
|
|
3
|
+
import { PgBossPayloadError, toPgBossError } from "#error.ts";
|
|
4
|
+
import type { PgBossService } from "#service.ts";
|
|
5
|
+
|
|
6
|
+
export function getJobFor(client: PgBossClient): PgBossService["getJob"] {
|
|
7
|
+
return (queue, id) =>
|
|
8
|
+
Effect.tryPromise({
|
|
9
|
+
catch: (error) => toPgBossError("getJob", error, queue.name),
|
|
10
|
+
try: async () => {
|
|
11
|
+
if (client.findJobs === undefined) {
|
|
12
|
+
throw new TypeError("This pg-boss client does not support findJobs");
|
|
13
|
+
}
|
|
14
|
+
return (await client.findJobs(queue.name, { id }))[0];
|
|
15
|
+
},
|
|
16
|
+
}).pipe(
|
|
17
|
+
Effect.flatMap((job) =>
|
|
18
|
+
job === undefined
|
|
19
|
+
? Effect.succeed(Option.none())
|
|
20
|
+
: Schema.decodeUnknownEffect(queue.schema)(job.data).pipe(
|
|
21
|
+
Effect.mapError((error) => new PgBossPayloadError({ direction: "decode", original: Redacted.make(error), queue: queue.name })),
|
|
22
|
+
Effect.map((data) => Option.some({ ...job, data })),
|
|
23
|
+
),
|
|
24
|
+
),
|
|
25
|
+
);
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
export function retryFor(client: PgBossClient): PgBossService["retry"] {
|
|
29
|
+
return (queue, id) =>
|
|
30
|
+
Effect.tryPromise({
|
|
31
|
+
catch: (error) => toPgBossError("retry", error, queue.name),
|
|
32
|
+
try: async () => {
|
|
33
|
+
if (client.findJobs === undefined || client.retry === undefined || client.update === undefined) {
|
|
34
|
+
throw new TypeError("This pg-boss client does not support findJobs, retry and update");
|
|
35
|
+
}
|
|
36
|
+
const job = (await client.findJobs(queue.name, { id }))[0];
|
|
37
|
+
if (job?.state !== "failed" && job?.state !== "retry") {
|
|
38
|
+
return;
|
|
39
|
+
}
|
|
40
|
+
if (job.state === "failed") {
|
|
41
|
+
await client.retry(queue.name, id);
|
|
42
|
+
}
|
|
43
|
+
await client.update(queue.name, undefined, { id, startAfter: 0 });
|
|
44
|
+
},
|
|
45
|
+
});
|
|
46
|
+
}
|
|
@@ -2,16 +2,8 @@ import { type Context, Effect, Redacted, Schema } from "effect";
|
|
|
2
2
|
import type { Job } from "pg-boss";
|
|
3
3
|
import type { PgBossClient } from "#client.ts";
|
|
4
4
|
import { type JobRegistration, jobContext, type QueueDefinition, type QueueWorker, type ScheduledWorker } from "#definition.ts";
|
|
5
|
-
import { PgBossPayloadError, toPgBossError } from "#error.ts";
|
|
6
|
-
import {
|
|
7
|
-
|
|
8
|
-
function queueOptions(options: Readonly<Record<string, unknown>>) {
|
|
9
|
-
return {
|
|
10
|
-
retryBackoff: true,
|
|
11
|
-
retryLimit: 3,
|
|
12
|
-
...options,
|
|
13
|
-
};
|
|
14
|
-
}
|
|
5
|
+
import { PgBossPayloadError, PgBossQueueOptionsError, toPgBossError } from "#error.ts";
|
|
6
|
+
import { ensureQueue } from "#internal/ensureQueue.ts";
|
|
15
7
|
|
|
16
8
|
function isQueueWorker(registration: JobRegistration): registration is QueueWorker {
|
|
17
9
|
return registration._tag === "QueueWorker";
|
|
@@ -70,15 +62,10 @@ async function registerQueue(
|
|
|
70
62
|
): Promise<void> {
|
|
71
63
|
const queue = registration._tag === "QueueWorker" ? registration.queue : registration;
|
|
72
64
|
const name = queue.name;
|
|
73
|
-
const deadLetter = deadLetterQueueName(name);
|
|
74
65
|
if (replaceWorker && registration._tag === "QueueWorker") {
|
|
75
66
|
await client.offWork(name);
|
|
76
67
|
}
|
|
77
|
-
await client.
|
|
78
|
-
await client.createQueue(name, {
|
|
79
|
-
...queueOptions(queue.queueOptions),
|
|
80
|
-
deadLetter,
|
|
81
|
-
});
|
|
68
|
+
await ensureQueue(client, name, queue.queueOptions);
|
|
82
69
|
if (registration._tag === "QueueDefinition") {
|
|
83
70
|
return;
|
|
84
71
|
}
|
|
@@ -96,15 +83,10 @@ async function registerSchedule(
|
|
|
96
83
|
replaceWorker: boolean,
|
|
97
84
|
): Promise<void> {
|
|
98
85
|
const { schedule } = worker;
|
|
99
|
-
const deadLetter = deadLetterQueueName(schedule.name);
|
|
100
86
|
if (replaceWorker) {
|
|
101
87
|
await client.offWork(schedule.name);
|
|
102
88
|
}
|
|
103
|
-
await client.
|
|
104
|
-
await client.createQueue(schedule.name, {
|
|
105
|
-
...queueOptions(schedule.queueOptions),
|
|
106
|
-
deadLetter,
|
|
107
|
-
});
|
|
89
|
+
await ensureQueue(client, schedule.name, schedule.queueOptions);
|
|
108
90
|
await client.work(schedule.name, schedule.workerOptions, async (jobs: readonly Job<unknown>[]) => {
|
|
109
91
|
for (const job of jobs) {
|
|
110
92
|
await Effect.runPromise(Effect.provide(worker.effect, context), {
|
|
@@ -128,9 +110,9 @@ export const registerJobs = <R>(
|
|
|
128
110
|
registrations: readonly JobRegistration[],
|
|
129
111
|
context: Context.Context<R>,
|
|
130
112
|
replaceWorkers: boolean,
|
|
131
|
-
): Effect.Effect<void, import("#error.ts").PgBossError> =>
|
|
113
|
+
): Effect.Effect<void, import("#error.ts").PgBossError | PgBossQueueOptionsError> =>
|
|
132
114
|
Effect.tryPromise({
|
|
133
|
-
catch: (error) => toPgBossError("register", error),
|
|
115
|
+
catch: (error) => (error instanceof PgBossQueueOptionsError ? error : toPgBossError("register", error)),
|
|
134
116
|
try: async () => {
|
|
135
117
|
registrationNames(registrations);
|
|
136
118
|
const provided = workerContext(context);
|
|
@@ -1,10 +1,13 @@
|
|
|
1
1
|
import { Effect, Option, Redacted, Schema } from "effect";
|
|
2
2
|
import type { PgBossClient } from "#client.ts";
|
|
3
3
|
import { type PgBossError, PgBossPayloadError, toPgBossError } from "#error.ts";
|
|
4
|
+
import { countsFor } from "#internal/countsFor.ts";
|
|
5
|
+
import { healthFor } from "#internal/health.ts";
|
|
6
|
+
import { getJobFor, retryFor } from "#internal/jobControl.ts";
|
|
4
7
|
import type { PgBossService } from "#service.ts";
|
|
5
|
-
import { healthFor } from "./health.ts";
|
|
6
8
|
|
|
7
|
-
export const makeService = (client: PgBossClient, names: readonly string[]): PgBossService => ({
|
|
9
|
+
export const makeService = (client: PgBossClient, names: readonly string[], schema = "pgboss"): PgBossService => ({
|
|
10
|
+
counts: (queue) => countsFor(client, schema, queue.name),
|
|
8
11
|
enqueue: (queue, payload, options) =>
|
|
9
12
|
Schema.encodeUnknownEffect(queue.schema)(payload).pipe(
|
|
10
13
|
Effect.mapError(
|
|
@@ -31,5 +34,7 @@ export const makeService = (client: PgBossClient, names: readonly string[]): PgB
|
|
|
31
34
|
}).pipe(Effect.map(Option.fromNullishOr));
|
|
32
35
|
}),
|
|
33
36
|
),
|
|
37
|
+
getJob: getJobFor(client),
|
|
34
38
|
health: healthFor(client, names),
|
|
39
|
+
retry: retryFor(client),
|
|
35
40
|
});
|
package/src/server/service.ts
CHANGED
|
@@ -1,20 +1,26 @@
|
|
|
1
1
|
import { Context, Effect, Layer, type Option, type Schema } from "effect";
|
|
2
|
-
import type { ConstructorOptions, SendOptions, StopOptions } from "pg-boss";
|
|
2
|
+
import type { ConstructorOptions, JobWithMetadata, SendOptions, StopOptions } from "pg-boss";
|
|
3
3
|
import type { PgBossClientFactory } from "#client.ts";
|
|
4
|
+
import type { JobPayloadSchema, JobRegistration, QueueDefinition, RegistrationRequirements } from "#definition.ts";
|
|
5
|
+
import type { PgBossError, PgBossPayloadError, PgBossQueueOptionsError } from "#error.ts";
|
|
6
|
+
import type { JobsHealth, QueueCounts } from "#health.ts";
|
|
4
7
|
import { acquireClient, releaseClient } from "#internal/lifecycle.ts";
|
|
5
8
|
import { registerJobs, registrationName } from "#internal/register.ts";
|
|
6
9
|
import { makeService } from "#internal/service.ts";
|
|
7
|
-
import type { JobPayloadSchema, JobRegistration, QueueDefinition, RegistrationRequirements } from "./definition.ts";
|
|
8
|
-
import type { PgBossError, PgBossPayloadError } from "./error.ts";
|
|
9
|
-
import type { JobsHealth } from "./health.ts";
|
|
10
10
|
|
|
11
11
|
export interface PgBossService {
|
|
12
|
+
readonly counts: (queue: QueueDefinition) => Effect.Effect<QueueCounts, PgBossError>;
|
|
12
13
|
readonly enqueue: <const TName extends string, const TPayload extends JobPayloadSchema>(
|
|
13
14
|
queue: QueueDefinition<TName, TPayload>,
|
|
14
15
|
payload: Schema.Schema.Type<TPayload>,
|
|
15
16
|
options?: SendOptions,
|
|
16
17
|
) => Effect.Effect<Option.Option<string>, PgBossError | PgBossPayloadError, TPayload["EncodingServices"]>;
|
|
18
|
+
readonly getJob: <const TName extends string, const TPayload extends JobPayloadSchema>(
|
|
19
|
+
queue: QueueDefinition<TName, TPayload>,
|
|
20
|
+
id: string,
|
|
21
|
+
) => Effect.Effect<Option.Option<JobWithMetadata<TPayload["Type"]>>, PgBossError | PgBossPayloadError, TPayload["DecodingServices"]>;
|
|
17
22
|
readonly health: Effect.Effect<JobsHealth, PgBossError>;
|
|
23
|
+
readonly retry: (queue: QueueDefinition, id: string) => Effect.Effect<void, PgBossError>;
|
|
18
24
|
}
|
|
19
25
|
|
|
20
26
|
export type PgBossLayerOptions<TRegistrations extends readonly JobRegistration[], TErrorRequirements> = ConstructorOptions & {
|
|
@@ -29,7 +35,7 @@ export type PgBossLayerOptions<TRegistrations extends readonly JobRegistration[]
|
|
|
29
35
|
export interface PgBossDefinition<TIdentifier extends string = string> extends Context.Service<TIdentifier, PgBossService> {
|
|
30
36
|
readonly layer: <const TRegistrations extends readonly JobRegistration[], TErrorRequirements = never>(
|
|
31
37
|
options: PgBossLayerOptions<TRegistrations, TErrorRequirements>,
|
|
32
|
-
) => Layer.Layer<TIdentifier, PgBossError, RegistrationRequirements<TRegistrations> | TErrorRequirements>;
|
|
38
|
+
) => Layer.Layer<TIdentifier, PgBossError | PgBossQueueOptionsError, RegistrationRequirements<TRegistrations> | TErrorRequirements>;
|
|
33
39
|
}
|
|
34
40
|
|
|
35
41
|
function logClientError(error: Error): Effect.Effect<void> {
|
|
@@ -41,7 +47,7 @@ export const makePgBoss = <const TIdentifier extends string>(identifier: TIdenti
|
|
|
41
47
|
|
|
42
48
|
const layer = <const TRegistrations extends readonly JobRegistration[], TErrorRequirements = never>(
|
|
43
49
|
options: PgBossLayerOptions<TRegistrations, TErrorRequirements>,
|
|
44
|
-
): Layer.Layer<TIdentifier, PgBossError, RegistrationRequirements<TRegistrations> | TErrorRequirements> => {
|
|
50
|
+
): Layer.Layer<TIdentifier, PgBossError | PgBossQueueOptionsError, RegistrationRequirements<TRegistrations> | TErrorRequirements> => {
|
|
45
51
|
type Requirements = RegistrationRequirements<TRegistrations> | TErrorRequirements;
|
|
46
52
|
const names = options.jobs.map(registrationName);
|
|
47
53
|
const { clientFactory, clientCacheKey, jobs, onError, stop, ...constructorOptions } = options;
|
|
@@ -76,7 +82,7 @@ export const makePgBoss = <const TIdentifier extends string>(identifier: TIdenti
|
|
|
76
82
|
|
|
77
83
|
return Layer.effect(
|
|
78
84
|
Service,
|
|
79
|
-
Effect.map(acquire, ({ acquired }) => makeService(acquired.client, names)),
|
|
85
|
+
Effect.map(acquire, ({ acquired }) => makeService(acquired.client, names, constructorOptions.schema)),
|
|
80
86
|
);
|
|
81
87
|
};
|
|
82
88
|
|