@convex-dev/workpool 0.2.0-beta.0 → 0.2.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/README.md +87 -18
- package/dist/commonjs/client/index.d.ts +33 -8
- package/dist/commonjs/client/index.d.ts.map +1 -1
- package/dist/commonjs/client/index.js +37 -7
- package/dist/commonjs/client/index.js.map +1 -1
- package/dist/commonjs/component/complete.d.ts +89 -0
- package/dist/commonjs/component/complete.d.ts.map +1 -0
- package/dist/commonjs/component/complete.js +82 -0
- package/dist/commonjs/component/complete.js.map +1 -0
- package/dist/commonjs/component/kick.d.ts +3 -3
- package/dist/commonjs/component/kick.d.ts.map +1 -1
- package/dist/commonjs/component/kick.js +17 -12
- package/dist/commonjs/component/kick.js.map +1 -1
- package/dist/commonjs/component/lib.d.ts +6 -6
- package/dist/commonjs/component/lib.d.ts.map +1 -1
- package/dist/commonjs/component/lib.js +53 -24
- package/dist/commonjs/component/lib.js.map +1 -1
- package/dist/commonjs/component/logging.d.ts +3 -2
- package/dist/commonjs/component/logging.d.ts.map +1 -1
- package/dist/commonjs/component/logging.js +34 -16
- package/dist/commonjs/component/logging.js.map +1 -1
- package/dist/commonjs/component/loop.d.ts +1 -14
- package/dist/commonjs/component/loop.d.ts.map +1 -1
- package/dist/commonjs/component/loop.js +216 -179
- package/dist/commonjs/component/loop.js.map +1 -1
- package/dist/commonjs/component/recovery.d.ts +45 -0
- package/dist/commonjs/component/recovery.d.ts.map +1 -1
- package/dist/commonjs/component/recovery.js +88 -65
- package/dist/commonjs/component/recovery.js.map +1 -1
- package/dist/commonjs/component/schema.d.ts +17 -13
- package/dist/commonjs/component/schema.d.ts.map +1 -1
- package/dist/commonjs/component/schema.js +5 -3
- package/dist/commonjs/component/schema.js.map +1 -1
- package/dist/commonjs/component/shared.d.ts +24 -15
- package/dist/commonjs/component/shared.d.ts.map +1 -1
- package/dist/commonjs/component/shared.js +20 -7
- package/dist/commonjs/component/shared.js.map +1 -1
- package/dist/commonjs/component/stats.d.ts +36 -29
- package/dist/commonjs/component/stats.d.ts.map +1 -1
- package/dist/commonjs/component/stats.js +110 -52
- package/dist/commonjs/component/stats.js.map +1 -1
- package/dist/commonjs/component/worker.d.ts +4 -14
- package/dist/commonjs/component/worker.d.ts.map +1 -1
- package/dist/commonjs/component/worker.js +23 -36
- package/dist/commonjs/component/worker.js.map +1 -1
- package/dist/esm/client/index.d.ts +33 -8
- package/dist/esm/client/index.d.ts.map +1 -1
- package/dist/esm/client/index.js +37 -7
- package/dist/esm/client/index.js.map +1 -1
- package/dist/esm/component/complete.d.ts +89 -0
- package/dist/esm/component/complete.d.ts.map +1 -0
- package/dist/esm/component/complete.js +82 -0
- package/dist/esm/component/complete.js.map +1 -0
- package/dist/esm/component/kick.d.ts +3 -3
- package/dist/esm/component/kick.d.ts.map +1 -1
- package/dist/esm/component/kick.js +17 -12
- package/dist/esm/component/kick.js.map +1 -1
- package/dist/esm/component/lib.d.ts +6 -6
- package/dist/esm/component/lib.d.ts.map +1 -1
- package/dist/esm/component/lib.js +53 -24
- package/dist/esm/component/lib.js.map +1 -1
- package/dist/esm/component/logging.d.ts +3 -2
- package/dist/esm/component/logging.d.ts.map +1 -1
- package/dist/esm/component/logging.js +34 -16
- package/dist/esm/component/logging.js.map +1 -1
- package/dist/esm/component/loop.d.ts +1 -14
- package/dist/esm/component/loop.d.ts.map +1 -1
- package/dist/esm/component/loop.js +216 -179
- package/dist/esm/component/loop.js.map +1 -1
- package/dist/esm/component/recovery.d.ts +45 -0
- package/dist/esm/component/recovery.d.ts.map +1 -1
- package/dist/esm/component/recovery.js +88 -65
- package/dist/esm/component/recovery.js.map +1 -1
- package/dist/esm/component/schema.d.ts +17 -13
- package/dist/esm/component/schema.d.ts.map +1 -1
- package/dist/esm/component/schema.js +5 -3
- package/dist/esm/component/schema.js.map +1 -1
- package/dist/esm/component/shared.d.ts +24 -15
- package/dist/esm/component/shared.d.ts.map +1 -1
- package/dist/esm/component/shared.js +20 -7
- package/dist/esm/component/shared.js.map +1 -1
- package/dist/esm/component/stats.d.ts +36 -29
- package/dist/esm/component/stats.d.ts.map +1 -1
- package/dist/esm/component/stats.js +110 -52
- package/dist/esm/component/stats.js.map +1 -1
- package/dist/esm/component/worker.d.ts +4 -14
- package/dist/esm/component/worker.d.ts.map +1 -1
- package/dist/esm/component/worker.js +23 -36
- package/dist/esm/component/worker.js.map +1 -1
- package/package.json +12 -12
- package/src/client/index.ts +82 -43
- package/src/component/README.md +15 -15
- package/src/component/_generated/api.d.ts +10 -5
- package/src/component/complete.test.ts +508 -0
- package/src/component/complete.ts +109 -0
- package/src/component/kick.test.ts +29 -19
- package/src/component/kick.ts +25 -17
- package/src/component/lib.test.ts +262 -17
- package/src/component/lib.ts +68 -30
- package/src/component/logging.test.ts +16 -0
- package/src/component/logging.ts +45 -24
- package/src/component/loop.test.ts +1158 -0
- package/src/component/loop.ts +292 -224
- package/src/component/recovery.test.ts +536 -0
- package/src/component/recovery.ts +100 -75
- package/src/component/schema.ts +6 -4
- package/src/component/shared.ts +23 -8
- package/src/component/stats.test.ts +345 -0
- package/src/component/stats.ts +149 -56
- package/src/component/worker.ts +25 -38
package/README.md
CHANGED
|
@@ -95,8 +95,8 @@ await pool.enqueueAction(ctx, internal.email.send, args, {
|
|
|
95
95
|
export const emailSent = internalMutation({
|
|
96
96
|
args: {
|
|
97
97
|
workId: workIdValidator,
|
|
98
|
+
result: resultValidator,
|
|
98
99
|
context: v.object({ emailType: v.string(), userId: v.id("users") }),
|
|
99
|
-
result: runResultValidator,
|
|
100
100
|
},
|
|
101
101
|
handler: async (ctx, args) => {
|
|
102
102
|
if (args.result.kind === "canceled") return;
|
|
@@ -263,8 +263,8 @@ The retry options work like this:
|
|
|
263
263
|
- If it fails, it will wait _around_ `initialBackoffMs` and then try again.
|
|
264
264
|
- Each subsequent retry waits `initialBackoffMs * base^<retryNumber - 1>`.
|
|
265
265
|
- The standard base is 2.
|
|
266
|
-
- The actual wait time uses "jitter" to avoid all retries happening at once
|
|
267
|
-
if they all
|
|
266
|
+
- The actual wait time uses "jitter" to avoid all retries happening at once
|
|
267
|
+
if they all fail at the same time.
|
|
268
268
|
|
|
269
269
|
You can override the retry behavior per-call with the `retry` option.
|
|
270
270
|
|
|
@@ -303,28 +303,97 @@ You can read the status of a function by calling `pool.status(id)`.
|
|
|
303
303
|
|
|
304
304
|
The status will be one of:
|
|
305
305
|
|
|
306
|
-
- `{ kind: "pending" }`: The function has not started yet.
|
|
307
|
-
- `{ kind: "
|
|
308
|
-
- `{ kind: "
|
|
309
|
-
finished.
|
|
306
|
+
- `{ kind: "pending"; previousAttempts: number }`: The function has not started yet.
|
|
307
|
+
- `{ kind: "running"; previousAttempts: number }`: The function is currently running.
|
|
308
|
+
- `{ kind: "finished" }`: The function has succeeded, failed, or been canceled.
|
|
310
309
|
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
- `"error"`: The function threw an error.
|
|
315
|
-
- `"canceled"`: The function was canceled.
|
|
316
|
-
- `"timeout"`: The function timed out.
|
|
310
|
+
To get the result of your function, you can either write to the database from
|
|
311
|
+
within your function, call or schedule another function from there, or use the
|
|
312
|
+
`onComplete` handler to respond to the job result.
|
|
317
313
|
|
|
318
314
|
## Canceling work
|
|
319
315
|
|
|
320
|
-
You can cancel work by calling `pool.cancel(id)
|
|
316
|
+
You can cancel work by calling `pool.cancel(id)` or all of them with
|
|
317
|
+
`pool.cancelAll()`.
|
|
321
318
|
|
|
322
|
-
This will
|
|
323
|
-
If the work has already started, it will wait for it to finish, but not allow
|
|
324
|
-
retries.
|
|
319
|
+
This will avoid starting or retrying, but will not stop in-progress work.
|
|
325
320
|
|
|
326
|
-
|
|
321
|
+
## Monitoring the workpool
|
|
322
|
+
|
|
323
|
+
If you want to know the status of your workpool, here are some queries to use
|
|
324
|
+
for [Axiom](https://axiom.co/docs/send-data/convex).
|
|
325
|
+
Just replace `your-dataset` with your dataset's name (which is also
|
|
326
|
+
what you enter in the log streaming configuration in the Convex dashboard).
|
|
327
|
+
|
|
328
|
+
Note: these are optimized for monitors. For dashboards, you might want to change
|
|
329
|
+
`bin(_time, X)` to `bin_auto(_time)`.
|
|
330
|
+
|
|
331
|
+
### Is it backlogged
|
|
327
332
|
|
|
333
|
+
Reports the current backlog length, where "backlog" is tasks that are past due,
|
|
334
|
+
not including tasks that have been scheduled for the future. This reports the
|
|
335
|
+
max for 1 minute intervals (which is roughly how often the report is generated).
|
|
336
|
+
|
|
337
|
+
```txt
|
|
338
|
+
['your-dataset']
|
|
339
|
+
| extend parsed_message = iff(isnotnull(parse_json(trim("'", tostring(["data.message"])))),
|
|
340
|
+
parse_json(trim("'", tostring(["data.message"]))),
|
|
341
|
+
parse_json('{}') )
|
|
342
|
+
| where parsed_message["component"] == "workpool" and parsed_message["event"] == "report"
|
|
343
|
+
| summarize max_backlog = max(toint(parsed_message["backlog"]))
|
|
344
|
+
by bin(_time, 1m), workpool = tostring(["data.function.component_path"])
|
|
328
345
|
```
|
|
329
346
|
|
|
347
|
+
### Are functions failing (after retries)
|
|
348
|
+
|
|
349
|
+
Reports the overall average failure rate per registered workpool in 5 minute intervals.
|
|
350
|
+
|
|
351
|
+
```txt
|
|
352
|
+
['your-dataset']
|
|
353
|
+
| extend parsed_message = iff(isnotnull(parse_json(trim("'", tostring(["data.message"])))),
|
|
354
|
+
parse_json(trim("'", tostring(["data.message"]))),
|
|
355
|
+
parse_json('{}') )
|
|
356
|
+
| where parsed_message["component"] == "workpool" and parsed_message["event"] == "report"
|
|
357
|
+
| extend permanentFailureRate = parsed_message["permanentFailureRate"]
|
|
358
|
+
| summarize avg(todouble(permanentFailureRate))
|
|
359
|
+
by bin(\_time, 5m), workpool = tostring(["data.function.component_path"])
|
|
330
360
|
```
|
|
361
|
+
|
|
362
|
+
### Are functions retrying a lot
|
|
363
|
+
|
|
364
|
+
Reports the ratio (0 to 1) of failures per function, in 5 minute intervals.
|
|
365
|
+
Note: to get this data, set the workpool `logLevel` to `"INFO"` (or `"DEBUG"`).
|
|
366
|
+
|
|
367
|
+
```txt
|
|
368
|
+
['your-dataset']
|
|
369
|
+
| extend parsed_message = iff( isnotnull(parse_json(trim("'", tostring(["data.message"])))),
|
|
370
|
+
parse_json(trim("'", tostring(["data.message"]))),
|
|
371
|
+
parse_json('{}') )
|
|
372
|
+
| where parsed_message["component"] == "workpool" and (parsed_message["event"] == "completed") and parsed_message["status"] != "canceled"
|
|
373
|
+
| summarize failure_ratio = avg(iff(parsed_message["status"] != "success", 1, 0))
|
|
374
|
+
by bin(_time, 5m), function = tostring(parsed_message["fnName"])
|
|
375
|
+
```
|
|
376
|
+
|
|
377
|
+
### Is there a big delay between being enqueued and starting
|
|
378
|
+
|
|
379
|
+
Reports the average time between enqueueing work and it actually starting.
|
|
380
|
+
Note: to get this data, set the workpool `logLevel` to `"INFO"` (or `"DEBUG"`).
|
|
381
|
+
|
|
382
|
+
```txt
|
|
383
|
+
['your-dataset']
|
|
384
|
+
| extend parsed_message = iff(isnotnull(parse_json(trim("'", tostring(["data.message"])))),
|
|
385
|
+
parse_json(trim("'", tostring(["data.message"]))),
|
|
386
|
+
parse_json('{}') )
|
|
387
|
+
| where parsed_message["component"] == "workpool" and parsed_message["event"] == "started"
|
|
388
|
+
| summarize start_lag_seconds = avg(todouble(parsed_message["startLag"])/1000)
|
|
389
|
+
by bin(_time, 1m), function = tostring(parsed_message["fnName"])
|
|
390
|
+
```
|
|
391
|
+
|
|
392
|
+
While similar to the backlog size, this is a more concrete value, since the
|
|
393
|
+
events in the backlog may take variable amounts of time. This is a more user-
|
|
394
|
+
visible metric, though it is a "lagging" indicator - this will be high when the
|
|
395
|
+
backlog was large enough to delay the processing of an entry. So alerting on
|
|
396
|
+
the backlog size will give you a faster indicator, while this is a metric of the
|
|
397
|
+
severity of the incident.
|
|
398
|
+
|
|
399
|
+
<!-- END: Include on https://convex.dev/components -->
|
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
import { DefaultFunctionArgs, FunctionReference, FunctionVisibility } from "convex/server";
|
|
2
2
|
import { VString } from "convex/values";
|
|
3
3
|
import { Mounts } from "../component/_generated/api.js";
|
|
4
|
-
import { runResult as runResultValidator, RunResult, type RetryBehavior, Status } from "../component/shared.js";
|
|
5
4
|
import { type LogLevel } from "../component/logging.js";
|
|
5
|
+
import { runResult as resultValidator, type RetryBehavior, RunResult, Status } from "../component/shared.js";
|
|
6
6
|
import { RunMutationCtx, RunQueryCtx, UseApi } from "./utils.js";
|
|
7
|
-
export {
|
|
7
|
+
export { resultValidator, type RunResult };
|
|
8
8
|
export declare const DEFAULT_RETRY_BEHAVIOR: RetryBehavior;
|
|
9
9
|
export type WorkId = string & {
|
|
10
10
|
__isWorkId: true;
|
|
@@ -32,17 +32,20 @@ export declare class Workpool {
|
|
|
32
32
|
maxParallelism?: number;
|
|
33
33
|
/** How much to log. This is updated on each call to `enqueue*`,
|
|
34
34
|
* `status`, or `cancel*`.
|
|
35
|
-
* Default is
|
|
36
|
-
* With INFO, you can see events for started and completed work
|
|
37
|
-
* be parsed by tools like
|
|
35
|
+
* Default is REPORT, which logs warnings, errors, and a periodic report.
|
|
36
|
+
* With INFO, you can also see events for started and completed work.
|
|
37
|
+
* Stats generated can be parsed by tools like
|
|
38
|
+
* [Axiom](https://axiom.co) for monitoring.
|
|
38
39
|
* With DEBUG, you can see timers and internal events for work being
|
|
39
40
|
* scheduled.
|
|
40
41
|
*/
|
|
41
42
|
logLevel?: LogLevel;
|
|
42
|
-
/** Default retry behavior for enqueued actions.
|
|
43
|
+
/** Default retry behavior for enqueued actions.
|
|
44
|
+
* See {@link RetryBehavior}.
|
|
45
|
+
*/
|
|
43
46
|
defaultRetryBehavior?: RetryBehavior;
|
|
44
47
|
/** Whether to retry actions that fail by default. Default: false.
|
|
45
|
-
* NOTE: Only
|
|
48
|
+
* NOTE: Only enable this if your actions are idempotent.
|
|
46
49
|
* See the docs (README.md) for more details.
|
|
47
50
|
*/
|
|
48
51
|
retryActionsByDefault?: boolean;
|
|
@@ -79,8 +82,30 @@ export declare class Workpool {
|
|
|
79
82
|
* and scheduling via `runAt` or `runAfter`.
|
|
80
83
|
*/
|
|
81
84
|
enqueueMutation<Args extends DefaultFunctionArgs, ReturnType>(ctx: RunMutationCtx, fn: FunctionReference<"mutation", FunctionVisibility, Args, ReturnType>, fnArgs: Args, options?: CallbackOptions & SchedulerOptions): Promise<WorkId>;
|
|
85
|
+
/**
|
|
86
|
+
* Cancels a work item. If it's already started, it will be allowed to finish
|
|
87
|
+
* but will not be retried.
|
|
88
|
+
*
|
|
89
|
+
* @param ctx - The mutation or action context that can call ctx.runMutation.
|
|
90
|
+
* @param id - The ID of the work to cancel.
|
|
91
|
+
*/
|
|
82
92
|
cancel(ctx: RunMutationCtx, id: WorkId): Promise<void>;
|
|
93
|
+
/**
|
|
94
|
+
* Cancels all pending work items. See {@link cancel}.
|
|
95
|
+
*
|
|
96
|
+
* @param ctx - The mutation or action context that can call ctx.runMutation.
|
|
97
|
+
*/
|
|
83
98
|
cancelAll(ctx: RunMutationCtx): Promise<void>;
|
|
99
|
+
/**
|
|
100
|
+
* Gets the status of a work item.
|
|
101
|
+
*
|
|
102
|
+
* @param ctx - The query context that can call ctx.runQuery.
|
|
103
|
+
* @param id - The ID of the work to get the status of.
|
|
104
|
+
* @returns The status of the work item. One of:
|
|
105
|
+
* - `{ state: "pending", previousAttempts: number }`
|
|
106
|
+
* - `{ state: "running", previousAttempts: number }`
|
|
107
|
+
* - `{ state: "finished" }`
|
|
108
|
+
*/
|
|
84
109
|
status(ctx: RunQueryCtx, id: WorkId): Promise<Status>;
|
|
85
110
|
}
|
|
86
111
|
export type SchedulerOptions = {
|
|
@@ -108,7 +133,7 @@ export type CallbackOptions = {
|
|
|
108
133
|
* args: {
|
|
109
134
|
* workId: workIdValidator,
|
|
110
135
|
* context: v.any(),
|
|
111
|
-
* result:
|
|
136
|
+
* result: resultValidator,
|
|
112
137
|
* },
|
|
113
138
|
* handler: async (ctx, args) => {
|
|
114
139
|
* console.log(args.result, "Got Context back -> ", args.context, Date.now() - args.context);
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/client/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAEL,mBAAmB,EACnB,iBAAiB,EACjB,kBAAkB,EAEnB,MAAM,eAAe,CAAC;AACvB,OAAO,EAAK,OAAO,EAAE,MAAM,eAAe,CAAC;AAC3C,OAAO,EAAE,MAAM,EAAE,MAAM,gCAAgC,CAAC;AACxD,OAAO,EAEL,
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/client/index.ts"],"names":[],"mappings":"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"}
|
|
@@ -1,10 +1,8 @@
|
|
|
1
1
|
import { createFunctionHandle, getFunctionName, } from "convex/server";
|
|
2
2
|
import { v } from "convex/values";
|
|
3
|
-
import {
|
|
4
|
-
import {
|
|
5
|
-
|
|
6
|
-
import { DEFAULT_MAX_PARALLELISM } from "../component/kick.js";
|
|
7
|
-
export { runResultValidator };
|
|
3
|
+
import { DEFAULT_LOG_LEVEL, logLevel, } from "../component/logging.js";
|
|
4
|
+
import { DEFAULT_MAX_PARALLELISM, runResult as resultValidator, } from "../component/shared.js";
|
|
5
|
+
export { resultValidator };
|
|
8
6
|
// Attempts will run with delay [0, 250, 500, 1000, 2000] (ms)
|
|
9
7
|
export const DEFAULT_RETRY_BEHAVIOR = {
|
|
10
8
|
maxAttempts: 5,
|
|
@@ -73,29 +71,63 @@ export class Workpool {
|
|
|
73
71
|
* and scheduling via `runAt` or `runAfter`.
|
|
74
72
|
*/
|
|
75
73
|
async enqueueMutation(ctx, fn, fnArgs, options) {
|
|
74
|
+
const onComplete = options?.onComplete
|
|
75
|
+
? {
|
|
76
|
+
fnHandle: await createFunctionHandle(options.onComplete),
|
|
77
|
+
context: options.context,
|
|
78
|
+
}
|
|
79
|
+
: undefined;
|
|
76
80
|
const id = await ctx.runMutation(this.component.lib.enqueue, {
|
|
77
81
|
...(await defaultEnqueueArgs(fn, this.options)),
|
|
78
82
|
fnArgs,
|
|
79
83
|
fnType: "mutation",
|
|
80
84
|
runAt: getRunAt(options),
|
|
85
|
+
onComplete,
|
|
81
86
|
});
|
|
82
87
|
return id;
|
|
83
88
|
}
|
|
89
|
+
/**
|
|
90
|
+
* Cancels a work item. If it's already started, it will be allowed to finish
|
|
91
|
+
* but will not be retried.
|
|
92
|
+
*
|
|
93
|
+
* @param ctx - The mutation or action context that can call ctx.runMutation.
|
|
94
|
+
* @param id - The ID of the work to cancel.
|
|
95
|
+
*/
|
|
84
96
|
async cancel(ctx, id) {
|
|
85
97
|
await ctx.runMutation(this.component.lib.cancel, {
|
|
86
98
|
id,
|
|
87
99
|
logLevel: this.options.logLevel ?? getDefaultLogLevel(),
|
|
88
100
|
});
|
|
89
101
|
}
|
|
102
|
+
/**
|
|
103
|
+
* Cancels all pending work items. See {@link cancel}.
|
|
104
|
+
*
|
|
105
|
+
* @param ctx - The mutation or action context that can call ctx.runMutation.
|
|
106
|
+
*/
|
|
90
107
|
async cancelAll(ctx) {
|
|
91
108
|
await ctx.runMutation(this.component.lib.cancelAll, {
|
|
92
109
|
logLevel: this.options.logLevel ?? getDefaultLogLevel(),
|
|
93
110
|
});
|
|
94
111
|
}
|
|
112
|
+
/**
|
|
113
|
+
* Gets the status of a work item.
|
|
114
|
+
*
|
|
115
|
+
* @param ctx - The query context that can call ctx.runQuery.
|
|
116
|
+
* @param id - The ID of the work to get the status of.
|
|
117
|
+
* @returns The status of the work item. One of:
|
|
118
|
+
* - `{ state: "pending", previousAttempts: number }`
|
|
119
|
+
* - `{ state: "running", previousAttempts: number }`
|
|
120
|
+
* - `{ state: "finished" }`
|
|
121
|
+
*/
|
|
95
122
|
async status(ctx, id) {
|
|
96
123
|
return ctx.runQuery(this.component.lib.status, { id });
|
|
97
124
|
}
|
|
98
125
|
}
|
|
126
|
+
// ensure OnCompleteArgs satisfies SharedOnCompleteArgs
|
|
127
|
+
const _ = {};
|
|
128
|
+
//
|
|
129
|
+
// Helper functions
|
|
130
|
+
//
|
|
99
131
|
function getRetryBehavior(defaultRetryBehavior, retryActionsByDefault, retryOverride) {
|
|
100
132
|
const defaultRetry = defaultRetryBehavior ?? DEFAULT_RETRY_BEHAVIOR;
|
|
101
133
|
const retryByDefault = retryActionsByDefault ?? false;
|
|
@@ -117,8 +149,6 @@ async function defaultEnqueueArgs(fn, { logLevel, maxParallelism }) {
|
|
|
117
149
|
},
|
|
118
150
|
};
|
|
119
151
|
}
|
|
120
|
-
// ensure OnCompleteArgs satisfies SharedOnCompleteArgs
|
|
121
|
-
const _ = {};
|
|
122
152
|
function getRunAt(options) {
|
|
123
153
|
if (!options) {
|
|
124
154
|
return Date.now();
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../../src/client/index.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,oBAAoB,EAIpB,eAAe,GAChB,MAAM,eAAe,CAAC;AACvB,OAAO,EAAE,CAAC,EAAW,MAAM,eAAe,CAAC;AAE3C,OAAO,
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../../src/client/index.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
import { Infer } from "convex/values";
|
|
2
|
+
import { MutationCtx } from "./_generated/server.js";
|
|
3
|
+
export type CompleteJob = Infer<typeof completeArgs.fields.jobs.element>;
|
|
4
|
+
export declare const completeArgs: import("convex/values").VObject<{
|
|
5
|
+
jobs: {
|
|
6
|
+
workId: import("convex/values").GenericId<"work">;
|
|
7
|
+
runResult: {
|
|
8
|
+
kind: "success";
|
|
9
|
+
returnValue: any;
|
|
10
|
+
} | {
|
|
11
|
+
kind: "failed";
|
|
12
|
+
error: string;
|
|
13
|
+
} | {
|
|
14
|
+
kind: "canceled";
|
|
15
|
+
};
|
|
16
|
+
attempt: number;
|
|
17
|
+
}[];
|
|
18
|
+
}, {
|
|
19
|
+
jobs: import("convex/values").VArray<{
|
|
20
|
+
workId: import("convex/values").GenericId<"work">;
|
|
21
|
+
runResult: {
|
|
22
|
+
kind: "success";
|
|
23
|
+
returnValue: any;
|
|
24
|
+
} | {
|
|
25
|
+
kind: "failed";
|
|
26
|
+
error: string;
|
|
27
|
+
} | {
|
|
28
|
+
kind: "canceled";
|
|
29
|
+
};
|
|
30
|
+
attempt: number;
|
|
31
|
+
}[], import("convex/values").VObject<{
|
|
32
|
+
workId: import("convex/values").GenericId<"work">;
|
|
33
|
+
runResult: {
|
|
34
|
+
kind: "success";
|
|
35
|
+
returnValue: any;
|
|
36
|
+
} | {
|
|
37
|
+
kind: "failed";
|
|
38
|
+
error: string;
|
|
39
|
+
} | {
|
|
40
|
+
kind: "canceled";
|
|
41
|
+
};
|
|
42
|
+
attempt: number;
|
|
43
|
+
}, {
|
|
44
|
+
runResult: import("convex/values").VUnion<{
|
|
45
|
+
kind: "success";
|
|
46
|
+
returnValue: any;
|
|
47
|
+
} | {
|
|
48
|
+
kind: "failed";
|
|
49
|
+
error: string;
|
|
50
|
+
} | {
|
|
51
|
+
kind: "canceled";
|
|
52
|
+
}, [import("convex/values").VObject<{
|
|
53
|
+
kind: "success";
|
|
54
|
+
returnValue: any;
|
|
55
|
+
}, {
|
|
56
|
+
kind: import("convex/values").VLiteral<"success", "required">;
|
|
57
|
+
returnValue: import("convex/values").VAny<any, "required", string>;
|
|
58
|
+
}, "required", "kind" | "returnValue" | `returnValue.${string}`>, import("convex/values").VObject<{
|
|
59
|
+
kind: "failed";
|
|
60
|
+
error: string;
|
|
61
|
+
}, {
|
|
62
|
+
kind: import("convex/values").VLiteral<"failed", "required">;
|
|
63
|
+
error: import("convex/values").VString<string, "required">;
|
|
64
|
+
}, "required", "kind" | "error">, import("convex/values").VObject<{
|
|
65
|
+
kind: "canceled";
|
|
66
|
+
}, {
|
|
67
|
+
kind: import("convex/values").VLiteral<"canceled", "required">;
|
|
68
|
+
}, "required", "kind">], "required", "kind" | "returnValue" | `returnValue.${string}` | "error">;
|
|
69
|
+
workId: import("convex/values").VId<import("convex/values").GenericId<"work">, "required">;
|
|
70
|
+
attempt: import("convex/values").VFloat64<number, "required">;
|
|
71
|
+
}, "required", "workId" | "runResult" | "runResult.kind" | "runResult.returnValue" | `runResult.returnValue.${string}` | "runResult.error" | "attempt">, "required">;
|
|
72
|
+
}, "required", "jobs">;
|
|
73
|
+
export declare function completeHandler(ctx: MutationCtx, args: Infer<typeof completeArgs>): Promise<void>;
|
|
74
|
+
export declare const complete: import("convex/server").RegisteredMutation<"internal", {
|
|
75
|
+
jobs: {
|
|
76
|
+
workId: import("convex/values").GenericId<"work">;
|
|
77
|
+
runResult: {
|
|
78
|
+
kind: "success";
|
|
79
|
+
returnValue: any;
|
|
80
|
+
} | {
|
|
81
|
+
kind: "failed";
|
|
82
|
+
error: string;
|
|
83
|
+
} | {
|
|
84
|
+
kind: "canceled";
|
|
85
|
+
};
|
|
86
|
+
attempt: number;
|
|
87
|
+
}[];
|
|
88
|
+
}, Promise<void>>;
|
|
89
|
+
//# sourceMappingURL=complete.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"complete.d.ts","sourceRoot":"","sources":["../../../src/component/complete.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,KAAK,EAAK,MAAM,eAAe,CAAC;AAEzC,OAAO,EAAoB,WAAW,EAAE,MAAM,wBAAwB,CAAC;AAMvE,MAAM,MAAM,WAAW,GAAG,KAAK,CAAC,OAAO,YAAY,CAAC,MAAM,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC;AAEzE,eAAO,MAAM,YAAY;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;sBAQvB,CAAC;AACH,wBAAsB,eAAe,CACnC,GAAG,EAAE,WAAW,EAChB,IAAI,EAAE,KAAK,CAAC,OAAO,YAAY,CAAC,iBAiFjC;AAED,eAAO,MAAM,QAAQ;;;;;;;;;;;;;;iBAGnB,CAAC"}
|
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
import { v } from "convex/values";
|
|
2
|
+
import { internalMutation } from "./_generated/server.js";
|
|
3
|
+
import { kickMainLoop } from "./kick.js";
|
|
4
|
+
import { createLogger } from "./logging.js";
|
|
5
|
+
import { runResult } from "./shared.js";
|
|
6
|
+
import { recordCompleted } from "./stats.js";
|
|
7
|
+
export const completeArgs = v.object({
|
|
8
|
+
jobs: v.array(v.object({
|
|
9
|
+
runResult: runResult,
|
|
10
|
+
workId: v.id("work"),
|
|
11
|
+
attempt: v.number(),
|
|
12
|
+
})),
|
|
13
|
+
});
|
|
14
|
+
export async function completeHandler(ctx, args) {
|
|
15
|
+
const globals = await ctx.db.query("globals").unique();
|
|
16
|
+
const console = createLogger(globals?.logLevel);
|
|
17
|
+
const pendingCompletions = [];
|
|
18
|
+
await Promise.all(args.jobs.map(async (job) => {
|
|
19
|
+
const work = await ctx.db.get(job.workId);
|
|
20
|
+
if (!work) {
|
|
21
|
+
console.warn(`[complete] ${job.workId} is done, but its work is gone`);
|
|
22
|
+
return;
|
|
23
|
+
}
|
|
24
|
+
if (work.attempts !== job.attempt) {
|
|
25
|
+
console.warn(`[complete] ${job.workId} mismatched attempt number`);
|
|
26
|
+
return;
|
|
27
|
+
}
|
|
28
|
+
work.attempts++;
|
|
29
|
+
await ctx.db.patch(work._id, { attempts: work.attempts });
|
|
30
|
+
const pendingCompletion = await ctx.db
|
|
31
|
+
.query("pendingCompletion")
|
|
32
|
+
.withIndex("workId", (q) => q.eq("workId", job.workId))
|
|
33
|
+
.unique();
|
|
34
|
+
if (pendingCompletion) {
|
|
35
|
+
console.warn(`[complete] ${job.workId} already in pendingCompletion`);
|
|
36
|
+
return;
|
|
37
|
+
}
|
|
38
|
+
const maxAttempts = work.retryBehavior?.maxAttempts;
|
|
39
|
+
const retry = job.runResult.kind === "failed" &&
|
|
40
|
+
!!maxAttempts &&
|
|
41
|
+
work.attempts < maxAttempts;
|
|
42
|
+
if (!retry) {
|
|
43
|
+
if (work.onComplete) {
|
|
44
|
+
try {
|
|
45
|
+
const handle = work.onComplete.fnHandle;
|
|
46
|
+
await ctx.runMutation(handle, {
|
|
47
|
+
workId: work._id,
|
|
48
|
+
context: work.onComplete.context,
|
|
49
|
+
result: job.runResult,
|
|
50
|
+
});
|
|
51
|
+
console.debug(`[complete] onComplete for ${job.workId} completed`);
|
|
52
|
+
}
|
|
53
|
+
catch (e) {
|
|
54
|
+
console.error(`[complete] error running onComplete for ${job.workId}`, e);
|
|
55
|
+
// TODO: store failures in a table for later debugging
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
recordCompleted(console, work, job.runResult.kind);
|
|
59
|
+
// This is the terminating state for work.
|
|
60
|
+
await ctx.db.delete(job.workId);
|
|
61
|
+
}
|
|
62
|
+
if (job.runResult.kind !== "canceled") {
|
|
63
|
+
pendingCompletions.push({
|
|
64
|
+
runResult: job.runResult,
|
|
65
|
+
workId: job.workId,
|
|
66
|
+
retry,
|
|
67
|
+
});
|
|
68
|
+
}
|
|
69
|
+
}));
|
|
70
|
+
if (pendingCompletions.length > 0) {
|
|
71
|
+
const segment = await kickMainLoop(ctx, "complete");
|
|
72
|
+
await Promise.all(pendingCompletions.map((completion) => ctx.db.insert("pendingCompletion", {
|
|
73
|
+
...completion,
|
|
74
|
+
segment,
|
|
75
|
+
})));
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
export const complete = internalMutation({
|
|
79
|
+
args: completeArgs,
|
|
80
|
+
handler: completeHandler,
|
|
81
|
+
});
|
|
82
|
+
//# sourceMappingURL=complete.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"complete.js","sourceRoot":"","sources":["../../../src/component/complete.ts"],"names":[],"mappings":"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"}
|
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
import { MutationCtx } from "./_generated/server.js";
|
|
2
2
|
import { Config } from "./shared.js";
|
|
3
|
-
export declare const DEFAULT_MAX_PARALLELISM = 10;
|
|
4
3
|
/**
|
|
5
|
-
* Called from outside the loop
|
|
4
|
+
* Called from outside the loop.
|
|
5
|
+
* Returns the soonest segment to enqueue work for the main loop.
|
|
6
6
|
*/
|
|
7
|
-
export declare function kickMainLoop(ctx: MutationCtx, source: "enqueue" | "cancel" | "
|
|
7
|
+
export declare function kickMainLoop(ctx: MutationCtx, source: "enqueue" | "cancel" | "complete", config?: Partial<Config>): Promise<bigint>;
|
|
8
8
|
export declare const forceKick: import("convex/server").RegisteredMutation<"internal", {}, Promise<void>>;
|
|
9
9
|
//# sourceMappingURL=kick.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"kick.d.ts","sourceRoot":"","sources":["../../../src/component/kick.ts"],"names":[],"mappings":"AACA,OAAO,EAAoB,WAAW,EAAE,MAAM,wBAAwB,CAAC;AAGvE,OAAO,
|
|
1
|
+
{"version":3,"file":"kick.d.ts","sourceRoot":"","sources":["../../../src/component/kick.ts"],"names":[],"mappings":"AACA,OAAO,EAAoB,WAAW,EAAE,MAAM,wBAAwB,CAAC;AAGvE,OAAO,EAEL,MAAM,EAKP,MAAM,aAAa,CAAC;AAErB;;;GAGG;AACH,wBAAsB,YAAY,CAChC,GAAG,EAAE,WAAW,EAChB,MAAM,EAAE,SAAS,GAAG,QAAQ,GAAG,UAAU,EACzC,MAAM,CAAC,EAAE,OAAO,CAAC,MAAM,CAAC,GACvB,OAAO,CAAC,MAAM,CAAC,CAiDjB;AAED,eAAO,MAAM,SAAS,2EAOpB,CAAC"}
|
|
@@ -2,30 +2,30 @@ import { internal } from "./_generated/api.js";
|
|
|
2
2
|
import { internalMutation } from "./_generated/server.js";
|
|
3
3
|
import { createLogger, DEFAULT_LOG_LEVEL } from "./logging.js";
|
|
4
4
|
import { INITIAL_STATE } from "./loop.js";
|
|
5
|
-
import {
|
|
6
|
-
export const DEFAULT_MAX_PARALLELISM = 10;
|
|
5
|
+
import { boundScheduledTime, DEFAULT_MAX_PARALLELISM, fromSegment, getCurrentSegment, getNextSegment, } from "./shared.js";
|
|
7
6
|
/**
|
|
8
|
-
* Called from outside the loop
|
|
7
|
+
* Called from outside the loop.
|
|
8
|
+
* Returns the soonest segment to enqueue work for the main loop.
|
|
9
9
|
*/
|
|
10
10
|
export async function kickMainLoop(ctx, source, config) {
|
|
11
11
|
const globals = await getOrUpdateGlobals(ctx, config);
|
|
12
12
|
const console = createLogger(globals.logLevel);
|
|
13
13
|
const runStatus = await getOrCreateRunStatus(ctx);
|
|
14
|
+
const next = getNextSegment();
|
|
14
15
|
// Only kick to run now if we're scheduled or idle.
|
|
15
16
|
if (runStatus.state.kind === "running") {
|
|
16
17
|
console.debug(`[${source}] main is actively running, so we don't need to kick it`);
|
|
17
|
-
return;
|
|
18
|
+
return next;
|
|
18
19
|
}
|
|
19
|
-
const segment = nextSegment();
|
|
20
20
|
// main is scheduled to run later, so we should cancel it and reschedule.
|
|
21
21
|
if (runStatus.state.kind === "scheduled") {
|
|
22
22
|
if (source === "enqueue" && runStatus.state.saturated) {
|
|
23
23
|
console.debug(`[${source}] main is saturated, so we don't need to kick it`);
|
|
24
|
-
return;
|
|
24
|
+
return next;
|
|
25
25
|
}
|
|
26
|
-
if (runStatus.state.segment <=
|
|
26
|
+
if (runStatus.state.segment <= next) {
|
|
27
27
|
console.debug(`[${source}] main is scheduled to run soon enough, so we don't need to kick it`);
|
|
28
|
-
return;
|
|
28
|
+
return next;
|
|
29
29
|
}
|
|
30
30
|
console.debug(`[${source}] main is scheduled to run later, so reschedule it to run now`);
|
|
31
31
|
const scheduled = await ctx.db.system.get(runStatus.state.scheduledId);
|
|
@@ -36,19 +36,24 @@ export async function kickMainLoop(ctx, source, config) {
|
|
|
36
36
|
console.warn(`[${source}] main is marked as scheduled, but it's status is ${scheduled?.state.kind}`);
|
|
37
37
|
}
|
|
38
38
|
}
|
|
39
|
-
|
|
39
|
+
else if (runStatus.state.kind === "idle") {
|
|
40
|
+
console.debug(`[${source}] main was idle, so run it now`);
|
|
41
|
+
}
|
|
40
42
|
await ctx.db.patch(runStatus._id, { state: { kind: "running" } });
|
|
41
|
-
|
|
43
|
+
const current = getCurrentSegment();
|
|
44
|
+
const scheduledTime = boundScheduledTime(fromSegment(current), console);
|
|
45
|
+
await ctx.scheduler.runAt(scheduledTime, internal.loop.main, {
|
|
42
46
|
generation: runStatus.state.generation,
|
|
43
|
-
segment,
|
|
47
|
+
segment: current,
|
|
44
48
|
});
|
|
49
|
+
return current;
|
|
45
50
|
}
|
|
46
51
|
export const forceKick = internalMutation({
|
|
47
52
|
args: {},
|
|
48
53
|
handler: async (ctx) => {
|
|
49
54
|
const runStatus = await getOrCreateRunStatus(ctx);
|
|
50
55
|
await ctx.db.delete(runStatus._id);
|
|
51
|
-
await kickMainLoop(ctx, "
|
|
56
|
+
await kickMainLoop(ctx, "complete");
|
|
52
57
|
},
|
|
53
58
|
});
|
|
54
59
|
async function getOrCreateRunStatus(ctx) {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"kick.js","sourceRoot":"","sources":["../../../src/component/kick.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,MAAM,qBAAqB,CAAC;AAC/C,OAAO,EAAE,gBAAgB,EAAe,MAAM,wBAAwB,CAAC;AACvE,OAAO,EAAE,YAAY,EAAE,iBAAiB,EAAE,MAAM,cAAc,CAAC;AAC/D,OAAO,EAAE,aAAa,EAAE,MAAM,WAAW,CAAC;AAC1C,OAAO,
|
|
1
|
+
{"version":3,"file":"kick.js","sourceRoot":"","sources":["../../../src/component/kick.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,MAAM,qBAAqB,CAAC;AAC/C,OAAO,EAAE,gBAAgB,EAAe,MAAM,wBAAwB,CAAC;AACvE,OAAO,EAAE,YAAY,EAAE,iBAAiB,EAAE,MAAM,cAAc,CAAC;AAC/D,OAAO,EAAE,aAAa,EAAE,MAAM,WAAW,CAAC;AAC1C,OAAO,EACL,kBAAkB,EAElB,uBAAuB,EACvB,WAAW,EACX,iBAAiB,EACjB,cAAc,GACf,MAAM,aAAa,CAAC;AAErB;;;GAGG;AACH,MAAM,CAAC,KAAK,UAAU,YAAY,CAChC,GAAgB,EAChB,MAAyC,EACzC,MAAwB;IAExB,MAAM,OAAO,GAAG,MAAM,kBAAkB,CAAC,GAAG,EAAE,MAAM,CAAC,CAAC;IACtD,MAAM,OAAO,GAAG,YAAY,CAAC,OAAO,CAAC,QAAQ,CAAC,CAAC;IAC/C,MAAM,SAAS,GAAG,MAAM,oBAAoB,CAAC,GAAG,CAAC,CAAC;IAClD,MAAM,IAAI,GAAG,cAAc,EAAE,CAAC;IAE9B,mDAAmD;IACnD,IAAI,SAAS,CAAC,KAAK,CAAC,IAAI,KAAK,SAAS,EAAE;QACtC,OAAO,CAAC,KAAK,CACX,IAAI,MAAM,yDAAyD,CACpE,CAAC;QACF,OAAO,IAAI,CAAC;KACb;IACD,yEAAyE;IACzE,IAAI,SAAS,CAAC,KAAK,CAAC,IAAI,KAAK,WAAW,EAAE;QACxC,IAAI,MAAM,KAAK,SAAS,IAAI,SAAS,CAAC,KAAK,CAAC,SAAS,EAAE;YACrD,OAAO,CAAC,KAAK,CACX,IAAI,MAAM,kDAAkD,CAC7D,CAAC;YACF,OAAO,IAAI,CAAC;SACb;QACD,IAAI,SAAS,CAAC,KAAK,CAAC,OAAO,IAAI,IAAI,EAAE;YACnC,OAAO,CAAC,KAAK,CACX,IAAI,MAAM,qEAAqE,CAChF,CAAC;YACF,OAAO,IAAI,CAAC;SACb;QACD,OAAO,CAAC,KAAK,CACX,IAAI,MAAM,+DAA+D,CAC1E,CAAC;QACF,MAAM,SAAS,GAAG,MAAM,GAAG,CAAC,EAAE,CAAC,MAAM,CAAC,GAAG,CAAC,SAAS,CAAC,KAAK,CAAC,WAAW,CAAC,CAAC;QACvE,IAAI,SAAS,IAAI,SAAS,CAAC,KAAK,CAAC,IAAI,KAAK,SAAS,EAAE;YACnD,MAAM,GAAG,CAAC,SAAS,CAAC,MAAM,CAAC,SAAS,CAAC,KAAK,CAAC,WAAW,CAAC,CAAC;SACzD;aAAM;YACL,OAAO,CAAC,IAAI,CACV,IAAI,MAAM,qDAAqD,SAAS,EAAE,KAAK,CAAC,IAAI,EAAE,CACvF,CAAC;SACH;KACF;SAAM,IAAI,SAAS,CAAC,KAAK,CAAC,IAAI,KAAK,MAAM,EAAE;QAC1C,OAAO,CAAC,KAAK,CAAC,IAAI,MAAM,gCAAgC,CAAC,CAAC;KAC3D;IACD,MAAM,GAAG,CAAC,EAAE,CAAC,KAAK,CAAC,SAAS,CAAC,GAAG,EAAE,EAAE,KAAK,EAAE,EAAE,IAAI,EAAE,SAAS,EAAE,EAAE,CAAC,CAAC;IAClE,MAAM,OAAO,GAAG,iBAAiB,EAAE,CAAC;IACpC,MAAM,aAAa,GAAG,kBAAkB,CAAC,WAAW,CAAC,OAAO,CAAC,EAAE,OAAO,CAAC,CAAC;IACxE,MAAM,GAAG,CAAC,SAAS,CAAC,KAAK,CAAC,aAAa,EAAE,QAAQ,CAAC,IAAI,CAAC,IAAI,EAAE;QAC3D,UAAU,EAAE,SAAS,CAAC,KAAK,CAAC,UAAU;QACtC,OAAO,EAAE,OAAO;KACjB,CAAC,CAAC;IACH,OAAO,OAAO,CAAC;AACjB,CAAC;AAED,MAAM,CAAC,MAAM,SAAS,GAAG,gBAAgB,CAAC;IACxC,IAAI,EAAE,EAAE;IACR,OAAO,EAAE,KAAK,EAAE,GAAG,EAAE,EAAE;QACrB,MAAM,SAAS,GAAG,MAAM,oBAAoB,CAAC,GAAG,CAAC,CAAC;QAClD,MAAM,GAAG,CAAC,EAAE,CAAC,MAAM,CAAC,SAAS,CAAC,GAAG,CAAC,CAAC;QACnC,MAAM,YAAY,CAAC,GAAG,EAAE,UAAU,CAAC,CAAC;IACtC,CAAC;CACF,CAAC,CAAC;AAEH,KAAK,UAAU,oBAAoB,CAAC,GAAgB;IAClD,IAAI,SAAS,GAAG,MAAM,GAAG,CAAC,EAAE,CAAC,KAAK,CAAC,WAAW,CAAC,CAAC,MAAM,EAAE,CAAC;IACzD,IAAI,CAAC,SAAS,EAAE;QACd,MAAM,KAAK,GAAG,MAAM,GAAG,CAAC,EAAE,CAAC,KAAK,CAAC,eAAe,CAAC,CAAC,MAAM,EAAE,CAAC;QAC3D,MAAM,EAAE,GAAG,MAAM,GAAG,CAAC,EAAE,CAAC,MAAM,CAAC,WAAW,EAAE;YAC1C,KAAK,EAAE;gBACL,IAAI,EAAE,MAAM;gBACZ,UAAU,EAAE,KAAK,EAAE,UAAU,IAAI,aAAa,CAAC,UAAU;aAC1D;SACF,CAAC,CAAC;QACH,SAAS,GAAG,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC,GAAG,CAAC,EAAE,CAAC,CAAE,CAAC;QACpC,IAAI,CAAC,KAAK,EAAE;YACV,MAAM,GAAG,CAAC,EAAE,CAAC,MAAM,CAAC,eAAe,EAAE,aAAa,CAAC,CAAC;SACrD;KACF;IACD,OAAO,SAAS,CAAC;AACnB,CAAC;AAED,KAAK,UAAU,kBAAkB,CAAC,GAAgB,EAAE,MAAwB;IAC1E,MAAM,OAAO,GAAG,MAAM,GAAG,CAAC,EAAE,CAAC,KAAK,CAAC,SAAS,CAAC,CAAC,MAAM,EAAE,CAAC;IACvD,IAAI,CAAC,OAAO,EAAE;QACZ,MAAM,EAAE,GAAG,MAAM,GAAG,CAAC,EAAE,CAAC,MAAM,CAAC,SAAS,EAAE;YACxC,cAAc,EAAE,MAAM,EAAE,cAAc,IAAI,uBAAuB;YACjE,QAAQ,EAAE,MAAM,EAAE,QAAQ,IAAI,iBAAiB;SAChD,CAAC,CAAC;QACH,OAAO,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC,GAAG,CAAC,EAAE,CAAC,CAAE,CAAC;KAChC;SAAM,IAAI,MAAM,EAAE;QACjB,IAAI,OAAO,GAAG,KAAK,CAAC;QACpB,IACE,MAAM,CAAC,cAAc;YACrB,MAAM,CAAC,cAAc,KAAK,OAAO,CAAC,cAAc,EAChD;YACA,OAAO,CAAC,cAAc,GAAG,MAAM,CAAC,cAAc,CAAC;YAC/C,OAAO,GAAG,IAAI,CAAC;SAChB;QACD,IAAI,MAAM,CAAC,QAAQ,IAAI,MAAM,CAAC,QAAQ,KAAK,OAAO,CAAC,QAAQ,EAAE;YAC3D,OAAO,CAAC,QAAQ,GAAG,MAAM,CAAC,QAAQ,CAAC;YACnC,OAAO,GAAG,IAAI,CAAC;SAChB;QACD,IAAI,OAAO,EAAE;YACX,MAAM,GAAG,CAAC,EAAE,CAAC,OAAO,CAAC,OAAO,CAAC,GAAG,EAAE,OAAO,CAAC,CAAC;SAC5C;KACF;IACD,OAAO,OAAO,CAAC;AACjB,CAAC"}
|