windmill-client 1.805.0 → 1.806.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.805.0",
129
+ VERSION: "1.806.0",
130
130
  WITH_CREDENTIALS: !getEnv$1("WM_RAW_APP"),
131
131
  interceptors: {
132
132
  request: new Interceptors(),
@@ -1655,6 +1655,59 @@ var GitSyncService = class {
1655
1655
  });
1656
1656
  }
1657
1657
  /**
1658
+ * List the GitLab projects a token can sync
1659
+ * Lists the projects the supplied GitLab token can push to, so a git repository resource can be filled in without hand-writing a project path. The token is used for this call only and is never stored. Requires workspace admin.
1660
+ * @param data The data for the request.
1661
+ * @param data.workspace
1662
+ * @param data.requestBody
1663
+ * @returns GitlabProject the projects the token can sync
1664
+ * @throws ApiError
1665
+ */
1666
+ static listGitlabProjects(data) {
1667
+ return request(OpenAPI, {
1668
+ method: "POST",
1669
+ url: "/w/{workspace}/git_sync/gitlab/projects",
1670
+ path: { workspace: data.workspace },
1671
+ body: data.requestBody,
1672
+ mediaType: "application/json"
1673
+ });
1674
+ }
1675
+ /**
1676
+ * Where a repository's credential comes from
1677
+ * Whether Windmill holds this repository's access token, and which host it talks to. `held` means this workspace stores it, `borrowed` means an ancestor does and it is not this workspace's to replace. Both absent means the repository authenticates with whatever its URL carries. Returns no secret. Requires workspace admin.
1678
+ * @param data The data for the request.
1679
+ * @param data.workspace
1680
+ * @param data.path Path of the git repository resource, with or without the `$res:` prefix. A path rather than a URL, because a resource URL may carry a token and a URL in a query string lands in logs.
1681
+ * @returns unknown where the credential comes from
1682
+ * @throws ApiError
1683
+ */
1684
+ static getCredentialOrigin(data) {
1685
+ return request(OpenAPI, {
1686
+ method: "GET",
1687
+ url: "/w/{workspace}/git_sync/credential/origin",
1688
+ path: { workspace: data.workspace },
1689
+ query: { path: data.path }
1690
+ });
1691
+ }
1692
+ /**
1693
+ * Store the credential for a git repository
1694
+ * Stores the access token a git repository authenticates with, so it does not have to be written into the repository URL or a workspace variable. The token is write-only: it is served only to a git-sync job that presents its own job token, and a fork of this workspace reads this copy instead of holding one of its own. Requires workspace admin.
1695
+ * @param data The data for the request.
1696
+ * @param data.workspace
1697
+ * @param data.requestBody
1698
+ * @returns string the credential was stored
1699
+ * @throws ApiError
1700
+ */
1701
+ static setGitCredential(data) {
1702
+ return request(OpenAPI, {
1703
+ method: "POST",
1704
+ url: "/w/{workspace}/git_sync/credential",
1705
+ path: { workspace: data.workspace },
1706
+ body: data.requestBody,
1707
+ mediaType: "application/json"
1708
+ });
1709
+ }
1710
+ /**
1658
1711
  * GHES installation callback
1659
1712
  * Register a self-managed GitHub App installation from GitHub Enterprise Server
1660
1713
  * @param data The data for the request.
@@ -1950,11 +2003,11 @@ var WorkspaceService = class {
1950
2003
  });
1951
2004
  }
1952
2005
  /**
1953
- * get github app token
2006
+ * get the git credential for a git-sync job (GitHub App token, or the credential stored for the repository)
1954
2007
  * @param data The data for the request.
1955
2008
  * @param data.workspace
1956
2009
  * @param data.requestBody jwt job token
1957
- * @returns unknown github app token
2010
+ * @returns unknown git credential
1958
2011
  * @throws ApiError
1959
2012
  */
1960
2013
  static getGithubAppToken(data) {
@@ -11059,6 +11112,29 @@ var JobService = class {
11059
11112
  });
