@gravito/flux 3.0.2 → 3.0.3

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,6 +1,6 @@
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
2
 
3
- var _chunkYXBEYVGYcjs = require('./chunk-YXBEYVGY.cjs');
3
+ var _chunkZE2RDS47cjs = require('./chunk-ZE2RDS47.cjs');
4
4
 
5
5
  // src/errors.ts
6
6
  var FluxErrorCode = /* @__PURE__ */ ((FluxErrorCode2) => {
@@ -1544,7 +1544,11 @@ function resetHistoryFrom(ctx, startIndex) {
1544
1544
  entry.status = "pending";
1545
1545
  entry.startedAt = void 0;
1546
1546
  entry.completedAt = void 0;
1547
+ entry.suspendedAt = void 0;
1548
+ entry.compensatedAt = void 0;
1549
+ entry.waitingFor = void 0;
1547
1550
  entry.duration = void 0;
1551
+ entry.output = void 0;
1548
1552
  entry.error = void 0;
1549
1553
  entry.retries = 0;
1550
1554
  }
@@ -1954,6 +1958,7 @@ var RollbackManager = class {
1954
1958
  error: originalError.message
1955
1959
  });
1956
1960
  let compensatedCount = 0;
1961
+ let skippedCount = 0;
1957
1962
  for (let i = failedAtIndex - 1; i >= 0; i--) {
1958
1963
  const step = definition.steps[i];
1959
1964
  let execution = currentCtx.history[i];
@@ -1988,11 +1993,36 @@ var RollbackManager = class {
1988
1993
  } catch (err) {
1989
1994
  const error = err instanceof Error ? err : new Error(String(err));
1990
1995
  const action = this.recoveryManager.getAction(step.name);
1991
- if (_optionalChain([action, 'optionalAccess', _58 => _58.type]) === "skip") {
1996
+ if (_optionalChain([action, 'optionalAccess', _58 => _58.type]) === "retry") {
1997
+ const retryResult = await new CompensationRetryPolicy({
1998
+ ...this.retryPolicy.getConfig(),
1999
+ maxAttempts: _nullishCoalesce(action.maxAttempts, () => ( this.retryPolicy.getConfig().maxAttempts))
2000
+ }).execute(async () => {
2001
+ await _optionalChain([step, 'access', _59 => _59.compensate, 'optionalCall', _60 => _60(currentCtx)]);
2002
+ });
2003
+ if (retryResult.success) {
2004
+ execution = { ...execution, status: "compensated", compensatedAt: /* @__PURE__ */ new Date() };
2005
+ currentCtx = updateWorkflowContext(currentCtx, {
2006
+ history: currentCtx.history.map((h, idx) => idx === i ? execution : h)
2007
+ });
2008
+ compensatedCount++;
2009
+ await this.traceEmitter.stepCompensate(currentCtx, step.name, i);
2010
+ currentCtx = await this.persist(currentCtx);
2011
+ continue;
2012
+ }
2013
+ }
2014
+ if (_optionalChain([action, 'optionalAccess', _61 => _61.type]) === "skip") {
2015
+ execution = { ...execution, status: "completed", error: error.message };
2016
+ currentCtx = updateWorkflowContext(currentCtx, {
2017
+ history: currentCtx.history.map((h, idx) => idx === i ? execution : h)
2018
+ });
2019
+ currentCtx = await this.persist(currentCtx);
2020
+ skippedCount++;
1992
2021
  continue;
1993
2022
  }
1994
- if (_optionalChain([action, 'optionalAccess', _59 => _59.type]) === "abort") {
2023
+ if (_optionalChain([action, 'optionalAccess', _62 => _62.type]) === "abort") {
1995
2024
  currentCtx = updateWorkflowContext(currentCtx, { status: "compensation_failed" });
2025
+ currentCtx = await this.persist(currentCtx);
1996
2026
  await this.traceEmitter.emit({
1997
2027
  type: "workflow:error",
1998
2028
  timestamp: Date.now(),
@@ -2005,6 +2035,7 @@ var RollbackManager = class {
2005
2035
  }
2006
2036
  await this.recoveryManager.notifyRecoveryNeeded(currentCtx, step.name, error);
2007
2037
  currentCtx = updateWorkflowContext(currentCtx, { status: "compensation_failed" });
2038
+ currentCtx = await this.persist(currentCtx);
2008
2039
  await this.traceEmitter.emit({
2009
2040
  type: "workflow:error",
2010
2041
  timestamp: Date.now(),
@@ -2017,7 +2048,7 @@ var RollbackManager = class {
2017
2048
  }
2018
2049
  currentCtx = await this.persist(currentCtx);
2019
2050
  }
2020
- if (compensatedCount > 0) {
2051
+ if (compensatedCount > 0 || skippedCount > 0) {
2021
2052
  currentCtx = updateWorkflowContext(currentCtx, { status: "rolled_back" });
2022
2053
  await this.traceEmitter.emit({
2023
2054
  type: "workflow:rollback_complete",
@@ -2083,7 +2114,7 @@ var TraceEmitter = class {
2083
2114
  */
2084
2115
  async emit(event) {
2085
2116
  try {
2086
- await _optionalChain([this, 'access', _60 => _60.traceSink, 'optionalAccess', _61 => _61.emit, 'call', _62 => _62(event)]);
2117
+ await _optionalChain([this, 'access', _63 => _63.traceSink, 'optionalAccess', _64 => _64.emit, 'call', _65 => _65(event)]);
2087
2118
  } catch (e3) {
2088
2119
  }
2089
2120
  }
@@ -2194,7 +2225,7 @@ var TraceEmitter = class {
2194
2225
  stepName,
2195
2226
  stepIndex,
2196
2227
  duration: result.duration,
2197
- error: _optionalChain([result, 'access', _63 => _63.error, 'optionalAccess', _64 => _64.message]),
2228
+ error: _optionalChain([result, 'access', _66 => _66.error, 'optionalAccess', _67 => _67.message]),
2198
2229
  status: "failed"
2199
2230
  });
2200
2231
  }
@@ -2351,7 +2382,7 @@ var WorkflowExecutor = class {
2351
2382
  stateMachine.transition("running");
2352
2383
  currentCtx = updateWorkflowContext(currentCtx, { status: "running" });
2353
2384
  }
2354
- if (!_optionalChain([meta, 'optionalAccess', _65 => _65.resume]) && !_optionalChain([meta, 'optionalAccess', _66 => _66.retry])) {
2385
+ if (!_optionalChain([meta, 'optionalAccess', _68 => _68.resume]) && !_optionalChain([meta, 'optionalAccess', _69 => _69.retry])) {
2355
2386
  await this.traceEmitter.workflowStart(currentCtx);
2356
2387
  }
2357
2388
  for (let i = startIndex; i < definition.steps.length; ) {
@@ -2388,7 +2419,7 @@ var WorkflowExecutor = class {
2388
2419
  stateMachine.transition("completed");
2389
2420
  currentCtx = updateWorkflowContext(currentCtx, { status: "completed" });
2390
2421
  currentCtx = await this.persist(currentCtx);
2391
- _optionalChain([this, 'access', _67 => _67.config, 'access', _68 => _68.on, 'optionalAccess', _69 => _69.workflowComplete, 'optionalCall', _70 => _70(currentCtx)]);
2422
+ _optionalChain([this, 'access', _70 => _70.config, 'access', _71 => _71.on, 'optionalAccess', _72 => _72.workflowComplete, 'optionalCall', _73 => _73(currentCtx)]);
2392
2423
  await this.traceEmitter.workflowComplete(currentCtx, Date.now() - startTime);
2393
2424
  return {
2394
2425
  id: currentCtx.id,
@@ -2407,7 +2438,7 @@ var WorkflowExecutor = class {
2407
2438
  stateMachine.forceStatus("failed");
2408
2439
  currentCtx = updateWorkflowContext(currentCtx, { status: "failed" });
2409
2440
  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)]);
2441
+ _optionalChain([this, 'access', _74 => _74.config, 'access', _75 => _75.on, 'optionalAccess', _76 => _76.workflowError, 'optionalCall', _77 => _77(currentCtx, err)]);
2411
2442
  await this.traceEmitter.workflowError(currentCtx, err, Date.now() - startTime);
2412
2443
  return {
2413
2444
  id: currentCtx.id,
@@ -2431,7 +2462,7 @@ var WorkflowExecutor = class {
2431
2462
  currentCtx = this.contextManager.setStepName(currentCtx, index, step.name);
2432
2463
  execution = currentCtx.history[index];
2433
2464
  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)]);
2465
+ _optionalChain([this, 'access', _78 => _78.config, 'access', _79 => _79.on, 'optionalAccess', _80 => _80.stepStart, 'optionalCall', _81 => _81(step.name, currentCtx)]);
2435
2466
  await this.traceEmitter.stepStart(currentCtx, step.name, index, Boolean(step.commit));
2436
2467
  const { result, execution: updatedExecution } = await this.stepExecutor.execute(
2437
2468
  step,
@@ -2457,7 +2488,7 @@ var WorkflowExecutor = class {
2457
2488
  };
2458
2489
  return { updatedCtx: currentCtx, shouldReturn: true, result: suspendedResult };
2459
2490
  }
2460
- _optionalChain([this, 'access', _79 => _79.config, 'access', _80 => _80.on, 'optionalAccess', _81 => _81.stepComplete, 'optionalCall', _82 => _82(step.name, currentCtx, result)]);
2491
+ _optionalChain([this, 'access', _82 => _82.config, 'access', _83 => _83.on, 'optionalAccess', _84 => _84.stepComplete, 'optionalCall', _85 => _85(step.name, currentCtx, result)]);
2461
2492
  await this.traceEmitter.stepComplete(currentCtx, step.name, index, result);
2462
2493
  } else {
2463
2494
  await this.traceEmitter.stepError(currentCtx, step.name, index, result);
@@ -2490,14 +2521,14 @@ var WorkflowExecutor = class {
2490
2521
  break;
2491
2522
  }
2492
2523
  }
2493
- const lastIndex = _optionalChain([groupSteps, 'access', _83 => _83[groupSteps.length - 1], 'optionalAccess', _84 => _84.index]);
2524
+ const lastIndex = _optionalChain([groupSteps, 'access', _86 => _86[groupSteps.length - 1], 'optionalAccess', _87 => _87.index]);
2494
2525
  let currentCtx = ctx;
2495
2526
  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]);
2527
+ const stepIndex = _optionalChain([groupSteps, 'access', _88 => _88.find, 'call', _89 => _89((gs) => gs.step === step), 'optionalAccess', _90 => _90.index]);
2497
2528
  let execution = stepCtx.history[stepIndex];
2498
2529
  const localCtx = this.contextManager.setStepName(stepCtx, stepIndex, step.name);
2499
2530
  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)]);
2531
+ _optionalChain([this, 'access', _91 => _91.config, 'access', _92 => _92.on, 'optionalAccess', _93 => _93.stepStart, 'optionalCall', _94 => _94(step.name, localCtx)]);
2501
2532
  await this.traceEmitter.stepStart(localCtx, step.name, stepIndex, Boolean(step.commit));
2502
2533
  const { result, execution: updatedExecution } = await this.stepExecutor.execute(
2503
2534
  step,
@@ -2505,7 +2536,7 @@ var WorkflowExecutor = class {
2505
2536
  execution
2506
2537
  );
2507
2538
  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)]);
2539
+ _optionalChain([this, 'access', _95 => _95.config, 'access', _96 => _96.on, 'optionalAccess', _97 => _97.stepComplete, 'optionalCall', _98 => _98(step.name, localCtx, result)]);
2509
2540
  await this.traceEmitter.stepComplete(localCtx, step.name, stepIndex, result);
2510
2541
  } else {
2511
2542
  await this.traceEmitter.stepError(localCtx, step.name, stepIndex, result);
@@ -2520,13 +2551,13 @@ var WorkflowExecutor = class {
2520
2551
  executeStepWrapper
2521
2552
  );
2522
2553
  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]);
2554
+ const stepIndex = _optionalChain([groupSteps, 'access', _99 => _99.find, 'call', _100 => _100((gs) => gs.step.name === execution.name), 'optionalAccess', _101 => _101.index]);
2524
2555
  currentCtx = updateWorkflowContext(currentCtx, {
2525
2556
  history: currentCtx.history.map((h, idx) => idx === stepIndex ? execution : h)
2526
2557
  });
2527
2558
  }
2528
2559
  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]);
