@gravito/flux 3.0.2 → 4.0.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.
@@ -1,117 +1,17 @@
1
- "use strict";Object.defineProperty(exports, "__esModule", {value: true}); function _interopRequireWildcard(obj) { if (obj && obj.__esModule) { return obj; } else { var newObj = {}; if (obj != null) { for (var key in obj) { if (Object.prototype.hasOwnProperty.call(obj, key)) { newObj[key] = obj[key]; } } } newObj.default = obj; return newObj; } } function _nullishCoalesce(lhs, rhsFn) { if (lhs != null) { return lhs; } else { return rhsFn(); } } function _optionalChain(ops) { let lastAccessLHS = undefined; let value = ops[0]; let i = 1; while (i < ops.length) { const op = ops[i]; const fn = ops[i + 1]; i += 2; if ((op === 'optionalAccess' || op === 'optionalCall') && value == null) { return undefined; } if (op === 'access' || op === 'optionalAccess') { lastAccessLHS = value; value = fn(value); } else if (op === 'call' || op === 'optionalCall') { value = fn((...args) => value.call(lastAccessLHS, ...args)); lastAccessLHS = undefined; } } return value; } var _class; var _class2; var _class3; var _class4; var _class5; var _class6; var _class7; var _class8;
1
+ "use strict";Object.defineProperty(exports, "__esModule", {value: true}); function _interopRequireWildcard(obj) { if (obj && obj.__esModule) { return obj; } else { var newObj = {}; if (obj != null) { for (var key in obj) { if (Object.prototype.hasOwnProperty.call(obj, key)) { newObj[key] = obj[key]; } } } newObj.default = obj; return newObj; } } function _nullishCoalesce(lhs, rhsFn) { if (lhs != null) { return lhs; } else { return rhsFn(); } } function _optionalChain(ops) { let lastAccessLHS = undefined; let value = ops[0]; let i = 1; while (i < ops.length) { const op = ops[i]; const fn = ops[i + 1]; i += 2; if ((op === 'optionalAccess' || op === 'optionalCall') && value == null) { return undefined; } if (op === 'access' || op === 'optionalAccess') { lastAccessLHS = value; value = fn(value); } else if (op === 'call' || op === 'optionalCall') { value = fn((...args) => value.call(lastAccessLHS, ...args)); lastAccessLHS = undefined; } } return value; } var _class; var _class2; var _class3; var _class4; var _class5; var _class6; var _class7; var _class8; var _class9;
2
+
3
+
4
+
2
5
 
3
- var _chunkYXBEYVGYcjs = require('./chunk-YXBEYVGY.cjs');
4
6
 
5
- // src/errors.ts
6
- var FluxErrorCode = /* @__PURE__ */ ((FluxErrorCode2) => {
7
- FluxErrorCode2["WORKFLOW_NOT_FOUND"] = "WORKFLOW_NOT_FOUND";
8
- FluxErrorCode2["WORKFLOW_INVALID_INPUT"] = "WORKFLOW_INVALID_INPUT";
9
- FluxErrorCode2["WORKFLOW_DEFINITION_CHANGED"] = "WORKFLOW_DEFINITION_CHANGED";
10
- FluxErrorCode2["WORKFLOW_NAME_MISMATCH"] = "WORKFLOW_NAME_MISMATCH";
11
- FluxErrorCode2["INVALID_STATE_TRANSITION"] = "INVALID_STATE_TRANSITION";
12
- FluxErrorCode2["WORKFLOW_NOT_SUSPENDED"] = "WORKFLOW_NOT_SUSPENDED";
13
- FluxErrorCode2["INVALID_STEP_INDEX"] = "INVALID_STEP_INDEX";
14
- FluxErrorCode2["STEP_TIMEOUT"] = "STEP_TIMEOUT";
15
- FluxErrorCode2["STEP_NOT_FOUND"] = "STEP_NOT_FOUND";
16
- FluxErrorCode2["CONCURRENT_MODIFICATION"] = "CONCURRENT_MODIFICATION";
17
- FluxErrorCode2["EMPTY_WORKFLOW"] = "EMPTY_WORKFLOW";
18
- FluxErrorCode2["NO_RECOVERY_ACTION"] = "NO_RECOVERY_ACTION";
19
- FluxErrorCode2["INVALID_JSON_POINTER"] = "INVALID_JSON_POINTER";
20
- FluxErrorCode2["INVALID_PATH_TRAVERSAL"] = "INVALID_PATH_TRAVERSAL";
21
- FluxErrorCode2["CANNOT_REPLACE_ROOT"] = "CANNOT_REPLACE_ROOT";
22
- FluxErrorCode2["CANNOT_REMOVE_ROOT"] = "CANNOT_REMOVE_ROOT";
23
- return FluxErrorCode2;
24
- })(FluxErrorCode || {});
25
- var FluxError = class extends Error {
26
- /**
27
- * Creates a new FluxError.
28
- *
29
- * @param message - Human-readable error description.
30
- * @param code - Machine-readable error code.
31
- * @param context - Additional metadata related to the error.
32
- */
33
- constructor(message, code, context) {
34
- super(message);
35
- this.code = code;
36
- this.context = context;
37
- this.name = "FluxError";
38
- }
39
- };
40
- function workflowNotFound(id) {
41
- return new FluxError(`Workflow not found: ${id}`, "WORKFLOW_NOT_FOUND" /* WORKFLOW_NOT_FOUND */, {
42
- workflowId: id
43
- });
44
- }
45
- function invalidStateTransition(from, to) {
46
- return new FluxError(
47
- `Invalid state transition: ${from} \u2192 ${to}`,
48
- "INVALID_STATE_TRANSITION" /* INVALID_STATE_TRANSITION */,
49
- { from, to }
50
- );
51
- }
52
- function invalidInput(workflowName) {
53
- return new FluxError(
54
- `Invalid input for workflow "${workflowName}"`,
55
- "WORKFLOW_INVALID_INPUT" /* WORKFLOW_INVALID_INPUT */,
56
- { workflowName }
57
- );
58
- }
59
- function workflowNameMismatch(expected, received) {
60
- return new FluxError(
61
- `Workflow name mismatch: ${received} !== ${expected}`,
62
- "WORKFLOW_NAME_MISMATCH" /* WORKFLOW_NAME_MISMATCH */,
63
- { expected, received }
64
- );
65
- }
66
- function workflowDefinitionChanged() {
67
- return new FluxError(
68
- "Workflow definition changed; operation is not safe",
69
- "WORKFLOW_DEFINITION_CHANGED" /* WORKFLOW_DEFINITION_CHANGED */
70
- );
71
- }
72
- function workflowNotSuspended(status) {
73
- return new FluxError(
74
- `Workflow is not suspended (status: ${status})`,
75
- "WORKFLOW_NOT_SUSPENDED" /* WORKFLOW_NOT_SUSPENDED */,
76
- { status }
77
- );
78
- }
79
- function stepNotFound(step) {
80
- return new FluxError(`Step not found: ${step}`, "STEP_NOT_FOUND" /* STEP_NOT_FOUND */, { step });
81
- }
82
- function invalidStepIndex(index) {
83
- return new FluxError(`Invalid step index: ${index}`, "INVALID_STEP_INDEX" /* INVALID_STEP_INDEX */, { index });
84
- }
85
- function emptyWorkflow(workflowName) {
86
- return new FluxError(`Workflow "${workflowName}" has no steps`, "EMPTY_WORKFLOW" /* EMPTY_WORKFLOW */, {
87
- workflowName
88
- });
89
- }
90
- function noRecoveryAction(stepName) {
91
- return new FluxError(
92
- `No recovery action registered for step: ${stepName}`,
93
- "NO_RECOVERY_ACTION" /* NO_RECOVERY_ACTION */,
94
- { stepName }
95
- );
96
- }
97
- function invalidJsonPointer(path) {
98
- return new FluxError(`Invalid JSON Pointer: ${path}`, "INVALID_JSON_POINTER" /* INVALID_JSON_POINTER */, {
99
- path
100
- });
101
- }
102
- function invalidPathTraversal(segment, current) {
103
- return new FluxError(
104
- `Cannot access property '${segment}' on ${current}`,
105
- "INVALID_PATH_TRAVERSAL" /* INVALID_PATH_TRAVERSAL */,
106
- { segment, currentType: typeof current }
107
- );
108
- }
109
- function cannotReplaceRoot() {
110
- return new FluxError("Cannot replace root object", "CANNOT_REPLACE_ROOT" /* CANNOT_REPLACE_ROOT */);
111
- }
112
- function cannotRemoveRoot() {
113
- return new FluxError("Cannot remove root object", "CANNOT_REMOVE_ROOT" /* CANNOT_REMOVE_ROOT */);
114
- }
7
+
8
+
9
+
10
+
11
+
12
+
13
+
14
+ var _chunkOAJWPPYGcjs = require('./chunk-OAJWPPYG.cjs');
115
15
 
