windmill-client 1.772.0 → 1.774.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/dist/client.d.ts +75 -7
- package/dist/client.mjs +97 -58
- package/dist/core/OpenAPI.mjs +1 -1
- package/dist/index.d.ts +1 -1
- package/dist/index.js +246 -60
- package/dist/services.gen.d.ts +23 -1
- package/dist/services.gen.mjs +48 -2
- package/dist/types.gen.d.ts +234 -3
- package/dist/wacError.d.ts +24 -0
- package/dist/wacError.mjs +103 -0
- package/package.json +1 -1
package/dist/client.d.ts
CHANGED
|
@@ -435,6 +435,50 @@ export declare class StepSuspend extends Error {
|
|
|
435
435
|
dispatchInfo: Record<string, any>;
|
|
436
436
|
constructor(dispatchInfo: Record<string, any>);
|
|
437
437
|
}
|
|
438
|
+
/** Values `JSON.stringify` cannot represent: it omits the property holding one.
|
|
439
|
+
* The type-level half of `checkpointableResult`, which nulls them at the top
|
|
440
|
+
* level, where there is no key to omit. */
|
|
441
|
+
type JsonDropped = ((...args: any[]) => any) | (abstract new (...args: any[]) => any) | symbol;
|
|
442
|
+
/**
|
|
443
|
+
* What a value looks like after the JSON round trip a checkpoint performs:
|
|
444
|
+
* `Date` → string, `Map`/`Set` → `{}`, `undefined` → null, methods gone.
|
|
445
|
+
* `NaN` and the infinities decode as null too, deliberately still typed
|
|
446
|
+
* `number`: `number | null` everywhere costs more than that case is worth.
|
|
447
|
+
*
|
|
448
|
+
* Mirrors `encodeCheckpointPayload` below — keep the two in step, or `step()`
|
|
449
|
+
* starts describing a value it does not return.
|
|
450
|
+
*/
|
|
451
|
+
export type Jsonified<T> = 0 extends 1 & T ? any : unknown extends T ? unknown : T extends string | number | boolean | null ? T : T extends undefined | void | symbol | ((...args: any[]) => any) ? null : T extends bigint ? string : T extends {
|
|
452
|
+
toJSON(): infer R;
|
|
453
|
+
} ? Jsonified<R> : T extends ReadonlyMap<any, any> | ReadonlySet<any> ? Record<string, never> : T extends readonly any[] ? {
|
|
454
|
+
[K in keyof T]: Jsonified<T[K]>;
|
|
455
|
+
} : T extends object ? JsonifiedObject<T> : never;
|
|
456
|
+
/**
|
|
457
|
+
* `JSON.stringify` keeps own enumerable string keys whose value it can
|
|
458
|
+
* represent. A key that can only hold an unrepresentable value is gone; one
|
|
459
|
+
* that merely might becomes optional, because it can come back missing —
|
|
460
|
+
* `| undefined` alone would still demand the key be there.
|
|
461
|
+
*/
|
|
462
|
+
type JsonifiedObject<T> = Flatten<{
|
|
463
|
+
[K in keyof T as K extends symbol ? never : [Exclude<T[K], JsonDropped>] extends [never] ? never : [T[K]] extends [Exclude<T[K], JsonDropped>] ? K : never]: Jsonified<T[K]>;
|
|
464
|
+
} & {
|
|
465
|
+
[K in keyof T as K extends symbol ? never : [Exclude<T[K], JsonDropped>] extends [never] ? never : [T[K]] extends [Exclude<T[K], JsonDropped>] ? never : K]?: Jsonified<Exclude<T[K], JsonDropped>>;
|
|
466
|
+
}>;
|
|
467
|
+
/** Collapse the two halves above into one object, so hovering `Jsonified` shows
|
|
468
|
+
* a shape rather than an intersection. */
|
|
469
|
+
type Flatten<T> = {
|
|
470
|
+
[K in keyof T]: T[K];
|
|
471
|
+
};
|
|
472
|
+
/**
|
|
473
|
+
* A task function as its callers see it. A task's result always crosses a JSON
|
|
474
|
+
* boundary — the child job's result is read back from the checkpoint, and the
|
|
475
|
+
* v1 path reads it back from the API — so only {@link Jsonified} of it survives.
|
|
476
|
+
*
|
|
477
|
+
* Rebuilding the signature costs some precision TypeScript cannot preserve: a
|
|
478
|
+
* generic task's type parameters instantiate at their constraints, and an
|
|
479
|
+
* overloaded one keeps only its last signature. Neither survives JSON anyway.
|
|
480
|
+
*/
|
|
481
|
+
export type JsonifiedFn<T extends (...args: any[]) => Promise<any>> = (...args: Parameters<T>) => Promise<Jsonified<Awaited<ReturnType<T>>>>;
|
|
438
482
|
export interface TaskOptions {
|
|
439
483
|
timeout?: number;
|
|
440
484
|
tag?: string;
|
|
@@ -460,6 +504,11 @@ export declare class WorkflowCtx {
|
|
|
460
504
|
* `complete` whose step never reached `completed_steps`. Python is immune:
|
|
461
505
|
* `_StepSuspend` derives from `BaseException`. */
|
|
462
506
|
private _pendingSuspend;
|
|
507
|
+
/** The failure raised by the step this child round is executing. That exception
|
|
508
|
+
* *is* the round's result, so a `catch` in the body must not be able to turn it
|
|
509
|
+
* into a `complete` — the parent would then record the caught branch's value as
|
|
510
|
+
* a successful step. Boxed: the thrown value may be any falsy value. */
|
|
511
|
+
private _pendingStepFailure;
|
|
463
512
|
/** When set, the task matching this key executes its inner function directly */
|
|
464
513
|
_executingKey: string | null;
|
|
465
514
|
/** Serializes fast-path POSTs across concurrent step() calls within one
|
|
@@ -502,18 +551,33 @@ export declare class WorkflowCtx {
|
|
|
502
551
|
/** Raise a suspend, parking it so a body that catches it cannot make it
|
|
503
552
|
* vanish. Every suspend raised for this ctx must go through here. */
|
|
504
553
|
_raiseSuspend(dispatchInfo: Record<string, any>): never;
|
|
505
|
-
/**
|
|
506
|
-
*
|
|
507
|
-
|
|
508
|
-
|
|
509
|
-
|
|
554
|
+
/** Park then raise the executing step's failure. Child mode only — in a parent
|
|
555
|
+
* round a task failure is an ordinary `TaskError` the body may handle. */
|
|
556
|
+
_raiseStepFailure(error: unknown): never;
|
|
557
|
+
/** Re-throw a swallowed suspend or step failure at the next SDK call. It
|
|
558
|
+
* happened before whatever the body is doing now, so it wins: the run is
|
|
559
|
+
* unwinding either way and everything after it re-runs on the replay. Left
|
|
560
|
+
* set, so a body that catches in a loop can't swallow it a second time. */
|
|
561
|
+
private _rethrowSwallowed;
|
|
510
562
|
/** Hand the runner a suspend the workflow body caught and swallowed, so it is
|
|
511
563
|
* honoured instead of silently turning into a `complete`. Returns null when
|
|
512
564
|
* the suspend propagated normally. */
|
|
513
565
|
_takePendingSuspend(): StepSuspend | null;
|
|
566
|
+
/** Same for the executing step's failure, so the runner can fail the child job. */
|
|
567
|
+
_takePendingStepFailure(): {
|
|
568
|
+
error: unknown;
|
|
569
|
+
} | null;
|
|
514
570
|
}
|
|
515
571
|
export declare function sleep(seconds: number): Promise<void>;
|
|
516
|
-
|
|
572
|
+
/**
|
|
573
|
+
* Execute `fn` inline and checkpoint the result. On replay the cached value is
|
|
574
|
+
* returned without re-executing `fn`.
|
|
575
|
+
*
|
|
576
|
+
* `fn`'s result is encoded as JSON and decoded back before it is returned, so
|
|
577
|
+
* the round that runs the body sees the same types every replay sees: a `Date`
|
|
578
|
+
* comes back as a string, a `Map` as `{}`. {@link Jsonified} is that shape.
|
|
579
|
+
*/
|
|
580
|
+
export declare function step<T>(name: string, fn: () => T | Promise<T>): Promise<Jsonified<Awaited<T>>>;
|
|
517
581
|
/**
|
|
518
582
|
* Wrap an async function as a workflow task.
|
|
519
583
|
*
|
|
@@ -523,8 +587,12 @@ export declare function step<T>(name: string, fn: () => T | Promise<T>): Promise
|
|
|
523
587
|
*
|
|
524
588
|
* Inside a `workflow()`, calling a task dispatches it as a step.
|
|
525
589
|
* Outside a workflow, the function body executes directly.
|
|
590
|
+
*
|
|
591
|
+
* A task runs as its own job, so its result is always encoded as JSON and
|
|
592
|
+
* decoded back before the caller sees it: a `Date` comes back as a string, a
|
|
593
|
+
* `Map` as `{}`. {@link JsonifiedFn} is that shape.
|
|
526
594
|
*/
|
|
527
|
-
export declare function task<T extends (...args: any[]) => Promise<any>>(fnOrPath: T | string, maybeFnOrOptions?: T | TaskOptions, maybeOptions?: TaskOptions): T
|
|
595
|
+
export declare function task<T extends (...args: any[]) => Promise<any>>(fnOrPath: T | string, maybeFnOrOptions?: T | TaskOptions, maybeOptions?: TaskOptions): JsonifiedFn<T>;
|
|
528
596
|
/**
|
|
529
597
|
* Create a task that dispatches to a separate Windmill script.
|
|
530
598
|
*
|
package/dist/client.mjs
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { OpenAPI } from "./core/OpenAPI.mjs";
|
|
2
2
|
import { AppService, HelpersService, JobService, KafkaTriggerService, MetricsService, OidcService, ResourceService, UserService, VariableService } from "./services.gen.mjs";
|
|
3
|
+
import { isSuspendSignal, stepErrorMarker, taskErrorFromMarker } from "./wacError.mjs";
|
|
3
4
|
import { parseS3Object } from "./s3Types.mjs";
|
|
4
5
|
import { appendPartition, datatable, ducklake, upsertPartition } from "./sqlUtils.mjs";
|
|
5
6
|
|
|
@@ -958,31 +959,24 @@ var StepSuspend = class extends Error {
|
|
|
958
959
|
this.name = "StepSuspend";
|
|
959
960
|
}
|
|
960
961
|
};
|
|
961
|
-
/**
|
|
962
|
-
*
|
|
963
|
-
|
|
964
|
-
|
|
965
|
-
|
|
966
|
-
return
|
|
967
|
-
|
|
968
|
-
|
|
969
|
-
|
|
970
|
-
|
|
971
|
-
|
|
972
|
-
|
|
973
|
-
|
|
974
|
-
|
|
975
|
-
|
|
976
|
-
|
|
977
|
-
|
|
978
|
-
|
|
979
|
-
function stepErrorFromMarker(marker, name) {
|
|
980
|
-
const err = new Error(marker?.message || `Step '${name}' failed`);
|
|
981
|
-
err.name = "TaskError";
|
|
982
|
-
err.result = marker?.result;
|
|
983
|
-
err.step_key = marker?.step_key;
|
|
984
|
-
err.child_job_id = marker?.child_job_id;
|
|
985
|
-
return err;
|
|
962
|
+
/** Encode a checkpoint payload the way the worker wrapper does on the suspend
|
|
963
|
+
* path, so both arms record the same value for the same step. `undefined` maps
|
|
964
|
+
* to null; a bigint to its digits, which the wrapper gets instead from the
|
|
965
|
+
* `BigInt.prototype.toJSON` it installs and the SDK cannot count on. */
|
|
966
|
+
function encodeCheckpointPayload(payload) {
|
|
967
|
+
return JSON.stringify(payload, (_key, value) => typeof value === "undefined" ? null : typeof value === "bigint" ? value.toString() : value);
|
|
968
|
+
}
|
|
969
|
+
/** A whole result that `JSON.stringify` drops the key for leaves a checkpoint
|
|
970
|
+
* with no `result`, which neither the endpoint nor `WacOutput` accepts. Only
|
|
971
|
+
* the top level: nested, a dropped key is what every other bun path does. */
|
|
972
|
+
function checkpointableResult(value) {
|
|
973
|
+
return typeof value === "function" || typeof value === "symbol" ? null : value;
|
|
974
|
+
}
|
|
975
|
+
/** Put a value through the checkpoint's encoding without checkpointing it, so
|
|
976
|
+
* the paths that never persist anything still hand back the shape the ones
|
|
977
|
+
* that do would. */
|
|
978
|
+
function jsonRoundTrip(value) {
|
|
979
|
+
return JSON.parse(encodeCheckpointPayload({ value: checkpointableResult(value) })).value;
|
|
986
980
|
}
|
|
987
981
|
/** A step key travels as one path segment when its URLs are minted, so it must be
|
|
988
982
|
* non-empty and free of `/` and dot segments — otherwise `waitForApproval` would
|
|
@@ -1010,6 +1004,11 @@ var WorkflowCtx = class {
|
|
|
1010
1004
|
* `complete` whose step never reached `completed_steps`. Python is immune:
|
|
1011
1005
|
* `_StepSuspend` derives from `BaseException`. */
|
|
1012
1006
|
_pendingSuspend = null;
|
|
1007
|
+
/** The failure raised by the step this child round is executing. That exception
|
|
1008
|
+
* *is* the round's result, so a `catch` in the body must not be able to turn it
|
|
1009
|
+
* into a `complete` — the parent would then record the caught branch's value as
|
|
1010
|
+
* a successful step. Boxed: the thrown value may be any falsy value. */
|
|
1011
|
+
_pendingStepFailure = null;
|
|
1013
1012
|
/** When set, the task matching this key executes its inner function directly */
|
|
1014
1013
|
_executingKey;
|
|
1015
1014
|
/** Serializes fast-path POSTs across concurrent step() calls within one
|
|
@@ -1042,16 +1041,12 @@ var WorkflowCtx = class {
|
|
|
1042
1041
|
return key;
|
|
1043
1042
|
}
|
|
1044
1043
|
_nextStep(name, script, args = {}, dispatch_type = "inline", options) {
|
|
1045
|
-
this.
|
|
1044
|
+
this._rethrowSwallowed();
|
|
1046
1045
|
const key = this._allocKey(name || script || "step");
|
|
1047
1046
|
if (key in this.completed) {
|
|
1048
1047
|
const value = this.completed[key];
|
|
1049
1048
|
if (value && typeof value === "object" && value.__wmill_error) {
|
|
1050
|
-
const err =
|
|
1051
|
-
err.name = "TaskError";
|
|
1052
|
-
err.result = value.result;
|
|
1053
|
-
err.step_key = value.step_key;
|
|
1054
|
-
err.child_job_id = value.child_job_id;
|
|
1049
|
+
const err = taskErrorFromMarker(value, `Task '${name}' failed`);
|
|
1055
1050
|
return { then: (_resolve, reject) => {
|
|
1056
1051
|
if (reject) reject(err);
|
|
1057
1052
|
else throw err;
|
|
@@ -1101,7 +1096,7 @@ var WorkflowCtx = class {
|
|
|
1101
1096
|
return steps;
|
|
1102
1097
|
}
|
|
1103
1098
|
_waitForApproval(options) {
|
|
1104
|
-
this.
|
|
1099
|
+
this._rethrowSwallowed();
|
|
1105
1100
|
if (options?.key !== void 0) assertUsableStepKey(options.key, "waitForApproval key");
|
|
1106
1101
|
const key = this._allocKey(options?.key || "approval");
|
|
1107
1102
|
if (options?.key && key !== options.key) throw new Error(`WAC step key "${options.key}" is already used in this workflow. Give each waitForApproval() its own key so getApprovalUrls() can address it.`);
|
|
@@ -1121,7 +1116,7 @@ var WorkflowCtx = class {
|
|
|
1121
1116
|
});
|
|
1122
1117
|
}
|
|
1123
1118
|
_sleep(seconds) {
|
|
1124
|
-
this.
|
|
1119
|
+
this._rethrowSwallowed();
|
|
1125
1120
|
const key = this._allocKey("sleep");
|
|
1126
1121
|
if (key in this.completed) return { then: (resolve) => resolve(void 0) };
|
|
1127
1122
|
if (this._executingKey !== null) return { then: () => new Promise(() => {}) };
|
|
@@ -1134,11 +1129,11 @@ var WorkflowCtx = class {
|
|
|
1134
1129
|
});
|
|
1135
1130
|
}
|
|
1136
1131
|
async _runInlineStep(name, fn) {
|
|
1137
|
-
this.
|
|
1132
|
+
this._rethrowSwallowed();
|
|
1138
1133
|
const key = this._allocKey(name || "step");
|
|
1139
1134
|
if (key in this.completed) {
|
|
1140
1135
|
const value = this.completed[key];
|
|
1141
|
-
if (value && typeof value === "object" && value.__wmill_error) throw
|
|
1136
|
+
if (value && typeof value === "object" && value.__wmill_error) throw taskErrorFromMarker(value, `Step '${name}' failed`);
|
|
1142
1137
|
return value;
|
|
1143
1138
|
}
|
|
1144
1139
|
if (this._executingKey !== null) return new Promise(() => {});
|
|
@@ -1150,22 +1145,36 @@ var WorkflowCtx = class {
|
|
|
1150
1145
|
})}`);
|
|
1151
1146
|
const t0 = Date.now();
|
|
1152
1147
|
let result;
|
|
1153
|
-
let stepError;
|
|
1154
1148
|
let errored = false;
|
|
1155
1149
|
try {
|
|
1156
1150
|
result = await fn();
|
|
1157
1151
|
} catch (e) {
|
|
1158
|
-
if (e
|
|
1152
|
+
if (isSuspendSignal(e, StepSuspend)) throw e;
|
|
1159
1153
|
errored = true;
|
|
1160
|
-
stepError = e;
|
|
1161
1154
|
result = stepErrorMarker(key, e);
|
|
1155
|
+
console.log(`--- WAC: ${key} failed ---`);
|
|
1156
|
+
console.log(result?.result?.error?.stack ?? result?.message ?? "step failed");
|
|
1162
1157
|
}
|
|
1163
1158
|
const durationMs = Date.now() - t0;
|
|
1164
1159
|
const fastPathFlagRaw = (getEnv("WM_WAC_INLINE_FAST_PATH") ?? "1").trim().toLowerCase();
|
|
1165
1160
|
const fastPathEnabled = fastPathFlagRaw !== "0" && fastPathFlagRaw !== "false" && fastPathFlagRaw !== "off" && fastPathFlagRaw !== "no";
|
|
1166
1161
|
const jobId = getEnv("WM_JOB_ID");
|
|
1167
1162
|
const workspace = getEnv("WM_WORKSPACE");
|
|
1168
|
-
|
|
1163
|
+
let payload;
|
|
1164
|
+
let checkpointed;
|
|
1165
|
+
if (fastPathEnabled && jobId && workspace && OpenAPI.BASE && OpenAPI.TOKEN) try {
|
|
1166
|
+
payload = encodeCheckpointPayload({
|
|
1167
|
+
key,
|
|
1168
|
+
result: checkpointableResult(result),
|
|
1169
|
+
started_at: startedAt,
|
|
1170
|
+
duration_ms: durationMs
|
|
1171
|
+
});
|
|
1172
|
+
checkpointed = JSON.parse(payload).result;
|
|
1173
|
+
} catch (e) {
|
|
1174
|
+
console.log(`WAC v2 inline fast path could not serialize key ${key}, falling back to suspend: ${e}`);
|
|
1175
|
+
}
|
|
1176
|
+
if (payload !== void 0) {
|
|
1177
|
+
const body = payload;
|
|
1169
1178
|
const chainTail = this._inlineChain.then(async () => {
|
|
1170
1179
|
const ctrl = new AbortController();
|
|
1171
1180
|
const t = setTimeout(() => ctrl.abort(), 1e4);
|
|
@@ -1176,37 +1185,36 @@ var WorkflowCtx = class {
|
|
|
1176
1185
|
"Content-Type": "application/json",
|
|
1177
1186
|
Authorization: `Bearer ${OpenAPI.TOKEN}`
|
|
1178
1187
|
},
|
|
1179
|
-
body
|
|
1180
|
-
key,
|
|
1181
|
-
result,
|
|
1182
|
-
started_at: startedAt,
|
|
1183
|
-
duration_ms: durationMs
|
|
1184
|
-
}),
|
|
1188
|
+
body,
|
|
1185
1189
|
signal: ctrl.signal
|
|
1186
1190
|
});
|
|
1187
1191
|
if (!resp.ok) throw new Error(`inline_checkpoint API ${resp.status}`);
|
|
1192
|
+
if (!errored) return void 0;
|
|
1193
|
+
if (!(resp.headers.get("content-type") ?? "").includes("json")) return void 0;
|
|
1194
|
+
return (await resp.json())?.failure;
|
|
1188
1195
|
} finally {
|
|
1189
1196
|
clearTimeout(t);
|
|
1190
1197
|
}
|
|
1191
1198
|
});
|
|
1192
1199
|
this._inlineChain = chainTail.catch(() => {});
|
|
1193
1200
|
let fastPathOk = false;
|
|
1201
|
+
let storedFailure;
|
|
1194
1202
|
try {
|
|
1195
|
-
await chainTail;
|
|
1203
|
+
storedFailure = await chainTail;
|
|
1196
1204
|
fastPathOk = true;
|
|
1197
1205
|
} catch (e) {
|
|
1198
1206
|
console.log(`WAC v2 inline fast path failed for key ${key}, falling back to suspend: ${e}`);
|
|
1199
1207
|
}
|
|
1200
1208
|
if (fastPathOk) {
|
|
1201
|
-
if (errored) throw
|
|
1202
|
-
return
|
|
1209
|
+
if (errored) throw taskErrorFromMarker(storedFailure ?? checkpointed, `Step '${name}' failed`);
|
|
1210
|
+
return checkpointed;
|
|
1203
1211
|
}
|
|
1204
1212
|
}
|
|
1205
1213
|
this._raiseSuspend({
|
|
1206
1214
|
mode: "inline_checkpoint",
|
|
1207
1215
|
steps: [],
|
|
1208
1216
|
key,
|
|
1209
|
-
result,
|
|
1217
|
+
result: checkpointableResult(result),
|
|
1210
1218
|
started_at: startedAt,
|
|
1211
1219
|
duration_ms: durationMs
|
|
1212
1220
|
});
|
|
@@ -1218,11 +1226,18 @@ var WorkflowCtx = class {
|
|
|
1218
1226
|
this._pendingSuspend = suspend;
|
|
1219
1227
|
throw suspend;
|
|
1220
1228
|
}
|
|
1221
|
-
/**
|
|
1222
|
-
*
|
|
1223
|
-
|
|
1224
|
-
|
|
1225
|
-
|
|
1229
|
+
/** Park then raise the executing step's failure. Child mode only — in a parent
|
|
1230
|
+
* round a task failure is an ordinary `TaskError` the body may handle. */
|
|
1231
|
+
_raiseStepFailure(error) {
|
|
1232
|
+
this._pendingStepFailure = { error };
|
|
1233
|
+
throw error;
|
|
1234
|
+
}
|
|
1235
|
+
/** Re-throw a swallowed suspend or step failure at the next SDK call. It
|
|
1236
|
+
* happened before whatever the body is doing now, so it wins: the run is
|
|
1237
|
+
* unwinding either way and everything after it re-runs on the replay. Left
|
|
1238
|
+
* set, so a body that catches in a loop can't swallow it a second time. */
|
|
1239
|
+
_rethrowSwallowed() {
|
|
1240
|
+
if (this._pendingStepFailure) throw this._pendingStepFailure.error;
|
|
1226
1241
|
if (this._pendingSuspend) throw this._pendingSuspend;
|
|
1227
1242
|
}
|
|
1228
1243
|
/** Hand the runner a suspend the workflow body caught and swallowed, so it is
|
|
@@ -1233,16 +1248,30 @@ var WorkflowCtx = class {
|
|
|
1233
1248
|
this._pendingSuspend = null;
|
|
1234
1249
|
return s;
|
|
1235
1250
|
}
|
|
1251
|
+
/** Same for the executing step's failure, so the runner can fail the child job. */
|
|
1252
|
+
_takePendingStepFailure() {
|
|
1253
|
+
const f = this._pendingStepFailure;
|
|
1254
|
+
this._pendingStepFailure = null;
|
|
1255
|
+
return f;
|
|
1256
|
+
}
|
|
1236
1257
|
};
|
|
1237
1258
|
async function sleep(seconds) {
|
|
1238
1259
|
const ctx = _workflowCtx ?? Reflect.get(globalThis, "__wmill_wf_ctx");
|
|
1239
1260
|
if (ctx) return ctx._sleep(seconds);
|
|
1240
1261
|
await new Promise((r) => setTimeout(r, seconds * 1e3));
|
|
1241
1262
|
}
|
|
1263
|
+
/**
|
|
1264
|
+
* Execute `fn` inline and checkpoint the result. On replay the cached value is
|
|
1265
|
+
* returned without re-executing `fn`.
|
|
1266
|
+
*
|
|
1267
|
+
* `fn`'s result is encoded as JSON and decoded back before it is returned, so
|
|
1268
|
+
* the round that runs the body sees the same types every replay sees: a `Date`
|
|
1269
|
+
* comes back as a string, a `Map` as `{}`. {@link Jsonified} is that shape.
|
|
1270
|
+
*/
|
|
1242
1271
|
async function step(name, fn) {
|
|
1243
1272
|
const ctx = _workflowCtx ?? Reflect.get(globalThis, "__wmill_wf_ctx");
|
|
1244
1273
|
if (ctx) return ctx._runInlineStep(name, fn);
|
|
1245
|
-
return fn();
|
|
1274
|
+
return jsonRoundTrip(await fn());
|
|
1246
1275
|
}
|
|
1247
1276
|
/**
|
|
1248
1277
|
* Wrap an async function as a workflow task.
|
|
@@ -1253,6 +1282,10 @@ async function step(name, fn) {
|
|
|
1253
1282
|
*
|
|
1254
1283
|
* Inside a `workflow()`, calling a task dispatches it as a step.
|
|
1255
1284
|
* Outside a workflow, the function body executes directly.
|
|
1285
|
+
*
|
|
1286
|
+
* A task runs as its own job, so its result is always encoded as JSON and
|
|
1287
|
+
* decoded back before the caller sees it: a `Date` comes back as a string, a
|
|
1288
|
+
* `Map` as `{}`. {@link JsonifiedFn} is that shape.
|
|
1256
1289
|
*/
|
|
1257
1290
|
function task(fnOrPath, maybeFnOrOptions, maybeOptions) {
|
|
1258
1291
|
let fn;
|
|
@@ -1277,11 +1310,17 @@ function task(fnOrPath, maybeFnOrOptions, maybeOptions) {
|
|
|
1277
1310
|
else kwargs[`arg${i}`] = args[i];
|
|
1278
1311
|
const stepResult = ctx._nextStep(taskName, script, kwargs, "inline", taskOptions);
|
|
1279
1312
|
if (stepResult?._execute_directly) return (async () => {
|
|
1280
|
-
|
|
1313
|
+
let result;
|
|
1314
|
+
try {
|
|
1315
|
+
result = await fn(...args);
|
|
1316
|
+
} catch (e) {
|
|
1317
|
+
if (isSuspendSignal(e, StepSuspend)) throw e;
|
|
1318
|
+
ctx._raiseStepFailure(e);
|
|
1319
|
+
}
|
|
1281
1320
|
ctx._raiseSuspend({
|
|
1282
1321
|
mode: "step_complete",
|
|
1283
1322
|
steps: [],
|
|
1284
|
-
result
|
|
1323
|
+
result: checkpointableResult(result)
|
|
1285
1324
|
});
|
|
1286
1325
|
})();
|
|
1287
1326
|
return stepResult;
|
|
@@ -1303,7 +1342,7 @@ function task(fnOrPath, maybeFnOrOptions, maybeOptions) {
|
|
|
1303
1342
|
console.log(`Task ${taskName} (${jobId}) completed`);
|
|
1304
1343
|
return r;
|
|
1305
1344
|
})();
|
|
1306
|
-
else return fn(...args);
|
|
1345
|
+
else return Promise.resolve(fn(...args)).then(jsonRoundTrip);
|
|
1307
1346
|
};
|
|
1308
1347
|
Object.defineProperty(wrapper, "name", { value: taskName });
|
|
1309
1348
|
wrapper._is_task = true;
|
package/dist/core/OpenAPI.mjs
CHANGED
package/dist/index.d.ts
CHANGED
|
@@ -4,7 +4,7 @@ export { OpenAPI, type OpenAPIConfig } from './core/OpenAPI';
|
|
|
4
4
|
export * from './services.gen';
|
|
5
5
|
export * from './types.gen';
|
|
6
6
|
export type { DenoS3LightClientSettings } from "./s3Types";
|
|
7
|
-
export { type Base64, setClient, getVariable, setVariable, getResource, setResource, getResumeUrls, setState, setProgress, getProgress, getState, getIdToken, denoS3LightClientSettings, loadS3FileStream, loadS3File, writeS3File, deleteS3File, signS3Objects, signS3Object, getPresignedS3PublicUrls, getPresignedS3PublicUrl, task, taskScript, taskFlow, workflow, step, sleep, parallel, waitForApproval, getApprovalUrls, type TaskOptions, WorkflowCtx, _workflowCtx, setWorkflowCtx, StepSuspend, runScript, runScriptAsync, runScriptByPath, runScriptByHash, runScriptByPathAsync, runScriptByHashAsync, runFlow, runFlowAsync, waitJob, getRootJobId, setFlowUserState, getFlowUserState, usernameToEmail, requestInteractiveSlackApproval, type Sql, requestInteractiveTeamsApproval, appendToResultStream, streamResult, datatable, ducklake, upsertPartition, appendPartition, type DucklakeMaterializeOptions, type SqlStatement, type DatatableSqlTemplateFunction, type SqlTemplateFunction, type S3Object, type S3ObjectRecord, type S3ObjectURI, commitKafkaOffsets } from "./client";
|
|
7
|
+
export { type Base64, setClient, getVariable, setVariable, getResource, setResource, getResumeUrls, setState, setProgress, getProgress, getState, getIdToken, denoS3LightClientSettings, loadS3FileStream, loadS3File, writeS3File, deleteS3File, signS3Objects, signS3Object, getPresignedS3PublicUrls, getPresignedS3PublicUrl, task, taskScript, taskFlow, workflow, step, sleep, parallel, waitForApproval, getApprovalUrls, type TaskOptions, type Jsonified, type JsonifiedFn, WorkflowCtx, _workflowCtx, setWorkflowCtx, StepSuspend, runScript, runScriptAsync, runScriptByPath, runScriptByHash, runScriptByPathAsync, runScriptByHashAsync, runFlow, runFlowAsync, waitJob, getRootJobId, setFlowUserState, getFlowUserState, usernameToEmail, requestInteractiveSlackApproval, type Sql, requestInteractiveTeamsApproval, appendToResultStream, streamResult, datatable, ducklake, upsertPartition, appendPartition, type DucklakeMaterializeOptions, type SqlStatement, type DatatableSqlTemplateFunction, type SqlTemplateFunction, type S3Object, type S3ObjectRecord, type S3ObjectURI, commitKafkaOffsets } from "./client";
|
|
8
8
|
import { setClient, getVariable, setVariable, getResource, setResource, getResumeUrls, setState, setProgress, getProgress, getState, getIdToken, denoS3LightClientSettings, loadS3FileStream, loadS3File, writeS3File, deleteS3File, signS3Objects, signS3Object, getPresignedS3PublicUrls, getPresignedS3PublicUrl, task, taskScript, taskFlow, workflow, step, sleep, parallel, waitForApproval, getApprovalUrls, WorkflowCtx, setWorkflowCtx, StepSuspend, runScript, runScriptAsync, runScriptByPath, runScriptByHash, runScriptByPathAsync, runScriptByHashAsync, runFlow, runFlowAsync, waitJob, getRootJobId, setFlowUserState, getFlowUserState, usernameToEmail, requestInteractiveSlackApproval, requestInteractiveTeamsApproval, appendToResultStream, streamResult, datatable, ducklake, upsertPartition, appendPartition, getWorkspace, getStatePath, getInternalState, setInternalState, getResumeEndpoints, getResult, getResultMaybe, resolveDefaultResource, databaseUrlFromResource, base64ToUint8Array, uint8ArrayToBase64, parseS3Object, commitKafkaOffsets } from "./client";
|
|
9
9
|
import { AdminService, AuditService, FlowService, GranularAclService, GroupService, JobService, ResourceService, VariableService, ScriptService, ScheduleService, SettingsService, UserService, WorkspaceService, TeamsService } from "./services.gen";
|
|
10
10
|
declare const wmill: {
|