2560
+ const stepIndex = _optionalChain([groupSteps, 'access', _102 => _102.find, 'call', _103 => _103((gs) => gs.step.name === failure.step.name), 'optionalAccess', _104 => _104.index]);
2530
2561
  currentCtx = updateWorkflowContext(currentCtx, {
2531
2562
  history: currentCtx.history.map((h, idx) => idx === stepIndex ? failure.execution : h)
2532
2563
  });
@@ -2591,7 +2622,7 @@ var FluxEngine = class {
2591
2622
  this.storage = _nullishCoalesce(config.storage, () => ( new MemoryStorage()));
2592
2623
  this.contextManager = new ContextManager();
2593
2624
  this.traceEmitter = new TraceEmitter(config.trace);
2594
- if (_optionalChain([config, 'access', _102 => _102.optimizer, 'optionalAccess', _103 => _103.enabled])) {
2625
+ if (_optionalChain([config, 'access', _105 => _105.optimizer, 'optionalAccess', _106 => _106.enabled])) {
2595
2626
  this.dataOptimizer = new DataOptimizer({
2596
2627
  threshold: config.optimizer.threshold,
2597
2628
  defaultLocation: config.optimizer.defaultLocation
@@ -2660,7 +2691,7 @@ var FluxEngine = class {
2660
2691
  this.storage
2661
2692
  );
2662
2693
  } finally {
2663
- await _optionalChain([lock, 'optionalAccess', _104 => _104.release, 'call', _105 => _105()]);
2694
+ await _optionalChain([lock, 'optionalAccess', _107 => _107.release, 'call', _108 => _108()]);
2664
2695
  }
2665
2696
  }
2666
2697
  async resume(workflow, workflowId, options) {
@@ -2676,14 +2707,14 @@ var FluxEngine = class {
2676
2707
  throw workflowDefinitionChanged();
2677
2708
  }
2678
2709
  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(
2710
+ _optionalChain([this, 'access', _109 => _109.config, 'access', _110 => _110.logger, 'optionalAccess', _111 => _111.warn, 'call', _112 => _112(
2680
2711
  `Workflow version mismatch: instance was created with v${state.definitionVersion}, but current definition is v${definition.version}. Continuing execution.`
2681
2712
  )]);
2682
2713
  }
2683
2714
  let ctx = this.contextManager.restore(
2684
2715
  state
2685
2716
  );
2686
- const startIndex = resolveStartIndex(definition, _optionalChain([options, 'optionalAccess', _110 => _110.fromStep]), ctx.currentStep);
2717
+ const startIndex = resolveStartIndex(definition, _optionalChain([options, 'optionalAccess', _113 => _113.fromStep]), ctx.currentStep);
2687
2718
  resetHistoryFrom(ctx, startIndex);
2688
2719
  ctx = updateWorkflowContext(ctx, { status: "pending", currentStep: startIndex });
2689
2720
  ctx = await this.persist(ctx);
@@ -2704,7 +2735,7 @@ var FluxEngine = class {
2704
2735
  const idx = ctx.currentStep;
2705
2736
  const exec = ctx.history[idx];
2706
2737
  if (!exec || exec.status !== "suspended" || exec.waitingFor !== signalName) {
2707
- const isSuspended = _optionalChain([exec, 'optionalAccess', _111 => _111.status]) === "suspended";
2738
+ const isSuspended = _optionalChain([exec, 'optionalAccess', _114 => _114.status]) === "suspended";
2708
2739
  throw new FluxError(
2709
2740
  isSuspended ? `Workflow waiting for signal "${exec.waitingFor}", received "${signalName}"` : "Workflow state invalid: no suspended step found",
2710
2741
  isSuspended ? "INVALID_STATE_TRANSITION" /* INVALID_STATE_TRANSITION */ : "STEP_NOT_FOUND" /* STEP_NOT_FOUND */
@@ -2712,7 +2743,14 @@ var FluxEngine = class {
2712
2743
  }
2713
2744
  ctx = updateWorkflowContext(ctx, {
2714
2745
  history: ctx.history.map(
2715
- (h, i) => i === idx ? { ...h, status: "completed", completedAt: /* @__PURE__ */ new Date(), output: payload } : h
2746
+ (h, i) => i === idx ? {
2747
+ ...h,
2748
+ status: "completed",
2749
+ completedAt: /* @__PURE__ */ new Date(),
2750
+ suspendedAt: void 0,
2751
+ waitingFor: void 0,
2752
+ output: payload
2753
+ } : h
2716
2754
  )
2717
2755
  });
2718
2756
  await this.traceEmitter.emit({
@@ -2803,7 +2841,7 @@ var FluxEngine = class {
2803
2841
  * Initializes the engine and its underlying storage, and starts the scheduler.
2804
2842
  */
2805
2843
  async init() {
2806
- await _optionalChain([this, 'access', _112 => _112.storage, 'access', _113 => _113.init, 'optionalCall', _114 => _114()]);
2844
+ await _optionalChain([this, 'access', _115 => _115.storage, 'access', _116 => _116.init, 'optionalCall', _117 => _117()]);
2807
2845
  this.cronTrigger.start();
2808
2846
  }
2809
2847
  /**
@@ -2811,7 +2849,7 @@ var FluxEngine = class {
2811
2849
  */
2812
2850
  async close() {
2813
2851
  this.cronTrigger.stop();
2814
- await _optionalChain([this, 'access', _115 => _115.storage, 'access', _116 => _116.close, 'optionalCall', _117 => _117()]);
2852
+ await _optionalChain([this, 'access', _118 => _118.storage, 'access', _119 => _119.close, 'optionalCall', _120 => _120()]);
2815
2853
  }
2816
2854
  async persist(ctx, definitionVersion) {
2817
2855
  return persistContext(ctx, this.storage, this.contextManager, definitionVersion);
@@ -2831,7 +2869,7 @@ var PostgreSQLStorage = (_class7 = class {
2831
2869
  */
2832
2870
  constructor(options = {}) {;_class7.prototype.__init13.call(this);
2833
2871
  this.options = options;
2834
- this.tableName = _nullishCoalesce(options.tableName, () => ( "flux_workflows"));
2872
+ this.tableName = validateSqlIdentifier(_nullishCoalesce(options.tableName, () => ( "flux_workflows")), "tableName");
2835
2873
  }
2836
2874
  /**
2837
2875
  * Initializes the database connection pool and schema.
@@ -2960,11 +2998,11 @@ var PostgreSQLStorage = (_class7 = class {
2960
2998
  let query = `SELECT * FROM ${this.tableName} WHERE 1=1`;
2961
2999
  const params = [];
2962
3000
  let paramIndex = 1;
2963
- if (_optionalChain([filter, 'optionalAccess', _118 => _118.name])) {
3001
+ if (_optionalChain([filter, 'optionalAccess', _121 => _121.name])) {
2964
3002
  query += ` AND name = $${paramIndex++}`;
2965
3003
  params.push(filter.name);
2966
3004
  }
2967
- if (_optionalChain([filter, 'optionalAccess', _119 => _119.status])) {
3005
+ if (_optionalChain([filter, 'optionalAccess', _122 => _122.status])) {
2968
3006
  if (Array.isArray(filter.status)) {
2969
3007
  const placeholders = filter.status.map(() => `$${paramIndex++}`).join(", ");
2970
3008
  query += ` AND status IN (${placeholders})`;
@@ -2974,16 +3012,16 @@ var PostgreSQLStorage = (_class7 = class {
2974
3012
  params.push(filter.status);
2975
3013
  }
2976
3014
  }
2977
- if (_optionalChain([filter, 'optionalAccess', _120 => _120.version])) {
3015
+ if (_optionalChain([filter, 'optionalAccess', _123 => _123.version])) {
2978
3016
  query += ` AND definition_version = $${paramIndex++}`;
2979
3017
  params.push(filter.version);
2980
3018
  }
2981
3019
  query += " ORDER BY created_at DESC";
2982
- if (_optionalChain([filter, 'optionalAccess', _121 => _121.limit])) {
3020
+ if (_optionalChain([filter, 'optionalAccess', _124 => _124.limit])) {
2983
3021
  query += ` LIMIT $${paramIndex++}`;
2984
3022
  params.push(filter.limit);
2985
3023
  }
2986
- if (_optionalChain([filter, 'optionalAccess', _122 => _122.offset])) {
3024
+ if (_optionalChain([filter, 'optionalAccess', _125 => _125.offset])) {
2987
3025
  query += ` OFFSET $${paramIndex++}`;
2988
3026
  params.push(filter.offset);
2989
3027
  }
@@ -3056,20 +3094,34 @@ var PostgreSQLStorage = (_class7 = class {
3056
3094
  await this.pool.query("VACUUM ANALYZE");
3057
3095
  }
3058
3096
  }, _class7);
3097
+ function validateSqlIdentifier(value, field) {
3098
+ if (!/^[A-Za-z_][A-Za-z0-9_]*$/.test(value)) {
3099
+ throw new Error(
3100
+ `Invalid ${field}: "${value}". Only letters, numbers, and underscores are allowed.`
3101
+ );
3102
+ }
3103
+ return value;
3104
+ }
3059
3105
 
3060
3106
  // src/trace/JsonFileTraceSink.ts
3061
3107
  var _promises = require('fs/promises');
3062
3108
  var _path = require('path');
3063
- var JsonFileTraceSink = class {
3109
+ var _core = require('@gravito/core');
3110
+ var JsonFileTraceSink = (_class8 = class {
3111
+
3064
3112
 
3113
+ __init14() {this.fileSink = null}
3114
+ __init15() {this.buffer = []}
3065
3115
 
3116
+ __init16() {this.adapter = _core.getDefaultRuntimeAdapter.call(void 0, )}
3066
3117
  /**
3067
- * Creates a new JSON file trace sink.
3118
+ * Creates a new JSON file trace sink with buffering support.
3068
3119
  *
3069
3120
  * @param options - Configuration options for the sink.
3070
3121
  */
3071
- constructor(options) {
3122
+ constructor(options) {;_class8.prototype.__init14.call(this);_class8.prototype.__init15.call(this);_class8.prototype.__init16.call(this);
3072
3123
  this.path = options.path;
3124
+ this.bufferSize = _nullishCoalesce(options.bufferSize, () => ( 50));
3073
3125
  this.ready = this.init(_nullishCoalesce(options.reset, () => ( true)));
3074
3126
  }
3075
3127
  /**
@@ -3080,14 +3132,22 @@ var JsonFileTraceSink = class {
3080
3132
  */
3081
3133
  async init(reset) {
3082
3134
  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");
3135
+ if (this.adapter.createFileSink) {
3136
+ this.fileSink = await _optionalChain([this, 'access', _126 => _126.adapter, 'access', _127 => _127.createFileSink, 'optionalCall', _128 => _128(this.path)]);
3137
+ }
3138
+ if (reset && !this.fileSink) {
3139
+ if (this.adapter.writeFile) {
3140
+ await this.adapter.writeFile(this.path, "");
3141
+ }
3142
+ } else if (reset && this.fileSink) {
3143
+ this.fileSink.write("");
3085
3144
  }
3086
3145
  }
3087
3146
  /**
3088
3147
  * Appends a trace event to the file in NDJSON format.
3089
3148
  *
3090
- * Waits for initialization to complete before writing.
3149
+ * Events are buffered and written in batches using FileSink
3150
+ * for improved I/O efficiency. Waits for initialization to complete before writing.
3091
3151
  *
3092
3152
  * @param event - The trace event to record.
3093
3153
  * @throws {Error} If writing to the file fails.
@@ -3104,14 +3164,54 @@ var JsonFileTraceSink = class {
3104
3164
  */
3105
3165
  async emit(event) {
3106
3166
  await this.ready;
3107
- await _promises.appendFile.call(void 0, this.path, `${JSON.stringify(event)}
3108
- `, "utf8");
3167
+ const eventLine = `${JSON.stringify(event)}
3168
+ `;
3169
+ if (this.fileSink) {
3170
+ this.buffer.push(eventLine);
3171
+ if (this.buffer.length >= this.bufferSize) {
3172
+ await this.flushBuffer();
3173
+ }
3174
+ } else {
3175
+ if (this.adapter.appendFile) {
3176
+ await this.adapter.appendFile(this.path, eventLine);
3177
+ }
3178
+ }
3109
3179
  }
3110
- };
3180
+ /**
3181
+ * Flushes buffered events to disk.
3182
+ *
3183
+ * This is called automatically when the buffer reaches the configured size.
3184
+ * Can also be called manually to ensure all events are written.
3185
+ *
3186
+ * @throws {Error} If flushing fails.
3187
+ */
3188
+ async flushBuffer() {
3189
+ if (!this.fileSink || this.buffer.length === 0) {
3190
+ return;
3191
+ }
3192
+ const content = this.buffer.join("");
3193
+ this.fileSink.write(content);
3194
+ this.buffer = [];
3195
+ await this.fileSink.flush();
3196
+ }
3197
+ /**
3198
+ * Closes the sink and flushes any remaining buffered events.
3199
+ *
3200
+ * Should be called before process exit to ensure all events are persisted.
3201
+ *
3202
+ * @throws {Error} If closing or flushing fails.
3203
+ */
3204
+ async close() {
3205
+ await this.flushBuffer();
3206
+ if (this.fileSink) {
3207
+ await this.fileSink.end();
3208
+ }
3209
+ }
3210
+ }, _class8);
3111
3211
 
3112
3212
  // src/core/LockProvider.ts
3113
- var MemoryLockProvider = (_class8 = class {constructor() { _class8.prototype.__init14.call(this); }
3114
- __init14() {this.locks = /* @__PURE__ */ new Map()}
3213
+ var MemoryLockProvider = (_class9 = class {constructor() { _class9.prototype.__init17.call(this); }
3214
+ __init17() {this.locks = /* @__PURE__ */ new Map()}
3115
3215
  async acquire(resourceId, owner, ttl) {
3116
3216
  const now = Date.now();
3117
3217
  const existing = this.locks.get(resourceId);
@@ -3146,7 +3246,7 @@ var MemoryLockProvider = (_class8 = class {constructor() { _class8.prototype.__i
3146
3246
  release: () => this.release(id)
3147
3247
  };
3148
3248
  }
3149
- }, _class8);
3249
+ }, _class9);
3150
3250
 
3151
3251
  // src/core/RedisLockProvider.ts
3152
3252
  var RELEASE_LOCK_SCRIPT = `
@@ -3384,7 +3484,7 @@ var OrbitFlux = class _OrbitFlux {
3384
3484
  if (typeof storage === "string") {
3385
3485
  switch (storage) {
3386
3486
  case "sqlite":
3387
- storageAdapter = new (0, _chunkYXBEYVGYcjs.BunSQLiteStorage)({ path: dbPath });
3487
+ storageAdapter = new (0, _chunkZE2RDS47cjs.BunSQLiteStorage)({ path: dbPath });
3388
3488
  break;
3389
3489
  default:
3390
3490
  storageAdapter = new MemoryStorage();
@@ -3392,7 +3492,7 @@ var OrbitFlux = class _OrbitFlux {
3392
3492
  } else {
3393
3493
  storageAdapter = storage;
3394
3494
  }
3395
- await _optionalChain([storageAdapter, 'access', _123 => _123.init, 'optionalCall', _124 => _124()]);
3495
+ await _optionalChain([storageAdapter, 'access', _129 => _129.init, 'optionalCall', _130 => _130()]);
3396
3496
  const engineConfig = {
3397
3497
  storage: storageAdapter,
3398
3498
  defaultRetries,
@@ -3483,4 +3583,4 @@ var OrbitFlux = class _OrbitFlux {
3483
3583
 
3484
3584
 
3485
3585
  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
3586
+ //# sourceMappingURL=chunk-DN7SIQ34.cjs.map