@shivaedev/jobs 0.0.0 → 0.20.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 +325 -0
- package/LICENSE +21 -0
- package/README.md +527 -1
- package/dist/server/client.d.ts +25 -0
- package/dist/server/client.d.ts.map +1 -0
- package/dist/server/client.js +3 -0
- package/dist/server/client.js.map +1 -0
- package/dist/server/definition.d.ts +96 -0
- package/dist/server/definition.d.ts.map +1 -0
- package/dist/server/definition.js +40 -0
- package/dist/server/definition.js.map +1 -0
- package/dist/server/error.d.ts +51 -0
- package/dist/server/error.d.ts.map +1 -0
- package/dist/server/error.js +37 -0
- package/dist/server/error.js.map +1 -0
- package/dist/server/health.d.ts +51 -0
- package/dist/server/health.d.ts.map +1 -0
- package/dist/server/health.js +4 -0
- package/dist/server/health.js.map +1 -0
- 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 +51 -0
- package/dist/server/internal/batchHandler.js.map +1 -0
- package/dist/server/internal/clientCache.d.ts +8 -0
- package/dist/server/internal/clientCache.d.ts.map +1 -0
- package/dist/server/internal/clientCache.js +61 -0
- package/dist/server/internal/clientCache.js.map +1 -0
- package/dist/server/internal/clientErrors.d.ts +8 -0
- package/dist/server/internal/clientErrors.d.ts.map +1 -0
- package/dist/server/internal/clientErrors.js +49 -0
- package/dist/server/internal/clientErrors.js.map +1 -0
- 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 +28 -0
- package/dist/server/internal/countsFor.js.map +1 -0
- package/dist/server/internal/deadlineOf.d.ts +3 -0
- package/dist/server/internal/deadlineOf.d.ts.map +1 -0
- package/dist/server/internal/deadlineOf.js +7 -0
- package/dist/server/internal/deadlineOf.js.map +1 -0
- package/dist/server/internal/deferrableAttempt.d.ts +7 -0
- package/dist/server/internal/deferrableAttempt.d.ts.map +1 -0
- package/dist/server/internal/deferrableAttempt.js +28 -0
- package/dist/server/internal/deferrableAttempt.js.map +1 -0
- package/dist/server/internal/deferral.d.ts +15 -0
- package/dist/server/internal/deferral.d.ts.map +1 -0
- package/dist/server/internal/deferral.js +14 -0
- package/dist/server/internal/deferral.js.map +1 -0
- package/dist/server/internal/deferralStorePlan.d.ts +2 -0
- package/dist/server/internal/deferralStorePlan.d.ts.map +1 -0
- package/dist/server/internal/deferralStorePlan.js +35 -0
- package/dist/server/internal/deferralStorePlan.js.map +1 -0
- package/dist/server/internal/definitionOptions.d.ts +17 -0
- package/dist/server/internal/definitionOptions.d.ts.map +1 -0
- package/dist/server/internal/definitionOptions.js +12 -0
- package/dist/server/internal/definitionOptions.js.map +1 -0
- package/dist/server/internal/enqueueFor.d.ts +4 -0
- package/dist/server/internal/enqueueFor.d.ts.map +1 -0
- package/dist/server/internal/enqueueFor.js +23 -0
- package/dist/server/internal/enqueueFor.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 +44 -0
- package/dist/server/internal/ensureQueue.js.map +1 -0
- package/dist/server/internal/expireAttempt.d.ts +3 -0
- package/dist/server/internal/expireAttempt.d.ts.map +1 -0
- package/dist/server/internal/expireAttempt.js +17 -0
- package/dist/server/internal/expireAttempt.js.map +1 -0
- package/dist/server/internal/healthFor.d.ts +5 -0
- package/dist/server/internal/healthFor.d.ts.map +1 -0
- package/dist/server/internal/healthFor.js +27 -0
- package/dist/server/internal/healthFor.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 +35 -0
- package/dist/server/internal/jobControl.js.map +1 -0
- package/dist/server/internal/lifecycle.d.ts +17 -0
- package/dist/server/internal/lifecycle.d.ts.map +1 -0
- package/dist/server/internal/lifecycle.js +55 -0
- package/dist/server/internal/lifecycle.js.map +1 -0
- package/dist/server/internal/listJobs.d.ts +5 -0
- package/dist/server/internal/listJobs.d.ts.map +1 -0
- package/dist/server/internal/listJobs.js +69 -0
- package/dist/server/internal/listJobs.js.map +1 -0
- package/dist/server/internal/makeService.d.ts +4 -0
- package/dist/server/internal/makeService.d.ts.map +1 -0
- package/dist/server/internal/makeService.js +17 -0
- package/dist/server/internal/makeService.js.map +1 -0
- package/dist/server/internal/nativeClient.d.ts +3 -0
- package/dist/server/internal/nativeClient.d.ts.map +1 -0
- package/dist/server/internal/nativeClient.js +28 -0
- package/dist/server/internal/nativeClient.js.map +1 -0
- package/dist/server/internal/observeTestingSettlements.d.ts +6 -0
- package/dist/server/internal/observeTestingSettlements.d.ts.map +1 -0
- package/dist/server/internal/observeTestingSettlements.js +35 -0
- package/dist/server/internal/observeTestingSettlements.js.map +1 -0
- package/dist/server/internal/register.d.ts +8 -0
- package/dist/server/internal/register.d.ts.map +1 -0
- package/dist/server/internal/register.js +98 -0
- package/dist/server/internal/register.js.map +1 -0
- package/dist/server/internal/registerReleasedJobs.d.ts +5 -0
- package/dist/server/internal/registerReleasedJobs.d.ts.map +1 -0
- package/dist/server/internal/registerReleasedJobs.js +35 -0
- package/dist/server/internal/registerReleasedJobs.js.map +1 -0
- package/dist/server/internal/registerWorker.d.ts +5 -0
- package/dist/server/internal/registerWorker.d.ts.map +1 -0
- package/dist/server/internal/registerWorker.js +33 -0
- package/dist/server/internal/registerWorker.js.map +1 -0
- package/dist/server/internal/runSchemaPlan.d.ts +7 -0
- package/dist/server/internal/runSchemaPlan.d.ts.map +1 -0
- package/dist/server/internal/runSchemaPlan.js +25 -0
- package/dist/server/internal/runSchemaPlan.js.map +1 -0
- package/dist/server/internal/scheduleDefinition.d.ts +3 -0
- package/dist/server/internal/scheduleDefinition.d.ts.map +1 -0
- package/dist/server/internal/scheduleDefinition.js +19 -0
- package/dist/server/internal/scheduleDefinition.js.map +1 -0
- package/dist/server/internal/schemaIndexBuilds.d.ts +4 -0
- package/dist/server/internal/schemaIndexBuilds.d.ts.map +1 -0
- package/dist/server/internal/schemaIndexBuilds.js +34 -0
- package/dist/server/internal/schemaIndexBuilds.js.map +1 -0
- package/dist/server/internal/settleDeferral.d.ts +7 -0
- package/dist/server/internal/settleDeferral.d.ts.map +1 -0
- package/dist/server/internal/settleDeferral.js +48 -0
- package/dist/server/internal/settleDeferral.js.map +1 -0
- package/dist/server/internal/storedRows.d.ts +6 -0
- package/dist/server/internal/storedRows.d.ts.map +1 -0
- package/dist/server/internal/storedRows.js +10 -0
- package/dist/server/internal/storedRows.js.map +1 -0
- package/dist/server/internal/testingClient.d.ts +13 -0
- package/dist/server/internal/testingClient.d.ts.map +1 -0
- package/dist/server/internal/testingClient.js +117 -0
- package/dist/server/internal/testingClient.js.map +1 -0
- package/dist/server/internal/testingDeferrals.d.ts +11 -0
- package/dist/server/internal/testingDeferrals.d.ts.map +1 -0
- package/dist/server/internal/testingDeferrals.js +29 -0
- package/dist/server/internal/testingDeferrals.js.map +1 -0
- package/dist/server/internal/testingExpirations.d.ts +6 -0
- package/dist/server/internal/testingExpirations.d.ts.map +1 -0
- package/dist/server/internal/testingExpirations.js +27 -0
- package/dist/server/internal/testingExpirations.js.map +1 -0
- package/dist/server/internal/testingIdle.d.ts +16 -0
- package/dist/server/internal/testingIdle.d.ts.map +1 -0
- package/dist/server/internal/testingIdle.js +31 -0
- package/dist/server/internal/testingIdle.js.map +1 -0
- package/dist/server/internal/testingReadout.d.ts +44 -0
- package/dist/server/internal/testingReadout.d.ts.map +1 -0
- package/dist/server/internal/testingReadout.js +13 -0
- package/dist/server/internal/testingReadout.js.map +1 -0
- package/dist/server/internal/testingSignal.d.ts +7 -0
- package/dist/server/internal/testingSignal.d.ts.map +1 -0
- package/dist/server/internal/testingSignal.js +29 -0
- package/dist/server/internal/testingSignal.js.map +1 -0
- package/dist/server/internal/testingSnapshot.d.ts +6 -0
- package/dist/server/internal/testingSnapshot.d.ts.map +1 -0
- package/dist/server/internal/testingSnapshot.js +32 -0
- package/dist/server/internal/testingSnapshot.js.map +1 -0
- package/dist/server/internal/tracing.d.ts +6 -0
- package/dist/server/internal/tracing.d.ts.map +1 -0
- package/dist/server/internal/tracing.js +37 -0
- package/dist/server/internal/tracing.js.map +1 -0
- package/dist/server/internal/waitForBatchExpiry.d.ts +6 -0
- package/dist/server/internal/waitForBatchExpiry.d.ts.map +1 -0
- package/dist/server/internal/waitForBatchExpiry.js +28 -0
- package/dist/server/internal/waitForBatchExpiry.js.map +1 -0
- package/dist/server/schema.d.ts +10 -0
- package/dist/server/schema.d.ts.map +1 -0
- package/dist/server/schema.js +67 -0
- package/dist/server/schema.js.map +1 -0
- package/dist/server/service.d.ts +29 -0
- package/dist/server/service.d.ts.map +1 -0
- package/dist/server/service.js +33 -0
- package/dist/server/service.js.map +1 -0
- package/dist/server/testing.d.ts +77 -0
- package/dist/server/testing.d.ts.map +1 -0
- package/dist/server/testing.js +90 -0
- package/dist/server/testing.js.map +1 -0
- package/package.json +71 -4
- package/src/server/client.ts +40 -0
- package/src/server/definition.ts +140 -0
- package/src/server/error.ts +73 -0
- package/src/server/health.ts +60 -0
- package/src/server/internal/batchHandler.ts +71 -0
- package/src/server/internal/clientCache.ts +73 -0
- package/src/server/internal/clientErrors.ts +64 -0
- package/src/server/internal/countsFor.ts +34 -0
- package/src/server/internal/deadlineOf.ts +8 -0
- package/src/server/internal/deferrableAttempt.ts +41 -0
- package/src/server/internal/deferral.ts +19 -0
- package/src/server/internal/deferralStorePlan.ts +35 -0
- package/src/server/internal/definitionOptions.ts +14 -0
- package/src/server/internal/enqueueFor.ts +39 -0
- package/src/server/internal/ensureQueue.ts +47 -0
- package/src/server/internal/expireAttempt.ts +21 -0
- package/src/server/internal/healthFor.ts +35 -0
- package/src/server/internal/jobControl.ts +44 -0
- package/src/server/internal/lifecycle.ts +68 -0
- package/src/server/internal/listJobs.ts +98 -0
- package/src/server/internal/makeService.ts +19 -0
- package/src/server/internal/nativeClient.ts +30 -0
- package/src/server/internal/observeTestingSettlements.ts +36 -0
- package/src/server/internal/register.ts +145 -0
- package/src/server/internal/registerReleasedJobs.ts +44 -0
- package/src/server/internal/registerWorker.ts +56 -0
- package/src/server/internal/runSchemaPlan.ts +32 -0
- package/src/server/internal/scheduleDefinition.ts +21 -0
- package/src/server/internal/schemaIndexBuilds.ts +44 -0
- package/src/server/internal/settleDeferral.ts +54 -0
- package/src/server/internal/storedRows.ts +17 -0
- package/src/server/internal/testingClient.ts +134 -0
- package/src/server/internal/testingDeferrals.ts +44 -0
- package/src/server/internal/testingExpirations.ts +31 -0
- package/src/server/internal/testingIdle.ts +52 -0
- package/src/server/internal/testingReadout.ts +19 -0
- package/src/server/internal/testingSignal.ts +30 -0
- package/src/server/internal/testingSnapshot.ts +53 -0
- package/src/server/internal/tracing.ts +44 -0
- package/src/server/internal/waitForBatchExpiry.ts +30 -0
- package/src/server/schema.ts +84 -0
- package/src/server/service.ts +100 -0
- package/src/server/testing.ts +118 -0
|
@@ -0,0 +1,100 @@
|
|
|
1
|
+
import { Context, Effect, Layer, type Option, type Schema } from "effect";
|
|
2
|
+
import type { ConstructorOptions, JobWithMetadata, SendOptions, StopOptions } from "pg-boss";
|
|
3
|
+
import type { PgBossClientFactory } from "#client.ts";
|
|
4
|
+
import type { JobPayloadSchema, JobRegistration, QueueDefinition, RegistrationRequirements } from "#definition.ts";
|
|
5
|
+
import type { PgBossClientOptionsError, PgBossError, PgBossPayloadError, PgBossQueueOptionsError } from "#error.ts";
|
|
6
|
+
import type { DeadLetteredJob, JobPage, JobPageOptions, JobsHealth, ListedJob, QueueCounts } from "#health.ts";
|
|
7
|
+
import { logClientError, subscribeClientErrors } from "#internal/clientErrors.ts";
|
|
8
|
+
import { acquireClient, releaseClient } from "#internal/lifecycle.ts";
|
|
9
|
+
import { makeService } from "#internal/makeService.ts";
|
|
10
|
+
import { registrationName } from "#internal/register.ts";
|
|
11
|
+
import { registerReleasedJobs } from "#internal/registerReleasedJobs.ts";
|
|
12
|
+
|
|
13
|
+
export interface PgBossService {
|
|
14
|
+
readonly counts: (queue: Pick<QueueDefinition, "name">) => Effect.Effect<QueueCounts, PgBossError>;
|
|
15
|
+
readonly deadLetteredJobs: <const TName extends string, const TPayload extends JobPayloadSchema>(
|
|
16
|
+
queue: Pick<QueueDefinition<TName, TPayload>, "name" | "schema">,
|
|
17
|
+
options: JobPageOptions,
|
|
18
|
+
) => Effect.Effect<JobPage<DeadLetteredJob<TPayload["Type"]>>, PgBossError | PgBossPayloadError, TPayload["DecodingServices"]>;
|
|
19
|
+
readonly enqueue: <const TName extends string, const TPayload extends JobPayloadSchema, const TQueue extends QueueDefinition<TName, TPayload>>(
|
|
20
|
+
queue: QueueDefinition<TName, TPayload> & TQueue,
|
|
21
|
+
payload: NoInfer<Schema.Schema.Type<TPayload>>,
|
|
22
|
+
options?: NoInfer<TQueue["key"] extends undefined ? SendOptions : Omit<SendOptions, "singletonKey"> & { readonly singletonKey?: never }>,
|
|
23
|
+
) => Effect.Effect<Option.Option<string>, PgBossError | PgBossPayloadError, TPayload["EncodingServices"]>;
|
|
24
|
+
readonly failedJobs: <const TName extends string, const TPayload extends JobPayloadSchema>(
|
|
25
|
+
queue: Pick<QueueDefinition<TName, TPayload>, "name" | "schema">,
|
|
26
|
+
options: JobPageOptions,
|
|
27
|
+
) => Effect.Effect<JobPage<ListedJob<TPayload["Type"]>>, PgBossError | PgBossPayloadError, TPayload["DecodingServices"]>;
|
|
28
|
+
readonly getJob: <const TName extends string, const TPayload extends JobPayloadSchema>(
|
|
29
|
+
queue: Pick<QueueDefinition<TName, TPayload>, "name" | "schema">,
|
|
30
|
+
id: string,
|
|
31
|
+
) => Effect.Effect<Option.Option<JobWithMetadata<TPayload["Type"]>>, PgBossError | PgBossPayloadError, TPayload["DecodingServices"]>;
|
|
32
|
+
readonly health: Effect.Effect<JobsHealth, PgBossError>;
|
|
33
|
+
readonly retry: (queue: Pick<QueueDefinition, "name">, id: string) => Effect.Effect<void, PgBossError>;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
export type PgBossLayerOptions<TRegistrations extends readonly JobRegistration[], TErrorRequirements> = ConstructorOptions & {
|
|
37
|
+
readonly clientFactory?: PgBossClientFactory;
|
|
38
|
+
// One started client is reference-counted per key, so development module reloads reuse it instead of starting another.
|
|
39
|
+
readonly clientCacheKey?: string | symbol | undefined;
|
|
40
|
+
readonly jobs: TRegistrations;
|
|
41
|
+
readonly onError?: (error: Error) => Effect.Effect<unknown, never, TErrorRequirements>;
|
|
42
|
+
readonly stop?: StopOptions;
|
|
43
|
+
};
|
|
44
|
+
|
|
45
|
+
export interface PgBossDefinition<TIdentifier extends string = string> extends Context.Service<TIdentifier, PgBossService> {
|
|
46
|
+
readonly layer: <const TRegistrations extends readonly JobRegistration[], TErrorRequirements = never>(
|
|
47
|
+
options: PgBossLayerOptions<TRegistrations, TErrorRequirements>,
|
|
48
|
+
) => Layer.Layer<
|
|
49
|
+
TIdentifier,
|
|
50
|
+
PgBossError | PgBossQueueOptionsError | PgBossClientOptionsError,
|
|
51
|
+
Exclude<RegistrationRequirements<TRegistrations>, TIdentifier> | TErrorRequirements
|
|
52
|
+
>;
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
export function makePgBoss<const TIdentifier extends string>(identifier: TIdentifier): PgBossDefinition<TIdentifier> {
|
|
56
|
+
const Service = Context.Service<TIdentifier, PgBossService>(identifier);
|
|
57
|
+
|
|
58
|
+
function layer<const TRegistrations extends readonly JobRegistration[], TErrorRequirements = never>(
|
|
59
|
+
options: PgBossLayerOptions<TRegistrations, TErrorRequirements>,
|
|
60
|
+
): Layer.Layer<
|
|
61
|
+
TIdentifier,
|
|
62
|
+
PgBossError | PgBossQueueOptionsError | PgBossClientOptionsError,
|
|
63
|
+
Exclude<RegistrationRequirements<TRegistrations>, TIdentifier> | TErrorRequirements
|
|
64
|
+
> {
|
|
65
|
+
type Requirements = Exclude<RegistrationRequirements<TRegistrations>, TIdentifier> | TErrorRequirements;
|
|
66
|
+
const names = options.jobs.map(registrationName);
|
|
67
|
+
const { clientFactory, clientCacheKey, jobs, onError, stop, ...constructorOptions } = options;
|
|
68
|
+
|
|
69
|
+
const acquire = Effect.acquireRelease(
|
|
70
|
+
Effect.gen(function* () {
|
|
71
|
+
const dependencies = yield* Effect.context<Requirements>();
|
|
72
|
+
const acquired = yield* acquireClient({
|
|
73
|
+
clientCacheKey,
|
|
74
|
+
clientFactory,
|
|
75
|
+
constructor: constructorOptions,
|
|
76
|
+
});
|
|
77
|
+
const service = makeService(acquired.client, names, constructorOptions.schema);
|
|
78
|
+
const context = Context.add(dependencies, Service, service);
|
|
79
|
+
const reportError: (error: Error) => Effect.Effect<unknown, never, Requirements | TIdentifier> = onError ?? logClientError;
|
|
80
|
+
function errorListener(error: Error): void {
|
|
81
|
+
void Effect.runPromise(Effect.exit(Effect.provide(reportError(error), context)));
|
|
82
|
+
}
|
|
83
|
+
const unsubscribe = subscribeClientErrors(acquired.client, errorListener);
|
|
84
|
+
const registration = registerReleasedJobs(acquired.client, jobs, context, acquired.reused, constructorOptions.schema);
|
|
85
|
+
return yield* registration.pipe(
|
|
86
|
+
Effect.as({ acquired, service, unsubscribe }),
|
|
87
|
+
Effect.onError(() => releaseClient(acquired, stop).pipe(Effect.ensuring(Effect.sync(unsubscribe)))),
|
|
88
|
+
);
|
|
89
|
+
}),
|
|
90
|
+
(resource) => releaseClient(resource.acquired, stop).pipe(Effect.ensuring(Effect.sync(resource.unsubscribe))),
|
|
91
|
+
);
|
|
92
|
+
|
|
93
|
+
return Layer.effect(
|
|
94
|
+
Service,
|
|
95
|
+
Effect.map(acquire, ({ service }) => service),
|
|
96
|
+
);
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
return Object.assign(Service, { layer });
|
|
100
|
+
}
|
|
@@ -0,0 +1,118 @@
|
|
|
1
|
+
import { type DateTime, Duration, Effect, Redacted } from "effect";
|
|
2
|
+
import type { PgBoss } from "pg-boss";
|
|
3
|
+
import type { PgBossClientFactory } from "#client.ts";
|
|
4
|
+
import type { JobPayloadSchema, QueueDefinition } from "#definition.ts";
|
|
5
|
+
import { PgBossTestingError } from "#error.ts";
|
|
6
|
+
import { nativeClient } from "#internal/nativeClient.ts";
|
|
7
|
+
import { testingClient } from "#internal/testingClient.ts";
|
|
8
|
+
import { testingDeferrals } from "#internal/testingDeferrals.ts";
|
|
9
|
+
import { type TestingWork, testingIdle } from "#internal/testingIdle.ts";
|
|
10
|
+
import { testingOperation, testingReadout } from "#internal/testingReadout.ts";
|
|
11
|
+
import { testingSignal } from "#internal/testingSignal.ts";
|
|
12
|
+
import { testingSnapshot } from "#internal/testingSnapshot.ts";
|
|
13
|
+
|
|
14
|
+
export type SnapshotQueue = Pick<QueueDefinition<string, JobPayloadSchema>, "name" | "schema">;
|
|
15
|
+
|
|
16
|
+
export interface UnfinishedJob<TQueue extends SnapshotQueue> {
|
|
17
|
+
readonly data: TQueue["schema"]["Type"];
|
|
18
|
+
readonly id: string;
|
|
19
|
+
readonly queue: TQueue["name"];
|
|
20
|
+
readonly startAfter: DateTime.Utc;
|
|
21
|
+
readonly state: "active" | "created" | "failed" | "retry";
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
export const testRetryPolicy = Object.freeze({ retryBackoff: false, retryDelay: 0 });
|
|
25
|
+
|
|
26
|
+
export interface PgBossTestingOptions {
|
|
27
|
+
readonly retryPolicy?: typeof testRetryPolicy;
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
export function makePgBossTesting(options: PgBossTestingOptions = {}) {
|
|
31
|
+
let current: PgBoss | undefined;
|
|
32
|
+
let schema = "pgboss";
|
|
33
|
+
const signal = testingSignal();
|
|
34
|
+
const work: TestingWork = { batches: new Set(), queues: new Set() };
|
|
35
|
+
const controls = {
|
|
36
|
+
changed: signal.changed,
|
|
37
|
+
expirations: new Map<string, number>(),
|
|
38
|
+
expirySeconds: new Map<string, number>(),
|
|
39
|
+
failures: new Set<string>(),
|
|
40
|
+
retryPolicy: options.retryPolicy ?? {},
|
|
41
|
+
work,
|
|
42
|
+
};
|
|
43
|
+
function client(): PgBoss {
|
|
44
|
+
if (current === undefined) {
|
|
45
|
+
throw new Error("Provide the testing clientFactory to a jobs Layer first");
|
|
46
|
+
}
|
|
47
|
+
return current;
|
|
48
|
+
}
|
|
49
|
+
const clientFactory: PgBossClientFactory = (clientOptions) => {
|
|
50
|
+
if (current !== undefined) {
|
|
51
|
+
throw new Error("A testing controller owns one jobs client; create a controller per runtime");
|
|
52
|
+
}
|
|
53
|
+
schema = clientOptions.schema ?? "pgboss";
|
|
54
|
+
current = nativeClient({ ...clientOptions, "__test__enableSpies": true });
|
|
55
|
+
return testingClient(current, clientOptions.schema ?? "pgboss", controls);
|
|
56
|
+
};
|
|
57
|
+
return {
|
|
58
|
+
...testingDeferrals(client, () => schema, signal.changed),
|
|
59
|
+
checkBatchWindow: (queue: Pick<QueueDefinition, "name" | "workerOptions">, millisecondsPerJob: number) =>
|
|
60
|
+
testingOperation(
|
|
61
|
+
"checkBatchWindow",
|
|
62
|
+
async () => {
|
|
63
|
+
const stored = await client().getQueue(queue.name);
|
|
64
|
+
if (stored === null) {
|
|
65
|
+
throw new Error("Queue is not registered");
|
|
66
|
+
}
|
|
67
|
+
if (stored.expireInSeconds === undefined) {
|
|
68
|
+
throw new Error("Queue has no expiry window");
|
|
69
|
+
}
|
|
70
|
+
if (!Number.isFinite(millisecondsPerJob) || millisecondsPerJob < 0) {
|
|
71
|
+
throw new RangeError("Job duration must be nonnegative and finite");
|
|
72
|
+
}
|
|
73
|
+
const required = (queue.workerOptions.batchSize ?? 10) * millisecondsPerJob;
|
|
74
|
+
if (required >= stored.expireInSeconds * 1000) {
|
|
75
|
+
throw new Error("Sequential batch work must fit within the queue expiry window");
|
|
76
|
+
}
|
|
77
|
+
},
|
|
78
|
+
queue.name,
|
|
79
|
+
),
|
|
80
|
+
checkSchemaVersion: (expected: number) =>
|
|
81
|
+
testingOperation("checkSchemaVersion", async () => {
|
|
82
|
+
if ((await client().schemaVersion()) !== expected) {
|
|
83
|
+
throw new Error(`Expected pg-boss schema version ${expected}`);
|
|
84
|
+
}
|
|
85
|
+
}),
|
|
86
|
+
clientFactory,
|
|
87
|
+
expireAfter: (queue: Pick<QueueDefinition, "name">, attempts: number) =>
|
|
88
|
+
Effect.try({
|
|
89
|
+
catch: (original) => new PgBossTestingError({ operation: "expireAfter", original: Redacted.make(original), queue: queue.name }),
|
|
90
|
+
try: () => {
|
|
91
|
+
if (!Number.isSafeInteger(attempts) || attempts < 1) {
|
|
92
|
+
throw new RangeError("Attempts must be a positive integer");
|
|
93
|
+
}
|
|
94
|
+
controls.expirations.set(queue.name, attempts);
|
|
95
|
+
},
|
|
96
|
+
}),
|
|
97
|
+
expireIn: (queue: Pick<QueueDefinition, "name">, duration: Duration.Input) =>
|
|
98
|
+
Effect.try({
|
|
99
|
+
catch: (original) => new PgBossTestingError({ operation: "expireIn", original: Redacted.make(original), queue: queue.name }),
|
|
100
|
+
try: () => {
|
|
101
|
+
const milliseconds = Duration.toMillis(Duration.fromInputUnsafe(duration));
|
|
102
|
+
if (!Number.isFinite(milliseconds) || milliseconds < 1000) {
|
|
103
|
+
throw new RangeError("Expiry must be finite and at least one second");
|
|
104
|
+
}
|
|
105
|
+
controls.expirySeconds.set(queue.name, Math.ceil(milliseconds / 1000));
|
|
106
|
+
},
|
|
107
|
+
}),
|
|
108
|
+
failNextSend: (queue: Pick<QueueDefinition, "name">) => Effect.sync(() => controls.failures.add(queue.name)).pipe(Effect.asVoid),
|
|
109
|
+
jobs: testingReadout(client),
|
|
110
|
+
revision: signal.revision,
|
|
111
|
+
schemaVersion: testingOperation("schemaVersion", () => client().schemaVersion()),
|
|
112
|
+
snapshot: testingSnapshot(client, () => schema),
|
|
113
|
+
waitForChange: signal.waitForChange,
|
|
114
|
+
waitForIdle: testingIdle(client, () => schema, work, signal),
|
|
115
|
+
};
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
export type PgBossTesting = ReturnType<typeof makePgBossTesting>;
|