@shivaedev/effect-pg-boss 0.1.2 → 0.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (44) hide show
  1. package/CHANGELOG.md +17 -1
  2. package/README.md +7 -5
  3. package/dist/definition.d.ts.map +1 -1
  4. package/dist/definition.js.map +1 -1
  5. package/dist/error.d.ts +1 -1
  6. package/dist/error.d.ts.map +1 -1
  7. package/dist/error.js.map +1 -1
  8. package/dist/health.d.ts.map +1 -1
  9. package/dist/health.js.map +1 -1
  10. package/dist/index.d.ts +5 -5
  11. package/dist/internal/client.d.ts +1 -1
  12. package/dist/internal/client.d.ts.map +1 -1
  13. package/dist/internal/client.js.map +1 -1
  14. package/dist/internal/health.d.ts +3 -3
  15. package/dist/internal/health.d.ts.map +1 -1
  16. package/dist/internal/health.js +2 -5
  17. package/dist/internal/health.js.map +1 -1
  18. package/dist/internal/lifecycle.d.ts +4 -4
  19. package/dist/internal/lifecycle.d.ts.map +1 -1
  20. package/dist/internal/lifecycle.js +15 -9
  21. package/dist/internal/lifecycle.js.map +1 -1
  22. package/dist/internal/register.d.ts +5 -4
  23. package/dist/internal/register.d.ts.map +1 -1
  24. package/dist/internal/register.js +19 -8
  25. package/dist/internal/register.js.map +1 -1
  26. package/dist/internal/service.d.ts +2 -2
  27. package/dist/internal/service.d.ts.map +1 -1
  28. package/dist/internal/service.js +1 -1
  29. package/dist/internal/service.js.map +1 -1
  30. package/dist/service.d.ts +6 -5
  31. package/dist/service.d.ts.map +1 -1
  32. package/dist/service.js +6 -13
  33. package/dist/service.js.map +1 -1
  34. package/package.json +3 -3
  35. package/src/definition.ts +9 -33
  36. package/src/error.ts +3 -12
  37. package/src/health.ts +1 -2
  38. package/src/index.ts +5 -5
  39. package/src/internal/client.ts +6 -32
  40. package/src/internal/health.ts +6 -19
  41. package/src/internal/lifecycle.ts +23 -29
  42. package/src/internal/register.ts +44 -67
  43. package/src/internal/service.ts +20 -36
  44. package/src/service.ts +29 -90
package/src/definition.ts CHANGED
@@ -14,33 +14,21 @@ export interface JobContext {
14
14
  readonly heartbeatSeconds: number | null;
15
15
  }
16
16
 