116
16
  // src/builder/WorkflowBuilder.ts
117
17
  var WorkflowBuilder = (_class = class {
@@ -244,7 +144,7 @@ var WorkflowBuilder = (_class = class {
244
144
  */
245
145
  build() {
246
146
  if (this._steps.length === 0) {
247
- throw emptyWorkflow(this._name);
147
+ throw _chunkOAJWPPYGcjs.emptyWorkflow.call(void 0, this._name);
248
148
  }
249
149
  return {
250
150
  name: this._name,
@@ -713,7 +613,7 @@ var StateMachine = (_class3 = class extends EventTarget {constructor(...args2) {
713
613
  */
714
614
  transition(to) {
715
615
  if (!this.canTransition(to)) {
716
- throw invalidStateTransition(this._status, to);
616
+ throw _chunkOAJWPPYGcjs.invalidStateTransition.call(void 0, this._status, to);
717
617
  }
718
618
  const from = this._status;
719
619
  this._status = to;
@@ -1522,14 +1422,14 @@ function resolveDefinition(workflow) {
1522
1422
  function resolveStartIndex(definition, fromStep, fallback) {
1523
1423
  if (typeof fromStep === "number") {
1524
1424
  if (fromStep < 0 || fromStep >= definition.steps.length) {
1525
- throw invalidStepIndex(fromStep);
1425
+ throw _chunkOAJWPPYGcjs.invalidStepIndex.call(void 0, fromStep);
1526
1426
  }
1527
1427
  return fromStep;
1528
1428
  }
1529
1429
  if (typeof fromStep === "string") {
1530
1430
  const index = definition.steps.findIndex((step) => step.name === fromStep);
1531
1431
  if (index === -1) {
1532
- throw stepNotFound(fromStep);
1432
+ throw _chunkOAJWPPYGcjs.stepNotFound.call(void 0, fromStep);
1533
1433
  }
1534
1434
  return index;
1535
1435
  }
@@ -1544,7 +1444,11 @@ function resetHistoryFrom(ctx, startIndex) {
1544
1444
  entry.status = "pending";
1545
1445
  entry.startedAt = void 0;
1546
1446
  entry.completedAt = void 0;
1447
+ entry.suspendedAt = void 0;
1448
+ entry.compensatedAt = void 0;
1449
+ entry.waitingFor = void 0;
1547
1450
  entry.duration = void 0;
1451
+ entry.output = void 0;
1548
1452
  entry.error = void 0;
1549
1453
  entry.retries = 0;
1550
1454
  }
@@ -1581,7 +1485,7 @@ async function persistContext(ctx, storage, contextManager, definitionVersion) {
1581
1485
  const state = contextManager.toState(ctx);
1582
1486
  const stored = await storage.load(state.id);
1583
1487
  if (stored && stored.version !== state.version) {
1584
- throw new FluxError(
1488
+ throw new (0, _chunkOAJWPPYGcjs.FluxError)(
1585
1489
  "Concurrent modification detected",
1586
1490
  "CONCURRENT_MODIFICATION" /* CONCURRENT_MODIFICATION */
1587
1491
  );
@@ -1856,7 +1760,7 @@ var RecoveryManager = (_class6 = class {constructor() { _class6.prototype.__init
1856
1760
  async executeRecovery(stepName) {
1857
1761
  const action = this.actions.get(stepName);
1858
1762
  if (!action) {
1859
- throw noRecoveryAction(stepName);
1763
+ throw _chunkOAJWPPYGcjs.noRecoveryAction.call(void 0, stepName);
1860
1764
  }
1861
1765
  if (action.type === "manual") {
1862
1766
  await action.handler();
@@ -1954,6 +1858,7 @@ var RollbackManager = class {
1954
1858
  error: originalError.message
1955
1859
  });
1956
1860
  let compensatedCount = 0;
1861
+ let skippedCount = 0;
1957
1862
  for (let i = failedAtIndex - 1; i >= 0; i--) {
1958
1863
  const step = definition.steps[i];
1959
1864
  let execution = currentCtx.history[i];
@@ -1988,11 +1893,36 @@ var RollbackManager = class {
1988
1893
  } catch (err) {
1989
1894
  const error = err instanceof Error ? err : new Error(String(err));
1990
1895
  const action = this.recoveryManager.getAction(step.name);
1991
- if (_optionalChain([action, 'optionalAccess', _58 => _58.type]) === "skip") {
1896
+ if (_optionalChain([action, 'optionalAccess', _58 => _58.type]) === "retry") {
1897
+ const retryResult = await new CompensationRetryPolicy({
1898
+ ...this.retryPolicy.getConfig(),
1899
+ maxAttempts: _nullishCoalesce(action.maxAttempts, () => ( this.retryPolicy.getConfig().maxAttempts))
1900
+ }).execute(async () => {
1901
+ await _optionalChain([step, 'access', _59 => _59.compensate, 'optionalCall', _60 => _60(currentCtx)]);
1902
+ });
1903
+ if (retryResult.success) {
1904
+ execution = { ...execution, status: "compensated", compensatedAt: /* @__PURE__ */ new Date() };
1905
+ currentCtx = updateWorkflowContext(currentCtx, {
1906
+ history: currentCtx.history.map((h, idx) => idx === i ? execution : h)
1907
+ });
1908
+ compensatedCount++;
1909
+ await this.traceEmitter.stepCompensate(currentCtx, step.name, i);
1910
+ currentCtx = await this.persist(currentCtx);
1911
+ continue;
1912
+ }
1913
+ }
1914
+ if (_optionalChain([action, 'optionalAccess', _61 => _61.type]) === "skip") {
1915
+ execution = { ...execution, status: "completed", error: error.message };
1916
+ currentCtx = updateWorkflowContext(currentCtx, {
1917
+ history: currentCtx.history.map((h, idx) => idx === i ? execution : h)
1918
+ });
1919
+ currentCtx = await this.persist(currentCtx);
1920
+ skippedCount++;
1992
1921
  continue;
1993
1922
  }
1994
- if (_optionalChain([action, 'optionalAccess', _59 => _59.type]) === "abort") {
1923
+ if (_optionalChain([action, 'optionalAccess', _62 => _62.type]) === "abort") {
1995
1924
  currentCtx = updateWorkflowContext(currentCtx, { status: "compensation_failed" });
1925
+ currentCtx = await this.persist(currentCtx);
1996
1926
  await this.traceEmitter.emit({
1997
1927
  type: "workflow:error",
1998
1928
  timestamp: Date.now(),
@@ -2005,6 +1935,7 @@ var RollbackManager = class {
2005
1935
  }
2006
1936
  await this.recoveryManager.notifyRecoveryNeeded(currentCtx, step.name, error);
2007
1937
  currentCtx = updateWorkflowContext(currentCtx, { status: "compensation_failed" });
1938
+ currentCtx = await this.persist(currentCtx);
2008
1939
  await this.traceEmitter.emit({
2009
1940
  type: "workflow:error",
2010
1941
  timestamp: Date.now(),
@@ -2017,7 +1948,7 @@ var RollbackManager = class {
2017
1948
  }
2018
1949
  currentCtx = await this.persist(currentCtx);
2019
1950
  }
2020
- if (compensatedCount > 0) {
1951
+ if (compensatedCount > 0 || skippedCount > 0) {
2021
1952
  currentCtx = updateWorkflowContext(currentCtx, { status: "rolled_back" });
2022
1953
  await this.traceEmitter.emit({
2023
1954
  type: "workflow:rollback_complete",
@@ -2083,7 +2014,7 @@ var TraceEmitter = class {
2083
2014
  */
2084
2015
  async emit(event) {
2085
2016
  try {
2086
- await _optionalChain([this, 'access', _60 => _60.traceSink, 'optionalAccess', _61 => _61.emit, 'call', _62 => _62(event)]);
2017
+ await _optionalChain([this, 'access', _63 => _63.traceSink, 'optionalAccess', _64 => _64.emit, 'call', _65 => _65(event)]);
2087
2018
  } catch (e3) {
2088
2019
  }
2089
2020
  }
@@ -2194,7 +2125,7 @@ var TraceEmitter = class {
2194
2125
  stepName,
2195
2126
  stepIndex,
2196
2127
  duration: result.duration,
2197
- error: _optionalChain([result, 'access', _63 => _63.error, 'optionalAccess', _64 => _64.message]),
2128
+ error: _optionalChain([result, 'access', _66 => _66.error, 'optionalAccess', _67 => _67.message]),
2198
2129
  status: "failed"
2199
2130
  });
2200
2131
  }
@@ -2351,7 +2282,7 @@ var WorkflowExecutor = class {
2351
2282
  stateMachine.transition("running");
2352
2283
  currentCtx = updateWorkflowContext(currentCtx, { status: "running" });
2353
2284
  }
2354
- if (!_optionalChain([meta, 'optionalAccess', _65 => _65.resume]) && !_optionalChain([meta, 'optionalAccess', _66 => _66.retry])) {
2285
+ if (!_optionalChain([meta, 'optionalAccess', _68 => _68.resume]) && !_optionalChain([meta, 'optionalAccess', _69 => _69.retry])) {
2355
2286
  await this.traceEmitter.workflowStart(currentCtx);
2356
2287
  }
2357
2288
  for (let i = startIndex; i < definition.steps.length; ) {
@@ -2388,7 +2319,7 @@ var WorkflowExecutor = class {
2388
2319
  stateMachine.transition("completed");
2389
2320
  currentCtx = updateWorkflowContext(currentCtx, { status: "completed" });
2390
2321
  currentCtx = await this.persist(currentCtx);
2391
- _optionalChain([this, 'access', _67 => _67.config, 'access', _68 => _68.on, 'optionalAccess', _69 => _69.workflowComplete, 'optionalCall', _70 => _70(currentCtx)]);
2322
+ _optionalChain([this, 'access', _70 => _70.config, 'access', _71 => _71.on, 'optionalAccess', _72 => _72.workflowComplete, 'optionalCall', _73 => _73(currentCtx)]);
2392
2323
  await this.traceEmitter.workflowComplete(currentCtx, Date.now() - startTime);
2393
2324
  return {
2394
2325
  id: currentCtx.id,
@@ -2407,7 +2338,7 @@ var WorkflowExecutor = class {
2407
2338
  stateMachine.forceStatus("failed");
2408
2339
  currentCtx = updateWorkflowContext(currentCtx, { status: "failed" });
2409
2340
  currentCtx = await this.persist(currentCtx);
2410
- _optionalChain([this, 'access', _71 => _71.config, 'access', _72 => _72.on, 'optionalAccess', _73 => _73.workflowError, 'optionalCall', _74 => _74(currentCtx, err)]);
2341
+ _optionalChain([this, 'access', _74 => _74.config, 'access', _75 => _75.on, 'optionalAccess', _76 => _76.workflowError, 'optionalCall', _77 => _77(currentCtx, err)]);
2411
2342
  await this.traceEmitter.workflowError(currentCtx, err, Date.now() - startTime);
2412
2343
  return {
2413
2344
  id: currentCtx.id,
@@ -2431,7 +2362,7 @@ var WorkflowExecutor = class {
2431
2362
  currentCtx = this.contextManager.setStepName(currentCtx, index, step.name);
2432
2363
  execution = currentCtx.history[index];
2433
2364
  currentCtx = updateWorkflowContext(currentCtx, { currentStep: index });
2434
- _optionalChain([this, 'access', _75 => _75.config, 'access', _76 => _76.on, 'optionalAccess', _77 => _77.stepStart, 'optionalCall', _78 => _78(step.name, currentCtx)]);
2365
+ _optionalChain([this, 'access', _78 => _78.config, 'access', _79 => _79.on, 'optionalAccess', _80 => _80.stepStart, 'optionalCall', _81 => _81(step.name, currentCtx)]);
2435
2366
  await this.traceEmitter.stepStart(currentCtx, step.name, index, Boolean(step.commit));
2436
2367
  const { result, execution: updatedExecution } = await this.stepExecutor.execute(
2437
2368
  step,
@@ -2457,7 +2388,7 @@ var WorkflowExecutor = class {
2457
2388
  };
2458
2389
  return { updatedCtx: currentCtx, shouldReturn: true, result: suspendedResult };
2459
2390
  }
2460
- _optionalChain([this, 'access', _79 => _79.config, 'access', _80 => _80.on, 'optionalAccess', _81 => _81.stepComplete, 'optionalCall', _82 => _82(step.name, currentCtx, result)]);
2391
+ _optionalChain([this, 'access', _82 => _82.config, 'access', _83 => _83.on, 'optionalAccess', _84 => _84.stepComplete, 'optionalCall', _85 => _85(step.name, currentCtx, result)]);
2461
2392
  await this.traceEmitter.stepComplete(currentCtx, step.name, index, result);
2462
2393
  } else {
2463
2394
  await this.traceEmitter.stepError(currentCtx, step.name, index, result);
@@ -2490,14 +2421,14 @@ var WorkflowExecutor = class {
2490
2421
  break;
2491
2422
  }
2492
2423
  }
2493
- const lastIndex = _optionalChain([groupSteps, 'access', _83 => _83[groupSteps.length - 1], 'optionalAccess', _84 => _84.index]);
2424
+ const lastIndex = _optionalChain([groupSteps, 'access', _86 => _86[groupSteps.length - 1], 'optionalAccess', _87 => _87.index]);
2494
2425
  let currentCtx = ctx;
2495
2426
  const executeStepWrapper = async (step, stepCtx) => {
2496
- const stepIndex = _optionalChain([groupSteps, 'access', _85 => _85.find, 'call', _86 => _86((gs) => gs.step === step), 'optionalAccess', _87 => _87.index]);
2427
+ const stepIndex = _optionalChain([groupSteps, 'access', _88 => _88.find, 'call', _89 => _89((gs) => gs.step === step), 'optionalAccess', _90 => _90.index]);
2497
2428
  let execution = stepCtx.history[stepIndex];
2498
2429
  const localCtx = this.contextManager.setStepName(stepCtx, stepIndex, step.name);
2499
2430
  execution = localCtx.history[stepIndex];
2500
- _optionalChain([this, 'access', _88 => _88.config, 'access', _89 => _89.on, 'optionalAccess', _90 => _90.stepStart, 'optionalCall', _91 => _91(step.name, localCtx)]);
2431
+ _optionalChain([this, 'access', _91 => _91.config, 'access', _92 => _92.on, 'optionalAccess', _93 => _93.stepStart, 'optionalCall', _94 => _94(step.name, localCtx)]);
2501
2432
  await this.traceEmitter.stepStart(localCtx, step.name, stepIndex, Boolean(step.commit));
2502
2433
  const { result, execution: updatedExecution } = await this.stepExecutor.execute(
2503
2434
  step,
@@ -2505,7 +2436,7 @@ var WorkflowExecutor = class {
2505
2436
  execution
2506
2437
  );
2507
2438
  if (result.success) {
2508
- _optionalChain([this, 'access', _92 => _92.config, 'access', _93 => _93.on, 'optionalAccess', _94 => _94.stepComplete, 'optionalCall', _95 => _95(step.name, localCtx, result)]);
2439
+ _optionalChain([this, 'access', _95 => _95.config, 'access', _96 => _96.on, 'optionalAccess', _97 => _97.stepComplete, 'optionalCall', _98 => _98(step.name, localCtx, result)]);
2509
2440
  await this.traceEmitter.stepComplete(localCtx, step.name, stepIndex, result);
2510
2441
  } else {
2511
2442
  await this.traceEmitter.stepError(localCtx, step.name, stepIndex, result);
@@ -2520,13 +2451,13 @@ var WorkflowExecutor = class {
2520
2451
  executeStepWrapper
2521
2452
  );
2522
2453
  for (const execution of parallelResult.successes) {
2523
- const stepIndex = _optionalChain([groupSteps, 'access', _96 => _96.find, 'call', _97 => _97((gs) => gs.step.name === execution.name), 'optionalAccess', _98 => _98.index]);
2454
+ const stepIndex = _optionalChain([groupSteps, 'access', _99 => _99.find, 'call', _100 => _100((gs) => gs.step.name === execution.name), 'optionalAccess', _101 => _101.index]);
2524
2455
  currentCtx = updateWorkflowContext(currentCtx, {
2525
2456
  history: currentCtx.history.map((h, idx) => idx === stepIndex ? execution : h)
2526
2457
  });
2527
2458
  }
2528
2459
  for (const failure of parallelResult.failures) {
2529
- const stepIndex = _optionalChain([groupSteps, 'access', _99 => _99.find, 'call', _100 => _100((gs) => gs.step.name === failure.step.name), 'optionalAccess', _101 => _101.index]);
2460
+ const stepIndex = _optionalChain([groupSteps, 'access', _102 => _102.find, 'call', _103 => _103((gs) => gs.step.name === failure.step.name), 'optionalAccess', _104 => _104.index]);
2530
2461
  currentCtx = updateWorkflowContext(currentCtx, {
2531
2462
  history: currentCtx.history.map((h, idx) => idx === stepIndex ? failure.execution : h)
2532
2463
  });
@@ -2591,7 +2522,7 @@ var FluxEngine = class {
2591
2522
  this.storage = _nullishCoalesce(config.storage, () => ( new MemoryStorage()));
2592
2523
  this.contextManager = new ContextManager();
2593
2524
  this.traceEmitter = new TraceEmitter(config.trace);
2594
- if (_optionalChain([config, 'access', _102 => _102.optimizer, 'optionalAccess', _103 => _103.enabled])) {
2525
+ if (_optionalChain([config, 'access', _105 => _105.optimizer, 'optionalAccess', _106 => _106.enabled])) {
2595
2526
  this.dataOptimizer = new DataOptimizer({
2596
2527
  threshold: config.optimizer.threshold,
2597
2528
  defaultLocation: config.optimizer.defaultLocation
@@ -2626,7 +2557,7 @@ var FluxEngine = class {
2626
2557
  async execute(workflow, input) {
2627
2558
  const definition = resolveDefinition(workflow);
2628
2559
  if (definition.validateInput && !definition.validateInput(input)) {
2629
- throw invalidInput(definition.name);
2560
+ throw _chunkOAJWPPYGcjs.invalidInput.call(void 0, definition.name);
2630
2561
  }
2631
2562
  let ctx = this.contextManager.create(
2632
2563
  definition.name,
@@ -2660,7 +2591,7 @@ var FluxEngine = class {
2660
2591
  this.storage
2661
2592
  );
2662
2593
  } finally {
2663
- await _optionalChain([lock, 'optionalAccess', _104 => _104.release, 'call', _105 => _105()]);
2594
+ await _optionalChain([lock, 'optionalAccess', _107 => _107.release, 'call', _108 => _108()]);
2664
2595
  }
2665
2596
  }
2666
2597
  async resume(workflow, workflowId, options) {
@@ -2670,20 +2601,20 @@ var FluxEngine = class {
2670
2601
  if (!state) {
2671
2602
  return null;
2672
2603
  }
2673
- throw workflowNameMismatch(definition.name, state.name);
2604
+ throw _chunkOAJWPPYGcjs.workflowNameMismatch.call(void 0, definition.name, state.name);
2674
2605
  }
2675
2606
  if (state.history.length !== definition.steps.length) {
2676
- throw workflowDefinitionChanged();
2607
+ throw _chunkOAJWPPYGcjs.workflowDefinitionChanged.call(void 0, );
2677
2608
  }
2678
2609
  if (state.definitionVersion && definition.version && state.definitionVersion !== definition.version) {
2679
- _optionalChain([this, 'access', _106 => _106.config, 'access', _107 => _107.logger, 'optionalAccess', _108 => _108.warn, 'call', _109 => _109(
2610
+ _optionalChain([this, 'access', _109 => _109.config, 'access', _110 => _110.logger, 'optionalAccess', _111 => _111.warn, 'call', _112 => _112(
2680
2611
  `Workflow version mismatch: instance was created with v${state.definitionVersion}, but current definition is v${definition.version}. Continuing execution.`
2681
2612
  )]);
2682
2613
  }
2683
2614
  let ctx = this.contextManager.restore(
2684
2615
  state
2685
2616
  );
2686
- const startIndex = resolveStartIndex(definition, _optionalChain([options, 'optionalAccess', _110 => _110.fromStep]), ctx.currentStep);
2617
+ const startIndex = resolveStartIndex(definition, _optionalChain([options, 'optionalAccess', _113 => _113.fromStep]), ctx.currentStep);
2687
2618
  resetHistoryFrom(ctx, startIndex);
2688
2619
  ctx = updateWorkflowContext(ctx, { status: "pending", currentStep: startIndex });
2689
2620
  ctx = await this.persist(ctx);
@@ -2693,10 +2624,10 @@ var FluxEngine = class {
2693
2624
  const definition = resolveDefinition(workflow);
2694
2625
  const state = await this.storage.load(workflowId);
2695
2626
  if (!state) {
2696
- throw workflowNotFound(workflowId);
2627
+ throw _chunkOAJWPPYGcjs.workflowNotFound.call(void 0, workflowId);
2697
2628
  }
2698
2629
  if (state.status !== "suspended") {
2699
- throw workflowNotSuspended(state.status);
2630
+ throw _chunkOAJWPPYGcjs.workflowNotSuspended.call(void 0, state.status);
2700
2631
  }
2701
2632
  let ctx = this.contextManager.restore(
2702
2633
  state
@@ -2704,15 +2635,22 @@ var FluxEngine = class {
2704
2635
  const idx = ctx.currentStep;
2705
2636
  const exec = ctx.history[idx];
2706
2637
  if (!exec || exec.status !== "suspended" || exec.waitingFor !== signalName) {
2707
- const isSuspended = _optionalChain([exec, 'optionalAccess', _111 => _111.status]) === "suspended";
2708
- throw new FluxError(
2638
+ const isSuspended = _optionalChain([exec, 'optionalAccess', _114 => _114.status]) === "suspended";
2639
+ throw new (0, _chunkOAJWPPYGcjs.FluxError)(
2709
2640
  isSuspended ? `Workflow waiting for signal "${exec.waitingFor}", received "${signalName}"` : "Workflow state invalid: no suspended step found",
2710
2641
  isSuspended ? "INVALID_STATE_TRANSITION" /* INVALID_STATE_TRANSITION */ : "STEP_NOT_FOUND" /* STEP_NOT_FOUND */
2711
2642
  );
2712
2643
  }
2713
2644
  ctx = updateWorkflowContext(ctx, {
2714
2645
  history: ctx.history.map(
2715
- (h, i) => i === idx ? { ...h, status: "completed", completedAt: /* @__PURE__ */ new Date(), output: payload } : h
2646
+ (h, i) => i === idx ? {
2647
+ ...h,
2648
+ status: "completed",
2649
+ completedAt: /* @__PURE__ */ new Date(),
2650
+ suspendedAt: void 0,
2651
+ waitingFor: void 0,
2652
+ output: payload
2653
+ } : h
2716
2654
  )
2717
2655
  });
2718
2656
  await this.traceEmitter.emit({
@@ -2732,10 +2670,10 @@ var FluxEngine = class {
2732
2670
  if (!state) {
2733
2671
  return null;
2734
2672
  }
2735
- throw workflowNameMismatch(definition.name, state.name);
2673
+ throw _chunkOAJWPPYGcjs.workflowNameMismatch.call(void 0, definition.name, state.name);
2736
2674
  }
2737
2675
  if (state.history.length !== definition.steps.length) {
2738
- throw workflowDefinitionChanged();
2676
+ throw _chunkOAJWPPYGcjs.workflowDefinitionChanged.call(void 0, );
2739
2677
  }
2740
2678
  let ctx = this.contextManager.restore(
2741
2679
  state
@@ -2758,7 +2696,7 @@ var FluxEngine = class {
2758
2696
  async saveState(state) {
2759
2697
  const stored = await this.storage.load(state.id);
2760
2698
  if (stored && stored.version !== state.version) {
2761
- throw new FluxError(
2699
+ throw new (0, _chunkOAJWPPYGcjs.FluxError)(
2762
2700
  "Concurrent modification detected",
2763
2701
  "CONCURRENT_MODIFICATION" /* CONCURRENT_MODIFICATION */
2764
2702
  );
@@ -2803,7 +2741,7 @@ var FluxEngine = class {
2803
2741
  * Initializes the engine and its underlying storage, and starts the scheduler.
2804
2742
  */
2805
2743
  async init() {
2806
- await _optionalChain([this, 'access', _112 => _112.storage, 'access', _113 => _113.init, 'optionalCall', _114 => _114()]);
2744
+ await _optionalChain([this, 'access', _115 => _115.storage, 'access', _116 => _116.init, 'optionalCall', _117 => _117()]);
2807
2745
  this.cronTrigger.start();
2808
2746
  }
2809
2747
  /**
@@ -2811,7 +2749,7 @@ var FluxEngine = class {
2811
2749
  */
2812
2750
  async close() {
2813
2751
  this.cronTrigger.stop();
2814
- await _optionalChain([this, 'access', _115 => _115.storage, 'access', _116 => _116.close, 'optionalCall', _117 => _117()]);
2752
+ await _optionalChain([this, 'access', _118 => _118.storage, 'access', _119 => _119.close, 'optionalCall', _120 => _120()]);
2815
2753
  }
2816
2754
  async persist(ctx, definitionVersion) {
2817
2755
  return persistContext(ctx, this.storage, this.contextManager, definitionVersion);
@@ -2831,7 +2769,7 @@ var PostgreSQLStorage = (_class7 = class {
2831
2769
  */
2832
2770
  constructor(options = {}) {;_class7.prototype.__init13.call(this);
2833
2771
  this.options = options;
2834
- this.tableName = _nullishCoalesce(options.tableName, () => ( "flux_workflows"));
2772
+ this.tableName = validateSqlIdentifier(_nullishCoalesce(options.tableName, () => ( "flux_workflows")), "tableName");
2835
2773
  }
2836
2774
  /**
2837
2775
  * Initializes the database connection pool and schema.
@@ -2960,11 +2898,11 @@ var PostgreSQLStorage = (_class7 = class {
2960
2898
  let query = `SELECT * FROM ${this.tableName} WHERE 1=1`;
2961
2899
  const params = [];
2962
2900
  let paramIndex = 1;
2963
- if (_optionalChain([filter, 'optionalAccess', _118 => _118.name])) {
2901
+ if (_optionalChain([filter, 'optionalAccess', _121 => _121.name])) {
2964
2902
  query += ` AND name = $${paramIndex++}`;
2965
2903
  params.push(filter.name);
2966
2904
  }
2967
- if (_optionalChain([filter, 'optionalAccess', _119 => _119.status])) {
2905
+ if (_optionalChain([filter, 'optionalAccess', _122 => _122.status])) {
2968
2906
  if (Array.isArray(filter.status)) {
2969
2907
  const placeholders = filter.status.map(() => `$${paramIndex++}`).join(", ");
2970
2908
  query += ` AND status IN (${placeholders})`;
@@ -2974,16 +2912,16 @@ var PostgreSQLStorage = (_class7 = class {
2974
2912
  params.push(filter.status);
2975
2913
  }
2976
2914
  }
2977
- if (_optionalChain([filter, 'optionalAccess', _120 => _120.version])) {
2915
+ if (_optionalChain([filter, 'optionalAccess', _123 => _123.version])) {
2978
2916
  query += ` AND definition_version = $${paramIndex++}`;
2979
2917
  params.push(filter.version);
2980
2918
  }
2981
2919
  query += " ORDER BY created_at DESC";
2982
- if (_optionalChain([filter, 'optionalAccess', _121 => _121.limit])) {
2920
+ if (_optionalChain([filter, 'optionalAccess', _124 => _124.limit])) {
2983
2921
  query += ` LIMIT $${paramIndex++}`;
2984
2922
  params.push(filter.limit);
2985
2923
  }
2986
- if (_optionalChain([filter, 'optionalAccess', _122 => _122.offset])) {
2924
+ if (_optionalChain([filter, 'optionalAccess', _125 => _125.offset])) {
2987
2925
  query += ` OFFSET $${paramIndex++}`;
2988
2926
  params.push(filter.offset);
2989
2927
  }
@@ -3056,20 +2994,36 @@ var PostgreSQLStorage = (_class7 = class {
3056
2994
  await this.pool.query("VACUUM ANALYZE");
3057
2995
  }
3058
2996
  }, _class7);
2997
+ function validateSqlIdentifier(value, field) {
2998
+ if (!/^[A-Za-z_][A-Za-z0-9_]*$/.test(value)) {
2999
+ throw new (0, _chunkOAJWPPYGcjs.FluxError)(
3000
+ `Invalid ${field}: "${value}". Only letters, numbers, and underscores are allowed.`,
3001
+ "WORKFLOW_INVALID_INPUT" /* WORKFLOW_INVALID_INPUT */,
3002
+ { field, value }
3003
+ );
3004
+ }
3005
+ return value;
3006
+ }
3059
3007
 
3060
3008
  // src/trace/JsonFileTraceSink.ts
3061
3009
  var _promises = require('fs/promises');
3062
3010
  var _path = require('path');
3063
- var JsonFileTraceSink = class {
3011
+ var _core = require('@gravito/core');
3012
+ var JsonFileTraceSink = (_class8 = class {
3064
3013
 
3065
3014
 
3015
+ __init14() {this.fileSink = null}
3016
+ __init15() {this.buffer = []}
3017
+
3018
+ __init16() {this.adapter = _core.getDefaultRuntimeAdapter.call(void 0, )}
3066
3019
  /**
3067
- * Creates a new JSON file trace sink.
3020
+ * Creates a new JSON file trace sink with buffering support.
3068
3021
  *
3069
3022
  * @param options - Configuration options for the sink.
3070
3023
  */
3071
- constructor(options) {
3024
+ constructor(options) {;_class8.prototype.__init14.call(this);_class8.prototype.__init15.call(this);_class8.prototype.__init16.call(this);
3072
3025
  this.path = options.path;
3026
+ this.bufferSize = _nullishCoalesce(options.bufferSize, () => ( 50));
3073
3027
  this.ready = this.init(_nullishCoalesce(options.reset, () => ( true)));
3074
3028
  }
3075
3029
  /**
@@ -3080,14 +3034,22 @@ var JsonFileTraceSink = class {
3080
3034
  */
3081
3035
  async init(reset) {
3082
3036
  await _promises.mkdir.call(void 0, _path.dirname.call(void 0, this.path), { recursive: true });
3083
- if (reset) {
3084
- await _promises.writeFile.call(void 0, this.path, "", "utf8");
3037
+ if (this.adapter.createFileSink) {
3038
+ this.fileSink = await _optionalChain([this, 'access', _126 => _126.adapter, 'access', _127 => _127.createFileSink, 'optionalCall', _128 => _128(this.path)]);
3039
+ }
3040
+ if (reset && !this.fileSink) {
3041
+ if (this.adapter.writeFile) {
3042
+ await this.adapter.writeFile(this.path, "");
3043
+ }
3044
+ } else if (reset && this.fileSink) {
3045
+ this.fileSink.write("");
3085
3046
  }
3086
3047
  }
3087
3048
  /**
3088
3049
  * Appends a trace event to the file in NDJSON format.
3089
3050
  *
3090
- * Waits for initialization to complete before writing.
3051
+ * Events are buffered and written in batches using FileSink
3052
+ * for improved I/O efficiency. Waits for initialization to complete before writing.
3091
3053
  *
3092
3054
  * @param event - The trace event to record.
3093
3055
  * @throws {Error} If writing to the file fails.
@@ -3104,14 +3066,54 @@ var JsonFileTraceSink = class {
3104
3066
  */
3105
3067
  async emit(event) {
3106
3068
  await this.ready;
3107
- await _promises.appendFile.call(void 0, this.path, `${JSON.stringify(event)}
3108
- `, "utf8");
3069
+ const eventLine = `${JSON.stringify(event)}
3070
+ `;
3071
+ if (this.fileSink) {
3072
+ this.buffer.push(eventLine);
3073
+ if (this.buffer.length >= this.bufferSize) {
3074
+ await this.flushBuffer();
3075
+ }
3076
+ } else {
3077
+ if (this.adapter.appendFile) {
3078
+ await this.adapter.appendFile(this.path, eventLine);
3079
+ }
3080
+ }
3109
3081
  }
3110
- };
3082
+ /**
3083
+ * Flushes buffered events to disk.
3084
+ *
3085
+ * This is called automatically when the buffer reaches the configured size.
3086
+ * Can also be called manually to ensure all events are written.
3087
+ *
3088
+ * @throws {Error} If flushing fails.
3089
+ */
3090
+ async flushBuffer() {
3091
+ if (!this.fileSink || this.buffer.length === 0) {
3092
+ return;
3093
+ }
3094
+ const content = this.buffer.join("");
3095
+ this.fileSink.write(content);
3096
+ this.buffer = [];
3097
+ await this.fileSink.flush();
3098
+ }
3099
+ /**
3100
+ * Closes the sink and flushes any remaining buffered events.
3101
+ *
3102
+ * Should be called before process exit to ensure all events are persisted.
3103
+ *
3104
+ * @throws {Error} If closing or flushing fails.
3105
+ */
3106
+ async close() {
3107
+ await this.flushBuffer();
3108
+ if (this.fileSink) {
3109
+ await this.fileSink.end();
3110
+ }
3111
+ }
3112
+ }, _class8);
3111
3113
 
3112
3114
  // src/core/LockProvider.ts
3113
- var MemoryLockProvider = (_class8 = class {constructor() { _class8.prototype.__init14.call(this); }
3114
- __init14() {this.locks = /* @__PURE__ */ new Map()}
3115
+ var MemoryLockProvider = (_class9 = class {constructor() { _class9.prototype.__init17.call(this); }
3116
+ __init17() {this.locks = /* @__PURE__ */ new Map()}
3115
3117
  async acquire(resourceId, owner, ttl) {
3116
3118
  const now = Date.now();
3117
3119
  const existing = this.locks.get(resourceId);
@@ -3146,7 +3148,7 @@ var MemoryLockProvider = (_class8 = class {constructor() { _class8.prototype.__i
3146
3148
  release: () => this.release(id)
3147
3149
  };
3148
3150
  }
3149
- }, _class8);
3151
+ }, _class9);
3150
3152
 
3151
3153
  // src/core/RedisLockProvider.ts
3152
3154
  var RELEASE_LOCK_SCRIPT = `
@@ -3384,7 +3386,7 @@ var OrbitFlux = class _OrbitFlux {
3384
3386
  if (typeof storage === "string") {
3385
3387
  switch (storage) {
3386
3388
  case "sqlite":
3387
- storageAdapter = new (0, _chunkYXBEYVGYcjs.BunSQLiteStorage)({ path: dbPath });
3389
+ storageAdapter = new (0, _chunkOAJWPPYGcjs.BunSQLiteStorage)({ path: dbPath });
3388
3390
  break;
3389
3391
  default:
3390
3392
  storageAdapter = new MemoryStorage();
@@ -3392,7 +3394,7 @@ var OrbitFlux = class _OrbitFlux {
3392
3394
  } else {
3393
3395
  storageAdapter = storage;
3394
3396
  }
3395
- await _optionalChain([storageAdapter, 'access', _123 => _123.init, 'optionalCall', _124 => _124()]);
3397
+ await _optionalChain([storageAdapter, 'access', _129 => _129.init, 'optionalCall', _130 => _130()]);
3396
3398
  const engineConfig = {
3397
3399
  storage: storageAdapter,
3398
3400
  defaultRetries,
@@ -3423,6 +3425,16 @@ var OrbitFlux = class _OrbitFlux {
3423
3425
  };
3424
3426
  this.engine = new FluxEngine(engineConfig);
3425
3427
  core.container.instance(exposeAs, this.engine);
3428
+ const health = core.container.make("health");
3429
+ if (health) {
3430
+ const engine = this.engine;
3431
+ health.register("flux", async () => ({
3432
+ status: engine ? "healthy" : "unhealthy",
3433
+ details: {
3434
+ storage: typeof storage === "string" ? storage : "custom"
3435
+ }
3436
+ }));
3437
+ }
3426
3438
  core.logger.info(
3427
3439
  `[OrbitFlux] Initialized (Storage: ${typeof storage === "string" ? storage : "custom"})`
3428
3440
  );
@@ -3466,21 +3478,5 @@ var OrbitFlux = class _OrbitFlux {
3466
3478
 
3467
3479
 
3468
3480
 
3469
-
3470
-
3471
-
3472
-
3473
-
3474
-
3475
-
3476
-
3477
-
3478
-
3479
-
3480
-
3481
-
3482
-
3483
-
3484
-
3485
- exports.FluxErrorCode = FluxErrorCode; exports.FluxError = FluxError; exports.workflowNotFound = workflowNotFound; exports.invalidStateTransition = invalidStateTransition; exports.invalidInput = invalidInput; exports.workflowNameMismatch = workflowNameMismatch; exports.workflowDefinitionChanged = workflowDefinitionChanged; exports.workflowNotSuspended = workflowNotSuspended; exports.stepNotFound = stepNotFound; exports.invalidStepIndex = invalidStepIndex; exports.emptyWorkflow = emptyWorkflow; exports.noRecoveryAction = noRecoveryAction; exports.invalidJsonPointer = invalidJsonPointer; exports.invalidPathTraversal = invalidPathTraversal; exports.cannotReplaceRoot = cannotReplaceRoot; exports.cannotRemoveRoot = cannotRemoveRoot; exports.WorkflowBuilder = WorkflowBuilder; exports.createWorkflow = createWorkflow; exports.BatchExecutor = BatchExecutor; exports.ContextManager = ContextManager; exports.StateMachine = StateMachine; exports.CronTrigger = CronTrigger; exports.MemoryStorage = MemoryStorage; exports.StepExecutor = StepExecutor; exports.FluxEngine = FluxEngine; exports.PostgreSQLStorage = PostgreSQLStorage; exports.JsonFileTraceSink = JsonFileTraceSink; exports.MemoryLockProvider = MemoryLockProvider; exports.RedisLockProvider = RedisLockProvider; exports.FluxConsoleLogger = FluxConsoleLogger; exports.FluxSilentLogger = FluxSilentLogger; exports.OrbitFlux = OrbitFlux;
3486
- //# sourceMappingURL=chunk-WAPZDXSX.cjs.map
3481
+ exports.WorkflowBuilder = WorkflowBuilder; exports.createWorkflow = createWorkflow; exports.BatchExecutor = BatchExecutor; exports.ContextManager = ContextManager; exports.StateMachine = StateMachine; exports.CronTrigger = CronTrigger; exports.MemoryStorage = MemoryStorage; exports.StepExecutor = StepExecutor; exports.FluxEngine = FluxEngine; exports.PostgreSQLStorage = PostgreSQLStorage; exports.JsonFileTraceSink = JsonFileTraceSink; exports.MemoryLockProvider = MemoryLockProvider; exports.RedisLockProvider = RedisLockProvider; exports.FluxConsoleLogger = FluxConsoleLogger; exports.FluxSilentLogger = FluxSilentLogger; exports.OrbitFlux = OrbitFlux;
3482
+ //# sourceMappingURL=chunk-AVWZYY7U.cjs.map