@shivaedev/effect-pg-boss 0.6.1 → 0.7.1
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 +20 -0
- package/README.md +46 -2
- package/dist/server/client.d.ts +2 -0
- package/dist/server/client.d.ts.map +1 -1
- package/dist/server/client.js.map +1 -1
- package/dist/server/definition.js +2 -2
- package/dist/server/definition.js.map +1 -1
- package/dist/server/internal/batchHandler.d.ts +5 -0
- package/dist/server/internal/batchHandler.d.ts.map +1 -0
- package/dist/server/internal/batchHandler.js +28 -0
- package/dist/server/internal/batchHandler.js.map +1 -0
- package/dist/server/internal/register.d.ts +1 -1
- package/dist/server/internal/register.d.ts.map +1 -1
- package/dist/server/internal/register.js +30 -32
- package/dist/server/internal/register.js.map +1 -1
- package/dist/server/service.d.ts +1 -1
- package/dist/server/service.d.ts.map +1 -1
- package/dist/server/service.js +5 -3
- package/dist/server/service.js.map +1 -1
- package/docs/roadmap.md +2 -0
- package/package.json +1 -1
- package/src/server/client.ts +2 -0
- package/src/server/definition.ts +2 -2
- package/src/server/internal/batchHandler.ts +33 -0
- package/src/server/internal/register.ts +57 -55
- package/src/server/service.ts +17 -7
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,25 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## 0.7.1 - 2026-10-17
|
|
4
|
+
|
|
5
|
+
### Added
|
|
6
|
+
|
|
7
|
+
- Custom `PgBossClient` factories can supply optional native `complete` and `fail` methods. Queue and schedule worker registration requires both capabilities and fails with a registration `PgBossError` when either is absent; producer-only clients do not need them.
|
|
8
|
+
|
|
9
|
+
### Fixed
|
|
10
|
+
|
|
11
|
+
- Settle each queue and scheduled job separately through pg-boss, recording typed failures and defects on only the failed job. In 0.7.0, one failing handler failed and retried the whole fetched batch, spending unrelated jobs’ retries and rerunning successful handlers. Batches still drain with `burstWhenBatchFull`. Interruption leaves unprocessed jobs to pg-boss expiry.
|
|
12
|
+
|
|
13
|
+
## 0.7.0 - 2026-10-17
|
|
14
|
+
|
|
15
|
+
### Changed
|
|
16
|
+
|
|
17
|
+
- Queue and schedule workers default to native pg-boss `batchSize: 10` and `burstWhenBatchFull: true`, draining full batches immediately instead of waiting for the notification polling backstop. Native worker options still override the defaults, including batch size and local concurrency. Handlers run sequentially within each batch; pg-boss retries a rejected batch together.
|
|
18
|
+
|
|
19
|
+
### Fixed
|
|
20
|
+
|
|
21
|
+
- `PgBossDefinition.layer` supplies its own jobs service to queue handlers and scheduled effects through the scoped Effect context. Handlers can enqueue follow-ups to worker or producer-only queues using that same client. Only the owning identifier is removed from the Layer's input requirements; other services remain typed. Startup creates every declared queue before starting consumers.
|
|
22
|
+
|
|
3
23
|
## 0.6.1 - 2026-10-17
|
|
4
24
|
|
|
5
25
|
### Fixed
|
package/README.md
CHANGED
|
@@ -99,6 +99,23 @@ The schedule runs a payload-free Effect. This schedule registers its cron expres
|
|
|
99
99
|
|
|
100
100
|
The Layer requires the workers' Effect services. Leaving `Mail` unprovided leaves that requirement in the resulting Effect's type. A primitive Schema such as `Schema.String` is rejected as a queue definition because the durable contract must be an object.
|
|
101
101
|
|
|
102
|
+
### Backlog throughput and concurrency
|
|
103
|
+
|
|
104
|
+
Queue and schedule workers default to `batchSize: 10` and `burstWhenBatchFull: true`. pg-boss fetches again immediately after a full batch completes, until a fetch returns fewer than ten jobs. The adapter runs handlers sequentially within each batch, and pg-boss defaults `localConcurrency` to one. Set native `localConcurrency` to run multiple batches at once:
|
|
105
|
+
|
|
106
|
+
```ts
|
|
107
|
+
const Compare = defineQueue({
|
|
108
|
+
name: "compare",
|
|
109
|
+
schema: Schema.Struct({ id: Schema.Number }),
|
|
110
|
+
queue: { notify: true },
|
|
111
|
+
worker: { batchSize: 2, localConcurrency: 5 },
|
|
112
|
+
});
|
|
113
|
+
```
|
|
114
|
+
|
|
115
|
+
This worker runs up to five handlers at once. `worker` accepts pg-boss 12's `WorkOptions`, including `batchSize`, `localConcurrency`, `pollingIntervalSeconds`, `notifyPollingIntervalSeconds` and the burst options. Explicit options override the adapter defaults. pg-boss ignores `burstWhenBatchFull` at `batchSize: 1`; keep a batch size of at least two for immediate backlog draining. Each job is settled separately through pg-boss: success completes that job, and a typed failure, payload decoding failure or defect fails only that job with its own error. A failed job does not spend the retries of other jobs in its batch or rerun their successful handlers. Interruption stops the batch and leaves the interrupted and remaining jobs to pg-boss expiry; handlers must still tolerate repeated side effects across retries and interrupted attempts.
|
|
116
|
+
|
|
117
|
+
Apps do not need to shorten the polling backstop to drain a backlog. With `notify: true` and an active LISTEN/NOTIFY listener, pg-boss wakes workers on notifications and defaults to a 30-second backstop (`notifyPollingIntervalSeconds`). Without active notifications its base poll defaults to two seconds (`pollingIntervalSeconds`). Bursting takes precedence over both intervals. Override the backstop only when the application needs a different bound for work that becomes runnable without a notification, such as delayed jobs or retries. The PostgreSQL backlog tests insert 30 jobs at once and verify completion within 2.5 seconds with both defaults and configured batches and concurrency.
|
|
118
|
+
|
|
102
119
|
### Once per runtime: provide the workers
|
|
103
120
|
|
|
104
121
|
Build the jobs Layer in `runtime.ts`:
|
|
@@ -243,6 +260,33 @@ export const producerLayer = (databaseUrl: string) =>
|
|
|
243
260
|
|
|
244
261
|
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.
|
|
245
262
|
|
|
263
|
+
### Enqueue follow-ups from a handler
|
|
264
|
+
|
|
265
|
+
A handler can yield the same `Jobs` service that owns it. The Layer supplies that service from its scoped client; other handler and Schema service requirements remain required inputs to the Layer.
|
|
266
|
+
|
|
267
|
+
```ts
|
|
268
|
+
const Receipt = defineQueue({
|
|
269
|
+
name: "receipt",
|
|
270
|
+
schema: Schema.Struct({ userId: Schema.Number }),
|
|
271
|
+
});
|
|
272
|
+
|
|
273
|
+
const sendAndQueueReceipt = SendEmail.handle(({ userId }) =>
|
|
274
|
+
Effect.gen(function* () {
|
|
275
|
+
const mail = yield* Mail;
|
|
276
|
+
yield* mail.send(userId);
|
|
277
|
+
const jobs = yield* Jobs;
|
|
278
|
+
yield* jobs.enqueue(Receipt, { userId });
|
|
279
|
+
}),
|
|
280
|
+
);
|
|
281
|
+
|
|
282
|
+
const live = Jobs.layer({
|
|
283
|
+
connectionString: databaseUrl,
|
|
284
|
+
jobs: [sendAndQueueReceipt, Receipt],
|
|
285
|
+
}).pipe(Layer.provide(MailLive));
|
|
286
|
+
```
|
|
287
|
+
|
|
288
|
+
Include every target queue in `jobs`, as either a worker or a producer-only definition. Startup creates all queues before starting consumers. The example registers `Receipt` for another process to consume. A follow-up target can also have a worker in the same Layer. Scheduled effects can yield `Jobs` too. Keep one jobs Layer in the process's native Layer graph; that Layer owns one client for enqueueing and handling. The PostgreSQL follow-up test completes a chain across two workers, enqueues to a producer-only queue and asserts one client acquisition and shutdown.
|
|
289
|
+
|
|
246
290
|
### API
|
|
247
291
|
|
|
248
292
|
Import the module that defines the name; the package has no root entry.
|
|
@@ -265,9 +309,9 @@ The application's service has these operations:
|
|
|
265
309
|
- `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.
|
|
266
310
|
- `retry(queue, id)`: an Effect returning `void`, with `PgBossError` in its error type.
|
|
267
311
|
|
|
268
|
-
`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`.
|
|
312
|
+
`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 must supply native `complete` and `fail` capabilities to register queue or schedule workers; missing settlement capabilities fail Layer acquisition with a registration `PgBossError`. Producer-only clients do not need them. Custom clients can also 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`.
|
|
269
313
|
|
|
270
|
-
`onError` handles pg-boss client `error` events using the services captured by the Layer. The Layer's requirement type includes the workers' services
|
|
314
|
+
`onError` handles pg-boss client `error` events using the services captured by the Layer. The Layer's requirement type includes the workers' services and their payload Schemas' `DecodingServices`, excluding its own jobs identifier, plus the callback's services. Only the owning jobs service is supplied automatically; a handler using another jobs definition still requires that definition's Layer. `enqueue` includes the payload Schema's `EncodingServices` in its requirement type.
|
|
271
315
|
|
|
272
316
|
`JobContext` has `id`, `name`, `signal`, `expireInSeconds` and `heartbeatSeconds`. `QueueHealth` has `name`, `activeCount`, `deadLetteredCount`, `failedCount`, `queuedCount` and `readyCount`. `JobsHealth` has `jobs` and the corresponding `activeTotal`, `deadLetteredTotal`, `failedTotal`, `queuedTotal` and `readyTotal` fields.
|
|
273
317
|
|
package/dist/server/client.d.ts
CHANGED
|
@@ -1,7 +1,9 @@
|
|
|
1
1
|
import type { ConstructorOptions, FindJobsOptions, Job, JobWithMetadata, Queue, QueueResult, ScheduleOptions, SendOptions, StopOptions, WorkOptions } from "pg-boss";
|
|
2
2
|
import { PgBoss } from "pg-boss";
|
|
3
3
|
export interface PgBossClient {
|
|
4
|
+
readonly complete?: PgBoss["complete"];
|
|
4
5
|
readonly createQueue: (name: string, options?: Omit<Queue, "name">) => Promise<void>;
|
|
6
|
+
readonly fail?: PgBoss["fail"];
|
|
5
7
|
readonly findJobs?: (name: string, options?: FindJobsOptions) => Promise<readonly JobWithMetadata<unknown>[]>;
|
|
6
8
|
readonly getDb?: PgBoss["getDb"];
|
|
7
9
|
readonly getQueue: (name: string) => Promise<QueueResult | null>;
|
|
@@ -1 +1 @@
|
|
|
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
|
+
{"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,QAAQ,CAAC,EAAE,MAAM,CAAC,UAAU,CAAC,CAAC;IACvC,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,IAAI,CAAC,EAAE,MAAM,CAAC,MAAM,CAAC,CAAC;IAC/B,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":"AAYA,OAAO,EAAE,MAAM,EAAE,MAAM,SAAS,CAAC;
|
|
1
|
+
{"version":3,"file":"client.js","sourceRoot":"","sources":["../../src/server/client.ts"],"names":[],"mappings":"AAYA,OAAO,EAAE,MAAM,EAAE,MAAM,SAAS,CAAC;AAwBjC,MAAM,CAAC,MAAM,oBAAoB,GAAwB,CAAC,OAAO,EAAE,EAAE,CAAC,IAAI,MAAM,CAAC,OAAO,CAAC,CAAC"}
|
|
@@ -9,7 +9,7 @@ export const defineQueue = (options) => {
|
|
|
9
9
|
name: options.name,
|
|
10
10
|
queueOptions: Object.freeze({ ...options.queue }),
|
|
11
11
|
schema: options.schema,
|
|
12
|
-
workerOptions: Object.freeze({ batchSize:
|
|
12
|
+
workerOptions: Object.freeze({ batchSize: 10, burstWhenBatchFull: true, ...options.worker }),
|
|
13
13
|
};
|
|
14
14
|
return Object.freeze(definition);
|
|
15
15
|
};
|
|
@@ -25,7 +25,7 @@ export const defineSchedule = (options) => {
|
|
|
25
25
|
schedule: definition,
|
|
26
26
|
}),
|
|
27
27
|
scheduleOptions: Object.freeze({ tz: "UTC", ...options.schedule }),
|
|
28
|
-
workerOptions: Object.freeze({ batchSize:
|
|
28
|
+
workerOptions: Object.freeze({ batchSize: 10, burstWhenBatchFull: true, ...options.worker }),
|
|
29
29
|
};
|
|
30
30
|
return Object.freeze(definition);
|
|
31
31
|
};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"definition.js","sourceRoot":"","sources":["../../src/server/definition.ts"],"names":[],"mappings":"AAoEA,MAAM,CAAC,MAAM,WAAW,GAAG,CAC1B,OAA0C,EACT,EAAE;IACnC,MAAM,UAAU,GAAmC;QAClD,IAAI,EAAE,iBAAiB;QACvB,MAAM,EAAE,CAAC,OAAO,EAAE,EAAE,CACnB,MAAM,CAAC,MAAM,CAAC;YACb,IAAI,EAAE,aAAsB;YAC5B,OAAO;YACP,KAAK,EAAE,UAAU;SACjB,CAAC;QACH,IAAI,EAAE,OAAO,CAAC,IAAI;QAClB,YAAY,EAAE,MAAM,CAAC,MAAM,CAAC,EAAE,GAAG,OAAO,CAAC,KAAK,EAAE,CAAC;QACjD,MAAM,EAAE,OAAO,CAAC,MAAM;QACtB,aAAa,EAAE,MAAM,CAAC,MAAM,CAAC,EAAE,SAAS,EAAE,
|
|
1
|
+
{"version":3,"file":"definition.js","sourceRoot":"","sources":["../../src/server/definition.ts"],"names":[],"mappings":"AAoEA,MAAM,CAAC,MAAM,WAAW,GAAG,CAC1B,OAA0C,EACT,EAAE;IACnC,MAAM,UAAU,GAAmC;QAClD,IAAI,EAAE,iBAAiB;QACvB,MAAM,EAAE,CAAC,OAAO,EAAE,EAAE,CACnB,MAAM,CAAC,MAAM,CAAC;YACb,IAAI,EAAE,aAAsB;YAC5B,OAAO;YACP,KAAK,EAAE,UAAU;SACjB,CAAC;QACH,IAAI,EAAE,OAAO,CAAC,IAAI;QAClB,YAAY,EAAE,MAAM,CAAC,MAAM,CAAC,EAAE,GAAG,OAAO,CAAC,KAAK,EAAE,CAAC;QACjD,MAAM,EAAE,OAAO,CAAC,MAAM;QACtB,aAAa,EAAE,MAAM,CAAC,MAAM,CAAC,EAAE,SAAS,EAAE,EAAE,EAAE,kBAAkB,EAAE,IAAI,EAAE,GAAG,OAAO,CAAC,MAAM,EAAE,CAAC;KAC5F,CAAC;IACF,OAAO,MAAM,CAAC,MAAM,CAAC,UAAU,CAAC,CAAC;AAClC,CAAC,CAAC;AAUF,MAAM,CAAC,MAAM,cAAc,GAAG,CAA4B,OAAoC,EAA4B,EAAE;IAC3H,MAAM,UAAU,GAA6B;QAC5C,IAAI,EAAE,oBAAoB;QAC1B,IAAI,EAAE,OAAO,CAAC,IAAI;QAClB,IAAI,EAAE,OAAO,CAAC,IAAI;QAClB,YAAY,EAAE,MAAM,CAAC,MAAM,CAAC,EAAE,GAAG,OAAO,CAAC,KAAK,EAAE,CAAC;QACjD,GAAG,EAAE,CAAC,MAAM,EAAE,EAAE,CACf,MAAM,CAAC,MAAM,CAAC;YACb,IAAI,EAAE,iBAA0B;YAChC,MAAM;YACN,QAAQ,EAAE,UAAU;SACpB,CAAC;QACH,eAAe,EAAE,MAAM,CAAC,MAAM,CAAC,EAAE,EAAE,EAAE,KAAK,EAAE,GAAG,OAAO,CAAC,QAAQ,EAAE,CAAC;QAClE,aAAa,EAAE,MAAM,CAAC,MAAM,CAAC,EAAE,SAAS,EAAE,EAAE,EAAE,kBAAkB,EAAE,IAAI,EAAE,GAAG,OAAO,CAAC,MAAM,EAAE,CAAC;KAC5F,CAAC;IACF,OAAO,MAAM,CAAC,MAAM,CAAC,UAAU,CAAC,CAAC;AAClC,CAAC,CAAC;AAEF,MAAM,CAAC,MAAM,UAAU,GAAG,CAAC,GAAiB,EAAc,EAAE,CAAC,CAAC;IAC7D,eAAe,EAAE,GAAG,CAAC,eAAe;IACpC,gBAAgB,EAAE,GAAG,CAAC,gBAAgB;IACtC,EAAE,EAAE,GAAG,CAAC,EAAE;IACV,IAAI,EAAE,GAAG,CAAC,IAAI;IACd,MAAM,EAAE,GAAG,CAAC,MAAM;CAClB,CAAC,CAAC"}
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
import { Effect } from "effect";
|
|
2
|
+
import type { Job } from "pg-boss";
|
|
3
|
+
import type { PgBossClient } from "#client.ts";
|
|
4
|
+
export declare function batchHandler(client: PgBossClient, name: string, run: (job: Job<unknown>) => Effect.Effect<unknown, unknown>): (jobs: readonly Job<unknown>[]) => Promise<void>;
|
|
5
|
+
//# sourceMappingURL=batchHandler.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"batchHandler.d.ts","sourceRoot":"","sources":["../../../src/server/internal/batchHandler.ts"],"names":[],"mappings":"AAAA,OAAO,EAAS,MAAM,EAAQ,MAAM,QAAQ,CAAC;AAC7C,OAAO,KAAK,EAAE,GAAG,EAAE,MAAM,SAAS,CAAC;AACnC,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,YAAY,CAAC;AAO/C,wBAAgB,YAAY,CAAC,MAAM,EAAE,YAAY,EAAE,IAAI,EAAE,MAAM,EAAE,GAAG,EAAE,CAAC,GAAG,EAAE,GAAG,CAAC,OAAO,CAAC,KAAK,MAAM,CAAC,MAAM,CAAC,OAAO,EAAE,OAAO,CAAC,UAMvG,SAAS,GAAG,CAAC,OAAO,CAAC,EAAE,KAAG,OAAO,CAAC,IAAI,CAAC,CAiB3D"}
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import { Cause, Effect, Exit } from "effect";
|
|
2
|
+
function failureOutput(cause) {
|
|
3
|
+
const error = Cause.squash(cause);
|
|
4
|
+
return typeof error === "object" && error !== null ? error : { message: String(error) };
|
|
5
|
+
}
|
|
6
|
+
export function batchHandler(client, name, run) {
|
|
7
|
+
const complete = client.complete?.bind(client);
|
|
8
|
+
const fail = client.fail?.bind(client);
|
|
9
|
+
if (complete === undefined || fail === undefined) {
|
|
10
|
+
throw new TypeError("This pg-boss client does not support complete and fail");
|
|
11
|
+
}
|
|
12
|
+
return async (jobs) => {
|
|
13
|
+
for (const job of jobs) {
|
|
14
|
+
const exit = await Effect.runPromiseExit(Effect.suspend(() => run(job)), { signal: job.signal });
|
|
15
|
+
if (job.signal.aborted || (Exit.isFailure(exit) && Cause.hasInterrupts(exit.cause))) {
|
|
16
|
+
// Returning would let pg-boss complete the unprocessed jobs; its expiry owns them instead.
|
|
17
|
+
return new Promise(() => undefined);
|
|
18
|
+
}
|
|
19
|
+
if (Exit.isSuccess(exit)) {
|
|
20
|
+
await complete(name, job);
|
|
21
|
+
}
|
|
22
|
+
else {
|
|
23
|
+
await fail(name, job, failureOutput(exit.cause));
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
};
|
|
27
|
+
}
|
|
28
|
+
//# sourceMappingURL=batchHandler.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"batchHandler.js","sourceRoot":"","sources":["../../../src/server/internal/batchHandler.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,QAAQ,CAAC;AAI7C,SAAS,aAAa,CAAC,KAA2B;IACjD,MAAM,KAAK,GAAG,KAAK,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC;IAClC,OAAO,OAAO,KAAK,KAAK,QAAQ,IAAI,KAAK,KAAK,IAAI,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,EAAE,OAAO,EAAE,MAAM,CAAC,KAAK,CAAC,EAAE,CAAC;AACzF,CAAC;AAED,MAAM,UAAU,YAAY,CAAC,MAAoB,EAAE,IAAY,EAAE,GAA2D;IAC3H,MAAM,QAAQ,GAAG,MAAM,CAAC,QAAQ,EAAE,IAAI,CAAC,MAAM,CAAC,CAAC;IAC/C,MAAM,IAAI,GAAG,MAAM,CAAC,IAAI,EAAE,IAAI,CAAC,MAAM,CAAC,CAAC;IACvC,IAAI,QAAQ,KAAK,SAAS,IAAI,IAAI,KAAK,SAAS,EAAE,CAAC;QAClD,MAAM,IAAI,SAAS,CAAC,wDAAwD,CAAC,CAAC;IAC/E,CAAC;IACD,OAAO,KAAK,EAAE,IAA6B,EAAiB,EAAE;QAC7D,KAAK,MAAM,GAAG,IAAI,IAAI,EAAE,CAAC;YACxB,MAAM,IAAI,GAAG,MAAM,MAAM,CAAC,cAAc,CACvC,MAAM,CAAC,OAAO,CAAC,GAAG,EAAE,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC,EAC9B,EAAE,MAAM,EAAE,GAAG,CAAC,MAAM,EAAE,CACtB,CAAC;YACF,IAAI,GAAG,CAAC,MAAM,CAAC,OAAO,IAAI,CAAC,IAAI,CAAC,SAAS,CAAC,IAAI,CAAC,IAAI,KAAK,CAAC,aAAa,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,EAAE,CAAC;gBACrF,2FAA2F;gBAC3F,OAAO,IAAI,OAAO,CAAQ,GAAG,EAAE,CAAC,SAAS,CAAC,CAAC;YAC5C,CAAC;YACD,IAAI,IAAI,CAAC,SAAS,CAAC,IAAI,CAAC,EAAE,CAAC;gBAC1B,MAAM,QAAQ,CAAC,IAAI,EAAE,GAAG,CAAC,CAAC;YAC3B,CAAC;iBAAM,CAAC;gBACP,MAAM,IAAI,CAAC,IAAI,EAAE,GAAG,EAAE,aAAa,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC;YAClD,CAAC;QACF,CAAC;IACF,CAAC,CAAC;AACH,CAAC"}
|
|
@@ -2,7 +2,7 @@ import { type Context, Effect } from "effect";
|
|
|
2
2
|
import type { PgBossClient } from "#client.ts";
|
|
3
3
|
import { type JobRegistration } from "#definition.ts";
|
|
4
4
|
import { PgBossQueueOptionsError } from "#error.ts";
|
|
5
|
-
export declare
|
|
5
|
+
export declare function registrationName(registration: JobRegistration): string;
|
|
6
6
|
export declare const registrationNames: (registrations: readonly JobRegistration[]) => readonly string[];
|
|
7
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>;
|
|
8
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,
|
|
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,EACN,KAAK,eAAe,EAMpB,MAAM,gBAAgB,CAAC;AACxB,OAAO,EAAsB,uBAAuB,EAAiB,MAAM,WAAW,CAAC;AA8BvF,wBAAgB,gBAAgB,CAAC,YAAY,EAAE,eAAe,GAAG,MAAM,CAEtE;AA2DD,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,CAe7E,CAAC"}
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { Effect, Redacted, Schema } from "effect";
|
|
2
|
-
import { jobContext } from "#definition.ts";
|
|
2
|
+
import { jobContext, } from "#definition.ts";
|
|
3
3
|
import { PgBossPayloadError, PgBossQueueOptionsError, toPgBossError } from "#error.ts";
|
|
4
|
+
import { batchHandler } from "#internal/batchHandler.ts";
|
|
4
5
|
import { ensureQueue } from "#internal/ensureQueue.ts";
|
|
5
6
|
import { payloadOf, traceJob } from "#internal/tracing.ts";
|
|
6
7
|
function isQueueWorker(registration) {
|
|
@@ -12,57 +13,53 @@ function isScheduledWorker(registration) {
|
|
|
12
13
|
function isQueueDefinition(registration) {
|
|
13
14
|
return registration._tag === "QueueDefinition";
|
|
14
15
|
}
|
|
15
|
-
|
|
16
|
+
function registrationDefinition(registration) {
|
|
16
17
|
if (isQueueDefinition(registration)) {
|
|
17
|
-
return registration
|
|
18
|
+
return registration;
|
|
18
19
|
}
|
|
19
20
|
if (isQueueWorker(registration)) {
|
|
20
|
-
return registration.queue
|
|
21
|
+
return registration.queue;
|
|
21
22
|
}
|
|
22
23
|
if (isScheduledWorker(registration)) {
|
|
23
|
-
return registration.schedule
|
|
24
|
+
return registration.schedule;
|
|
24
25
|
}
|
|
25
26
|
throw new TypeError(`Unknown pg-boss registration: ${registration._tag}`);
|
|
26
|
-
}
|
|
27
|
+
}
|
|
28
|
+
export function registrationName(registration) {
|
|
29
|
+
return registrationDefinition(registration).name;
|
|
30
|
+
}
|
|
31
|
+
async function prepareQueues(client, registrations, replaceWorkers) {
|
|
32
|
+
for (const registration of registrations) {
|
|
33
|
+
if (replaceWorkers && !isQueueDefinition(registration)) {
|
|
34
|
+
await client.offWork(registrationName(registration));
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
for (const registration of registrations) {
|
|
38
|
+
const definition = registrationDefinition(registration);
|
|
39
|
+
await ensureQueue(client, definition.name, definition.queueOptions);
|
|
40
|
+
}
|
|
41
|
+
}
|
|
27
42
|
function workerContext(context) {
|
|
28
43
|
return context;
|
|
29
44
|
}
|
|
30
45
|
function runQueueWorker(worker, job, context) {
|
|
31
|
-
return
|
|
46
|
+
return Schema.decodeUnknownEffect(worker.queue.schema)(payloadOf(job.data)).pipe(Effect.mapError((error) => new PgBossPayloadError({
|
|
32
47
|
direction: "decode",
|
|
33
48
|
original: Redacted.make(error),
|
|
34
49
|
queue: worker.queue.name,
|
|
35
|
-
})), Effect.flatMap((payload) => worker.handler(payload, jobContext(job))), (effect) => traceJob(effect, job, worker.queue.name), Effect.provide(context))
|
|
50
|
+
})), Effect.flatMap((payload) => worker.handler(payload, jobContext(job))), (effect) => traceJob(effect, job, worker.queue.name), Effect.provide(context));
|
|
36
51
|
}
|
|
37
|
-
async function registerQueue(client, registration, context
|
|
52
|
+
async function registerQueue(client, registration, context) {
|
|
38
53
|
const queue = registration._tag === "QueueWorker" ? registration.queue : registration;
|
|
39
54
|
const name = queue.name;
|
|
40
|
-
if (replaceWorker && registration._tag === "QueueWorker") {
|
|
41
|
-
await client.offWork(name);
|
|
42
|
-
}
|
|
43
|
-
await ensureQueue(client, name, queue.queueOptions);
|
|
44
55
|
if (registration._tag === "QueueDefinition") {
|
|
45
56
|
return;
|
|
46
57
|
}
|
|
47
|
-
await client.work(name, queue.workerOptions,
|
|
48
|
-
for (const job of jobs) {
|
|
49
|
-
await runQueueWorker(registration, job, context);
|
|
50
|
-
}
|
|
51
|
-
});
|
|
58
|
+
await client.work(name, queue.workerOptions, batchHandler(client, name, (job) => runQueueWorker(registration, job, context)));
|
|
52
59
|
}
|
|
53
|
-
async function registerSchedule(client, worker, context
|
|
60
|
+
async function registerSchedule(client, worker, context) {
|
|
54
61
|
const { schedule } = worker;
|
|
55
|
-
|
|
56
|
-
await client.offWork(schedule.name);
|
|
57
|
-
}
|
|
58
|
-
await ensureQueue(client, schedule.name, schedule.queueOptions);
|
|
59
|
-
await client.work(schedule.name, schedule.workerOptions, async (jobs) => {
|
|
60
|
-
for (const job of jobs) {
|
|
61
|
-
await Effect.runPromise(Effect.provide(traceJob(worker.effect, job, schedule.name), context), {
|
|
62
|
-
signal: job.signal,
|
|
63
|
-
});
|
|
64
|
-
}
|
|
65
|
-
});
|
|
62
|
+
await client.work(schedule.name, schedule.workerOptions, batchHandler(client, schedule.name, (job) => Effect.provide(traceJob(worker.effect, job, schedule.name), context)));
|
|
66
63
|
await client.schedule(schedule.name, schedule.cron, null, schedule.scheduleOptions);
|
|
67
64
|
}
|
|
68
65
|
export const registrationNames = (registrations) => {
|
|
@@ -76,13 +73,14 @@ export const registerJobs = (client, registrations, context, replaceWorkers) =>
|
|
|
76
73
|
catch: (error) => (error instanceof PgBossQueueOptionsError ? error : toPgBossError("register", error)),
|
|
77
74
|
try: async () => {
|
|
78
75
|
registrationNames(registrations);
|
|
76
|
+
await prepareQueues(client, registrations, replaceWorkers);
|
|
79
77
|
const provided = workerContext(context);
|
|
80
78
|
for (const registration of registrations) {
|
|
81
79
|
if (isQueueWorker(registration) || isQueueDefinition(registration)) {
|
|
82
|
-
await registerQueue(client, registration, provided
|
|
80
|
+
await registerQueue(client, registration, provided);
|
|
83
81
|
}
|
|
84
82
|
else if (isScheduledWorker(registration)) {
|
|
85
|
-
await registerSchedule(client, registration, provided
|
|
83
|
+
await registerSchedule(client, registration, provided);
|
|
86
84
|
}
|
|
87
85
|
}
|
|
88
86
|
},
|
|
@@ -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,
|
|
1
|
+
{"version":3,"file":"register.js","sourceRoot":"","sources":["../../../src/server/internal/register.ts"],"names":[],"mappings":"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"}
|
package/dist/server/service.d.ts
CHANGED
|
@@ -19,7 +19,7 @@ export type PgBossLayerOptions<TRegistrations extends readonly JobRegistration[]
|
|
|
19
19
|
readonly stop?: StopOptions;
|
|
20
20
|
};
|
|
21
21
|
export interface PgBossDefinition<TIdentifier extends string = string> extends Context.Service<TIdentifier, PgBossService> {
|
|
22
|
-
readonly layer: <const TRegistrations extends readonly JobRegistration[], TErrorRequirements = never>(options: PgBossLayerOptions<TRegistrations, TErrorRequirements>) => Layer.Layer<TIdentifier, PgBossError | PgBossQueueOptionsError, RegistrationRequirements<TRegistrations> | TErrorRequirements>;
|
|
22
|
+
readonly layer: <const TRegistrations extends readonly JobRegistration[], TErrorRequirements = never>(options: PgBossLayerOptions<TRegistrations, TErrorRequirements>) => Layer.Layer<TIdentifier, PgBossError | PgBossQueueOptionsError, Exclude<RegistrationRequirements<TRegistrations>, TIdentifier> | TErrorRequirements>;
|
|
23
23
|
}
|
|
24
24
|
export declare const makePgBoss: <const TIdentifier extends string>(identifier: TIdentifier) => PgBossDefinition<TIdentifier>;
|
|
25
25
|
//# sourceMappingURL=service.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"service.d.ts","sourceRoot":"","sources":["../../src/server/service.ts"],"names":[],"mappings":"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
|
|
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
|
@@ -11,19 +11,21 @@ export const makePgBoss = (identifier) => {
|
|
|
11
11
|
const names = options.jobs.map(registrationName);
|
|
12
12
|
const { clientFactory, clientCacheKey, jobs, onError, stop, ...constructorOptions } = options;
|
|
13
13
|
const acquire = Effect.acquireRelease(Effect.gen(function* () {
|
|
14
|
-
const
|
|
14
|
+
const dependencies = yield* Effect.context();
|
|
15
15
|
const acquired = yield* acquireClient({
|
|
16
16
|
clientCacheKey,
|
|
17
17
|
clientFactory,
|
|
18
18
|
constructor: constructorOptions,
|
|
19
19
|
});
|
|
20
|
+
const service = makeService(acquired.client, names, constructorOptions.schema);
|
|
21
|
+
const context = Context.add(dependencies, Service, service);
|
|
20
22
|
const reportError = onError ?? logClientError;
|
|
21
23
|
const errorListener = (error) => {
|
|
22
24
|
void Effect.runPromise(Effect.exit(Effect.provide(reportError(error), context)));
|
|
23
25
|
};
|
|
24
26
|
acquired.client.on("error", errorListener);
|
|
25
27
|
const registration = registerJobs(acquired.client, jobs, context, acquired.reused);
|
|
26
|
-
return yield* registration.pipe(Effect.as({ acquired,
|
|
28
|
+
return yield* registration.pipe(Effect.as({ acquired, errorListener, service }), Effect.onError(() => {
|
|
27
29
|
acquired.client.off("error", errorListener);
|
|
28
30
|
return releaseClient(acquired, stop);
|
|
29
31
|
}));
|
|
@@ -31,7 +33,7 @@ export const makePgBoss = (identifier) => {
|
|
|
31
33
|
resource.acquired.client.off("error", resource.errorListener);
|
|
32
34
|
return releaseClient(resource.acquired, stop);
|
|
33
35
|
});
|
|
34
|
-
return Layer.effect(Service, Effect.map(acquire, ({
|
|
36
|
+
return Layer.effect(Service, Effect.map(acquire, ({ service }) => service));
|
|
35
37
|
};
|
|
36
38
|
return Object.assign(Service, { layer });
|
|
37
39
|
};
|
|
@@ -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;AAM1E,OAAO,EAAE,aAAa,EAAE,aAAa,EAAE,MAAM,wBAAwB,CAAC;AACtE,OAAO,EAAE,YAAY,EAAE,gBAAgB,EAAE,MAAM,uBAAuB,CAAC;AACvE,OAAO,EAAE,WAAW,EAAE,MAAM,sBAAsB,CAAC;
|
|
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;AAM1E,OAAO,EAAE,aAAa,EAAE,aAAa,EAAE,MAAM,wBAAwB,CAAC;AACtE,OAAO,EAAE,YAAY,EAAE,gBAAgB,EAAE,MAAM,uBAAuB,CAAC;AACvE,OAAO,EAAE,WAAW,EAAE,MAAM,sBAAsB,CAAC;AAoCnD,SAAS,cAAc,CAAC,KAAY;IACnC,OAAO,MAAM,CAAC,QAAQ,CAAC,EAAE,KAAK,EAAE,eAAe,EAAE,OAAO,EAAE,KAAK,CAAC,OAAO,EAAE,CAAC,CAAC;AAC5E,CAAC;AAED,MAAM,CAAC,MAAM,UAAU,GAAG,CAAmC,UAAuB,EAAiC,EAAE;IACtH,MAAM,OAAO,GAAG,OAAO,CAAC,OAAO,CAA6B,UAAU,CAAC,CAAC;IAExE,MAAM,KAAK,GAAG,CACb,OAA+D,EAK9D,EAAE;QAEH,MAAM,KAAK,GAAG,OAAO,CAAC,IAAI,CAAC,GAAG,CAAC,gBAAgB,CAAC,CAAC;QACjD,MAAM,EAAE,aAAa,EAAE,cAAc,EAAE,IAAI,EAAE,OAAO,EAAE,IAAI,EAAE,GAAG,kBAAkB,EAAE,GAAG,OAAO,CAAC;QAE9F,MAAM,OAAO,GAAG,MAAM,CAAC,cAAc,CACpC,MAAM,CAAC,GAAG,CAAC,QAAQ,CAAC;YACnB,MAAM,YAAY,GAAG,KAAK,CAAC,CAAC,MAAM,CAAC,OAAO,EAAgB,CAAC;YAC3D,MAAM,QAAQ,GAAG,KAAK,CAAC,CAAC,aAAa,CAAC;gBACrC,cAAc;gBACd,aAAa;gBACb,WAAW,EAAE,kBAAkB;aAC/B,CAAC,CAAC;YACH,MAAM,OAAO,GAAG,WAAW,CAAC,QAAQ,CAAC,MAAM,EAAE,KAAK,EAAE,kBAAkB,CAAC,MAAM,CAAC,CAAC;YAC/E,MAAM,OAAO,GAAG,OAAO,CAAC,GAAG,CAAC,YAAY,EAAE,OAAO,EAAE,OAAO,CAAC,CAAC;YAC5D,MAAM,WAAW,GAAgF,OAAO,IAAI,cAAc,CAAC;YAC3H,MAAM,aAAa,GAAG,CAAC,KAAY,EAAE,EAAE;gBACtC,KAAK,MAAM,CAAC,UAAU,CAAC,MAAM,CAAC,IAAI,CAAC,MAAM,CAAC,OAAO,CAAC,WAAW,CAAC,KAAK,CAAC,EAAE,OAAO,CAAC,CAAC,CAAC,CAAC;YAClF,CAAC,CAAC;YACF,QAAQ,CAAC,MAAM,CAAC,EAAE,CAAC,OAAO,EAAE,aAAa,CAAC,CAAC;YAC3C,MAAM,YAAY,GAAG,YAAY,CAAC,QAAQ,CAAC,MAAM,EAAE,IAAI,EAAE,OAAO,EAAE,QAAQ,CAAC,MAAM,CAAC,CAAC;YACnF,OAAO,KAAK,CAAC,CAAC,YAAY,CAAC,IAAI,CAC9B,MAAM,CAAC,EAAE,CAAC,EAAE,QAAQ,EAAE,aAAa,EAAE,OAAO,EAAE,CAAC,EAC/C,MAAM,CAAC,OAAO,CAAC,GAAG,EAAE;gBACnB,QAAQ,CAAC,MAAM,CAAC,GAAG,CAAC,OAAO,EAAE,aAAa,CAAC,CAAC;gBAC5C,OAAO,aAAa,CAAC,QAAQ,EAAE,IAAI,CAAC,CAAC;YACtC,CAAC,CAAC,CACF,CAAC;QACH,CAAC,CAAC,EACF,CAAC,QAAQ,EAAE,EAAE;YACZ,QAAQ,CAAC,QAAQ,CAAC,MAAM,CAAC,GAAG,CAAC,OAAO,EAAE,QAAQ,CAAC,aAAa,CAAC,CAAC;YAC9D,OAAO,aAAa,CAAC,QAAQ,CAAC,QAAQ,EAAE,IAAI,CAAC,CAAC;QAC/C,CAAC,CACD,CAAC;QAEF,OAAO,KAAK,CAAC,MAAM,CAClB,OAAO,EACP,MAAM,CAAC,GAAG,CAAC,OAAO,EAAE,CAAC,EAAE,OAAO,EAAE,EAAE,EAAE,CAAC,OAAO,CAAC,CAC7C,CAAC;IACH,CAAC,CAAC;IAEF,OAAO,MAAM,CAAC,MAAM,CAAC,OAAO,EAAE,EAAE,KAAK,EAAE,CAAC,CAAC;AAC1C,CAAC,CAAC"}
|
package/docs/roadmap.md
CHANGED
|
@@ -9,6 +9,8 @@ This file owns the package's implementation inventory and open API choices. The
|
|
|
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
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.
|
|
12
|
+
- [x] Native pg-boss burst defaults (`batchSize: 10`, `burstWhenBatchFull: true`) and configurable batches and local concurrency. [PostgreSQL backlog evidence](../src/server/throughputPostgres.spec.ts) drains 30 jobs within 2.5 seconds.
|
|
13
|
+
- [x] Handlers and scheduled effects can yield their owning jobs service without an external self requirement. [PostgreSQL follow-up evidence](../src/server/followUpPostgres.spec.ts) completes a worker chain and enqueues to a producer-only queue using one scoped client. All declared queues exist before consumers start.
|
|
12
14
|
- [x] Worker and scheduled Effect service capture. Runtime and packed-consumer type tests preserve handler requirements.
|
|
13
15
|
- [x] Scoped startup and shutdown, including cleanup when startup or registration fails.
|
|
14
16
|
- [x] Explicit keyed client reuse and unkeyed per-build acquisition. Lifecycle tests cover two builds sharing one client and replacing the registered worker.
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@shivaedev/effect-pg-boss",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.7.1",
|
|
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": {
|
package/src/server/client.ts
CHANGED
|
@@ -13,7 +13,9 @@ import type {
|
|
|
13
13
|
import { PgBoss } from "pg-boss";
|
|
14
14
|
|
|
15
15
|
export interface PgBossClient {
|
|
16
|
+
readonly complete?: PgBoss["complete"];
|
|
16
17
|
readonly createQueue: (name: string, options?: Omit<Queue, "name">) => Promise<void>;
|
|
18
|
+
readonly fail?: PgBoss["fail"];
|
|
17
19
|
readonly findJobs?: (name: string, options?: FindJobsOptions) => Promise<readonly JobWithMetadata<unknown>[]>;
|
|
18
20
|
readonly getDb?: PgBoss["getDb"];
|
|
19
21
|
readonly getQueue: (name: string) => Promise<QueueResult | null>;
|
package/src/server/definition.ts
CHANGED
|
@@ -80,7 +80,7 @@ export const defineQueue = <const Name extends string, const Payload extends Job
|
|
|
80
80
|
name: options.name,
|
|
81
81
|
queueOptions: Object.freeze({ ...options.queue }),
|
|
82
82
|
schema: options.schema,
|
|
83
|
-
workerOptions: Object.freeze({ batchSize:
|
|
83
|
+
workerOptions: Object.freeze({ batchSize: 10, burstWhenBatchFull: true, ...options.worker }),
|
|
84
84
|
};
|
|
85
85
|
return Object.freeze(definition);
|
|
86
86
|
};
|
|
@@ -106,7 +106,7 @@ export const defineSchedule = <const Name extends string>(options: DefineSchedul
|
|
|
106
106
|
schedule: definition,
|
|
107
107
|
}),
|
|
108
108
|
scheduleOptions: Object.freeze({ tz: "UTC", ...options.schedule }),
|
|
109
|
-
workerOptions: Object.freeze({ batchSize:
|
|
109
|
+
workerOptions: Object.freeze({ batchSize: 10, burstWhenBatchFull: true, ...options.worker }),
|
|
110
110
|
};
|
|
111
111
|
return Object.freeze(definition);
|
|
112
112
|
};
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
import { Cause, Effect, Exit } from "effect";
|
|
2
|
+
import type { Job } from "pg-boss";
|
|
3
|
+
import type { PgBossClient } from "#client.ts";
|
|
4
|
+
|
|
5
|
+
function failureOutput(cause: Cause.Cause<unknown>): object {
|
|
6
|
+
const error = Cause.squash(cause);
|
|
7
|
+
return typeof error === "object" && error !== null ? error : { message: String(error) };
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
export function batchHandler(client: PgBossClient, name: string, run: (job: Job<unknown>) => Effect.Effect<unknown, unknown>) {
|
|
11
|
+
const complete = client.complete?.bind(client);
|
|
12
|
+
const fail = client.fail?.bind(client);
|
|
13
|
+
if (complete === undefined || fail === undefined) {
|
|
14
|
+
throw new TypeError("This pg-boss client does not support complete and fail");
|
|
15
|
+
}
|
|
16
|
+
return async (jobs: readonly Job<unknown>[]): Promise<void> => {
|
|
17
|
+
for (const job of jobs) {
|
|
18
|
+
const exit = await Effect.runPromiseExit(
|
|
19
|
+
Effect.suspend(() => run(job)),
|
|
20
|
+
{ signal: job.signal },
|
|
21
|
+
);
|
|
22
|
+
if (job.signal.aborted || (Exit.isFailure(exit) && Cause.hasInterrupts(exit.cause))) {
|
|
23
|
+
// Returning would let pg-boss complete the unprocessed jobs; its expiry owns them instead.
|
|
24
|
+
return new Promise<never>(() => undefined);
|
|
25
|
+
}
|
|
26
|
+
if (Exit.isSuccess(exit)) {
|
|
27
|
+
await complete(name, job);
|
|
28
|
+
} else {
|
|
29
|
+
await fail(name, job, failureOutput(exit.cause));
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
};
|
|
33
|
+
}
|
|
@@ -1,8 +1,16 @@
|
|
|
1
1
|
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
|
-
import {
|
|
4
|
+
import {
|
|
5
|
+
type JobRegistration,
|
|
6
|
+
jobContext,
|
|
7
|
+
type QueueDefinition,
|
|
8
|
+
type QueueWorker,
|
|
9
|
+
type ScheduleDefinition,
|
|
10
|
+
type ScheduledWorker,
|
|
11
|
+
} from "#definition.ts";
|
|
5
12
|
import { PgBossPayloadError, PgBossQueueOptionsError, toPgBossError } from "#error.ts";
|
|
13
|
+
import { batchHandler } from "#internal/batchHandler.ts";
|
|
6
14
|
import { ensureQueue } from "#internal/ensureQueue.ts";
|
|
7
15
|
import { payloadOf, traceJob } from "#internal/tracing.ts";
|
|
8
16
|
|
|
@@ -18,18 +26,34 @@ function isQueueDefinition(registration: JobRegistration): registration is Queue
|
|
|
18
26
|
return registration._tag === "QueueDefinition";
|
|
19
27
|
}
|
|
20
28
|
|
|
21
|
-
|
|
29
|
+
function registrationDefinition(registration: JobRegistration): QueueDefinition | ScheduleDefinition {
|
|
22
30
|
if (isQueueDefinition(registration)) {
|
|
23
|
-
return registration
|
|
31
|
+
return registration;
|
|
24
32
|
}
|
|
25
33
|
if (isQueueWorker(registration)) {
|
|
26
|
-
return registration.queue
|
|
34
|
+
return registration.queue;
|
|
27
35
|
}
|
|
28
36
|
if (isScheduledWorker(registration)) {
|
|
29
|
-
return registration.schedule
|
|
37
|
+
return registration.schedule;
|
|
30
38
|
}
|
|
31
39
|
throw new TypeError(`Unknown pg-boss registration: ${registration._tag}`);
|
|
32
|
-
}
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
export function registrationName(registration: JobRegistration): string {
|
|
43
|
+
return registrationDefinition(registration).name;
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
async function prepareQueues(client: PgBossClient, registrations: readonly JobRegistration[], replaceWorkers: boolean): Promise<void> {
|
|
47
|
+
for (const registration of registrations) {
|
|
48
|
+
if (replaceWorkers && !isQueueDefinition(registration)) {
|
|
49
|
+
await client.offWork(registrationName(registration));
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
for (const registration of registrations) {
|
|
53
|
+
const definition = registrationDefinition(registration);
|
|
54
|
+
await ensureQueue(client, definition.name, definition.queueOptions);
|
|
55
|
+
}
|
|
56
|
+
}
|
|
33
57
|
|
|
34
58
|
// The layer requires RegistrationRequirements of its jobs, so its context provides every registered worker.
|
|
35
59
|
function workerContext<R>(context: Context.Context<R>): Context.Context<unknown>;
|
|
@@ -37,65 +61,42 @@ function workerContext(context: unknown): unknown {
|
|
|
37
61
|
return context;
|
|
38
62
|
}
|
|
39
63
|
|
|
40
|
-
function runQueueWorker(worker: QueueWorker, job: Job<unknown>, context: Context.Context<unknown>)
|
|
41
|
-
return
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
(
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
}),
|
|
50
|
-
),
|
|
51
|
-
Effect.flatMap((payload) => worker.handler(payload, jobContext(job))),
|
|
52
|
-
(effect) => traceJob(effect, job, worker.queue.name),
|
|
53
|
-
Effect.provide(context),
|
|
64
|
+
function runQueueWorker(worker: QueueWorker, job: Job<unknown>, context: Context.Context<unknown>) {
|
|
65
|
+
return Schema.decodeUnknownEffect(worker.queue.schema)(payloadOf(job.data)).pipe(
|
|
66
|
+
Effect.mapError(
|
|
67
|
+
(error) =>
|
|
68
|
+
new PgBossPayloadError({
|
|
69
|
+
direction: "decode",
|
|
70
|
+
original: Redacted.make(error),
|
|
71
|
+
queue: worker.queue.name,
|
|
72
|
+
}),
|
|
54
73
|
),
|
|
55
|
-
|
|
74
|
+
Effect.flatMap((payload) => worker.handler(payload, jobContext(job))),
|
|
75
|
+
(effect) => traceJob(effect, job, worker.queue.name),
|
|
76
|
+
Effect.provide(context),
|
|
56
77
|
);
|
|
57
78
|
}
|
|
58
79
|
|
|
59
|
-
async function registerQueue(
|
|
60
|
-
client: PgBossClient,
|
|
61
|
-
registration: QueueWorker | QueueDefinition,
|
|
62
|
-
context: Context.Context<unknown>,
|
|
63
|
-
replaceWorker: boolean,
|
|
64
|
-
): Promise<void> {
|
|
80
|
+
async function registerQueue(client: PgBossClient, registration: QueueWorker | QueueDefinition, context: Context.Context<unknown>): Promise<void> {
|
|
65
81
|
const queue = registration._tag === "QueueWorker" ? registration.queue : registration;
|
|
66
82
|
const name = queue.name;
|
|
67
|
-
if (replaceWorker && registration._tag === "QueueWorker") {
|
|
68
|
-
await client.offWork(name);
|
|
69
|
-
}
|
|
70
|
-
await ensureQueue(client, name, queue.queueOptions);
|
|
71
83
|
if (registration._tag === "QueueDefinition") {
|
|
72
84
|
return;
|
|
73
85
|
}
|
|
74
|
-
await client.work(
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
86
|
+
await client.work(
|
|
87
|
+
name,
|
|
88
|
+
queue.workerOptions,
|
|
89
|
+
batchHandler(client, name, (job) => runQueueWorker(registration, job, context)),
|
|
90
|
+
);
|
|
79
91
|
}
|
|
80
92
|
|
|
81
|
-
async function registerSchedule(
|
|
82
|
-
client: PgBossClient,
|
|
83
|
-
worker: ScheduledWorker,
|
|
84
|
-
context: Context.Context<unknown>,
|
|
85
|
-
replaceWorker: boolean,
|
|
86
|
-
): Promise<void> {
|
|
93
|
+
async function registerSchedule(client: PgBossClient, worker: ScheduledWorker, context: Context.Context<unknown>): Promise<void> {
|
|
87
94
|
const { schedule } = worker;
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
for (const job of jobs) {
|
|
94
|
-
await Effect.runPromise(Effect.provide(traceJob(worker.effect, job, schedule.name), context), {
|
|
95
|
-
signal: job.signal,
|
|
96
|
-
});
|
|
97
|
-
}
|
|
98
|
-
});
|
|
95
|
+
await client.work(
|
|
96
|
+
schedule.name,
|
|
97
|
+
schedule.workerOptions,
|
|
98
|
+
batchHandler(client, schedule.name, (job) => Effect.provide(traceJob(worker.effect, job, schedule.name), context)),
|
|
99
|
+
);
|
|
99
100
|
await client.schedule(schedule.name, schedule.cron, null, schedule.scheduleOptions);
|
|
100
101
|
}
|
|
101
102
|
|
|
@@ -117,12 +118,13 @@ export const registerJobs = <R>(
|
|
|
117
118
|
catch: (error) => (error instanceof PgBossQueueOptionsError ? error : toPgBossError("register", error)),
|
|
118
119
|
try: async () => {
|
|
119
120
|
registrationNames(registrations);
|
|
121
|
+
await prepareQueues(client, registrations, replaceWorkers);
|
|
120
122
|
const provided = workerContext(context);
|
|
121
123
|
for (const registration of registrations) {
|
|
122
124
|
if (isQueueWorker(registration) || isQueueDefinition(registration)) {
|
|
123
|
-
await registerQueue(client, registration, provided
|
|
125
|
+
await registerQueue(client, registration, provided);
|
|
124
126
|
} else if (isScheduledWorker(registration)) {
|
|
125
|
-
await registerSchedule(client, registration, provided
|
|
127
|
+
await registerSchedule(client, registration, provided);
|
|
126
128
|
}
|
|
127
129
|
}
|
|
128
130
|
},
|
package/src/server/service.ts
CHANGED
|
@@ -35,7 +35,11 @@ export type PgBossLayerOptions<TRegistrations extends readonly JobRegistration[]
|
|
|
35
35
|
export interface PgBossDefinition<TIdentifier extends string = string> extends Context.Service<TIdentifier, PgBossService> {
|
|
36
36
|
readonly layer: <const TRegistrations extends readonly JobRegistration[], TErrorRequirements = never>(
|
|
37
37
|
options: PgBossLayerOptions<TRegistrations, TErrorRequirements>,
|
|
38
|
-
) => Layer.Layer<
|
|
38
|
+
) => Layer.Layer<
|
|
39
|
+
TIdentifier,
|
|
40
|
+
PgBossError | PgBossQueueOptionsError,
|
|
41
|
+
Exclude<RegistrationRequirements<TRegistrations>, TIdentifier> | TErrorRequirements
|
|
42
|
+
>;
|
|
39
43
|
}
|
|
40
44
|
|
|
41
45
|
function logClientError(error: Error): Effect.Effect<void> {
|
|
@@ -47,27 +51,33 @@ export const makePgBoss = <const TIdentifier extends string>(identifier: TIdenti
|
|
|
47
51
|
|
|
48
52
|
const layer = <const TRegistrations extends readonly JobRegistration[], TErrorRequirements = never>(
|
|
49
53
|
options: PgBossLayerOptions<TRegistrations, TErrorRequirements>,
|
|
50
|
-
): Layer.Layer<
|
|
51
|
-
|
|
54
|
+
): Layer.Layer<
|
|
55
|
+
TIdentifier,
|
|
56
|
+
PgBossError | PgBossQueueOptionsError,
|
|
57
|
+
Exclude<RegistrationRequirements<TRegistrations>, TIdentifier> | TErrorRequirements
|
|
58
|
+
> => {
|
|
59
|
+
type Requirements = Exclude<RegistrationRequirements<TRegistrations>, TIdentifier> | TErrorRequirements;
|
|
52
60
|
const names = options.jobs.map(registrationName);
|
|
53
61
|
const { clientFactory, clientCacheKey, jobs, onError, stop, ...constructorOptions } = options;
|
|
54
62
|
|
|
55
63
|
const acquire = Effect.acquireRelease(
|
|
56
64
|
Effect.gen(function* () {
|
|
57
|
-
const
|
|
65
|
+
const dependencies = yield* Effect.context<Requirements>();
|
|
58
66
|
const acquired = yield* acquireClient({
|
|
59
67
|
clientCacheKey,
|
|
60
68
|
clientFactory,
|
|
61
69
|
constructor: constructorOptions,
|
|
62
70
|
});
|
|
63
|
-
const
|
|
71
|
+
const service = makeService(acquired.client, names, constructorOptions.schema);
|
|
72
|
+
const context = Context.add(dependencies, Service, service);
|
|
73
|
+
const reportError: (error: Error) => Effect.Effect<unknown, never, Requirements | TIdentifier> = onError ?? logClientError;
|
|
64
74
|
const errorListener = (error: Error) => {
|
|
65
75
|
void Effect.runPromise(Effect.exit(Effect.provide(reportError(error), context)));
|
|
66
76
|
};
|
|
67
77
|
acquired.client.on("error", errorListener);
|
|
68
78
|
const registration = registerJobs(acquired.client, jobs, context, acquired.reused);
|
|
69
79
|
return yield* registration.pipe(
|
|
70
|
-
Effect.as({ acquired,
|
|
80
|
+
Effect.as({ acquired, errorListener, service }),
|
|
71
81
|
Effect.onError(() => {
|
|
72
82
|
acquired.client.off("error", errorListener);
|
|
73
83
|
return releaseClient(acquired, stop);
|
|
@@ -82,7 +92,7 @@ export const makePgBoss = <const TIdentifier extends string>(identifier: TIdenti
|
|
|
82
92
|
|
|
83
93
|
return Layer.effect(
|
|
84
94
|
Service,
|
|
85
|
-
Effect.map(acquire, ({
|
|
95
|
+
Effect.map(acquire, ({ service }) => service),
|
|
86
96
|
);
|
|
87
97
|
};
|
|
88
98
|
|