11060
11113
  }
11061
11114
  /**
11115
+ * Get relations' project column lineage as one run saw it
11116
+ * The same answer as `assets/column_lineage`, for the project version a single job ran — including the dbt editor's parse of its own buffer, whose graph belongs to that job and is reachable no other way. One project answers here, the one the run is of, since the graph this annotates is that project's too. Authorized through the job, the same gate as `dbt_graph`. Reaching the run is not on its own enough to read the project: a caller with no access to the script gets its relations and `ref()` edges from `dbt_graph` and an empty answer here, exactly as that endpoint redacts the model's SQL.
11117
+ *
11118
+ * @param data The data for the request.
11119
+ * @param data.workspace
11120
+ * @param data.id The job whose graph the lineage is read from
11121
+ * @param data.assetPath The `dbt://` relations whose lineage to return. Repeated, once per relation, and answered as one union. At least one, and at most 1000 — a request naming none, or more than that, is refused rather than answered with an empty component.
11122
+ *
11123
+ * @returns DbtColumnLineage the relations' column-level lineage
11124
+ * @throws ApiError
11125
+ */
11126
+ static getDbtRunColumnLineage(data) {
11127
+ return request(OpenAPI, {
11128
+ method: "GET",
11129
+ url: "/w/{workspace}/jobs/dbt_column_lineage/{id}",
11130
+ path: {
11131
+ workspace: data.workspace,
11132
+ id: data.id
11133
+ },
11134
+ query: { asset_path: data.assetPath }
11135
+ });
11136
+ }
11137
+ /**
11062
11138
  * List the per-relation progress one job has recorded so far
11063
11139
  * Live materialization state for the relations a single job writes, keyed by relation. Polled while a run is in flight so a graph can move a node as its model completes. Authorized through the job, so a caller who may not read it is refused rather than told the run has no progress. An empty list means the job exists and has recorded nothing yet, or is unknown to this workspace.
11064
11140
  *
@@ -13012,6 +13088,33 @@ var NativeTriggerService = class {
13012
13088
  });
13013
13089
  }
13014
13090
  /**
13091
+ * set enabled state of native trigger
13092
+ * Enables or disables a native trigger. A disabled trigger stays registered on the
13093
+ * external service but starts no job when it fires.
13094
+ * Requires write access to the script or flow that the trigger is associated with.
13095
+ *
13096
+ * @param data The data for the request.
13097
+ * @param data.workspace
13098
+ * @param data.serviceName
13099
+ * @param data.externalId The external ID of the trigger from the external service
13100
+ * @param data.requestBody updated enabled state
13101
+ * @returns string native trigger enabled state updated
13102
+ * @throws ApiError
13103
+ */
13104
+ static setNativeTriggerEnabled(data) {
13105
+ return request(OpenAPI, {
13106
+ method: "POST",
13107
+ url: "/w/{workspace}/native_triggers/{service_name}/setenabled/{external_id}",
13108
+ path: {
13109
+ workspace: data.workspace,
13110
+ service_name: data.serviceName,
13111
+ external_id: data.externalId
13112
+ },
13113
+ body: data.requestBody,
13114
+ mediaType: "application/json"
13115
+ });
13116
+ }
13117
+ /**
13015
13118
  * list native triggers
13016
13119
  * Lists all native triggers for the specified service in the workspace.
13017
13120
  * @param data The data for the request.
@@ -16797,6 +16900,33 @@ var AssetService = class {
16797
16900
  });
16798
16901
  }
16799
16902
  /**
16903
+ * Column-level lineage of a set of dbt relations
16904
+ * The direct (`copy` / `mod`) column-to-column lineage the given relations' columns sit in — the connected component around them, from the engine's static analysis. Not their own edges, which would stop one hop out since a column trace walks transitively, and not a whole project's, which carries model families the selection cannot reach.
16905
+ * Several relations, answered as one union, because one selection reaches several: a script's output column can derive from columns of several dbt models. Unpinned, the component crosses projects — a relation one project produces is another's source — and the caller's access is decided again for every project it reaches, so a trace ends where their grants do. A pinned answer, by version here or by job on the run route, is one project's.
16906
+ * Its own endpoint rather than a field on the asset graph: the graph is folder-wide and polled by a run page, while this is rendered for one selection at a time. Empty for projects that did not opt into the analysis pass (`column_lineage: true`), which is the ordinary case. The indirect `scan` kind is stored but never served: it reaches every output column of its model.
16907
+ *
16908
+ * @param data The data for the request.
16909
+ * @param data.workspace
16910
+ * @param data.assetPath The `dbt://` relations whose lineage to return. Repeated, once per relation, and answered as one union. At least one, and at most 1000 — a request naming none, or more than that, is refused rather than answered with an empty component.
16911
+ *
16912
+ * @param data.dbtScriptHash The deployed version a view is drawing, when it is drawing one — the dbt editor, which shows a single project as of a single deploy. A version-pinned answer is that version's project alone, the same as a job-pinned one, and only the unpinned answer crosses projects: a pin says which stored graph is on screen, and another project's live graph is not part of it.
16913
+ * A run's or an editor buffer's own graph is not reachable here: that pins to a job, and costs the job-read gate — see `jobs/dbt_column_lineage/{id}`.
16914
+ *
16915
+ * @returns DbtColumnLineage the relations' column-level lineage
16916
+ * @throws ApiError
16917
+ */
16918
+ static getDbtColumnLineage(data) {
16919
+ return request(OpenAPI, {
16920
+ method: "GET",
16921
+ url: "/w/{workspace}/assets/column_lineage",
16922
+ path: { workspace: data.workspace },
16923
+ query: {
16924
+ asset_path: data.assetPath,
16925
+ dbt_script_hash: data.dbtScriptHash
16926
+ }
16927
+ });
16928
+ }
16929
+ /**
16800
16930
  * List every workspace DuckDB macro (deployed `// macros` libraries)
16801
16931
  * @param data The data for the request.
16802
16932
  * @param data.workspace
@@ -19021,6 +19151,35 @@ function checkpointableResult(value) {
19021
19151
  function jsonRoundTrip(value) {
19022
19152
  return JSON.parse(encodeCheckpointPayload({ value: checkpointableResult(value) })).value;
19023
19153
  }
19154
+ /** The worker deserializes a sleep into a `u32` of seconds and fails the whole
19155
+ * job on anything wider, so a delay a multiplier has run away with has to be
19156
+ * capped here rather than sent. */
19157
+ const MAX_SLEEP_SECONDS = 4294967295;
19158
+ /** Every attempt claims its keys before the first one is dispatched, so an
19159
+ * unbounded `attempts` is a workflow that hangs allocating rather than a very
19160
+ * patient one. */
19161
+ const MAX_RETRY_ATTEMPTS = 100;
19162
+ /** Rejected where the policy is written, so a workflow fails at its first line
19163
+ * rather than mid-run on a replay. */
19164
+ function assertUsableRetry(retry) {
19165
+ if (retry === void 0) return;
19166
+ const { attempts } = retry;
19167
+ if (!Number.isInteger(attempts) || attempts < 0 || attempts > MAX_RETRY_ATTEMPTS) throw new Error(`retry.attempts must be a whole number between 0 and ${MAX_RETRY_ATTEMPTS}, got ${attempts}`);
19168
+ }
19169
+ /** How many retries the policy asks for, defended against a value that reached
19170
+ * `_nextStep` without going through `assertUsableRetry`. */
19171
+ function retryAttempts(retry) {
19172
+ const attempts = Math.trunc(retry?.attempts ?? 0) || 0;
19173
+ return Math.min(Math.max(attempts, 0), MAX_RETRY_ATTEMPTS);
19174
+ }
19175
+ /** Seconds to wait before retry number `attempt` (0 is the first retry). */
19176
+ function retryDelaySeconds(retry, attempt) {
19177
+ const base = retry.delay ?? 0;
19178
+ if (!(base > 0)) return 0;
19179
+ const grown = base * Math.pow(retry.multiplier ?? 1, attempt);
19180
+ const seconds = Math.floor(Math.min(retry.max_delay ?? grown, grown, MAX_SLEEP_SECONDS));
19181
+ return seconds > 0 ? seconds : 0;
19182
+ }
19024
19183
  /** A step key travels as one path segment when its URLs are minted, so it must be
19025
19184
  * non-empty and free of `/` and dot segments — otherwise `waitForApproval` would
19026
19185
  * accept a key `getApprovalUrls` can never address. */
