@pumped-fn/core-next 0.5.81 → 0.5.82

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/CHANGELOG.md CHANGED
@@ -1,5 +1,43 @@
1
1
  # @pumped-fn/core-next
2
2
 
3
+ ## 0.5.82
4
+
5
+ ### Patch Changes
6
+
7
+ - 40be7e4: Fix unhandled promise rejections in Promised wrapper
8
+ - 596bf02: Add flow execution tracking and extension authoring improvements
9
+
10
+ **Flow Execution Tracking:**
11
+
12
+ - FlowExecution return type with id, status, abort, ctx access
13
+ - Cancellation via AbortController with ctx.signal and ctx.throwIfAborted()
14
+ - Timeout support at scope.exec() and ctx.exec() levels
15
+ - Status tracking with observable status changes
16
+ - Execution registry with auto-cleanup
17
+
18
+ **Extension Authoring:**
19
+
20
+ - Extension authoring documentation and examples
21
+ - Scope capabilities and lifecycle documentation
22
+ - Real-world patterns for extensions
23
+
24
+ **API Changes:**
25
+
26
+ - scope.exec() now uses named parameters: `scope.exec({ flow, input, ...options })`
27
+ - scope.exec() returns FlowExecution<T> (thenable, backward compatible)
28
+
29
+ **Migration:**
30
+
31
+ ```ts
32
+ // Before
33
+ await scope.exec(myFlow, input);
34
+ await scope.exec(myFlow, input, { tags: [tag1] });
35
+
36
+ // After
37
+ await scope.exec({ flow: myFlow, input });
38
+ await scope.exec({ flow: myFlow, input, tags: [tag1] });
39
+ ```
40
+
3
41
  ## 0.5.81
4
42
 
5
43
  ### Patch Changes
