@convex-dev/workpool 0.2.14 → 0.2.15
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/README.md +22 -13
- package/dist/commonjs/client/index.d.ts +191 -56
- package/dist/commonjs/client/index.d.ts.map +1 -1
- package/dist/commonjs/client/index.js +55 -6
- package/dist/commonjs/client/index.js.map +1 -1
- package/dist/commonjs/component/complete.js +2 -2
- package/dist/commonjs/component/complete.js.map +1 -1
- package/dist/commonjs/component/schema.js +2 -2
- package/dist/commonjs/component/schema.js.map +1 -1
- package/dist/commonjs/component/shared.d.ts +2 -2
- package/dist/commonjs/component/shared.d.ts.map +1 -1
- package/dist/commonjs/component/shared.js +1 -1
- package/dist/commonjs/component/shared.js.map +1 -1
- package/dist/esm/client/index.d.ts +191 -56
- package/dist/esm/client/index.d.ts.map +1 -1
- package/dist/esm/client/index.js +55 -6
- package/dist/esm/client/index.js.map +1 -1
- package/dist/esm/component/complete.js +2 -2
- package/dist/esm/component/complete.js.map +1 -1
- package/dist/esm/component/schema.js +2 -2
- package/dist/esm/component/schema.js.map +1 -1
- package/dist/esm/component/shared.d.ts +2 -2
- package/dist/esm/component/shared.d.ts.map +1 -1
- package/dist/esm/component/shared.js +1 -1
- package/dist/esm/component/shared.js.map +1 -1
- package/package.json +1 -1
- package/src/client/index.ts +145 -59
- package/src/component/complete.ts +2 -2
- package/src/component/schema.ts +2 -2
- package/src/component/shared.ts +2 -2
package/src/client/index.ts
CHANGED
|
@@ -5,29 +5,45 @@ import {
|
|
|
5
5
|
FunctionReference,
|
|
6
6
|
FunctionType,
|
|
7
7
|
FunctionVisibility,
|
|
8
|
+
GenericDataModel,
|
|
9
|
+
GenericMutationCtx,
|
|
8
10
|
getFunctionName,
|
|
11
|
+
internalMutationGeneric,
|
|
12
|
+
RegisteredMutation,
|
|
9
13
|
} from "convex/server";
|
|
10
|
-
import { v, VString } from "convex/values";
|
|
14
|
+
import { Infer, v, Validator, VAny, VString } from "convex/values";
|
|
11
15
|
import { Mounts } from "../component/_generated/api.js";
|
|
12
16
|
import { DEFAULT_LOG_LEVEL, type LogLevel } from "../component/logging.js";
|
|
13
17
|
import {
|
|
14
18
|
Config,
|
|
15
19
|
DEFAULT_MAX_PARALLELISM,
|
|
16
20
|
OnComplete,
|
|
17
|
-
|
|
21
|
+
vResultValidator,
|
|
18
22
|
type RetryBehavior,
|
|
19
23
|
RunResult,
|
|
20
24
|
OnCompleteArgs as SharedOnCompleteArgs,
|
|
21
25
|
Status,
|
|
22
26
|
} from "../component/shared.js";
|
|
23
27
|
import { RunMutationCtx, RunQueryCtx, UseApi } from "./utils.js";
|
|
24
|
-
export {
|
|
28
|
+
export {
|
|
29
|
+
vResultValidator,
|
|
30
|
+
type RunResult,
|
|
31
|
+
type RetryBehavior,
|
|
32
|
+
type OnComplete,
|
|
33
|
+
};
|
|
25
34
|
export {
|
|
26
35
|
retryBehavior as vRetryBehavior,
|
|
27
36
|
onComplete as vOnComplete,
|
|
28
37
|
} from "../component/shared.js";
|
|
29
38
|
export { logLevel as vLogLevel, type LogLevel } from "../component/logging.js";
|
|
30
|
-
export {
|
|
39
|
+
export type WorkId = string & { __isWorkId: true };
|
|
40
|
+
export const vWorkIdValidator = v.string() as VString<WorkId>;
|
|
41
|
+
export {
|
|
42
|
+
/** @deprecated Use `vWorkIdValidator` instead. */
|
|
43
|
+
vWorkIdValidator as workIdValidator,
|
|
44
|
+
/** @deprecated Use `vResultValidator` instead. */
|
|
45
|
+
vResultValidator as resultValidator,
|
|
46
|
+
};
|
|
31
47
|
|
|
32
48
|
// Attempts will run with delay [0, 250, 500, 1000, 2000] (ms)
|
|
33
49
|
export const DEFAULT_RETRY_BEHAVIOR: RetryBehavior = {
|
|
@@ -35,56 +51,6 @@ export const DEFAULT_RETRY_BEHAVIOR: RetryBehavior = {
|
|
|
35
51
|
initialBackoffMs: 250,
|
|
36
52
|
base: 2,
|
|
37
53
|
};
|
|
38
|
-
export type WorkId = string & { __isWorkId: true };
|
|
39
|
-
export const workIdValidator = v.string() as VString<WorkId>;
|
|
40
|
-
export { workIdValidator as vWorkIdValidator };
|
|
41
|
-
|
|
42
|
-
export type NameOption = {
|
|
43
|
-
/**
|
|
44
|
-
* The name of the function. By default, if you pass in api.foo.bar.baz,
|
|
45
|
-
* it will use "foo/bar:baz" as the name. If you pass in a function handle,
|
|
46
|
-
* it will use the function handle directly.
|
|
47
|
-
*/
|
|
48
|
-
name?: string;
|
|
49
|
-
};
|
|
50
|
-
|
|
51
|
-
export type RetryOption = {
|
|
52
|
-
/** Whether to retry the action if it fails.
|
|
53
|
-
* If true, it will use the default retry behavior.
|
|
54
|
-
* If custom behavior is provided, it will retry using that behavior.
|
|
55
|
-
* If unset, it will use the Workpool's configured default.
|
|
56
|
-
*/
|
|
57
|
-
retry?: boolean | RetryBehavior;
|
|
58
|
-
};
|
|
59
|
-
|
|
60
|
-
export type WorkpoolOptions = {
|
|
61
|
-
/** How many actions/mutations can be running at once within this pool.
|
|
62
|
-
* Min 1, Max 300.
|
|
63
|
-
*/
|
|
64
|
-
maxParallelism?: number;
|
|
65
|
-
/** How much to log. This is updated on each call to `enqueue*`,
|
|
66
|
-
* `status`, or `cancel*`.
|
|
67
|
-
* Default is REPORT, which logs warnings, errors, and a periodic report.
|
|
68
|
-
* With INFO, you can also see events for started and completed work.
|
|
69
|
-
* Stats generated can be parsed by tools like
|
|
70
|
-
* [Axiom](https://axiom.co) for monitoring.
|
|
71
|
-
* With DEBUG, you can see timers and internal events for work being
|
|
72
|
-
* scheduled.
|
|
73
|
-
*/
|
|
74
|
-
logLevel?: LogLevel;
|
|
75
|
-
} & WorkpoolRetryOptions;
|
|
76
|
-
|
|
77
|
-
export type WorkpoolRetryOptions = {
|
|
78
|
-
/** Default retry behavior for enqueued actions.
|
|
79
|
-
* See {@link RetryBehavior}.
|
|
80
|
-
*/
|
|
81
|
-
defaultRetryBehavior?: RetryBehavior;
|
|
82
|
-
/** Whether to retry actions that fail by default. Default: false.
|
|
83
|
-
* NOTE: Only enable this if your actions are idempotent.
|
|
84
|
-
* See the docs (README.md) for more details.
|
|
85
|
-
*/
|
|
86
|
-
retryActionsByDefault?: boolean;
|
|
87
|
-
};
|
|
88
54
|
|
|
89
55
|
export class Workpool {
|
|
90
56
|
/**
|
|
@@ -233,8 +199,122 @@ export class Workpool {
|
|
|
233
199
|
async status(ctx: RunQueryCtx, id: WorkId): Promise<Status> {
|
|
234
200
|
return ctx.runQuery(this.component.lib.status, { id });
|
|
235
201
|
}
|
|
202
|
+
|
|
203
|
+
/**
|
|
204
|
+
* Defines a mutation that will be run after a work item completes.
|
|
205
|
+
* You can pass this to a call to enqueue* like so:
|
|
206
|
+
* ```ts
|
|
207
|
+
* export const myOnComplete = workpool.defineOnComplete({
|
|
208
|
+
* context: v.literal("myContextValue"), // optional
|
|
209
|
+
* handler: async (ctx, {workId, context, result}) => {
|
|
210
|
+
* // ... do something with the result
|
|
211
|
+
* },
|
|
212
|
+
* });
|
|
213
|
+
*
|
|
214
|
+
* // in some other function:
|
|
215
|
+
* const workId = await workpool.enqueueAction(ctx, internal.foo.bar, {
|
|
216
|
+
* // ... args to action
|
|
217
|
+
* }, {
|
|
218
|
+
* onComplete: internal.foo.myOnComplete,
|
|
219
|
+
* });
|
|
220
|
+
* ```
|
|
221
|
+
*/
|
|
222
|
+
defineOnComplete<
|
|
223
|
+
DataModel extends GenericDataModel,
|
|
224
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
225
|
+
V extends Validator<any, "required", any> = VAny,
|
|
226
|
+
>({
|
|
227
|
+
context,
|
|
228
|
+
handler,
|
|
229
|
+
}: {
|
|
230
|
+
context?: V;
|
|
231
|
+
handler: (
|
|
232
|
+
ctx: GenericMutationCtx<DataModel>,
|
|
233
|
+
args: {
|
|
234
|
+
workId: WorkId;
|
|
235
|
+
context: Infer<V>;
|
|
236
|
+
result: RunResult;
|
|
237
|
+
}
|
|
238
|
+
) => Promise<void>;
|
|
239
|
+
}): RegisteredMutation<"internal", OnCompleteArgs, null> {
|
|
240
|
+
return internalMutationGeneric({
|
|
241
|
+
args: vOnCompleteValidator(context),
|
|
242
|
+
handler,
|
|
243
|
+
});
|
|
244
|
+
}
|
|
245
|
+
}
|
|
246
|
+
|
|
247
|
+
/**
|
|
248
|
+
* Returns a validator to use for the onComplete mutation.
|
|
249
|
+
* To be used like:
|
|
250
|
+
* ```ts
|
|
251
|
+
* export const myOnComplete = internalMutation({
|
|
252
|
+
* args: vOnCompleteValidator(v.string()),
|
|
253
|
+
* handler: async (ctx, {workId, context, result}) => {
|
|
254
|
+
* // context has been validated as a string
|
|
255
|
+
* // ... do something with the result
|
|
256
|
+
* },
|
|
257
|
+
* });
|
|
258
|
+
* @param context - The context validator. If not provided, it will be `v.any()`.
|
|
259
|
+
* @returns The validator for the onComplete mutation.
|
|
260
|
+
*/
|
|
261
|
+
export function vOnCompleteValidator<
|
|
262
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
263
|
+
V extends Validator<any, "required", any> = VAny,
|
|
264
|
+
>(context?: V) {
|
|
265
|
+
return v.object({
|
|
266
|
+
workId: vWorkIdValidator,
|
|
267
|
+
context: context ?? v.any(),
|
|
268
|
+
result: vResultValidator,
|
|
269
|
+
});
|
|
236
270
|
}
|
|
237
271
|
|
|
272
|
+
export type NameOption = {
|
|
273
|
+
/**
|
|
274
|
+
* The name of the function. By default, if you pass in api.foo.bar.baz,
|
|
275
|
+
* it will use "foo/bar:baz" as the name. If you pass in a function handle,
|
|
276
|
+
* it will use the function handle directly.
|
|
277
|
+
*/
|
|
278
|
+
name?: string;
|
|
279
|
+
};
|
|
280
|
+
|
|
281
|
+
export type RetryOption = {
|
|
282
|
+
/** Whether to retry the action if it fails.
|
|
283
|
+
* If true, it will use the default retry behavior.
|
|
284
|
+
* If custom behavior is provided, it will retry using that behavior.
|
|
285
|
+
* If unset, it will use the Workpool's configured default.
|
|
286
|
+
*/
|
|
287
|
+
retry?: boolean | RetryBehavior;
|
|
288
|
+
};
|
|
289
|
+
|
|
290
|
+
export type WorkpoolOptions = {
|
|
291
|
+
/** How many actions/mutations can be running at once within this pool.
|
|
292
|
+
* Min 1, Max 300.
|
|
293
|
+
*/
|
|
294
|
+
maxParallelism?: number;
|
|
295
|
+
/** How much to log. This is updated on each call to `enqueue*`,
|
|
296
|
+
* `status`, or `cancel*`.
|
|
297
|
+
* Default is REPORT, which logs warnings, errors, and a periodic report.
|
|
298
|
+
* With INFO, you can also see events for started and completed work.
|
|
299
|
+
* Stats generated can be parsed by tools like
|
|
300
|
+
* [Axiom](https://axiom.co) for monitoring.
|
|
301
|
+
* With DEBUG, you can see timers and internal events for work being
|
|
302
|
+
* scheduled.
|
|
303
|
+
*/
|
|
304
|
+
logLevel?: LogLevel;
|
|
305
|
+
} & WorkpoolRetryOptions;
|
|
306
|
+
|
|
307
|
+
export type WorkpoolRetryOptions = {
|
|
308
|
+
/** Default retry behavior for enqueued actions.
|
|
309
|
+
* See {@link RetryBehavior}.
|
|
310
|
+
*/
|
|
311
|
+
defaultRetryBehavior?: RetryBehavior;
|
|
312
|
+
/** Whether to retry actions that fail by default. Default: false.
|
|
313
|
+
* NOTE: Only enable this if your actions are idempotent.
|
|
314
|
+
* See the docs (README.md) for more details.
|
|
315
|
+
*/
|
|
316
|
+
retryActionsByDefault?: boolean;
|
|
317
|
+
};
|
|
238
318
|
export type SchedulerOptions =
|
|
239
319
|
| {
|
|
240
320
|
/**
|
|
@@ -259,12 +339,18 @@ export type CallbackOptions = {
|
|
|
259
339
|
* The context type is for your use, feel free to provide a validator for it.
|
|
260
340
|
* e.g.
|
|
261
341
|
* ```ts
|
|
342
|
+
* export const completion = workpool.defineOnComplete({
|
|
343
|
+
* context: v.string(),
|
|
344
|
+
* handler: async (ctx, {workId, context, result}) => {
|
|
345
|
+
* // context has been validated as a string
|
|
346
|
+
* // ... do something with the result
|
|
347
|
+
* },
|
|
348
|
+
* });
|
|
349
|
+
* ```
|
|
350
|
+
* or more manually:
|
|
351
|
+
* ```ts
|
|
262
352
|
* export const completion = internalMutation({
|
|
263
|
-
* args:
|
|
264
|
-
* workId: workIdValidator,
|
|
265
|
-
* context: v.any(),
|
|
266
|
-
* result: resultValidator,
|
|
267
|
-
* },
|
|
353
|
+
* args: vOnCompleteValidator(v.string()),
|
|
268
354
|
* handler: async (ctx, args) => {
|
|
269
355
|
* console.log(args.result, "Got Context back -> ", args.context, Date.now() - args.context);
|
|
270
356
|
* },
|
|
@@ -4,7 +4,7 @@ import { Id } from "./_generated/dataModel.js";
|
|
|
4
4
|
import { internalMutation, MutationCtx } from "./_generated/server.js";
|
|
5
5
|
import { kickMainLoop } from "./kick.js";
|
|
6
6
|
import { createLogger } from "./logging.js";
|
|
7
|
-
import { OnCompleteArgs, RunResult,
|
|
7
|
+
import { OnCompleteArgs, RunResult, vResultValidator } from "./shared.js";
|
|
8
8
|
import { recordCompleted } from "./stats.js";
|
|
9
9
|
|
|
10
10
|
export type CompleteJob = Infer<typeof completeArgs.fields.jobs.element>;
|
|
@@ -12,7 +12,7 @@ export type CompleteJob = Infer<typeof completeArgs.fields.jobs.element>;
|
|
|
12
12
|
export const completeArgs = v.object({
|
|
13
13
|
jobs: v.array(
|
|
14
14
|
v.object({
|
|
15
|
-
runResult:
|
|
15
|
+
runResult: vResultValidator,
|
|
16
16
|
workId: v.id("work"),
|
|
17
17
|
attempt: v.number(),
|
|
18
18
|
})
|
package/src/component/schema.ts
CHANGED
|
@@ -5,7 +5,7 @@ import {
|
|
|
5
5
|
config,
|
|
6
6
|
onComplete,
|
|
7
7
|
retryBehavior,
|
|
8
|
-
|
|
8
|
+
vResultValidator,
|
|
9
9
|
} from "./shared.js";
|
|
10
10
|
|
|
11
11
|
// Represents a slice of time to process work.
|
|
@@ -80,7 +80,7 @@ export default defineSchema({
|
|
|
80
80
|
// Written by complete, read & deleted by `main`.
|
|
81
81
|
pendingCompletion: defineTable({
|
|
82
82
|
segment,
|
|
83
|
-
runResult,
|
|
83
|
+
runResult: vResultValidator,
|
|
84
84
|
workId: v.id("work"),
|
|
85
85
|
retry: v.boolean(),
|
|
86
86
|
})
|
package/src/component/shared.ts
CHANGED
|
@@ -64,7 +64,7 @@ export type RetryBehavior = {
|
|
|
64
64
|
// This ensures that the type satisfies the schema.
|
|
65
65
|
const _ = {} as RetryBehavior satisfies Infer<typeof retryBehavior>;
|
|
66
66
|
|
|
67
|
-
export const
|
|
67
|
+
export const vResultValidator = v.union(
|
|
68
68
|
v.object({
|
|
69
69
|
kind: v.literal("success"),
|
|
70
70
|
returnValue: v.any(),
|
|
@@ -77,7 +77,7 @@ export const runResult = v.union(
|
|
|
77
77
|
kind: v.literal("canceled"),
|
|
78
78
|
})
|
|
79
79
|
);
|
|
80
|
-
export type RunResult = Infer<typeof
|
|
80
|
+
export type RunResult = Infer<typeof vResultValidator>;
|
|
81
81
|
|
|
82
82
|
export const onComplete = v.object({
|
|
83
83
|
fnHandle: v.string(), // mutation
|