@@ -19052,6 +19211,11 @@ var WorkflowCtx = class {
19052
19211
  * into a `complete` — the parent would then record the caught branch's value as
19053
19212
  * a successful step. Boxed: the thrown value may be any falsy value. */
19054
19213
  _pendingStepFailure = null;
19214
+ /** Failed tasks whose rejection nothing has consumed, by step key. An unawaited
19215
+ * task is still dispatched and still fails, but nothing drives the rejecting
19216
+ * thenable it returned. The first `.then()` on that thenable drops the entry,
19217
+ * so what remains is only what the body never looked at. */
19218
+ _unobservedTaskFailures = new Map();
19055
19219
  /** When set, the task matching this key executes its inner function directly */
19056
19220
  _executingKey;
19057
19221
  /** Serializes fast-path POSTs across concurrent step() calls within one
@@ -19085,52 +19249,90 @@ var WorkflowCtx = class {
19085
19249
  }
19086
19250
  _nextStep(name, script, args = {}, dispatch_type = "inline", options) {
19087
19251
  this._rethrowSwallowed();
19088
- const key = this._allocKey(name || script || "step");
19089
- if (key in this.completed) {
19090
- const value = this.completed[key];
19091
- if (value && typeof value === "object" && value.__wmill_error) {
19092
- const err = taskErrorFromMarker(value, `Task '${name}' failed`);
19093
- return { then: (_resolve, reject) => {
19094
- if (reject) reject(err);
19095
- else throw err;
19096
- } };
19252
+ const stepName = name || script || "step";
19253
+ const maxRetries = retryAttempts(options?.retry);
19254
+ const baseKey = this._allocKey(stepName);
19255
+ const attemptKeys = [baseKey];
19256
+ const backoffKeys = [];
19257
+ for (let i = 0; i < maxRetries; i++) {
19258
+ backoffKeys.push(this._allocKey(`${baseKey}#retry${i + 2}`));
19259
+ attemptKeys.push(this._allocKey(`${baseKey}#${i + 2}`));
19260
+ }
19261
+ for (let attempt = 0;; attempt++) {
19262
+ const key = attemptKeys[attempt];
19263
+ if (key in this.completed) {
19264
+ const value = this.completed[key];
19265
+ if (value && typeof value === "object" && value.__wmill_error) {
19266
+ if (attempt < maxRetries) {
19267
+ this._retryBackoff(backoffKeys[attempt], baseKey, options.retry, attempt);
19268
+ continue;
19269
+ }
19270
+ const err = taskErrorFromMarker(value, `Task '${name}' failed`);
19271
+ this._unobservedTaskFailures.set(baseKey, err);
19272
+ return { then: (_resolve, reject) => {
19273
+ this._unobservedTaskFailures.delete(baseKey);
19274
+ if (reject) reject(err);
19275
+ else throw err;
19276
+ } };
19277
+ }
19278
+ return { then: (resolve$1) => resolve$1(value) };
19097
19279
  }
19098
- return { then: (resolve$1) => resolve$1(value) };
19280
+ if (this._executingKey === key) return {
19281
+ then: (resolve$1) => resolve$1(null),
19282
+ _execute_directly: true
19283
+ };
19284
+ if (this._executingKey !== null) return { then: () => new Promise(() => {}) };
19285
+ const stepInfo = {
19286
+ name: name || key,
19287
+ script: script || key,
19288
+ args,
19289
+ key,
19290
+ dispatch_type
19291
+ };
19292
+ if (options) {
19293
+ if (options.timeout !== void 0) stepInfo.timeout = options.timeout;
19294
+ if (options.tag !== void 0) stepInfo.tag = options.tag;
19295
+ if (options.cache_ttl !== void 0) stepInfo.cache_ttl = options.cache_ttl;
19296
+ if (options.priority !== void 0) stepInfo.priority = options.priority;
19297
+ if (options.concurrency_limit !== void 0) stepInfo.concurrent_limit = options.concurrency_limit;
19298
+ if (options.concurrency_key !== void 0) stepInfo.concurrency_key = options.concurrency_key;
19299
+ if (options.concurrency_time_window_s !== void 0) stepInfo.concurrency_time_window_s = options.concurrency_time_window_s;
19300
+ }
19301
+ this.pending.push(stepInfo);
19302
+ return { then: () => {
19303
+ if (this._suspended) return new Promise(() => {});
19304
+ this._suspended = true;
19305
+ const steps = [...this.pending];
19306
+ this.pending = [];
19307
+ const names = steps.map((s) => s.name).join(", ");
19308
+ console.log(`\n--- WAC: ${names} ---`);
19309
+ this._raiseSuspend({
19310
+ mode: steps.length > 1 ? "parallel" : "sequential",
19311
+ steps
19312
+ });
19313
+ } };
19099
19314
  }
19100
- if (this._executingKey === key) return {
19101
- then: (resolve$1) => resolve$1(null),
19102
- _execute_directly: true
19103
- };
19104
- if (this._executingKey !== null) return { then: () => new Promise(() => {}) };
19105
- const stepInfo = {
19106
- name: name || key,
19107
- script: script || key,
19108
- args,
19315
+ }
19316
+ /** Wait out the backoff between two attempts of a retried task, as a durable
19317
+ * sleep, and return once there is nothing to wait for — no delay configured,
19318
+ * or the sleep already in the checkpoint.
19319
+ *
19320
+ * Raises where it stands, the way `_sleep` does, rather than handing back a
19321
+ * thenable: a task call the body never awaits is still dispatched (the runner
19322
+ * flushes `pending`), so a backoff that only fired when awaited would drop
19323
+ * the retry and let the round report the workflow complete. */
19324
+ _retryBackoff(key, baseKey, retry, attempt) {
19325
+ const seconds = retryDelaySeconds(retry, attempt);
19326
+ if (seconds < 1) return;
19327
+ if (key in this.completed) return;
19328
+ if (this._executingKey !== null) return;
19329
+ console.log(`\n--- WAC: sleep(${key}, ${seconds}s) before retrying ${baseKey} ---`);
19330
+ this._raiseSuspend({
19331
+ mode: "sleep",
19109
19332
  key,
19110
- dispatch_type
19111
- };
19112
- if (options) {
19113
- if (options.timeout !== void 0) stepInfo.timeout = options.timeout;
19114
- if (options.tag !== void 0) stepInfo.tag = options.tag;
19115
- if (options.cache_ttl !== void 0) stepInfo.cache_ttl = options.cache_ttl;
19116
- if (options.priority !== void 0) stepInfo.priority = options.priority;
19117
- if (options.concurrency_limit !== void 0) stepInfo.concurrent_limit = options.concurrency_limit;
19118
- if (options.concurrency_key !== void 0) stepInfo.concurrency_key = options.concurrency_key;
19119
- if (options.concurrency_time_window_s !== void 0) stepInfo.concurrency_time_window_s = options.concurrency_time_window_s;
19120
- }
19121
- this.pending.push(stepInfo);
19122
- return { then: () => {
19123
- if (this._suspended) return new Promise(() => {});
19124
- this._suspended = true;
19125
- const steps = [...this.pending];
19126
- this.pending = [];
19127
- const names = steps.map((s) => s.name).join(", ");
19128
- console.log(`\n--- WAC: ${names} ---`);
19129
- this._raiseSuspend({
19130
- mode: steps.length > 1 ? "parallel" : "sequential",
19131
- steps
19132
- });
19133
- } };
19333
+ seconds,
19334
+ steps: []
19335
+ });
19134
19336
  }