package/dist/index.cjs CHANGED
@@ -372,6 +372,7 @@ var Promised = class Promised {
372
372
  constructor(promise, executionDataPromise) {
373
373
  this.promise = promise instanceof Promised ? promise.promise : promise;
374
374
  this.executionDataPromise = executionDataPromise;
375
+ this.promise.catch(() => {});
375
376
  }
376
377
  static create(promise, executionDataPromise) {
377
378
  return new Promised(promise, executionDataPromise);
@@ -738,12 +739,20 @@ var FlowContext = class FlowContext {
738
739
  scope;
739
740
  reversedExtensions;
740
741
  tags;
741
- constructor(scope, extensions, tags, parent) {
742
+ abortController;
743
+ constructor(scope, extensions, tags, parent, abortController) {
742
744
  this.extensions = extensions;
743
745
  this.parent = parent;
744
746
  this.scope = scope;
745
747
  this.reversedExtensions = [...extensions].reverse();
746
748
  this.tags = tags;
749
+ this.abortController = abortController || new AbortController();
750
+ }
751
+ get signal() {
752
+ return this.abortController.signal;
753
+ }
754
+ throwIfAborted() {
755
+ if (this.signal.aborted) throw new Error("Flow execution cancelled");
747
756
  }
748
757
  resolve(executor) {
749
758
  return this.scope.resolve(executor);
@@ -792,45 +801,133 @@ var FlowContext = class FlowContext {
792
801
  this.contextData.set(key, value);
793
802
  return value;
794
803
  }
795
- run(key, fn, ...params) {
796
- if (!this.journal) this.journal = /* @__PURE__ */ new Map();
797
- const flowName = this.find(flowMeta.flowName) || "unknown";
798
- const depth = this.get(flowMeta.depth);
799
- const journalKey = `${flowName}:${depth}:${key}`;
800
- const promise = (async () => {
801
- const journal = this.journal;
802
- const isReplay = journal.has(journalKey);
803
- const executeCore = () => {
804
- if (isReplay) {
805
- const entry = journal.get(journalKey);
806
- if (isErrorEntry(entry)) throw entry.error;
807
- return Promised.create(Promise.resolve(entry));
808
- }
809
- return Promised.try(async () => {
810
- const result = params.length > 0 ? await fn(...params) : await fn();
811
- journal.set(journalKey, result);
812
- return result;
813
- }).catch((error) => {
814
- journal.set(journalKey, {
815
- __error: true,
816
- error
817
- });
818
- throw error;
804
+ exec(keyOrFlowOrConfig, flowOrInput, inputOrUndefined) {
805
+ if (typeof keyOrFlowOrConfig === "object" && keyOrFlowOrConfig !== null && !("factory" in keyOrFlowOrConfig)) {
806
+ const config = keyOrFlowOrConfig;
807
+ this.throwIfAborted();
808
+ const childAbort = new AbortController();
809
+ let timeoutId;
810
+ if (config.timeout) timeoutId = setTimeout(() => {
811
+ if (!childAbort.signal.aborted) childAbort.abort(/* @__PURE__ */ new Error(`Operation timeout after ${config.timeout}ms`));
812
+ }, config.timeout);
813
+ if (this.signal.aborted) {
814
+ if (timeoutId) clearTimeout(timeoutId);
815
+ childAbort.abort(this.signal.reason);
816
+ } else this.signal.addEventListener("abort", () => {
817
+ if (timeoutId) clearTimeout(timeoutId);
818
+ childAbort.abort(this.signal.reason);
819
+ }, { once: true });
820
+ if ("flow" in config) {
821
+ const flow$2 = config.flow;
822
+ const input$1 = config.input;
823
+ if (config.key) {
824
+ if (!this.journal) this.journal = /* @__PURE__ */ new Map();
825
+ const journalKey = `${this.find(flowMeta.flowName) || "unknown"}:${this.get(flowMeta.depth)}:${config.key}`;
826
+ const promise$1 = (async () => {
827
+ const journal = this.journal;
828
+ if (journal.has(journalKey)) {
829
+ const entry = journal.get(journalKey);
830
+ if (isErrorEntry(entry)) throw entry.error;
831
+ return entry;
832
+ }
833
+ this.throwIfAborted();
834
+ const handler = await this.scope.resolve(flow$2);
835
+ const definition = flowDefinitionMeta.readFrom(flow$2);
836
+ if (definition) {
837
+ const validated = validate(definition.input, input$1);
838
+ const childContext = new FlowContext(this.scope, this.extensions, config.tags, this, childAbort);
839
+ childContext.initializeExecutionContext(definition.name, false);
840
+ try {
841
+ const result = await handler(childContext, validated);
842
+ validate(definition.output, result);
843
+ journal.set(journalKey, result);
844
+ return result;
845
+ } catch (error) {
846
+ journal.set(journalKey, {
847
+ __error: true,
848
+ error
849
+ });
850
+ throw error;
851
+ } finally {
852
+ if (timeoutId) clearTimeout(timeoutId);
853
+ }
854
+ } else throw new Error("Flow definition not found");
855
+ })();
856
+ return Promised.create(promise$1);
857
+ } else return this.scope.resolve(flow$2).map(async (handler) => {
858
+ this.throwIfAborted();
859
+ const definition = flowDefinitionMeta.readFrom(flow$2);
860
+ if (definition) {
861
+ const validated = validate(definition.input, input$1);
862
+ const childContext = new FlowContext(this.scope, this.extensions, config.tags, this, childAbort);
863
+ childContext.initializeExecutionContext(definition.name, false);
864
+ try {
865
+ const result = await handler(childContext, validated);
866
+ validate(definition.output, result);
867
+ return result;
868
+ } finally {
869
+ if (timeoutId) clearTimeout(timeoutId);
870
+ }
871
+ } else throw new Error("Flow definition not found");
819
872
  });
820
- };
821
- return this.wrapWithExtensions(executeCore, {
822
- kind: "journal",
823
- key,
824
- flowName,
825
- depth,
826
- isReplay,
827
- context: this,
828
- params: params.length > 0 ? params : void 0
829
- })();
830
- })();
831
- return Promised.create(promise);
832
- }
833
- exec(keyOrFlow, flowOrInput, inputOrUndefined) {
873
+ } else if ("fn" in config) {
874
+ const fn = config.fn;
875
+ const params = "params" in config ? config.params || [] : [];
876
+ if (config.key) {
877
+ if (!this.journal) this.journal = /* @__PURE__ */ new Map();
878
+ const flowName = this.find(flowMeta.flowName) || "unknown";
879
+ const depth = this.get(flowMeta.depth);
880
+ const journalKey = `${flowName}:${depth}:${config.key}`;
881
+ const promise$1 = (async () => {
882
+ const journal = this.journal;
883
+ const isReplay = journal.has(journalKey);
884
+ const executeCore = () => {
885
+ if (isReplay) {
886
+ const entry = journal.get(journalKey);
887
+ if (isErrorEntry(entry)) throw entry.error;
888
+ return Promised.create(Promise.resolve(entry));
889
+ }
890
+ return Promised.try(async () => {
891
+ const result = await fn(...params);
892
+ journal.set(journalKey, result);
893
+ return result;
894
+ }).catch((error) => {
895
+ journal.set(journalKey, {
896
+ __error: true,
897
+ error
898
+ });
899
+ throw error;
900
+ });
901
+ };
902
+ const executor = this.wrapWithExtensions(executeCore, {
903
+ kind: "journal",
904
+ key: config.key,
905
+ flowName,
906
+ depth,
907
+ isReplay,
908
+ context: this,
909
+ params: params.length > 0 ? params : void 0
910
+ });
911
+ try {
912
+ return await executor();
913
+ } finally {
914
+ if (timeoutId) clearTimeout(timeoutId);
915
+ }
916
+ })();
917
+ return Promised.create(promise$1);
918
+ } else {
919
+ this.throwIfAborted();
920
+ return Promised.try(async () => {
921
+ try {
922
+ return await fn(...params);
923
+ } finally {
924
+ if (timeoutId) clearTimeout(timeoutId);
925
+ }
926
+ });
927
+ }
928
+ } else throw new Error("Invalid config: must have either 'flow' or 'fn'");
929
+ }
930
+ const keyOrFlow = keyOrFlowOrConfig;
834
931
  if (typeof keyOrFlow === "string") {
835
932
  if (!this.journal) this.journal = /* @__PURE__ */ new Map();
836
933
  const key = keyOrFlow;
@@ -999,11 +1096,29 @@ var FlowContext = class FlowContext {
999
1096
  };
1000
1097
  function execute(flow$1, input, options) {
1001
1098
  if (options && "scope" in options) {
1002
- if (options.details === true) return options.scope.exec(flow$1, input, {
1003
- tags: options.executionTags,
1004
- details: true
1099
+ const execution$1 = options.scope.exec({
1100
+ flow: flow$1,
1101
+ input,
1102
+ tags: options.executionTags
1005
1103
  });
1006
- return options.scope.exec(flow$1, input, { tags: options.executionTags });
1104
+ if (options.details === true) return Promised.create(execution$1.result.then(async (r) => {
1105
+ const ctx = await execution$1.result.ctx();
1106
+ if (!ctx) throw new Error("Execution context not available");
1107
+ return {
1108
+ success: true,
1109
+ result: r,
1110
+ ctx
1111
+ };
1112
+ }).catch(async (error) => {
1113
+ const ctx = await execution$1.result.ctx();
1114
+ if (!ctx) throw new Error("Execution context not available");
1115
+ return {
1116
+ success: false,
1117
+ error,
1118
+ ctx
1119
+ };
1120
+ }));
1121
+ return execution$1.result;
1007
1122
  }
1008
1123
  const scope = options ? createScope({
1009
1124
  initialValues: options.initialValues,
@@ -1011,15 +1126,31 @@ function execute(flow$1, input, options) {
1011
1126
  extensions: options.extensions,
1012
1127
  tags: options.scopeTags
1013
1128
  }) : createScope();
1014
- if (options?.details === true) {
1015
- const result$1 = scope.exec(flow$1, input, {
1016
- tags: options.executionTags,
1017
- details: true
1018
- });
1019
- return Promised.create(result$1.then((r) => scope.dispose().then(() => r)), result$1.ctx());
1020
- }
1021
- const result = scope.exec(flow$1, input, { tags: options?.executionTags });
1022
- return Promised.create(result.then((r) => scope.dispose().then(() => r)), result.ctx());
1129
+ const execution = scope.exec({
1130
+ flow: flow$1,
1131
+ input,
1132
+ tags: options?.executionTags
1133
+ });
1134
+ if (options?.details === true) return Promised.create(execution.result.then(async (r) => {
1135
+ await scope.dispose();
1136
+ const ctx = await execution.result.ctx();
1137
+ if (!ctx) throw new Error("Execution context not available");
1138
+ return {
1139
+ success: true,
1140
+ result: r,
1141
+ ctx
1142
+ };
1143
+ }).catch(async (error) => {
1144
+ await scope.dispose();
1145
+ const ctx = await execution.result.ctx();
1146
+ if (!ctx) throw new Error("Execution context not available");
1147
+ return {
1148
+ success: false,
1149
+ error,
1150
+ ctx
1151
+ };
1152
+ }));
1153
+ return Promised.create(execution.result.then((r) => scope.dispose().then(() => r)), execution.result.ctx());
1023
1154
  }
1024
1155
  function flowImpl(first, second, third) {
1025
1156
  if (typeof first === "function") {
@@ -1071,6 +1202,78 @@ function flowImpl(first, second, third) {
1071
1202
  }
1072
1203
  const flow = Object.assign(flowImpl, { execute });
1073
1204
 
1205
+ //#endregion
1206
+ //#region src/flow-execution.ts
1207
+ var FlowExecutionImpl = class {
1208
+ result;
1209
+ id;
1210
+ flowName;
1211
+ abort;
1212
+ _status = "pending";
1213
+ statusCallbacks = /* @__PURE__ */ new Set();
1214
+ callbackErrors = [];
1215
+ _ctx;
1216
+ statusTracking;
1217
+ statusTrackingActive = false;
1218
+ constructor(config) {
1219
+ this.id = config.id;
1220
+ this.flowName = config.flowName;
1221
+ this.abort = config.abort;
1222
+ this._ctx = config.ctx;
1223
+ this.result = config.result;
1224
+ this.statusTracking = config.statusTracking ?? null;
1225
+ }
1226
+ get status() {
1227
+ this["~ensureStatusTracking"]();
1228
+ return this._status;
1229
+ }
1230
+ get ctx() {
1231
+ return this._ctx ?? void 0;
1232
+ }
1233
+ get statusCallbackErrors() {
1234
+ return this.callbackErrors;
1235
+ }
1236
+ "~setStatus"(newStatus) {
1237
+ if (this._status === newStatus) return;
1238
+ this._status = newStatus;
1239
+ for (const callback of this.statusCallbacks) Promise.resolve(callback(newStatus, this)).catch((err) => {
1240
+ const error = err instanceof Error ? err : new Error(String(err));
1241
+ this.callbackErrors.push(error);
1242
+ console.error("Error in status change callback:", err);
1243
+ });
1244
+ }
1245
+ "~setCtx"(ctx) {
1246
+ this._ctx = ctx;
1247
+ }
1248
+ "~ensureStatusTracking"() {
1249
+ if (this.statusTrackingActive || !this.statusTracking) return;
1250
+ this.statusTrackingActive = true;
1251
+ const { promise, timeoutId, abortController } = this.statusTracking;
1252
+ promise.then(async () => {
1253
+ const ctx = await promise.ctx().catch(() => void 0);
1254
+ if (ctx) this["~setCtx"](ctx);
1255
+ this["~setStatus"]("completed");
1256
+ }).catch(async () => {
1257
+ const ctx = await promise.ctx().catch(() => void 0);
1258
+ if (ctx) this["~setCtx"](ctx);
1259
+ if (abortController.signal.aborted) this["~setStatus"]("cancelled");
1260
+ else this["~setStatus"]("failed");
1261
+ }).finally(() => {
1262
+ if (timeoutId) clearTimeout(timeoutId);
1263
+ });
1264
+ }
1265
+ onStatusChange(callback) {
1266
+ this["~ensureStatusTracking"]();
1267
+ this.statusCallbacks.add(callback);
1268
+ return () => {
1269
+ this.statusCallbacks.delete(callback);
1270
+ };
1271
+ }
1272
+ then(onfulfilled, onrejected) {
1273
+ return this.result.then(onfulfilled, onrejected);
1274
+ }
1275
+ };
1276
+
1074
1277
  //#endregion
1075
1278
  //#region src/scope.ts
1076
1279
  var AccessorImpl = class {
@@ -1279,6 +1482,7 @@ function getExecutor(e) {
1279
1482
  var BaseScope = class {
1280
1483
  disposed = false;
1281
1484
  cache = /* @__PURE__ */ new Map();
1485
+ executions = /* @__PURE__ */ new Map();
1282
1486
  onEvents = {
1283
1487
  change: /* @__PURE__ */ new Set(),
1284
1488
  release: /* @__PURE__ */ new Set(),
@@ -1570,6 +1774,7 @@ var BaseScope = class {
1570
1774
  this.onEvents.change.clear();
1571
1775
  this.onEvents.release.clear();
1572
1776
  this.onEvents.error.clear();
1777
+ this.executions.clear();
1573
1778
  })());
1574
1779
  }
1575
1780
  onUpdate(e, cb) {
@@ -1647,37 +1852,56 @@ var BaseScope = class {
1647
1852
  use(extension$1) {
1648
1853
  return this.useExtension(extension$1);
1649
1854
  }
1650
- exec(flow$1, input, options) {
1855
+ exec(config) {
1651
1856
  this["~ensureNotDisposed"]();
1652
- if (options?.details === true) {
1653
- const result = this["~executeFlow"](flow$1, input, options.tags);
1654
- return Promised.create(result.then(async (r) => {
1655
- const ctx = await result.ctx();
1656
- if (!ctx) throw new Error("Execution context not available");
1657
- return {
1658
- success: true,
1659
- result: r,
1660
- ctx
1661
- };
1662
- }).catch(async (error) => {
1663
- const ctx = await result.ctx();
1664
- if (!ctx) throw new Error("Execution context not available");
1665
- return {
1666
- success: false,
1667
- error,
1668
- ctx
1669
- };
1670
- }));
1857
+ const executionId = typeof crypto !== "undefined" && crypto.randomUUID ? crypto.randomUUID() : `exec-${Date.now()}-${Math.random()}`;
1858
+ const abortController = new AbortController();
1859
+ let timeoutId;
1860
+ if (config.timeout) timeoutId = setTimeout(() => {
1861
+ if (!abortController.signal.aborted) abortController.abort(/* @__PURE__ */ new Error(`Flow execution timeout after ${config.timeout}ms`));
1862
+ }, config.timeout);
1863
+ let flowPromise;
1864
+ let flowName;
1865
+ if ("flow" in config) {
1866
+ flowPromise = this["~executeFlow"](config.flow, config.input, config.tags, abortController);
1867
+ flowName = flowDefinitionMeta.readFrom(config.flow)?.name;
1868
+ } else {
1869
+ flowPromise = Promised.create((async () => {
1870
+ const deps = await this["~resolveDependencies"](config.dependencies, { [executorSymbol]: "main" });
1871
+ if ("input" in config) return config.fn(deps, config.input);
1872
+ else return config.fn(deps);
1873
+ })());
1874
+ flowName = void 0;
1671
1875
  }
1672
- return this["~executeFlow"](flow$1, input, options?.tags);
1876
+ const execution = new FlowExecutionImpl({
1877
+ id: executionId,
1878
+ flowName,
1879
+ abort: abortController,
1880
+ result: flowPromise,
1881
+ ctx: null,
1882
+ statusTracking: {
1883
+ promise: flowPromise,
1884
+ timeoutId: timeoutId ?? null,
1885
+ abortController
1886
+ }
1887
+ });
1888
+ this.executions.set(executionId, {
1889
+ execution,
1890
+ startTime: Date.now()
1891
+ });
1892
+ flowPromise.finally(() => {
1893
+ this.executions.delete(executionId);
1894
+ });
1895
+ execution["~setStatus"]("running");
1896
+ return execution;
1673
1897
  }
1674
- "~executeFlow"(flow$1, input, executionTags) {
1898
+ "~executeFlow"(flow$1, input, executionTags, abortController) {
1675
1899
  let resolveSnapshot;
1676
1900
  const snapshotPromise = new Promise((resolve) => {
1677
1901
  resolveSnapshot = resolve;
1678
1902
  });
1679
1903
  const promise = (async () => {
1680
- const context = new FlowContext(this, this.extensions, executionTags);
1904
+ const context = new FlowContext(this, this.extensions, executionTags, void 0, abortController);
1681
1905
  try {
1682
1906
  const executeCore = () => {
1683
1907
  return this.resolve(flow$1).map(async (handler) => {
package/dist/index.d.cts CHANGED
@@ -229,14 +229,25 @@ declare namespace Core {
229
229
  onError<T>(executor: Executor<T>, callback: ErrorCallback<T>): Cleanup;
230
230
  onError(callback: GlobalErrorCallback): Cleanup;
231
231
  useExtension(extension: Extension.Extension): Cleanup;
232
- exec<S, I = undefined>(flow: Core.Executor<Flow.Handler<S, I>>, input?: I, options?: {
232
+ exec<S, I>(config: {
233
+ flow: Executor<Flow.Handler<S, I>>;
234
+ input?: I;
235
+ timeout?: number;
233
236
  tags?: Tag.Tagged[];
234
- details?: false;
235
- }): Promised<S>;
236
- exec<S, I = undefined>(flow: Core.Executor<Flow.Handler<S, I>>, input: I | undefined, options: {
237
+ }): Flow.FlowExecution<S>;
238
+ exec<S, D extends DependencyLike>(config: {
239
+ dependencies: D;
240
+ fn: (deps: InferOutput<D>) => S | Promise<S>;
241
+ timeout?: number;
237
242
  tags?: Tag.Tagged[];
238
- details: true;
239
- }): Promised<Flow.ExecutionDetails<S>>;
243
+ }): Flow.FlowExecution<S>;
244
+ exec<S, I, D extends DependencyLike>(config: {
245
+ dependencies: D;
246
+ fn: (deps: InferOutput<D>, input: I) => S | Promise<S>;
247
+ input: I;
248
+ timeout?: number;
249
+ tags?: Tag.Tagged[];
250
+ }): Flow.FlowExecution<S>;
240
251
  }
241
252
  }
242
253
  declare class FlowError extends Error {
@@ -306,14 +317,38 @@ declare namespace Flow {
306
317
  type C = {
307
318
  readonly scope: Core.Scope;
308
319
  readonly tags: Tag.Tagged[] | undefined;
320
+ readonly signal: AbortSignal;
321
+ throwIfAborted(): void;
309
322
  get<T>(accessor: Tag.Tag<T, false> | Tag.Tag<T, true>): T;
310
323
  find<T>(accessor: Tag.Tag<T, false>): T | undefined;
311
324
  find<T>(accessor: Tag.Tag<T, true>): T;
312
325
  set<T>(accessor: Tag.Tag<T, false> | Tag.Tag<T, true>, value: T): void;
313
- run<T>(key: string, fn: () => Promised<T> | T): Promised<T>;
314
- run<T, P extends readonly unknown[]>(key: string, fn: (...args: P) => Promised<T> | T, ...params: P): Promised<T>;
315
326
  exec<F extends UFlow>(flow: F, input: InferInput<F>): Promised<InferOutput<F>>;
316
327
  exec<F extends UFlow>(key: string, flow: F, input: InferInput<F>): Promised<InferOutput<F>>;
328
+ exec<F extends UFlow>(config: {
329
+ flow: F;
330
+ input: InferInput<F>;
331
+ key?: string;
332
+ timeout?: number;
333
+ retry?: number;
334
+ tags?: Tag.Tagged[];
335
+ }): Promised<InferOutput<F>>;
336
+ exec<T>(config: {
337
+ fn: () => T | Promise<T>;
338
+ params?: never;
339
+ key?: string;
340
+ timeout?: number;
341
+ retry?: number;
342
+ tags?: Tag.Tagged[];
343
+ }): Promised<T>;
344
+ exec<Fn extends (...args: any[]) => any>(config: {
345
+ fn: Fn;
346
+ params: Parameters<Fn>;
347
+ key?: string;
348
+ timeout?: number;
349
+ retry?: number;
350
+ tags?: Tag.Tagged[];
351
+ }): Promised<ReturnType<Fn>>;
317
352
  parallel<T extends readonly Promised<any>[]>(promises: [...T]): Promised<ParallelResult<{ [K in keyof T]: T[K] extends Promised<infer R> ? R : never }>>;
318
353
  parallelSettled<T extends readonly Promised<any>[]>(promises: [...T]): Promised<ParallelSettledResult<{ [K in keyof T]: T[K] extends Promised<infer R> ? R : never }>>;
319
354
  };
@@ -334,6 +369,18 @@ declare namespace Flow {
334
369
  error: unknown;
335
370
  ctx: ExecutionData;
336
371
  };
372
+ type ExecutionStatus = "pending" | "running" | "completed" | "failed" | "cancelled";
373
+ interface FlowExecution<T> {
374
+ readonly result: Promised<T>;
375
+ readonly id: string;
376
+ readonly flowName: string | undefined;
377
+ readonly status: ExecutionStatus;
378
+ readonly ctx: ExecutionData | undefined;
379
+ readonly abort: AbortController;
380
+ readonly statusCallbackErrors: readonly Error[];
381
+ onStatusChange(callback: (status: ExecutionStatus, execution: FlowExecution<T>) => void | Promise<void>): Core.Cleanup;
382
+ then<TResult1 = T, TResult2 = never>(onfulfilled?: ((value: T) => TResult1 | PromiseLike<TResult1>) | null | undefined, onrejected?: ((reason: unknown) => TResult2 | PromiseLike<TResult2>) | null | undefined): PromiseLike<TResult1 | TResult2>;
383
+ }
337
384
  }
338
385
  declare namespace Extension {
339
386
  type Operation = {