windmill-client 1.773.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/index.js CHANGED
@@ -126,7 +126,7 @@ const OpenAPI = {
126
126
  PASSWORD: void 0,
127
127
  TOKEN: getEnv$1("WM_TOKEN"),
128
128
  USERNAME: void 0,
129
- VERSION: "1.773.0",
129
+ VERSION: "1.774.0",
130
130
  WITH_CREDENTIALS: true,
131
131
  interceptors: {
132
132
  request: new Interceptors(),
@@ -2761,6 +2761,24 @@ var WorkspaceService = class {
2761
2761
  });
2762
2762
  }
2763
2763
  /**
2764
+ * check what the data table's database lets its role do
2765
+ * @param data The data for the request.
2766
+ * @param data.workspace
2767
+ * @param data.datatableName
2768
+ * @returns unknown connection and privilege report
2769
+ * @throws ApiError
2770
+ */
2771
+ static testDataTableConnection(data) {
2772
+ return request(OpenAPI, {
2773
+ method: "GET",
2774
+ url: "/w/{workspace}/workspaces/test_datatable_connection/{datatable_name}",
2775
+ path: {
2776
+ workspace: data.workspace,
2777
+ datatable_name: data.datatableName
2778
+ }
2779
+ });
2780
+ }
2781
+ /**
2764
2782
  * list tables of all connected Datatables
2765
2783
  * @param data The data for the request.
2766
2784
  * @param data.workspace
@@ -9582,6 +9600,30 @@ var JobService = class {
9582
9600
  });
9583
9601
  }
9584
9602
  /**
9603
+ * get statuses and truncated results for all jobs of a flow job's execution tree
9604
+ * @param data The data for the request.
9605
+ * @param data.workspace
9606
+ * @param data.id
9607
+ * @param data.maxResultLen per-entry cap (in characters of JSON text) on result_prefix (default 2000, max 30000)
9608
+ * @param data.step step address to resolve to a single job instead of enumerating the tree: "b", "b/c", "b[12]" (1-based iteration/branch), composable as "b[12]/c"
9609
+ * @returns unknown per-job statuses and truncated results of the flow's execution tree, or the single resolved entry when step is provided
9610
+ * @throws ApiError
9611
+ */
9612
+ static getFlowAllResults(data) {
9613
+ return request(OpenAPI, {
9614
+ method: "GET",
9615
+ url: "/w/{workspace}/jobs_u/get_flow_all_results/{id}",
9616
+ path: {
9617
+ workspace: data.workspace,
9618
+ id: data.id
9619
+ },
9620
+ query: {
9621
+ max_result_len: data.maxResultLen,
9622
+ step: data.step
9623
+ }
9624
+ });
9625
+ }
9626
+ /**
9585
9627
  * get completed job logs tail
9586
9628
  * @param data The data for the request.
9587
9629
  * @param data.workspace
@@ -16453,6 +16495,108 @@ var HubPublishService = class {
16453
16495
  }
16454
16496
  };
16455
16497
 
16498
+ //#endregion
16499
+ //#region src/wacError.ts
16500
+ /** Error properties the executors already report as named fields, so they must
16501
+ * not be repeated inside `extra`. Kept identical to the skip-list in the
16502
+ * generated bun/deno job wrappers. */
16503
+ const SERIALIZED_ERROR_FIELDS = [
16504
+ "line",
16505
+ "name",
16506
+ "stack",
16507
+ "column",
16508
+ "message",
16509
+ "sourceURL",
16510
+ "originalLine",
16511
+ "originalColumn"
16512
+ ];
16513
+ /** Read a property off a value that may fight back: a proxy `get` trap or a
16514
+ * throwing accessor turns an ordinary field read into an exception. Callers
16515
+ * are on the failure-reporting path, where nothing has been checkpointed yet. */
16516
+ function safeRead(o, k) {
16517
+ try {
16518
+ return o?.[k];
16519
+ } catch {
16520
+ return void 0;
16521
+ }
16522
+ }
16523
+ /** @internal
16524
+ * Whether a caught value is the SDK's own suspend signal, which must be
16525
+ * rethrown rather than reported as a step failure.
16526
+ *
16527
+ * Both halves of the check can throw on a hostile value: `instanceof` consults
16528
+ * a proxy's `getPrototypeOf` trap, and reading `.name` its `get` trap. This
16529
+ * runs before anything has been checkpointed, so neither may escape — a value
16530
+ * that fights back is simply not a suspend. */
16531
+ function isSuspendSignal(e, suspendCtor) {
16532
+ try {
16533
+ if (e instanceof suspendCtor) return true;
16534
+ } catch {}
16535
+ return safeRead(e, "name") === "StepSuspend";
16536
+ }
16537
+ /** @internal
16538
+ * Serialize a failed `step()` body into the `__wmill_error` marker that task
16539
+ * failures also use, so it can be stored in `completed_steps`.
16540
+ *
16541
+ * The record's final shape is decided by the backend (`wac_failure_record`),
16542
+ * which normalizes task failures through the same function; what is built here
16543
+ * is the raw material plus the envelope the backend recognizes. */
16544
+ function stepErrorMarker(key, e) {
16545
+ const thrown = e;
16546
+ const rawName = safeRead(thrown, "name");
16547
+ const rawMessage = safeRead(thrown, "message");
16548
+ const rawStack = safeRead(thrown, "stack");
16549
+ const name = typeof rawName === "string" ? rawName : void 0;
16550
+ const message = typeof rawMessage === "string" ? rawMessage : void 0;
16551
+ const stack = typeof rawStack === "string" ? rawStack : void 0;
16552
+ const error = {};
16553
+ if (name) error.name = name;
16554
+ if (message !== void 0) error.message = message;
16555
+ if (stack) error.stack = stack;
16556
+ if (thrown !== null && typeof thrown === "object") {
16557
+ const extra = {};
16558
+ let keys = [];
16559
+ try {
16560
+ keys = Object.getOwnPropertyNames(thrown);
16561
+ } catch {
16562
+ keys = [];
16563
+ }
16564
+ for (const k of keys) {
16565
+ if (SERIALIZED_ERROR_FIELDS.includes(k)) continue;
16566
+ try {
16567
+ extra[k] = JSON.parse(JSON.stringify(thrown[k]));
16568
+ } catch {}
16569
+ }
16570
+ if (Object.keys(extra).length > 0) error.extra = extra;
16571
+ }
16572
+ let fallback;
16573
+ try {
16574
+ fallback = String(e);
16575
+ } catch {
16576
+ fallback = `unrepresentable ${typeof e} thrown`;
16577
+ }
16578
+ return {
16579
+ __wmill_error: true,
16580
+ message: message ?? fallback,
16581
+ step_key: key,
16582
+ result: { error }
16583
+ };
16584
+ }
16585
+ /** @internal
16586
+ * Rebuild the error a failed task or step throws. The run that produced the
16587
+ * failure and every later replay go through here: a catch is control flow,
16588
+ * `workflow()` re-runs its body from the top every round, so a handler that
16589
+ * branches on the failure it caught must be handed the same thing in every
16590
+ * round or it dispatches different tasks on the way back. */
16591
+ function taskErrorFromMarker(marker, fallbackMessage) {
16592
+ const err = new Error(marker?.message || fallbackMessage);
16593
+ err.name = "TaskError";
16594
+ err.result = marker?.result;
16595
+ err.step_key = marker?.step_key;
16596
+ err.child_job_id = marker?.child_job_id;
16597
+ return err;
16598
+ }
16599
+
16456
16600
  //#endregion