19135
19337
  /** Return and clear any pending (unawaited) steps. */
19136
19338
  _flushPending() {
@@ -19297,6 +19499,13 @@ var WorkflowCtx = class {
19297
19499
  this._pendingStepFailure = null;
19298
19500
  return f;
19299
19501
  }
19502
+ /** Report the task failures the body never looked at, and forget them. Which
19503
+ * rounds may call this is the runner's constraint, stated where it is enforced. */
19504
+ _warnUnobservedTaskFailures() {
19505
+ if (this._executingKey !== null) return;
19506
+ for (const [key, err] of this._unobservedTaskFailures) console.log(`\n--- WAC: task '${key}' failed but was never awaited, so the workflow result does not reflect it: ${err.message} ---`);
19507
+ this._unobservedTaskFailures.clear();
19508
+ }
19300
19509
  };
19301
19510
  async function sleep(seconds) {
19302
19511
  const ctx = _workflowCtx ?? Reflect.get(globalThis, "__wmill_wf_ctx");
@@ -19322,9 +19531,11 @@ async function step(name, fn) {
19322
19531
  * @example
19323
19532
  * const extract_data = task(async (url: string) => { ... });
19324
19533
  * const run_external = task("f/external_script", async (x: number) => { ... });
19534
+ * const call_api = task(fetchOrders, { retry: { attempts: 3, delay: 30, multiplier: 2 } });
19325
19535
  *
19326
19536
  * Inside a `workflow()`, calling a task dispatches it as a step.
19327
- * Outside a workflow, the function body executes directly.
19537
+ * Outside a workflow, the function body executes directly and
19538
+ * {@link TaskOptions} — retry included — does not apply.
19328
19539
  *
19329
19540
  * A task runs as its own job, so its result is always encoded as JSON and
19330
19541
  * decoded back before the caller sees it: a `Date` comes back as a string, a
@@ -19342,6 +19553,7 @@ function task(fnOrPath, maybeFnOrOptions, maybeOptions) {
19342
19553
  fn = fnOrPath;
19343
19554
  taskOptions = maybeFnOrOptions;
19344
19555
  }
19556
+ assertUsableRetry(taskOptions?.retry);
19345
19557
  const taskName = fn.name || taskPath || "";
19346
19558
  const wrapper = function(...args) {
19347
19559
  const ctx = _workflowCtx ?? Reflect.get(globalThis, "__wmill_wf_ctx");
@@ -19400,6 +19612,7 @@ function task(fnOrPath, maybeFnOrOptions, maybeOptions) {
19400
19612
  * // inside workflow: await extract({ url: "https://..." })
19401
19613
  */
19402
19614
  function taskScript(path, options) {
19615
+ assertUsableRetry(options?.retry);
19403
19616
  const name = path.split("/").pop() || path;
19404
19617
  const wrapper = function(...args) {
19405
19618
  const ctx = _workflowCtx ?? Reflect.get(globalThis, "__wmill_wf_ctx");
@@ -19425,6 +19638,7 @@ function taskScript(path, options) {
19425
19638
  * // inside workflow: await pipeline({ input: data })
19426
19639
  */
19427
19640
  function taskFlow(path, options) {
19641
+ assertUsableRetry(options?.retry);
19428
19642
  const name = path.split("/").pop() || path;
19429
19643
  const wrapper = function(...args) {
19430
19644
  const ctx = _workflowCtx ?? Reflect.get(globalThis, "__wmill_wf_ctx");