17
- export interface QueueDefinition<
18
- Name extends string = string,
19
- Payload extends JobPayloadSchema = JobPayloadSchema,
20
- > {
17
+ export interface QueueDefinition<Name extends string = string, Payload extends JobPayloadSchema = JobPayloadSchema> {
21
18
  readonly _tag: "QueueDefinition";
22
19
  readonly name: Name;
23
20
  readonly schema: Payload;
24
21
  readonly queueOptions: Omit<QueueOptions, "deadLetter">;
25
22
  readonly workerOptions: WorkOptions;
26
23
  readonly handle: <E, R>(
27
- handler: (
28
- payload: Schema.Schema.Type<Payload>,
29
- job: JobContext,
30
- ) => Effect.Effect<unknown, E, R>,
24
+ handler: (payload: Schema.Schema.Type<Payload>, job: JobContext) => Effect.Effect<unknown, E, R>,
31
25
  ) => QueueWorker<Payload, R>;
32
26
  }
33
27
 
34
- export interface QueueWorker<
35
- Payload extends JobPayloadSchema = JobPayloadSchema,
36
- Requirements = unknown,
37
- > {
28
+ export interface QueueWorker<Payload extends JobPayloadSchema = JobPayloadSchema, Requirements = unknown> {
38
29
  readonly _tag: "QueueWorker";
39
30
  readonly queue: QueueDefinition<string, Payload>;
40
- readonly handler: (
41
- payload: Schema.Schema.Type<Payload>,
42
- job: JobContext,
43
- ) => Effect.Effect<unknown, unknown, Requirements>;
31
+ readonly handler: (payload: Schema.Schema.Type<Payload>, job: JobContext) => Effect.Effect<unknown, unknown, Requirements>;
44
32
  }
45
33
 
46
34
  export interface JobRegistration {
@@ -54,9 +42,7 @@ export interface ScheduleDefinition<Name extends string = string> {
54
42
  readonly queueOptions: Omit<QueueOptions, "deadLetter">;
55
43
  readonly scheduleOptions: ScheduleOptions;
56
44
  readonly workerOptions: WorkOptions;
57
- readonly run: <E, R>(
58
- effect: Effect.Effect<unknown, E, R>,
59
- ) => ScheduledWorker<R>;
45
+ readonly run: <E, R>(effect: Effect.Effect<unknown, E, R>) => ScheduledWorker<R>;
60
46
  }
61
47
 
62
48
  export interface ScheduledWorker<Requirements = unknown> {
@@ -65,9 +51,7 @@ export interface ScheduledWorker<Requirements = unknown> {
65
51
  readonly effect: Effect.Effect<unknown, unknown, Requirements>;
66
52
  }
67
53
 
68
- export type RegistrationRequirements<
69
- Registrations extends readonly JobRegistration[],
70
- > = Registrations[number] extends infer Registration
54
+ export type RegistrationRequirements<Registrations extends readonly JobRegistration[]> = Registrations[number] extends infer Registration
71
55
  ? Registration extends QueueWorker<infer Payload, infer Requirements>
72
56
  ? Requirements | Payload["DecodingServices"]
73
57
  : Registration extends ScheduledWorker<infer Requirements>
@@ -75,20 +59,14 @@ export type RegistrationRequirements<
75
59
  : never
76
60
  : never;
77
61
 
78
- export interface DefineQueueOptions<
79
- Name extends string,
80
- Payload extends JobPayloadSchema,
81
- > {
62
+ export interface DefineQueueOptions<Name extends string, Payload extends JobPayloadSchema> {
82
63
  readonly name: Name;
83
64
  readonly schema: Payload;
84
65
  readonly queue?: Omit<QueueOptions, "deadLetter">;
85
66
  readonly worker?: WorkOptions;
86
67
  }
87
68
 
88
- export const defineQueue = <
89
- const Name extends string,
90
- const Payload extends JobPayloadSchema,
91
- >(
69
+ export const defineQueue = <const Name extends string, const Payload extends JobPayloadSchema>(
92
70
  options: DefineQueueOptions<Name, Payload>,
93
71
  ): QueueDefinition<Name, Payload> => {
94
72
  const definition: QueueDefinition<Name, Payload> = {
@@ -115,9 +93,7 @@ export interface DefineScheduleOptions<Name extends string> {
115
93
  readonly worker?: WorkOptions;
116
94
  }
117
95
 
118
- export const defineSchedule = <const Name extends string>(
119
- options: DefineScheduleOptions<Name>,
120
- ): ScheduleDefinition<Name> => {
96
+ export const defineSchedule = <const Name extends string>(options: DefineScheduleOptions<Name>): ScheduleDefinition<Name> => {
121
97
  const definition: ScheduleDefinition<Name> = {
122
98
  _tag: "ScheduleDefinition",
123
99
  cron: options.cron,
package/src/error.ts CHANGED
@@ -1,15 +1,10 @@
1
1
  import { Data, Redacted } from "effect";
2
2
 
3
- export type PgBossOperation =
4
- | "enqueue"
5
- | "health"
6
- | "register"
7
- | "start"
8
- | "stop";
3
+ export type PgBossOperation = "enqueue" | "health" | "register" | "start" | "stop";
9
4
 
10
5
  export class PgBossError extends Data.TaggedError("PgBossError")<{
11
6
  readonly operation: PgBossOperation;
12
- readonly queue?: string;
7
+ readonly queue?: string | undefined;
13
8
  readonly original: Redacted.Redacted<unknown>;
14
9
  }> {}
15
10
 
@@ -19,11 +14,7 @@ export class PgBossPayloadError extends Data.TaggedError("PgBossPayloadError")<{
19
14
  readonly original: Redacted.Redacted<unknown>;
20
15
  }> {}
21
16
 
22
- export const toPgBossError = (
23
- operation: PgBossOperation,
24
- error: unknown,
25
- queue?: string,
26
- ): PgBossError =>
17
+ export const toPgBossError = (operation: PgBossOperation, error: unknown, queue?: string): PgBossError =>
27
18
  new PgBossError({
28
19
  operation,
29
20
  queue,
package/src/health.ts CHANGED
@@ -16,5 +16,4 @@ export interface JobsHealth {
16
16
  readonly readyTotal: number;
17
17
  }
18
18
 
19
- export const deadLetterQueueName = (queueName: string): string =>
20
- `${queueName}-dlq`;
19
+ export const deadLetterQueueName = (queueName: string): string => `${queueName}-dlq`;
package/src/index.ts CHANGED
@@ -10,24 +10,24 @@ export {
10
10
  type QueueWorker,
11
11
  type ScheduleDefinition,
12
12
  type ScheduledWorker,
13
- } from "./definition.js";
13
+ } from "./definition.ts";
14
14
  export {
15
15
  PgBossError,
16
16
  type PgBossOperation,
17
17
  PgBossPayloadError,
18
- } from "./error.js";
18
+ } from "./error.ts";
19
19
  export {
20
20
  deadLetterQueueName,
21
21
  type JobsHealth,
22
22
  type QueueHealth,
23
- } from "./health.js";
23
+ } from "./health.ts";
24
24
  export type {
25
25
  PgBossClient,
26
26
  PgBossClientFactory,
27
- } from "./internal/client.js";
27
+ } from "./internal/client.ts";
28
28
  export {
29
29
  makePgBoss,
30
30
  type PgBossDefinition,
31
31
  type PgBossLayerOptions,
32
32
  type PgBossService,
33
- } from "./service.js";
33
+ } from "./service.ts";
@@ -1,45 +1,19 @@
1
- import type {
2
- ConstructorOptions,
3
- Job,
4
- Queue,
5
- QueueResult,
6
- ScheduleOptions,
7
- SendOptions,
8
- StopOptions,
9
- WorkOptions,
10
- } from "pg-boss";
1
+ import type { ConstructorOptions, Job, Queue, QueueResult, ScheduleOptions, SendOptions, StopOptions, WorkOptions } from "pg-boss";
11
2
  import { PgBoss } from "pg-boss";
12
3
 
13
4
  export interface PgBossClient {
14
- readonly createQueue: (
15
- name: string,
16
- options?: Omit<Queue, "name">,
17
- ) => Promise<void>;
5
+ readonly createQueue: (name: string, options?: Omit<Queue, "name">) => Promise<void>;
18
6
  readonly getQueue: (name: string) => Promise<QueueResult | null>;
19
7
  readonly offWork: (name: string) => Promise<void>;
20
8
  readonly on: (event: "error", listener: (error: Error) => void) => unknown;
21
9
  readonly off: (event: "error", listener: (error: Error) => void) => unknown;
22
- readonly schedule: (
23
- name: string,
24
- cron: string,
25
- data?: object | null,
26
- options?: ScheduleOptions,
27
- ) => Promise<void>;
28
- readonly send: (
29
- name: string,
30
- data?: object | null,
31
- options?: SendOptions,
32
- ) => Promise<string | null>;
10
+ readonly schedule: (name: string, cron: string, data?: object | null, options?: ScheduleOptions) => Promise<void>;
11
+ readonly send: (name: string, data?: object | null, options?: SendOptions) => Promise<string | null>;
33
12
  readonly start: () => Promise<unknown>;
34
13
  readonly stop: (options?: StopOptions) => Promise<void>;
35
- readonly work: <Payload>(
36
- name: string,
37
- options: WorkOptions,
38
- handler: (jobs: readonly Job<Payload>[]) => Promise<unknown>,
39
- ) => Promise<string>;
14
+ readonly work: (name: string, options: WorkOptions, handler: (jobs: readonly Job<unknown>[]) => Promise<unknown>) => Promise<string>;
40
15
  }
41
16
 
42
17
  export type PgBossClientFactory = (options: ConstructorOptions) => PgBossClient;
43
18
 
44
- export const defaultClientFactory: PgBossClientFactory = (options) =>
45
- new PgBoss(options);
19
+ export const defaultClientFactory: PgBossClientFactory = (options) => new PgBoss(options);
@@ -1,25 +1,15 @@
1
1
  import { Effect } from "effect";
2
- import { toPgBossError } from "../error.js";
3
- import {
4
- deadLetterQueueName,
5
- type JobsHealth,
6
- type QueueHealth,
7
- } from "../health.js";
8
- import type { PgBossClient } from "./client.js";
2
+ import { toPgBossError } from "../error.ts";
3
+ import { deadLetterQueueName, type JobsHealth, type QueueHealth } from "../health.ts";
4
+ import type { PgBossClient } from "./client.ts";
9
5
 
10
- export const healthFor = (
11
- client: PgBossClient,
12
- names: readonly string[],
13
- ): Effect.Effect<JobsHealth, import("../error.js").PgBossError> =>
6
+ export const healthFor = (client: PgBossClient, names: readonly string[]): Effect.Effect<JobsHealth, import("../error.ts").PgBossError> =>
14
7
  Effect.forEach(
15
8
  names,
16
9
  (name) =>
17
10
  Effect.tryPromise({
18
11
  try: async (): Promise<QueueHealth> => {
19
- const [queue, deadLetter] = await Promise.all([
20
- client.getQueue(name),
21
- client.getQueue(deadLetterQueueName(name)),
22
- ]);
12
+ const [queue, deadLetter] = await Promise.all([client.getQueue(name), client.getQueue(deadLetterQueueName(name))]);
23
13
  return {
24
14
  activeCount: queue?.activeCount ?? 0,
25
15
  deadLetteredCount: deadLetter?.queuedCount ?? 0,
@@ -35,10 +25,7 @@ export const healthFor = (
35
25
  ).pipe(
36
26
  Effect.map((jobs) => ({
37
27
  activeTotal: jobs.reduce((sum, job) => sum + job.activeCount, 0),
38
- deadLetteredTotal: jobs.reduce(
39
- (sum, job) => sum + job.deadLetteredCount,
40
- 0,
41
- ),
28
+ deadLetteredTotal: jobs.reduce((sum, job) => sum + job.deadLetteredCount, 0),
42
29
  failedTotal: jobs.reduce((sum, job) => sum + job.failedCount, 0),
43
30
  jobs,
44
31
  queuedTotal: jobs.reduce((sum, job) => sum + job.queuedCount, 0),
@@ -1,11 +1,7 @@
1
1
  import { Effect } from "effect";
2
2
  import type { ConstructorOptions, StopOptions } from "pg-boss";
3
- import { toPgBossError } from "../error.js";
4
- import {
5
- defaultClientFactory,
6
- type PgBossClient,
7
- type PgBossClientFactory,
8
- } from "./client.js";
3
+ import { toPgBossError } from "../error.ts";
4
+ import { defaultClientFactory, type PgBossClient, type PgBossClientFactory } from "./client.ts";
9
5
 
10
6
  interface CachedClient {
11
7
  readonly client: Promise<PgBossClient>;
@@ -13,17 +9,25 @@ interface CachedClient {
13
9
  }
14
10
 
15
11
  const CacheKey = Symbol.for("@shivaedev/effect-pg-boss/client-cache");
16
- const globalCache = globalThis as typeof globalThis & {
17
- [CacheKey]?: Map<string | symbol, CachedClient>;
12
+ const sharedCache: unknown = Reflect.get(globalThis, CacheKey);
13
+ const clientCache: Map<string | symbol, unknown> = sharedCache instanceof Map ? sharedCache : new Map<string | symbol, unknown>();
14
+ Reflect.set(globalThis, CacheKey, clientCache);
15
+
16
+ const isCachedClient = (value: unknown): value is CachedClient =>
17
+ typeof value === "object" &&
18
+ value !== null &&
19
+ Reflect.get(value, "client") instanceof Promise &&
20
+ typeof Reflect.get(value, "references") === "number";
21
+
22
+ const cachedClient = (key: string | symbol): CachedClient | undefined => {
23
+ const entry = clientCache.get(key);
24
+ return isCachedClient(entry) ? entry : undefined;
18
25
  };
19
- const clientCache =
20
- globalCache[CacheKey] ?? new Map<string | symbol, CachedClient>();
21
- globalCache[CacheKey] = clientCache;
22
26
 
23
27
  export interface AcquireClientOptions {
24
- readonly clientFactory?: PgBossClientFactory;
28
+ readonly clientFactory?: PgBossClientFactory | undefined;
25
29
  readonly constructor: ConstructorOptions;
26
- readonly developmentCacheKey?: string | symbol;
30
+ readonly clientCacheKey?: string | symbol | undefined;
27
31
  }
28
32
 
29
33
  export interface AcquiredClient {
@@ -33,9 +37,7 @@ export interface AcquiredClient {
33
37
  }
34
38
 
35
39
  const startClient = (options: AcquireClientOptions): Promise<PgBossClient> => {
36
- const client = (options.clientFactory ?? defaultClientFactory)(
37
- options.constructor,
38
- );
40
+ const client = (options.clientFactory ?? defaultClientFactory)(options.constructor);
39
41
  return client.start().then(
40
42
  () => client,
41
43
  async (error) => {
@@ -49,15 +51,10 @@ const startClient = (options: AcquireClientOptions): Promise<PgBossClient> => {
49
51
  );
50
52
  };
51
53
 
52
- export const acquireClient = (
53
- options: AcquireClientOptions,
54
- ): Effect.Effect<AcquiredClient, import("../error.js").PgBossError> =>
54
+ export const acquireClient = (options: AcquireClientOptions): Effect.Effect<AcquiredClient, import("../error.ts").PgBossError> =>
55
55
  Effect.tryPromise({
56
56
  try: async () => {
57
- const cacheKey =
58
- process.env.NODE_ENV === "production"
59
- ? undefined
60
- : options.developmentCacheKey;
57
+ const cacheKey = options.clientCacheKey;
61
58
  if (cacheKey === undefined) {
62
59
  return {
63
60
  client: await startClient(options),
@@ -65,7 +62,7 @@ export const acquireClient = (
65
62
  };
66
63
  }
67
64
 
68
- const existing = clientCache.get(cacheKey);
65
+ const existing = cachedClient(cacheKey);
69
66
  if (existing !== undefined) {
70
67
  existing.references += 1;
71
68
  try {
@@ -99,10 +96,7 @@ export const acquireClient = (
99
96
  catch: (error) => toPgBossError("start", error),
100
97
  });
101
98
 
102
- export const releaseClient = (
103
- acquired: AcquiredClient,
104
- stopOptions?: StopOptions,
105
- ): Effect.Effect<void> =>
99
+ export const releaseClient = (acquired: AcquiredClient, stopOptions?: StopOptions): Effect.Effect<void> =>
106
100
  Effect.tryPromise({
107
101
  try: async () => {
108
102
  if (acquired.cacheKey === undefined) {
@@ -110,7 +104,7 @@ export const releaseClient = (
110
104
  return;
111
105
  }
112
106
 
113
- const entry = clientCache.get(acquired.cacheKey);
107
+ const entry = cachedClient(acquired.cacheKey);
114
108
  if (entry === undefined) return;
115
109
  entry.references -= 1;
116
110
  if (entry.references > 0) return;
@@ -1,15 +1,9 @@
1
- import { Effect, Redacted, Schema } from "effect";
1
+ import { type Context, Effect, Redacted, Schema } from "effect";
2
2
  import type { Job } from "pg-boss";
3
- import {
4
- type JobPayloadSchema,
5
- type JobRegistration,
6
- jobContext,
7
- type QueueWorker,
8
- type ScheduledWorker,
9
- } from "../definition.js";
10
- import { PgBossPayloadError, toPgBossError } from "../error.js";
11
- import { deadLetterQueueName } from "../health.js";
12
- import type { PgBossClient } from "./client.js";
3
+ import { type JobRegistration, jobContext, type QueueWorker, type ScheduledWorker } from "../definition.ts";
4
+ import { PgBossPayloadError, toPgBossError } from "../error.ts";
5
+ import { deadLetterQueueName } from "../health.ts";
6
+ import type { PgBossClient } from "./client.ts";
13
7
 
14
8
  const queueOptions = (options: Readonly<Record<string, unknown>>) => ({
15
9
  retryBackoff: true,
@@ -17,11 +11,23 @@ const queueOptions = (options: Readonly<Record<string, unknown>>) => ({
17
11
  ...options,
18
12
  });
19
13
 
20
- const runQueueWorker = <R>(
21
- worker: QueueWorker<JobPayloadSchema, R>,
22
- job: Job<unknown>,
23
- context: import("effect").Context.Context<R>,
24
- ): Promise<unknown> =>
14
+ const isQueueWorker = (registration: JobRegistration): registration is QueueWorker => registration._tag === "QueueWorker";
15
+
16
+ const isScheduledWorker = (registration: JobRegistration): registration is ScheduledWorker => registration._tag === "ScheduledWorker";
17
+
18
+ export const registrationName = (registration: JobRegistration): string => {
19
+ if (isQueueWorker(registration)) return registration.queue.name;
20
+ if (isScheduledWorker(registration)) return registration.schedule.name;
21
+ throw new TypeError(`Unknown pg-boss registration: ${registration._tag}`);
22
+ };
23
+
24
+ // The layer requires RegistrationRequirements of its jobs, so its context provides every registered worker.
25
+ function workerContext<R>(context: Context.Context<R>): Context.Context<unknown>;
26
+ function workerContext(context: unknown): unknown {
27
+ return context;
28
+ }
29
+
30
+ const runQueueWorker = (worker: QueueWorker, job: Job<unknown>, context: Context.Context<unknown>): Promise<unknown> =>
25
31
  Effect.runPromise(
26
32
  Schema.decodeUnknownEffect(worker.queue.schema)(job.data).pipe(
27
33
  Effect.mapError(
@@ -34,16 +40,11 @@ const runQueueWorker = <R>(
34
40
  ),
35
41
  Effect.flatMap((payload) => worker.handler(payload, jobContext(job))),
36
42
  Effect.provide(context),
37
- ) as Effect.Effect<unknown, unknown>,
43
+ ),
38
44
  { signal: job.signal },
39
45
  );
40
46
 
41
- const registerQueue = async <R>(
42
- client: PgBossClient,
43
- worker: QueueWorker<JobPayloadSchema, R>,
44
- context: import("effect").Context.Context<R>,
45
- replaceWorker: boolean,
46
- ): Promise<void> => {
47
+ const registerQueue = async (client: PgBossClient, worker: QueueWorker, context: Context.Context<unknown>, replaceWorker: boolean): Promise<void> => {
47
48
  const name = worker.queue.name;
48
49
  const deadLetter = deadLetterQueueName(name);
49
50
  if (replaceWorker) await client.offWork(name);
@@ -57,10 +58,10 @@ const registerQueue = async <R>(
57
58
  });
58
59
  };
59
60
 
60
- const registerSchedule = async <R>(
61
+ const registerSchedule = async (
61
62
  client: PgBossClient,
62
- worker: ScheduledWorker<R>,
63
- context: import("effect").Context.Context<R>,
63
+ worker: ScheduledWorker,
64
+ context: Context.Context<unknown>,
64
65
  replaceWorker: boolean,
65
66
  ): Promise<void> => {
66
67
  const { schedule } = worker;
@@ -71,33 +72,18 @@ const registerSchedule = async <R>(
71
72
  ...queueOptions(schedule.queueOptions),
72
73
  deadLetter,
73
74
  });
74
- await client.work(
75
- schedule.name,
76
- schedule.workerOptions,
77
- async (jobs: readonly Job<unknown>[]) => {
78
- for (const job of jobs) {
79
- await Effect.runPromise(Effect.provide(worker.effect, context), {
80
- signal: job.signal,
81
- });
82
- }
83
- },
84
- );
85
- await client.schedule(
86
- schedule.name,
87
- schedule.cron,
88
- null,
89
- schedule.scheduleOptions,
90
- );
75
+ await client.work(schedule.name, schedule.workerOptions, async (jobs: readonly Job<unknown>[]) => {
76
+ for (const job of jobs) {
77
+ await Effect.runPromise(Effect.provide(worker.effect, context), {
78
+ signal: job.signal,
79
+ });
80
+ }
81
+ });
82
+ await client.schedule(schedule.name, schedule.cron, null, schedule.scheduleOptions);
91
83
  };
92
84
 
93
- export const registrationNames = (
94
- registrations: readonly JobRegistration[],
95
- ): readonly string[] => {
96
- const names = registrations.map((registration) =>
97
- registration._tag === "QueueWorker"
98
- ? (registration as QueueWorker).queue.name
99
- : (registration as ScheduledWorker).schedule.name,
100
- );
85
+ export const registrationNames = (registrations: readonly JobRegistration[]): readonly string[] => {
86
+ const names = registrations.map(registrationName);
101
87
  if (new Set(names).size !== names.length) {
102
88
  throw new TypeError("pg-boss job names must be unique");
103
89
  }
@@ -107,27 +93,18 @@ export const registrationNames = (
107
93
  export const registerJobs = <R>(
108
94
  client: PgBossClient,
109
95
  registrations: readonly JobRegistration[],
110
- context: import("effect").Context.Context<R>,
96
+ context: Context.Context<R>,
111
97
  replaceWorkers: boolean,
112
- ): Effect.Effect<void, import("../error.js").PgBossError> =>
98
+ ): Effect.Effect<void, import("../error.ts").PgBossError> =>
113
99
  Effect.tryPromise({
114
100
  try: async () => {
115
101
  registrationNames(registrations);
102
+ const provided = workerContext(context);
116
103
  for (const registration of registrations) {
117
- if (registration._tag === "QueueWorker") {
118
- await registerQueue(
119
- client,
120
- registration as QueueWorker<JobPayloadSchema, R>,
121
- context,
122
- replaceWorkers,
123
- );
124
- } else {
125
- await registerSchedule(
126
- client,
127
- registration as ScheduledWorker<R>,
128
- context,
129
- replaceWorkers,
130
- );
104
+ if (isQueueWorker(registration)) {
105
+ await registerQueue(client, registration, provided, replaceWorkers);
106
+ } else if (isScheduledWorker(registration)) {
107
+ await registerSchedule(client, registration, provided, replaceWorkers);
131
108
  }
132
109
  }
133
110
  },
@@ -1,17 +1,10 @@
1
1
  import { Effect, Option, Redacted, Schema } from "effect";
2
- import {
3
- type PgBossError,
4
- PgBossPayloadError,
5
- toPgBossError,
6
- } from "../error.js";
7
- import type { PgBossService } from "../service.js";
8
- import type { PgBossClient } from "./client.js";
9
- import { healthFor } from "./health.js";
2
+ import { type PgBossError, PgBossPayloadError, toPgBossError } from "../error.ts";
3
+ import type { PgBossService } from "../service.ts";
4
+ import type { PgBossClient } from "./client.ts";
5
+ import { healthFor } from "./health.ts";
10
6
 
11
- export const makeService = (
12
- client: PgBossClient,
13
- names: readonly string[],
14
- ): PgBossService => ({
7
+ export const makeService = (client: PgBossClient, names: readonly string[]): PgBossService => ({
15
8
  enqueue: (queue, payload, options) =>
16
9
  Schema.encodeUnknownEffect(queue.schema)(payload).pipe(
17
10
  Effect.mapError(
@@ -22,30 +15,21 @@ export const makeService = (
22
15
  queue: queue.name,
23
16
  }),
24
17
  ),
25
- Effect.flatMap(
26
- (
27
- encoded,
28
- ): Effect.Effect<
29
- Option.Option<string>,
30
- PgBossError | PgBossPayloadError
31
- > => {
32
- if (typeof encoded !== "object" || encoded === null) {
33
- return Effect.fail(
34
- new PgBossPayloadError({
35
- direction: "encode",
36
- original: Redacted.make(
37
- new TypeError("pg-boss payloads must encode to objects"),
38
- ),
39
- queue: queue.name,
40
- }),
41
- );
42
- }
43
- return Effect.tryPromise({
44
- try: () => client.send(queue.name, encoded, options),
45
- catch: (error) => toPgBossError("enqueue", error, queue.name),
46
- }).pipe(Effect.map(Option.fromNullishOr));
47
- },
48
- ),
18
+ Effect.flatMap((encoded): Effect.Effect<Option.Option<string>, PgBossError | PgBossPayloadError> => {
19
+ if (typeof encoded !== "object" || encoded === null) {
20
+ return Effect.fail(
21
+ new PgBossPayloadError({
22
+ direction: "encode",
23
+ original: Redacted.make(new TypeError("pg-boss payloads must encode to objects")),
24
+ queue: queue.name,
25
+ }),
26
+ );
27
+ }
28
+ return Effect.tryPromise({
29
+ try: () => client.send(queue.name, encoded, options),
30
+ catch: (error) => toPgBossError("enqueue", error, queue.name),
31
+ }).pipe(Effect.map(Option.fromNullishOr));
32
+ }),
49
33
  ),
50
34
  health: healthFor(client, names),
51
35
  });