16457
16601
  //#region src/s3Types.ts
16458
16602
  /**
@@ -17786,31 +17930,24 @@ var StepSuspend = class extends Error {
17786
17930
  this.name = "StepSuspend";
17787
17931
  }
17788
17932
  };
17789
- /** Serialize a failed `step()` body into the `__wmill_error` marker that task
17790
- * failures also use, so it can be stored in `completed_steps`. */
17791
- function stepErrorMarker(key, e) {
17792
- const message = e instanceof Error ? e.message : String(e);
17793
- const type = e instanceof Error ? e.constructor?.name ?? e.name : typeof e;
17794
- return {
17795
- __wmill_error: true,
17796
- message,
17797
- step_key: key,
17798
- result: {
17799
- error: message,
17800
- type
17801
- }
17802
- };
17933
+ /** Encode a checkpoint payload the way the worker wrapper does on the suspend
17934
+ * path, so both arms record the same value for the same step. `undefined` maps
17935
+ * to null; a bigint to its digits, which the wrapper gets instead from the
17936
+ * `BigInt.prototype.toJSON` it installs and the SDK cannot count on. */
17937
+ function encodeCheckpointPayload(payload) {
17938
+ return JSON.stringify(payload, (_key, value) => typeof value === "undefined" ? null : typeof value === "bigint" ? value.toString() : value);
17803
17939
  }
17804
- /** Rebuild the error a failed step throws. Both the run that produced the
17805
- * failure and every later replay go through here, so a workflow's catch block
17806
- * sees the same shape either way. */
17807
- function stepErrorFromMarker(marker, name) {
17808
- const err = new Error(marker?.message || `Step '${name}' failed`);
17809
- err.name = "TaskError";
17810
- err.result = marker?.result;
17811
- err.step_key = marker?.step_key;
17812
- err.child_job_id = marker?.child_job_id;
17813
- return err;
17940
+ /** A whole result that `JSON.stringify` drops the key for leaves a checkpoint
17941
+ * with no `result`, which neither the endpoint nor `WacOutput` accepts. Only
17942
+ * the top level: nested, a dropped key is what every other bun path does. */
17943
+ function checkpointableResult(value) {
17944
+ return typeof value === "function" || typeof value === "symbol" ? null : value;
17945
+ }
17946
+ /** Put a value through the checkpoint's encoding without checkpointing it, so
17947
+ * the paths that never persist anything still hand back the shape the ones
17948
+ * that do would. */
17949
+ function jsonRoundTrip(value) {
17950
+ return JSON.parse(encodeCheckpointPayload({ value: checkpointableResult(value) })).value;
17814
17951
  }
17815
17952
  /** A step key travels as one path segment when its URLs are minted, so it must be
17816
17953
  * non-empty and free of `/` and dot segments — otherwise `waitForApproval` would
@@ -17838,6 +17975,11 @@ var WorkflowCtx = class {
17838
17975
  * `complete` whose step never reached `completed_steps`. Python is immune:
17839
17976
  * `_StepSuspend` derives from `BaseException`. */
17840
17977
  _pendingSuspend = null;
17978
+ /** The failure raised by the step this child round is executing. That exception
17979
+ * *is* the round's result, so a `catch` in the body must not be able to turn it
17980
+ * into a `complete` — the parent would then record the caught branch's value as
17981
+ * a successful step. Boxed: the thrown value may be any falsy value. */
17982
+ _pendingStepFailure = null;
17841
17983
  /** When set, the task matching this key executes its inner function directly */
17842
17984
  _executingKey;
17843
17985
  /** Serializes fast-path POSTs across concurrent step() calls within one
@@ -17870,16 +18012,12 @@ var WorkflowCtx = class {
17870
18012
  return key;
17871
18013
  }
17872
18014
  _nextStep(name, script, args = {}, dispatch_type = "inline", options) {
17873
- this._rethrowSwallowedSuspend();
18015
+ this._rethrowSwallowed();
17874
18016
  const key = this._allocKey(name || script || "step");
17875
18017
  if (key in this.completed) {
17876
18018
  const value = this.completed[key];
17877
18019
  if (value && typeof value === "object" && value.__wmill_error) {
17878
- const err = new Error(value.message || `Task '${name}' failed`);
17879
- err.name = "TaskError";
17880
- err.result = value.result;
17881
- err.step_key = value.step_key;
17882
- err.child_job_id = value.child_job_id;
18020
+ const err = taskErrorFromMarker(value, `Task '${name}' failed`);
17883
18021
  return { then: (_resolve, reject) => {
17884
18022
  if (reject) reject(err);
17885
18023
  else throw err;
@@ -17929,7 +18067,7 @@ var WorkflowCtx = class {
17929
18067
  return steps;
17930
18068
  }
17931
18069
  _waitForApproval(options) {
17932
- this._rethrowSwallowedSuspend();
18070
+ this._rethrowSwallowed();
17933
18071
  if (options?.key !== void 0) assertUsableStepKey(options.key, "waitForApproval key");
17934
18072
  const key = this._allocKey(options?.key || "approval");
17935
18073
  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.`);
@@ -17949,7 +18087,7 @@ var WorkflowCtx = class {
17949
18087
  });
17950
18088
  }
17951
18089
  _sleep(seconds) {
17952
- this._rethrowSwallowedSuspend();
18090
+ this._rethrowSwallowed();
17953
18091
  const key = this._allocKey("sleep");
17954
18092
  if (key in this.completed) return { then: (resolve$1) => resolve$1(void 0) };
17955
18093
  if (this._executingKey !== null) return { then: () => new Promise(() => {}) };
@@ -17962,11 +18100,11 @@ var WorkflowCtx = class {
17962
18100
  });
17963
18101
  }
17964
18102
  async _runInlineStep(name, fn) {
17965
- this._rethrowSwallowedSuspend();
18103
+ this._rethrowSwallowed();
17966
18104
  const key = this._allocKey(name || "step");
17967
18105
  if (key in this.completed) {
17968
18106
  const value = this.completed[key];
17969
- if (value && typeof value === "object" && value.__wmill_error) throw stepErrorFromMarker(value, name);
18107
+ if (value && typeof value === "object" && value.__wmill_error) throw taskErrorFromMarker(value, `Step '${name}' failed`);
17970
18108
  return value;
17971
18109
  }
17972
18110
  if (this._executingKey !== null) return new Promise(() => {});
@@ -17978,22 +18116,36 @@ var WorkflowCtx = class {
17978
18116
  })}`);
17979
18117
  const t0 = Date.now();
17980
18118
  let result;
17981
- let stepError;
17982
18119
  let errored = false;
17983
18120
  try {
17984
18121
  result = await fn();
17985
18122
  } catch (e) {
17986
- if (e?.name === "StepSuspend" || e instanceof StepSuspend) throw e;
18123
+ if (isSuspendSignal(e, StepSuspend)) throw e;
17987
18124
  errored = true;
17988
- stepError = e;
17989
18125
  result = stepErrorMarker(key, e);
18126
+ console.log(`--- WAC: ${key} failed ---`);
18127
+ console.log(result?.result?.error?.stack ?? result?.message ?? "step failed");
17990
18128
  }
17991
18129
  const durationMs = Date.now() - t0;
17992
18130
  const fastPathFlagRaw = (getEnv("WM_WAC_INLINE_FAST_PATH") ?? "1").trim().toLowerCase();
17993
18131
  const fastPathEnabled = fastPathFlagRaw !== "0" && fastPathFlagRaw !== "false" && fastPathFlagRaw !== "off" && fastPathFlagRaw !== "no";
17994
18132
  const jobId = getEnv("WM_JOB_ID");
17995
18133
  const workspace = getEnv("WM_WORKSPACE");
17996
- if (fastPathEnabled && jobId && workspace && OpenAPI.BASE && OpenAPI.TOKEN) {
18134
+ let payload;
18135
+ let checkpointed;
18136
+ if (fastPathEnabled && jobId && workspace && OpenAPI.BASE && OpenAPI.TOKEN) try {
18137
+ payload = encodeCheckpointPayload({
18138
+ key,
18139
+ result: checkpointableResult(result),
18140
+ started_at: startedAt,
18141
+ duration_ms: durationMs
18142
+ });
18143
+ checkpointed = JSON.parse(payload).result;
18144
+ } catch (e) {
18145
+ console.log(`WAC v2 inline fast path could not serialize key ${key}, falling back to suspend: ${e}`);
18146
+ }
18147
+ if (payload !== void 0) {
18148
+ const body = payload;
17997
18149
  const chainTail = this._inlineChain.then(async () => {
17998
18150
  const ctrl = new AbortController();
17999
18151
  const t = setTimeout(() => ctrl.abort(), 1e4);
@@ -18004,37 +18156,36 @@ var WorkflowCtx = class {
18004
18156
  "Content-Type": "application/json",
18005
18157
  Authorization: `Bearer ${OpenAPI.TOKEN}`
18006
18158
  },
18007
- body: JSON.stringify({
18008
- key,
18009
- result,
18010
- started_at: startedAt,
18011
- duration_ms: durationMs
18012
- }),
18159
+ body,
18013
18160
  signal: ctrl.signal
18014
18161
  });
18015
18162
  if (!resp.ok) throw new Error(`inline_checkpoint API ${resp.status}`);
18163
+ if (!errored) return void 0;
18164
+ if (!(resp.headers.get("content-type") ?? "").includes("json")) return void 0;
18165
+ return (await resp.json())?.failure;
18016
18166
  } finally {
18017
18167
  clearTimeout(t);
18018
18168
  }
18019
18169
  });
18020
18170
  this._inlineChain = chainTail.catch(() => {});
18021
18171
  let fastPathOk = false;
18172
+ let storedFailure;
18022
18173
  try {
18023
- await chainTail;
18174
+ storedFailure = await chainTail;
18024
18175
  fastPathOk = true;
18025
18176
  } catch (e) {
18026
18177
  console.log(`WAC v2 inline fast path failed for key ${key}, falling back to suspend: ${e}`);
18027
18178
  }
18028
18179
  if (fastPathOk) {
18029
- if (errored) throw Object.assign(stepErrorFromMarker(result, name), { cause: stepError });
18030
- return result;
18180
+ if (errored) throw taskErrorFromMarker(storedFailure ?? checkpointed, `Step '${name}' failed`);
18181
+ return checkpointed;
18031
18182
  }
18032
18183
  }
18033
18184
  this._raiseSuspend({
18034
18185
  mode: "inline_checkpoint",
18035
18186
  steps: [],
18036
18187
  key,
18037
- result,
18188
+ result: checkpointableResult(result),
18038
18189
  started_at: startedAt,
18039
18190
  duration_ms: durationMs
18040
18191
  });
@@ -18046,11 +18197,18 @@ var WorkflowCtx = class {
18046
18197
  this._pendingSuspend = suspend;
18047
18198
  throw suspend;
18048
18199
  }
18049
- /** Re-throw a swallowed suspend at the next SDK call. It happened before
18050
- * whatever the body is doing now, so it wins: the run is unwinding either
18051
- * way and everything after it re-runs on the replay. Left set, so a body
18052
- * that catches in a loop can't swallow it a second time. */
18053
- _rethrowSwallowedSuspend() {
18200
+ /** Park then raise the executing step's failure. Child mode only — in a parent
18201
+ * round a task failure is an ordinary `TaskError` the body may handle. */
18202
+ _raiseStepFailure(error) {
18203
+ this._pendingStepFailure = { error };
18204
+ throw error;
18205
+ }
18206
+ /** Re-throw a swallowed suspend or step failure at the next SDK call. It
18207
+ * happened before whatever the body is doing now, so it wins: the run is
18208
+ * unwinding either way and everything after it re-runs on the replay. Left
18209
+ * set, so a body that catches in a loop can't swallow it a second time. */
18210
+ _rethrowSwallowed() {
18211
+ if (this._pendingStepFailure) throw this._pendingStepFailure.error;
18054
18212
  if (this._pendingSuspend) throw this._pendingSuspend;
18055
18213
  }
18056
18214
  /** Hand the runner a suspend the workflow body caught and swallowed, so it is
@@ -18061,16 +18219,30 @@ var WorkflowCtx = class {
18061
18219
  this._pendingSuspend = null;
18062
18220
  return s;
18063
18221
  }
18222
+ /** Same for the executing step's failure, so the runner can fail the child job. */
18223
+ _takePendingStepFailure() {
18224
+ const f = this._pendingStepFailure;
18225
+ this._pendingStepFailure = null;
18226
+ return f;
18227
+ }
18064
18228
  };
18065
18229
  async function sleep(seconds) {
18066
18230
  const ctx = _workflowCtx ?? Reflect.get(globalThis, "__wmill_wf_ctx");
18067
18231
  if (ctx) return ctx._sleep(seconds);
18068
18232
  await new Promise((r) => setTimeout(r, seconds * 1e3));
18069
18233
  }
18234
+ /**
18235
+ * Execute `fn` inline and checkpoint the result. On replay the cached value is
18236
+ * returned without re-executing `fn`.
18237
+ *
18238
+ * `fn`'s result is encoded as JSON and decoded back before it is returned, so
18239
+ * the round that runs the body sees the same types every replay sees: a `Date`
18240
+ * comes back as a string, a `Map` as `{}`. {@link Jsonified} is that shape.
18241
+ */
18070
18242
  async function step(name, fn) {
18071
18243
  const ctx = _workflowCtx ?? Reflect.get(globalThis, "__wmill_wf_ctx");
18072
18244
  if (ctx) return ctx._runInlineStep(name, fn);
18073
- return fn();
18245
+ return jsonRoundTrip(await fn());
18074
18246
  }
18075
18247
  /**
18076
18248
  * Wrap an async function as a workflow task.
@@ -18081,6 +18253,10 @@ async function step(name, fn) {
18081
18253
  *
18082
18254
  * Inside a `workflow()`, calling a task dispatches it as a step.
18083
18255
  * Outside a workflow, the function body executes directly.
18256
+ *
18257
+ * A task runs as its own job, so its result is always encoded as JSON and
18258
+ * decoded back before the caller sees it: a `Date` comes back as a string, a
18259
+ * `Map` as `{}`. {@link JsonifiedFn} is that shape.
18084
18260
  */
18085
18261
  function task(fnOrPath, maybeFnOrOptions, maybeOptions) {
18086
18262
  let fn;
@@ -18105,11 +18281,17 @@ function task(fnOrPath, maybeFnOrOptions, maybeOptions) {
18105
18281
  else kwargs[`arg${i}`] = args[i];
18106
18282
  const stepResult = ctx._nextStep(taskName, script, kwargs, "inline", taskOptions);
18107
18283
  if (stepResult?._execute_directly) return (async () => {
18108
- const result = await fn(...args);
18284
+ let result;
18285
+ try {
18286
+ result = await fn(...args);
18287
+ } catch (e) {
18288
+ if (isSuspendSignal(e, StepSuspend)) throw e;
18289
+ ctx._raiseStepFailure(e);
18290
+ }
18109
18291
  ctx._raiseSuspend({
18110
18292
  mode: "step_complete",
18111
18293
  steps: [],
18112
- result
18294
+ result: checkpointableResult(result)
18113
18295
  });
18114
18296
  })();
18115
18297
  return stepResult;
@@ -18131,7 +18313,7 @@ function task(fnOrPath, maybeFnOrOptions, maybeOptions) {
18131
18313
  console.log(`Task ${taskName} (${jobId}) completed`);
18132
18314
  return r;
18133
18315
  })();
18134
- else return fn(...args);
18316
+ else return Promise.resolve(fn(...args)).then(jsonRoundTrip);
18135
18317
  };
18136
18318
  Object.defineProperty(wrapper, "name", { value: taskName });
18137
18319
  wrapper._is_task = true;