@bluelibs/runner 5.2.0 → 5.3.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.
Files changed (44) hide show
  1. package/README.md +73 -10
  2. package/dist/browser/index.cjs +149 -59
  3. package/dist/browser/index.cjs.map +1 -1
  4. package/dist/browser/index.mjs +149 -60
  5. package/dist/browser/index.mjs.map +1 -1
  6. package/dist/edge/index.cjs +149 -59
  7. package/dist/edge/index.cjs.map +1 -1
  8. package/dist/edge/index.mjs +149 -60
  9. package/dist/edge/index.mjs.map +1 -1
  10. package/dist/node/node.cjs +384 -201
  11. package/dist/node/node.cjs.map +1 -1
  12. package/dist/node/node.mjs +382 -202
  13. package/dist/node/node.mjs.map +1 -1
  14. package/dist/types/definers/builders/error/fluent-builder.interface.d.ts +6 -0
  15. package/dist/types/definers/builders/error/index.d.ts +10 -1
  16. package/dist/types/definers/builders/error/types.d.ts +2 -0
  17. package/dist/types/definers/defineError.d.ts +5 -3
  18. package/dist/types/defs.d.ts +2 -0
  19. package/dist/types/globals/resources/tunnel/protocol.d.ts +3 -0
  20. package/dist/types/models/Store.d.ts +5 -3
  21. package/dist/types/models/StoreRegistry.d.ts +5 -3
  22. package/dist/types/node/durable/core/DurableResource.d.ts +23 -9
  23. package/dist/types/node/durable/core/DurableService.d.ts +15 -9
  24. package/dist/types/node/durable/core/interfaces/service.d.ts +27 -19
  25. package/dist/types/node/durable/core/interfaces/store.d.ts +1 -1
  26. package/dist/types/node/durable/core/managers/ExecutionManager.d.ts +34 -4
  27. package/dist/types/node/durable/core/managers/ScheduleManager.d.ts +5 -3
  28. package/dist/types/node/durable/core/managers/TaskRegistry.d.ts +5 -5
  29. package/dist/types/node/durable/core/managers/WaitManager.d.ts +1 -1
  30. package/dist/types/node/durable/index.d.ts +1 -0
  31. package/dist/types/node/durable/tags/durableWorkflow.tag.d.ts +14 -0
  32. package/dist/types/node/node.d.ts +2 -1
  33. package/dist/types/public.d.ts +4 -1
  34. package/dist/types/testing.d.ts +0 -1
  35. package/dist/types/types/error.d.ts +22 -1
  36. package/dist/types/types/resource.d.ts +2 -4
  37. package/dist/types/types/symbols.d.ts +2 -2
  38. package/dist/types/types/tagged.d.ts +18 -0
  39. package/dist/universal/index.cjs +149 -59
  40. package/dist/universal/index.cjs.map +1 -1
  41. package/dist/universal/index.mjs +149 -60
  42. package/dist/universal/index.mjs.map +1 -1
  43. package/package.json +2 -2
  44. package/readmes/AI.md +25 -9
@@ -194,6 +194,7 @@ function toTunnelError(input, fallbackMessage) {
194
194
  const pe = input;
195
195
  const msg = pe.message || fallbackMessage || "Tunnel error";
196
196
  return new TunnelError(pe.code, msg, pe.details, {
197
+ httpCode: pe.httpCode,
197
198
  id: pe.id,
198
199
  data: pe.data
199
200
  });
@@ -239,6 +240,7 @@ var init_protocol = __esm({
239
240
  this.name = "TunnelError";
240
241
  this.code = code;
241
242
  this.details = details;
243
+ this.httpCode = extras?.httpCode;
242
244
  this.id = extras?.id;
243
245
  this.data = extras?.data;
244
246
  }
@@ -28588,9 +28590,7 @@ var symbolPhantomTask = Symbol.for(
28588
28590
  "runner.task.phantom"
28589
28591
  );
28590
28592
  var symbolResource = Symbol.for("runner.resource");
28591
- var symbolResourceForkedFrom = Symbol.for(
28592
- "runner.resourceForkedFrom"
28593
- );
28593
+ var symbolForkedFrom = Symbol.for("runner.forkedFrom");
28594
28594
  var symbolResourceWithConfig = Symbol.for(
28595
28595
  "runner.resourceWithConfig"
28596
28596
  );
@@ -28657,13 +28657,13 @@ __export(defs_exports, {
28657
28657
  symbolError: () => symbolError,
28658
28658
  symbolEvent: () => symbolEvent,
28659
28659
  symbolFilePath: () => symbolFilePath,
28660
+ symbolForkedFrom: () => symbolForkedFrom,
28660
28661
  symbolHook: () => symbolHook,
28661
28662
  symbolMiddleware: () => symbolMiddleware,
28662
28663
  symbolMiddlewareConfigured: () => symbolMiddlewareConfigured,
28663
28664
  symbolOptionalDependency: () => symbolOptionalDependency,
28664
28665
  symbolPhantomTask: () => symbolPhantomTask,
28665
28666
  symbolResource: () => symbolResource,
28666
- symbolResourceForkedFrom: () => symbolResourceForkedFrom,
28667
28667
  symbolResourceMiddleware: () => symbolResourceMiddleware,
28668
28668
  symbolResourceWithConfig: () => symbolResourceWithConfig,
28669
28669
  symbolTag: () => symbolTag,
@@ -28868,12 +28868,26 @@ __export(errors_exports, {
28868
28868
  });
28869
28869
 
28870
28870
  // src/definers/defineError.ts
28871
+ var isValidHttpCode = /* @__PURE__ */ __name((value) => Number.isInteger(value) && value >= 100 && value <= 599, "isValidHttpCode");
28872
+ var assertHttpCode = /* @__PURE__ */ __name((value) => {
28873
+ if (!isValidHttpCode(value)) {
28874
+ throw new Error(
28875
+ `Error httpCode must be an integer between 100 and 599. Received: ${value}`
28876
+ );
28877
+ }
28878
+ }, "assertHttpCode");
28871
28879
  var RunnerError = class extends Error {
28872
- constructor(id2, message, data) {
28873
- super(message);
28880
+ constructor(id2, message, data, httpCode, remediation) {
28881
+ super(
28882
+ remediation !== void 0 ? `${message}
28883
+
28884
+ Remediation: ${remediation}` : message
28885
+ );
28874
28886
  this.id = id2;
28875
28887
  this.data = data;
28876
28888
  this.name = id2;
28889
+ this.httpCode = httpCode;
28890
+ this.remediation = remediation;
28877
28891
  }
28878
28892
  static {
28879
28893
  __name(this, "RunnerError");
@@ -28893,10 +28907,20 @@ var ErrorHelper = class {
28893
28907
  get id() {
28894
28908
  return this.definition.id;
28895
28909
  }
28910
+ get httpCode() {
28911
+ return this.definition.httpCode;
28912
+ }
28896
28913
  throw(data) {
28897
28914
  const parsed = this.definition.dataSchema ? this.definition.dataSchema.parse(data) : data;
28898
28915
  const message = this.definition.format(parsed);
28899
- throw new RunnerError(this.definition.id, message, parsed);
28916
+ const remediation = typeof this.definition.remediation === "function" ? this.definition.remediation(parsed) : this.definition.remediation;
28917
+ throw new RunnerError(
28918
+ this.definition.id,
28919
+ message,
28920
+ parsed,
28921
+ this.definition.httpCode,
28922
+ remediation
28923
+ );
28900
28924
  }
28901
28925
  is(error2) {
28902
28926
  return error2 instanceof RunnerError && error2.name === this.definition.id;
@@ -28909,6 +28933,9 @@ var ErrorHelper = class {
28909
28933
  }
28910
28934
  };
28911
28935
  function defineError(definition, filePath) {
28936
+ if (definition.httpCode !== void 0) {
28937
+ assertHttpCode(definition.httpCode);
28938
+ }
28912
28939
  if (!definition.format) {
28913
28940
  definition.format = (data) => `${JSON.stringify(data)}`;
28914
28941
  }
@@ -28930,9 +28957,22 @@ function clone(s, patch) {
28930
28957
  __name(clone, "clone");
28931
28958
 
28932
28959
  // src/definers/builders/error/fluent-builder.ts
28960
+ var isValidHttpCode2 = /* @__PURE__ */ __name((value) => Number.isInteger(value) && value >= 100 && value <= 599, "isValidHttpCode");
28961
+ var assertHttpCode2 = /* @__PURE__ */ __name((value) => {
28962
+ if (!isValidHttpCode2(value)) {
28963
+ throw new Error(
28964
+ `Error httpCode must be an integer between 100 and 599. Received: ${value}`
28965
+ );
28966
+ }
28967
+ }, "assertHttpCode");
28933
28968
  function makeErrorBuilder(state) {
28934
28969
  const builder = {
28935
28970
  id: state.id,
28971
+ httpCode(code) {
28972
+ assertHttpCode2(code);
28973
+ const next = clone(state, { httpCode: code });
28974
+ return makeErrorBuilder(next);
28975
+ },
28936
28976
  serialize(fn) {
28937
28977
  const next = clone(state, { serialize: fn });
28938
28978
  return makeErrorBuilder(next);
@@ -28949,6 +28989,10 @@ function makeErrorBuilder(state) {
28949
28989
  const next = clone(state, { format: fn });
28950
28990
  return makeErrorBuilder(next);
28951
28991
  },
28992
+ remediation(advice) {
28993
+ const next = clone(state, { remediation: advice });
28994
+ return makeErrorBuilder(next);
28995
+ },
28952
28996
  meta(m) {
28953
28997
  const next = clone(state, { meta: m });
28954
28998
  return makeErrorBuilder(next);
@@ -28957,10 +29001,12 @@ function makeErrorBuilder(state) {
28957
29001
  return defineError(
28958
29002
  {
28959
29003
  id: state.id,
29004
+ httpCode: state.httpCode,
28960
29005
  serialize: state.serialize,
28961
29006
  parse: state.parse,
28962
29007
  dataSchema: state.dataSchema,
28963
29008
  format: state.format,
29009
+ remediation: state.remediation,
28964
29010
  meta: state.meta
28965
29011
  },
28966
29012
  state.filePath
@@ -28977,6 +29023,7 @@ function errorBuilder(id2) {
28977
29023
  const initial = Object.freeze({
28978
29024
  id: id2,
28979
29025
  filePath,
29026
+ httpCode: void 0,
28980
29027
  serialize: void 0,
28981
29028
  parse: void 0,
28982
29029
  dataSchema: void 0,
@@ -28985,7 +29032,13 @@ function errorBuilder(id2) {
28985
29032
  return makeErrorBuilder(initial);
28986
29033
  }
28987
29034
  __name(errorBuilder, "errorBuilder");
28988
- var error = errorBuilder;
29035
+ function isRunnerError(error2) {
29036
+ return error2 instanceof RunnerError;
29037
+ }
29038
+ __name(isRunnerError, "isRunnerError");
29039
+ var error = Object.assign(errorBuilder, {
29040
+ is: isRunnerError
29041
+ });
28989
29042
 
28990
29043
  // src/platform/adapters/node-als.ts
28991
29044
  async function loadAsyncLocalStorageClass() {
@@ -29416,9 +29469,13 @@ var PlatformAdapter = class {
29416
29469
  // src/errors.ts
29417
29470
  var duplicateRegistrationError = error("runner.errors.duplicateRegistration").format(
29418
29471
  ({ type, id: id2 }) => `${type} "${id2.toString()}" already registered. You might have used the same 'id' in two different components or you may have registered the same element twice.`
29472
+ ).remediation(
29473
+ ({ type }) => `Ensure each ${type} has a unique id. If you need the same definition in multiple places, use .fork() to create a copy with a new id.`
29419
29474
  ).build();
29420
29475
  var dependencyNotFoundError = error("runner.errors.dependencyNotFound").format(
29421
29476
  ({ key }) => `Dependency ${key.toString()} not found. Did you forget to register it through a resource?`
29477
+ ).remediation(
29478
+ ({ key }) => `Register the dependency "${key.toString()}" in a parent resource using .register([${key.toString()}]). If the dependency is optional, use .optional() when declaring it.`
29422
29479
  ).build();
29423
29480
  var unknownItemTypeError = error(
29424
29481
  "runner.errors.unknownItemType"
@@ -29426,10 +29483,14 @@ var unknownItemTypeError = error(
29426
29483
  ({ item }) => `Unknown item type: ${String(
29427
29484
  item
29428
29485
  )}. Please ensure you are not using different versions of '@bluelibs/runner'`
29486
+ ).remediation(
29487
+ "Check that all packages depend on the same version of '@bluelibs/runner'. Run 'npm ls @bluelibs/runner' to detect duplicates."
29429
29488
  ).build();
29430
29489
  var contextError = error(
29431
29490
  "runner.errors.context"
29432
- ).format(({ details }) => details ?? "Context error").build();
29491
+ ).format(({ details }) => details ?? "Context error").remediation(
29492
+ "Verify the async context is registered in a parent resource and that .provide() was called before .use(). If the context is optional, use .optional() when declaring the dependency."
29493
+ ).build();
29433
29494
  var circularDependenciesError = error("runner.errors.circularDependencies").format(({ cycles }) => {
29434
29495
  const cycleDetails = cycles.map((cycle) => ` \u2022 ${cycle}`).join("\n");
29435
29496
  const hasMiddleware = cycles.some((cycle) => cycle.includes("middleware"));
@@ -29442,36 +29503,54 @@ var circularDependenciesError = error("runner.errors.circularDependencies").form
29442
29503
  }
29443
29504
  return `Circular dependencies detected:
29444
29505
  ${cycleDetails}${guidance}`;
29445
- }).build();
29506
+ }).remediation(
29507
+ "Break the cycle by extracting shared state into a new resource that both sides depend on, or use events for indirect communication."
29508
+ ).build();
29446
29509
  var eventNotFoundError = error(
29447
29510
  "runner.errors.eventNotFound"
29448
29511
  ).format(
29449
29512
  ({ id: id2 }) => `Event "${id2.toString()}" not found. Did you forget to register it?`
29513
+ ).remediation(
29514
+ ({ id: id2 }) => `Add the event "${id2.toString()}" to a parent resource via .register([yourEvent]). Ensure the event definition is built with r.event("${id2.toString()}").build().`
29450
29515
  ).build();
29451
29516
  var resourceNotFoundError = error(
29452
29517
  "runner.errors.resourceNotFound"
29453
29518
  ).format(
29454
29519
  ({ id: id2 }) => `Resource "${id2.toString()}" not found. Did you forget to register it or are you using the correct id?`
29520
+ ).remediation(
29521
+ ({ id: id2 }) => `Register the resource "${id2.toString()}" in a parent resource via .register([yourResource]). Verify the id string matches exactly (ids are case-sensitive).`
29455
29522
  ).build();
29456
29523
  var middlewareNotRegisteredError = error("runner.errors.middlewareNotRegistered").format(
29457
29524
  ({ type, source, middlewareId }) => `Middleware inside ${type} "${source}" depends on "${middlewareId}" but it's not registered. Did you forget to register it?`
29525
+ ).remediation(
29526
+ ({ middlewareId }) => `Register the middleware "${middlewareId}" alongside its consumer in a parent resource via .register([yourMiddleware]).`
29458
29527
  ).build();
29459
29528
  var tagNotFoundError = error(
29460
29529
  "runner.errors.tagNotFound"
29461
29530
  ).format(
29462
29531
  ({ id: id2 }) => `Tag "${id2}" not registered. Did you forget to register it inside a resource?`
29532
+ ).remediation(
29533
+ ({ id: id2 }) => `Register the tag "${id2}" in a parent resource via .register([yourTag]). Tags must be registered before they can be queried.`
29463
29534
  ).build();
29464
29535
  var lockedError = error(
29465
29536
  "runner.errors.locked"
29466
29537
  ).format(
29467
29538
  ({ what }) => `Cannot modify the ${what.toString()} when it is locked.`
29539
+ ).remediation(
29540
+ ({ what }) => `The ${what.toString()} is locked after initialization. Perform all modifications before calling run().`
29468
29541
  ).build();
29469
29542
  var storeAlreadyInitializedError = error(
29470
29543
  "runner.errors.storeAlreadyInitialized"
29471
- ).format(() => "Store already initialized. Cannot reinitialize.").build();
29544
+ ).format(() => "Store already initialized. Cannot reinitialize.").remediation(
29545
+ "Do not call run() more than once on the same resource tree. Create a fresh resource if you need a separate runtime."
29546
+ ).build();
29472
29547
  var validationError = error("runner.errors.validation").format(({ subject, id: id2, originalError }) => {
29473
29548
  const errorMessage2 = originalError instanceof Error ? originalError.message : String(originalError);
29474
29549
  return `${subject} validation failed for ${id2.toString()}: ${errorMessage2}`;
29550
+ }).remediation(({ subject, id: id2 }) => {
29551
+ const lower = subject.toLowerCase();
29552
+ const schemaHint = lower.includes("input") ? "inputSchema" : lower.includes("config") ? "configSchema" : lower.includes("result") ? "resultSchema" : "schema";
29553
+ return `Check the ${subject} passed to "${id2.toString()}". Ensure it matches the schema defined via .${schemaHint}().`;
29475
29554
  }).build();
29476
29555
  var eventCycleError = error("runner.errors.eventCycle").format(({ path: path4 }) => {
29477
29556
  const chain = path4.map((p) => `${p.id}\u2190${p.source}`).join(" -> ");
@@ -29479,33 +29558,49 @@ var eventCycleError = error("runner.errors.eventCycle").format(({ path: path4 })
29479
29558
  ${chain}
29480
29559
 
29481
29560
  Break the cycle by changing hook logic (avoid mutual emits) or gate with conditions/tags.`;
29482
- }).build();
29561
+ }).remediation(
29562
+ "Refactor hooks to avoid circular event emissions. Use conditional guards, split events into finer-grained signals, or introduce an intermediate task to break the cycle."
29563
+ ).build();
29483
29564
  var eventEmissionCycleError = error("runner.errors.eventEmissionCycle").format(({ cycles }) => {
29484
29565
  const list = cycles.map((c) => ` \u2022 ${c}`).join("\n");
29485
29566
  return `Event emission cycles detected between hooks and events:
29486
29567
  ${list}
29487
29568
 
29488
29569
  This was detected at compile time (dry-run). Break the cycle by avoiding mutual emits between hooks or scoping hooks using tags.`;
29489
- }).build();
29570
+ }).remediation(
29571
+ "Redesign the event/hook graph so no hook emits an event that eventually triggers itself. Use tags or conditional logic to prevent re-entrant emissions."
29572
+ ).build();
29490
29573
  var platformUnsupportedFunctionError = error("runner.errors.platformUnsupportedFunction").format(
29491
29574
  ({ functionName }) => `Platform function not supported in this environment: ${functionName}. Detected platform: ${detectEnvironment()}.`
29575
+ ).remediation(
29576
+ ({ functionName }) => `The function "${functionName}" requires a Node.js environment. If running in a browser or edge runtime, use a platform-compatible alternative or guard the call with a platform check.`
29492
29577
  ).build();
29493
29578
  var cancellationError = error(
29494
29579
  "runner.errors.cancellation"
29495
- ).format(({ reason }) => reason || "Operation cancelled").build();
29580
+ ).format(({ reason }) => reason || "Operation cancelled").remediation(
29581
+ "The operation was cancelled, typically via an AbortController signal. If this is unexpected, check timeout middleware settings or ensure the caller is not aborting prematurely."
29582
+ ).build();
29496
29583
  var tunnelOwnershipConflictError = error("runner.errors.tunnelOwnershipConflict").format(
29497
29584
  ({ taskId, currentOwnerId, attemptedOwnerId }) => `Task "${taskId}" is already tunneled by resource "${currentOwnerId}". Resource "${attemptedOwnerId}" cannot tunnel it again. Ensure each task is owned by a single tunnel client.`
29585
+ ).remediation(
29586
+ ({ taskId }) => `Each task can only be tunneled by one client. Remove the duplicate tunnel registration for "${taskId}" or split the task into separate definitions with distinct ids.`
29498
29587
  ).build();
29499
29588
  var phantomTaskNotRoutedError = error("runner.errors.phantomTaskNotRouted").format(
29500
29589
  ({ taskId }) => `Phantom task "${taskId}" is not routed through any tunnel. Ensure a tunnel client selects this task id (or avoid calling the phantom task directly).`
29590
+ ).remediation(
29591
+ ({ taskId }) => `Configure a tunnel client resource to select "${taskId}" so it routes to a remote server. Phantom tasks cannot be executed locally \u2014 they only serve as local proxies for remote tasks.`
29501
29592
  ).build();
29502
29593
  var taskNotRegisteredError = error("runner.errors.taskNotRegistered").format(
29503
29594
  ({ taskId }) => `Task "${taskId}" is not registered in the Store. This is an internal error\u2014ensure the task is registered before execution.`
29595
+ ).remediation(
29596
+ ({ taskId }) => `Register the task "${taskId}" in a parent resource via .register([yourTask]) before calling run(). If this error persists, it may indicate an internal framework bug.`
29504
29597
  ).build();
29505
29598
  var builderIncompleteError = error("runner.errors.builderIncomplete").format(({ type, builderId, missingFields }) => {
29506
29599
  const typeLabel = type === "hook" ? "Hook" : type === "task-middleware" ? "Task middleware" : "Resource middleware";
29507
29600
  return `${typeLabel} "${builderId}" is incomplete. Missing required: ${missingFields.join(", ")}. Call ${missingFields.map((f) => `.${f}()`).join(" and ")} before .build().`;
29508
- }).build();
29601
+ }).remediation(
29602
+ ({ missingFields }) => `Add the missing builder steps: ${missingFields.map((f) => `.${f}()`).join(", ")} before calling .build().`
29603
+ ).build();
29509
29604
  function isCancellationError(err) {
29510
29605
  return cancellationError.is(err);
29511
29606
  }
@@ -29739,6 +29834,7 @@ function defineResource(constConfig) {
29739
29834
  };
29740
29835
  },
29741
29836
  fork(newId, options) {
29837
+ const forkCallerFilePath = getCallerFile();
29742
29838
  const forkedParts = resolveForkedRegisterAndDependencies({
29743
29839
  register: constConfig.register,
29744
29840
  dependencies: constConfig.dependencies,
@@ -29750,11 +29846,10 @@ function defineResource(constConfig) {
29750
29846
  id: newId,
29751
29847
  register: forkedParts.register,
29752
29848
  dependencies: forkedParts.dependencies,
29753
- [symbolFilePath]: filePath
29849
+ [symbolFilePath]: forkCallerFilePath
29754
29850
  });
29755
- forked[symbolResourceForkedFrom] = {
29756
- fromId: id2,
29757
- forkedAtFilePath: getCallerFile()
29851
+ forked[symbolForkedFrom] = {
29852
+ fromId: id2
29758
29853
  };
29759
29854
  return forked;
29760
29855
  }
@@ -29985,7 +30080,7 @@ __name(defineTag, "defineTag");
29985
30080
 
29986
30081
  // src/globals/middleware/requireContext.middleware.ts
29987
30082
  var requireContextTaskMiddleware = defineTaskMiddleware({
29988
- id: "globals.middleware.requireContext",
30083
+ id: "globals.middleware.task.requireContext",
29989
30084
  async run({ task: task2, next }, _deps, config) {
29990
30085
  if (!config.context) {
29991
30086
  throw new Error(
@@ -31629,7 +31724,7 @@ var cacheFactoryTask = defineTask({
31629
31724
  });
31630
31725
  var journalKeys = {
31631
31726
  /** Whether the result was served from cache (true) or freshly computed (false) */
31632
- hit: journal.createKey("globals.middleware.cache.hit")
31727
+ hit: journal.createKey("globals.middleware.task.cache.hit")
31633
31728
  };
31634
31729
  var cacheResource = defineResource({
31635
31730
  id: "globals.resources.cache",
@@ -31659,7 +31754,7 @@ var cacheResource = defineResource({
31659
31754
  });
31660
31755
  var defaultKeyBuilder = /* @__PURE__ */ __name((taskId, input) => `${taskId}-${JSON.stringify(input)}`, "defaultKeyBuilder");
31661
31756
  var cacheMiddleware = defineTaskMiddleware({
31662
- id: "globals.middleware.cache",
31757
+ id: "globals.middleware.task.cache",
31663
31758
  dependencies: { cache: cacheResource },
31664
31759
  async run({ task: task2, next, journal: journal2 }, deps, config) {
31665
31760
  const { cache } = deps;
@@ -31710,11 +31805,11 @@ var CircuitBreakerOpenError = class extends Error {
31710
31805
  var journalKeys2 = {
31711
31806
  /** Current state of the circuit breaker (CLOSED, OPEN, or HALF_OPEN) */
31712
31807
  state: journal.createKey(
31713
- "globals.middleware.circuitBreaker.state"
31808
+ "globals.middleware.task.circuitBreaker.state"
31714
31809
  ),
31715
31810
  /** Current failure count */
31716
31811
  failures: journal.createKey(
31717
- "globals.middleware.circuitBreaker.failures"
31812
+ "globals.middleware.task.circuitBreaker.failures"
31718
31813
  )
31719
31814
  };
31720
31815
  var circuitBreakerResource = defineResource({
@@ -31727,7 +31822,7 @@ var circuitBreakerResource = defineResource({
31727
31822
  }, "init")
31728
31823
  });
31729
31824
  var circuitBreakerMiddleware = defineTaskMiddleware({
31730
- id: "globals.middleware.circuitBreaker",
31825
+ id: "globals.middleware.task.circuitBreaker",
31731
31826
  dependencies: { state: circuitBreakerResource },
31732
31827
  async run({ task: task2, next, journal: journal2 }, { state }, config) {
31733
31828
  const taskId = task2.definition.id;
@@ -32307,12 +32402,12 @@ var EventManager = class {
32307
32402
 
32308
32403
  // src/models/Semaphore.ts
32309
32404
  var SemaphoreEvents = {
32310
- queued: defineEvent({ id: "semaphore.queued" }),
32311
- acquired: defineEvent({ id: "semaphore.acquired" }),
32312
- released: defineEvent({ id: "semaphore.released" }),
32313
- timeout: defineEvent({ id: "semaphore.timeout" }),
32314
- aborted: defineEvent({ id: "semaphore.aborted" }),
32315
- disposed: defineEvent({ id: "semaphore.disposed" })
32405
+ queued: defineEvent({ id: "semaphore.events.queued" }),
32406
+ acquired: defineEvent({ id: "semaphore.events.acquired" }),
32407
+ released: defineEvent({ id: "semaphore.events.released" }),
32408
+ timeout: defineEvent({ id: "semaphore.events.timeout" }),
32409
+ aborted: defineEvent({ id: "semaphore.events.aborted" }),
32410
+ disposed: defineEvent({ id: "semaphore.events.disposed" })
32316
32411
  };
32317
32412
  var Semaphore = class {
32318
32413
  constructor(maxPermits) {
@@ -32585,7 +32680,7 @@ var concurrencyResource = defineResource({
32585
32680
  }), "init")
32586
32681
  });
32587
32682
  var concurrencyTaskMiddleware = defineTaskMiddleware({
32588
- id: "globals.middleware.concurrency",
32683
+ id: "globals.middleware.task.concurrency",
32589
32684
  dependencies: { state: concurrencyResource },
32590
32685
  async run({ task: task2, next }, { state }, config) {
32591
32686
  let semaphore = config.semaphore;
@@ -32664,14 +32759,16 @@ var RateLimitError = class extends Error {
32664
32759
  var journalKeys3 = {
32665
32760
  /** Number of remaining requests in the current window */
32666
32761
  remaining: journal.createKey(
32667
- "globals.middleware.rateLimit.remaining"
32762
+ "globals.middleware.task.rateLimit.remaining"
32668
32763
  ),
32669
32764
  /** Timestamp when the current window resets */
32670
32765
  resetTime: journal.createKey(
32671
- "globals.middleware.rateLimit.resetTime"
32766
+ "globals.middleware.task.rateLimit.resetTime"
32672
32767
  ),
32673
32768
  /** Maximum requests allowed per window */
32674
- limit: journal.createKey("globals.middleware.rateLimit.limit")
32769
+ limit: journal.createKey(
32770
+ "globals.middleware.task.rateLimit.limit"
32771
+ )
32675
32772
  };
32676
32773
  var rateLimitResource = defineResource({
32677
32774
  id: "globals.resources.rateLimit",
@@ -32683,7 +32780,7 @@ var rateLimitResource = defineResource({
32683
32780
  }, "init")
32684
32781
  });
32685
32782
  var rateLimitTaskMiddleware = defineTaskMiddleware({
32686
- id: "globals.middleware.rateLimit",
32783
+ id: "globals.middleware.task.rateLimit",
32687
32784
  configSchema: rateLimitConfigSchema,
32688
32785
  dependencies: { state: rateLimitResource },
32689
32786
  async run({ task: task2, next, journal: journal2 }, { state }, config) {
@@ -32731,7 +32828,7 @@ var temporalResource = defineResource({
32731
32828
  }, "init")
32732
32829
  });
32733
32830
  var debounceTaskMiddleware = defineTaskMiddleware({
32734
- id: "globals.middleware.debounce",
32831
+ id: "globals.middleware.task.debounce",
32735
32832
  dependencies: { state: temporalResource },
32736
32833
  async run({ task: task2, next }, { state }, config) {
32737
32834
  const { debounceStates } = state;
@@ -32768,7 +32865,7 @@ var debounceTaskMiddleware = defineTaskMiddleware({
32768
32865
  }
32769
32866
  });
32770
32867
  var throttleTaskMiddleware = defineTaskMiddleware({
32771
- id: "globals.middleware.throttle",
32868
+ id: "globals.middleware.task.throttle",
32772
32869
  dependencies: { state: temporalResource },
32773
32870
  async run({ task: task2, next }, { state }, config) {
32774
32871
  const { throttleStates } = state;
@@ -32840,12 +32937,12 @@ var throttleTaskMiddleware = defineTaskMiddleware({
32840
32937
 
32841
32938
  // src/models/Queue.ts
32842
32939
  var QueueEvents = {
32843
- enqueue: defineEvent({ id: "queue.enqueue" }),
32844
- start: defineEvent({ id: "queue.start" }),
32845
- finish: defineEvent({ id: "queue.finish" }),
32846
- error: defineEvent({ id: "queue.error" }),
32847
- cancel: defineEvent({ id: "queue.cancel" }),
32848
- disposed: defineEvent({ id: "queue.disposed" })
32940
+ enqueue: defineEvent({ id: "queue.events.enqueue" }),
32941
+ start: defineEvent({ id: "queue.events.start" }),
32942
+ finish: defineEvent({ id: "queue.events.finish" }),
32943
+ error: defineEvent({ id: "queue.events.error" }),
32944
+ cancel: defineEvent({ id: "queue.events.cancel" }),
32945
+ disposed: defineEvent({ id: "queue.events.disposed" })
32849
32946
  };
32850
32947
  var Queue = class {
32851
32948
  constructor() {
@@ -33454,13 +33551,15 @@ var retryResourceMiddleware = defineResourceMiddleware({
33454
33551
  var journalKeys6 = {
33455
33552
  /** Whether the fallback path was taken (true) or primary succeeded (false) */
33456
33553
  active: journal.createKey(
33457
- "globals.middleware.fallback.active"
33554
+ "globals.middleware.task.fallback.active"
33458
33555
  ),
33459
33556
  /** The error that triggered the fallback (only set when active is true) */
33460
- error: journal.createKey("globals.middleware.fallback.error")
33557
+ error: journal.createKey(
33558
+ "globals.middleware.task.fallback.error"
33559
+ )
33461
33560
  };
33462
33561
  var fallbackTaskMiddleware = defineTaskMiddleware({
33463
- id: "globals.middleware.fallback",
33562
+ id: "globals.middleware.task.fallback",
33464
33563
  dependencies: {
33465
33564
  taskRunner: globalResources.taskRunner
33466
33565
  },
@@ -36140,21 +36239,11 @@ var Store = class {
36140
36239
  storeGenericItem(item) {
36141
36240
  return this.registry.storeGenericItem(item);
36142
36241
  }
36143
- /**
36144
- * Returns all tasks with the given tag.
36145
- * @param tag - The tag to filter by.
36146
- * @returns The tasks with the given tag.
36147
- */
36148
36242
  getTasksWithTag(tag2) {
36149
- return this.registry.getTasksWithTag(tag2);
36243
+ return typeof tag2 === "string" ? this.registry.getTasksWithTag(tag2) : this.registry.getTasksWithTag(tag2);
36150
36244
  }
36151
- /**
36152
- * Returns all resources with the given tag.
36153
- * @param tag - The tag to filter by.
36154
- * @returns The resources with the given tag.
36155
- */
36156
36245
  getResourcesWithTag(tag2) {
36157
- return this.registry.getResourcesWithTag(tag2);
36246
+ return typeof tag2 === "string" ? this.registry.getResourcesWithTag(tag2) : this.registry.getResourcesWithTag(tag2);
36158
36247
  }
36159
36248
  };
36160
36249
 
@@ -39391,6 +39480,7 @@ var UNSAFE_ERROR_FIELDS = /* @__PURE__ */ new Set([
39391
39480
  var isRecord2 = /* @__PURE__ */ __name((value) => !!value && typeof value === "object", "isRecord");
39392
39481
  var isJsonBody = /* @__PURE__ */ __name((value) => isRecord2(value) && typeof value.ok === "boolean", "isJsonBody");
39393
39482
  var toErrorRecord = /* @__PURE__ */ __name((value) => isRecord2(value) ? value : void 0, "toErrorRecord");
39483
+ var isValidHttpCode3 = /* @__PURE__ */ __name((value) => typeof value === "number" && Number.isInteger(value) && value >= 100 && value <= 599, "isValidHttpCode");
39394
39484
  var sanitizeErrorResponse = /* @__PURE__ */ __name((response) => {
39395
39485
  const asRecord = isRecord2(response) ? response : void 0;
39396
39486
  const statusRaw = asRecord?.status ?? asRecord?.statusCode;
@@ -39432,6 +39522,9 @@ var sanitizeErrorResponse = /* @__PURE__ */ __name((response) => {
39432
39522
  if (bodyError.data !== void 0) {
39433
39523
  safeError.data = bodyError.data;
39434
39524
  }
39525
+ if (isValidHttpCode3(bodyError.httpCode)) {
39526
+ safeError.httpCode = bodyError.httpCode;
39527
+ }
39435
39528
  return {
39436
39529
  ok: false,
39437
39530
  error: safeError
@@ -39453,11 +39546,19 @@ var resolveAppErrorExtra = /* @__PURE__ */ __name((store2, error2) => {
39453
39546
  try {
39454
39547
  for (const helper of store2.errors.values()) {
39455
39548
  if (helper.is(error2)) {
39456
- if (!isRecord2(error2)) return { id: void 0, data: void 0 };
39549
+ if (!isRecord2(error2)) {
39550
+ return {
39551
+ id: void 0,
39552
+ data: void 0,
39553
+ httpCode: isValidHttpCode3(helper.httpCode) ? helper.httpCode : void 0
39554
+ };
39555
+ }
39457
39556
  const name = error2["name" /* Name */];
39458
39557
  const id2 = typeof name === "string" ? name : void 0;
39459
39558
  const data = error2["data" /* Data */];
39460
- return { id: id2, data };
39559
+ const runtimeHttpCode = error2["httpCode" /* HttpCode */];
39560
+ const httpCode = isValidHttpCode3(runtimeHttpCode) ? runtimeHttpCode : isValidHttpCode3(helper.httpCode) ? helper.httpCode : void 0;
39561
+ return { id: id2, data, httpCode };
39461
39562
  }
39462
39563
  }
39463
39564
  } catch {
@@ -39467,6 +39568,7 @@ var resolveAppErrorExtra = /* @__PURE__ */ __name((store2, error2) => {
39467
39568
  var handleRequestError = /* @__PURE__ */ __name((options) => {
39468
39569
  const { error: error2, req, res, store: store2, logger, cors, serializer: serializer3, logKey } = options;
39469
39570
  const appErrorExtra = resolveAppErrorExtra(store2, error2);
39571
+ const responseStatus = appErrorExtra?.httpCode ?? 500;
39470
39572
  const displayMessage = appErrorExtra && error2 instanceof Error && error2.message ? error2.message : "Internal Error" /* InternalError */;
39471
39573
  safeLogError(logger, logKey, { error: errorMessage(error2) });
39472
39574
  applyCorsActual(req, res, cors);
@@ -39474,7 +39576,7 @@ var handleRequestError = /* @__PURE__ */ __name((options) => {
39474
39576
  res,
39475
39577
  sanitizeErrorResponse(
39476
39578
  jsonErrorResponse(
39477
- 500,
39579
+ responseStatus,
39478
39580
  displayMessage,
39479
39581
  "INTERNAL_ERROR" /* InternalError */,
39480
39582
  appErrorExtra
@@ -39486,7 +39588,7 @@ var handleRequestError = /* @__PURE__ */ __name((options) => {
39486
39588
 
39487
39589
  // src/node/exposure/requestContext.ts
39488
39590
  var ExposureRequestContext = defineAsyncContext({
39489
- id: "platform.node.exposure.request"
39591
+ id: "platform.node.ctx.exposureRequest"
39490
39592
  });
39491
39593
  function useExposureContext() {
39492
39594
  return ExposureRequestContext.use();
@@ -40433,6 +40535,15 @@ async function recordFlowShape(descriptor) {
40433
40535
  }
40434
40536
  __name(recordFlowShape, "recordFlowShape");
40435
40537
 
40538
+ // src/node/durable/tags/durableWorkflow.tag.ts
40539
+ var durableWorkflowTag = defineTag({
40540
+ id: "globals.tags.durableWorkflow",
40541
+ meta: {
40542
+ title: "Durable Workflow",
40543
+ description: "Marks tasks intended to run as durable workflows so they can be discovered at runtime."
40544
+ }
40545
+ });
40546
+
40436
40547
  // src/node/durable/core/DurableResource.ts
40437
40548
  var DurableResource = class _DurableResource {
40438
40549
  constructor(service, contextStorage, store2, runnerStore) {
@@ -40489,6 +40600,14 @@ var DurableResource = class _DurableResource {
40489
40600
  await effectiveTask.run(input, depsWithRecorder);
40490
40601
  });
40491
40602
  }
40603
+ getWorkflows() {
40604
+ if (!this.runnerStore) {
40605
+ throw new Error(
40606
+ "Durable workflow discovery is not available: runner store was not provided to DurableResource. Use a Runner durable resource (durableResource/memoryDurableResource/redisDurableResource) instead of manually constructing DurableResource."
40607
+ );
40608
+ }
40609
+ return this.runnerStore.getTasksWithTag(durableWorkflowTag);
40610
+ }
40492
40611
  injectRecorderIntoDurableDeps(deps, ctx) {
40493
40612
  const next = { ...deps };
40494
40613
  for (const [key, value] of Object.entries(deps)) {
@@ -40506,8 +40625,11 @@ var DurableResource = class _DurableResource {
40506
40625
  }
40507
40626
  return next;
40508
40627
  }
40509
- startExecution(task2, input, options) {
40510
- return this.service.startExecution(task2, input, options);
40628
+ start(task2, input, options) {
40629
+ if (typeof task2 === "string") {
40630
+ return this.service.start(task2, input, options);
40631
+ }
40632
+ return this.service.start(task2, input, options);
40511
40633
  }
40512
40634
  cancelExecution(executionId, reason) {
40513
40635
  return this.service.cancelExecution(executionId, reason);
@@ -40515,16 +40637,22 @@ var DurableResource = class _DurableResource {
40515
40637
  wait(executionId, options) {
40516
40638
  return this.service.wait(executionId, options);
40517
40639
  }
40518
- execute(task2, input, options) {
40519
- return this.service.execute(task2, input, options);
40520
- }
40521
- executeStrict(task2, input, options) {
40522
- return this.service.executeStrict(task2, input, options);
40640
+ startAndWait(task2, input, options) {
40641
+ if (typeof task2 === "string") {
40642
+ return this.service.startAndWait(task2, input, options);
40643
+ }
40644
+ return this.service.startAndWait(task2, input, options);
40523
40645
  }
40524
40646
  schedule(task2, input, options) {
40647
+ if (typeof task2 === "string") {
40648
+ return this.service.schedule(task2, input, options);
40649
+ }
40525
40650
  return this.service.schedule(task2, input, options);
40526
40651
  }
40527
40652
  ensureSchedule(task2, input, options) {
40653
+ if (typeof task2 === "string") {
40654
+ return this.service.ensureSchedule(task2, input, options);
40655
+ }
40528
40656
  return this.service.ensureSchedule(task2, input, options);
40529
40657
  }
40530
40658
  pauseSchedule(scheduleId) {
@@ -41047,10 +41175,11 @@ var ScheduleManager = class {
41047
41175
  static {
41048
41176
  __name(this, "ScheduleManager");
41049
41177
  }
41050
- async ensureSchedule(task2, input, options) {
41178
+ async ensureSchedule(taskRef, input, options) {
41051
41179
  if (!options.cron && options.interval === void 0) {
41052
41180
  throw new Error("ensureSchedule() requires cron or interval");
41053
41181
  }
41182
+ const task2 = this.resolveTaskReference(taskRef, "ensureSchedule");
41054
41183
  this.taskRegistry.register(task2);
41055
41184
  const scheduleId = options.id;
41056
41185
  const lockTtlMs = 1e4;
@@ -41113,7 +41242,8 @@ var ScheduleManager = class {
41113
41242
  }
41114
41243
  }
41115
41244
  }
41116
- async schedule(task2, input, options) {
41245
+ async schedule(taskRef, input, options) {
41246
+ const task2 = this.resolveTaskReference(taskRef, "schedule");
41117
41247
  this.taskRegistry.register(task2);
41118
41248
  const id2 = options.id ?? createExecutionId();
41119
41249
  if (options.cron || options.interval !== void 0) {
@@ -41196,6 +41326,18 @@ var ScheduleManager = class {
41196
41326
  async remove(id2) {
41197
41327
  await this.store.deleteSchedule(id2);
41198
41328
  }
41329
+ resolveTaskReference(taskRef, apiMethod) {
41330
+ if (typeof taskRef !== "string") {
41331
+ return taskRef;
41332
+ }
41333
+ const resolved = this.taskRegistry.find(taskRef);
41334
+ if (!resolved) {
41335
+ throw new Error(
41336
+ `DurableService.${apiMethod}() could not resolve task id "${taskRef}". Ensure the task is registered in the runtime store.`
41337
+ );
41338
+ }
41339
+ return resolved;
41340
+ }
41199
41341
  };
41200
41342
 
41201
41343
  // src/node/durable/core/managers/SignalHandler.ts
@@ -42086,99 +42228,117 @@ var ExecutionManager = class {
42086
42228
  static {
42087
42229
  __name(this, "ExecutionManager");
42088
42230
  }
42089
- async startExecution(task2, input, options) {
42231
+ async start(taskRef, input, options) {
42232
+ const task2 = this.resolveTaskReference(taskRef, "start");
42090
42233
  this.taskRegistry.register(task2);
42234
+ this.assertCanExecute();
42235
+ if (options?.idempotencyKey) {
42236
+ return this.startWithIdempotencyKey(
42237
+ task2,
42238
+ input,
42239
+ options.idempotencyKey,
42240
+ options
42241
+ );
42242
+ }
42243
+ const executionId = await this.persistNewExecution(task2, input, options);
42244
+ await this.kickoffWithFailsafe(executionId);
42245
+ return executionId;
42246
+ }
42247
+ // ─── Idempotent start ──────────────────────────────────────────────────────
42248
+ /**
42249
+ * Start a workflow with deduplication: if the same (taskId, idempotencyKey)
42250
+ * was already started, returns the existing executionId instead of creating
42251
+ * a duplicate. Uses a distributed lock to prevent concurrent races.
42252
+ */
42253
+ async startWithIdempotencyKey(task2, input, idempotencyKey, options) {
42254
+ this.assertStoreSupportsIdempotency();
42255
+ return this.withIdempotencyLock(task2.id, idempotencyKey, async () => {
42256
+ const existingId = await this.config.store.getExecutionIdByIdempotencyKey({
42257
+ taskId: task2.id,
42258
+ idempotencyKey
42259
+ });
42260
+ if (existingId) return existingId;
42261
+ const executionId = createExecutionId();
42262
+ const claimed = await this.config.store.setExecutionIdByIdempotencyKey({
42263
+ taskId: task2.id,
42264
+ idempotencyKey,
42265
+ executionId
42266
+ });
42267
+ if (!claimed) {
42268
+ return this.resolveRacedIdempotencyKey(task2.id, idempotencyKey);
42269
+ }
42270
+ await this.persistNewExecution(task2, input, options, executionId);
42271
+ await this.kickoffExecution(executionId);
42272
+ return executionId;
42273
+ });
42274
+ }
42275
+ assertCanExecute() {
42091
42276
  if (!this.config.queue && !this.config.taskExecutor) {
42092
42277
  throw new Error(
42093
42278
  "DurableService requires `taskExecutor` to execute Runner tasks (when no queue is configured). Use `durableResource.fork(...).with(...)` in a Runner runtime, or provide a custom executor in config."
42094
42279
  );
42095
42280
  }
42096
- const idempotencyKey = options?.idempotencyKey;
42097
- if (idempotencyKey) {
42098
- if (!this.config.store.getExecutionIdByIdempotencyKey || !this.config.store.setExecutionIdByIdempotencyKey) {
42099
- throw new Error(
42100
- "Durable store does not support execution idempotency keys. Implement getExecutionIdByIdempotencyKey/setExecutionIdByIdempotencyKey on the store to use ExecuteOptions.idempotencyKey."
42101
- );
42102
- }
42103
- const lockTtlMs = 1e4;
42104
- const lockResource = `idempotency:${task2.id}:${idempotencyKey}`;
42105
- const canLock = !!this.config.store.acquireLock && !!this.config.store.releaseLock;
42106
- let lockId = null;
42107
- if (canLock) {
42108
- const maxAttempts = 50;
42109
- for (let attempt = 0; attempt < maxAttempts; attempt += 1) {
42110
- lockId = await this.config.store.acquireLock(
42111
- lockResource,
42112
- lockTtlMs
42113
- );
42114
- if (lockId !== null) break;
42115
- await sleepMs(5);
42116
- }
42117
- if (lockId === null) {
42118
- throw new Error(
42119
- `Failed to acquire idempotency lock for '${task2.id}:${idempotencyKey}'`
42120
- );
42121
- }
42122
- }
42281
+ }
42282
+ assertStoreSupportsIdempotency() {
42283
+ if (!this.config.store.getExecutionIdByIdempotencyKey || !this.config.store.setExecutionIdByIdempotencyKey) {
42284
+ throw new Error(
42285
+ "Durable store does not support execution idempotency keys. Implement getExecutionIdByIdempotencyKey/setExecutionIdByIdempotencyKey on the store to use ExecuteOptions.idempotencyKey."
42286
+ );
42287
+ }
42288
+ }
42289
+ /**
42290
+ * Acquires a distributed lock around the idempotency check-and-set,
42291
+ * falling back to lock-free operation when the store has no locking support.
42292
+ */
42293
+ async withIdempotencyLock(taskId, idempotencyKey, fn) {
42294
+ const canLock = !!this.config.store.acquireLock && !!this.config.store.releaseLock;
42295
+ if (!canLock) return fn();
42296
+ const lockResource = `idempotency:${taskId}:${idempotencyKey}`;
42297
+ const lockTtlMs = 1e4;
42298
+ const maxAttempts = 50;
42299
+ let lockId = null;
42300
+ for (let attempt = 0; attempt < maxAttempts; attempt += 1) {
42301
+ lockId = await this.config.store.acquireLock(lockResource, lockTtlMs);
42302
+ if (lockId !== null) break;
42303
+ await sleepMs(5);
42304
+ }
42305
+ if (lockId === null) {
42306
+ throw new Error(
42307
+ `Failed to acquire idempotency lock for '${taskId}:${idempotencyKey}'`
42308
+ );
42309
+ }
42310
+ try {
42311
+ return await fn();
42312
+ } finally {
42123
42313
  try {
42124
- const existing = await this.config.store.getExecutionIdByIdempotencyKey(
42125
- {
42126
- taskId: task2.id,
42127
- idempotencyKey
42128
- }
42129
- );
42130
- if (existing) return existing;
42131
- const executionId2 = createExecutionId();
42132
- const setOk = await this.config.store.setExecutionIdByIdempotencyKey({
42133
- taskId: task2.id,
42134
- idempotencyKey,
42135
- executionId: executionId2
42136
- });
42137
- if (!setOk) {
42138
- const raced = await this.config.store.getExecutionIdByIdempotencyKey({
42139
- taskId: task2.id,
42140
- idempotencyKey
42141
- });
42142
- if (raced) return raced;
42143
- throw new Error(
42144
- "Failed to set idempotency mapping but no existing mapping found."
42145
- );
42146
- }
42147
- const execution2 = {
42148
- id: executionId2,
42149
- taskId: task2.id,
42150
- input,
42151
- status: ExecutionStatus.Pending,
42152
- attempt: 1,
42153
- maxAttempts: this.config.execution?.maxAttempts ?? 3,
42154
- timeout: options?.timeout ?? this.config.execution?.timeout,
42155
- createdAt: /* @__PURE__ */ new Date(),
42156
- updatedAt: /* @__PURE__ */ new Date()
42157
- };
42158
- await this.config.store.saveExecution(execution2);
42159
- await this.auditLogger.log({
42160
- kind: DurableAuditEntryKind.ExecutionStatusChanged,
42161
- executionId: executionId2,
42162
- taskId: task2.id,
42163
- attempt: execution2.attempt,
42164
- from: null,
42165
- to: ExecutionStatus.Pending,
42166
- reason: "created"
42167
- });
42168
- await this.kickoffExecution(executionId2);
42169
- return executionId2;
42170
- } finally {
42171
- if (canLock && lockId !== null) {
42172
- try {
42173
- await this.config.store.releaseLock(lockResource, lockId);
42174
- } catch {
42175
- }
42176
- }
42314
+ await this.config.store.releaseLock(lockResource, lockId);
42315
+ } catch {
42177
42316
  }
42178
42317
  }
42179
- const executionId = createExecutionId();
42318
+ }
42319
+ /**
42320
+ * Recovery path: `setExecutionIdByIdempotencyKey` returned false (another
42321
+ * writer won the race), so we re-read the mapping to get their executionId.
42322
+ */
42323
+ async resolveRacedIdempotencyKey(taskId, idempotencyKey) {
42324
+ const racedId = await this.config.store.getExecutionIdByIdempotencyKey({
42325
+ taskId,
42326
+ idempotencyKey
42327
+ });
42328
+ if (racedId) return racedId;
42329
+ throw new Error(
42330
+ "Failed to set idempotency mapping but no existing mapping found."
42331
+ );
42332
+ }
42333
+ // ─── Execution persistence ─────────────────────────────────────────────────
42334
+ /**
42335
+ * Creates a new execution record, persists it to the store, and logs an
42336
+ * audit entry. Returns the executionId.
42337
+ */
42338
+ async persistNewExecution(task2, input, options, executionId) {
42339
+ const id2 = executionId ?? createExecutionId();
42180
42340
  const execution = {
42181
- id: executionId,
42341
+ id: id2,
42182
42342
  taskId: task2.id,
42183
42343
  input,
42184
42344
  status: ExecutionStatus.Pending,
@@ -42191,37 +42351,40 @@ var ExecutionManager = class {
42191
42351
  await this.config.store.saveExecution(execution);
42192
42352
  await this.auditLogger.log({
42193
42353
  kind: DurableAuditEntryKind.ExecutionStatusChanged,
42194
- executionId,
42354
+ executionId: id2,
42195
42355
  taskId: task2.id,
42196
42356
  attempt: execution.attempt,
42197
42357
  from: null,
42198
42358
  to: ExecutionStatus.Pending,
42199
42359
  reason: "created"
42200
42360
  });
42201
- const kickoffTimerId = `kickoff:${executionId}`;
42202
- const kickoffFailsafeDelayMs = this.config.execution?.kickoffFailsafeDelayMs ?? 1e4;
42203
- const shouldArmKickoffFailsafe = Boolean(this.config.queue) && kickoffFailsafeDelayMs > 0;
42204
- if (shouldArmKickoffFailsafe) {
42361
+ return id2;
42362
+ }
42363
+ /**
42364
+ * Kicks off an execution with a failsafe timer for queue mode.
42365
+ * If the queue enqueue succeeds, the timer is cleaned up immediately.
42366
+ * If enqueue fails, the timer remains so the polling loop can retry later.
42367
+ */
42368
+ async kickoffWithFailsafe(executionId) {
42369
+ const failsafeDelayMs = this.config.execution?.kickoffFailsafeDelayMs ?? 1e4;
42370
+ const shouldArmFailsafe = Boolean(this.config.queue) && failsafeDelayMs > 0;
42371
+ if (shouldArmFailsafe) {
42372
+ const timerId = `kickoff:${executionId}`;
42205
42373
  await this.config.store.createTimer({
42206
- id: kickoffTimerId,
42374
+ id: timerId,
42207
42375
  executionId,
42208
42376
  type: TimerType.Retry,
42209
- fireAt: new Date(Date.now() + kickoffFailsafeDelayMs),
42377
+ fireAt: new Date(Date.now() + failsafeDelayMs),
42210
42378
  status: TimerStatus.Pending
42211
42379
  });
42212
- }
42213
- try {
42214
42380
  await this.kickoffExecution(executionId);
42215
- if (shouldArmKickoffFailsafe) {
42216
- try {
42217
- await this.config.store.deleteTimer(kickoffTimerId);
42218
- } catch {
42219
- }
42381
+ try {
42382
+ await this.config.store.deleteTimer(timerId);
42383
+ } catch {
42220
42384
  }
42221
- } catch (error2) {
42222
- throw error2;
42385
+ return;
42223
42386
  }
42224
- return executionId;
42387
+ await this.kickoffExecution(executionId);
42225
42388
  }
42226
42389
  async cancelExecution(executionId, reason) {
42227
42390
  const execution = await this.config.store.getExecution(executionId);
@@ -42252,16 +42415,16 @@ var ExecutionManager = class {
42252
42415
  timestamp: /* @__PURE__ */ new Date()
42253
42416
  });
42254
42417
  }
42255
- async execute(task2, input, options) {
42256
- const executionId = await this.startExecution(task2, input, options);
42257
- return await this.waitManager.waitForResult(executionId, {
42418
+ async startAndWait(taskRef, input, options) {
42419
+ const executionId = await this.start(taskRef, input, options);
42420
+ const data = await this.waitManager.waitForResult(executionId, {
42258
42421
  timeout: options?.timeout,
42259
42422
  waitPollIntervalMs: options?.waitPollIntervalMs
42260
42423
  });
42261
- }
42262
- async executeStrict(task2, input, options) {
42263
- const actualTask = task2;
42264
- return await this.execute(actualTask, input, options);
42424
+ return {
42425
+ durable: { executionId },
42426
+ data
42427
+ };
42265
42428
  }
42266
42429
  async processExecution(executionId) {
42267
42430
  const execution = await this.config.store.getExecution(executionId);
@@ -42470,6 +42633,18 @@ var ExecutionManager = class {
42470
42633
  });
42471
42634
  }
42472
42635
  }
42636
+ resolveTaskReference(taskRef, apiMethod) {
42637
+ if (typeof taskRef !== "string") {
42638
+ return taskRef;
42639
+ }
42640
+ const resolved = this.taskRegistry.find(taskRef);
42641
+ if (!resolved) {
42642
+ throw new Error(
42643
+ `DurableService.${apiMethod}() could not resolve task id "${taskRef}". Ensure the task is registered in the runtime store.`
42644
+ );
42645
+ }
42646
+ return resolved;
42647
+ }
42473
42648
  };
42474
42649
  var PollingManager = class {
42475
42650
  constructor(workerId, config, store2, queue, maxAttempts, defaultTimeout, taskRegistry, auditLogger, scheduleManager, callbacks) {
@@ -42664,16 +42839,26 @@ var DurableService = class {
42664
42839
  this.config = config;
42665
42840
  this.workerId = config.workerId ?? createExecutionId();
42666
42841
  this.taskRegistry = new TaskRegistry(config.taskResolver);
42667
- if (config.schedules) {
42668
- for (const schedule of config.schedules) {
42669
- this.taskRegistry.register(schedule.task);
42670
- }
42671
- }
42672
42842
  if (config.tasks) {
42673
42843
  for (const task2 of config.tasks) {
42674
42844
  this.taskRegistry.register(task2);
42675
42845
  }
42676
42846
  }
42847
+ if (config.schedules) {
42848
+ for (const schedule of config.schedules) {
42849
+ if (typeof schedule.task === "string") {
42850
+ const resolved = this.taskRegistry.find(schedule.task);
42851
+ if (!resolved) {
42852
+ throw new Error(
42853
+ `Cannot initialize durable schedule "${schedule.id}": task "${schedule.task}" is not registered.`
42854
+ );
42855
+ }
42856
+ this.taskRegistry.register(resolved);
42857
+ continue;
42858
+ }
42859
+ this.taskRegistry.register(schedule.task);
42860
+ }
42861
+ }
42677
42862
  this.auditLogger = new AuditLogger(
42678
42863
  { enabled: config.audit?.enabled, emitter: config.audit?.emitter },
42679
42864
  config.store
@@ -42733,17 +42918,18 @@ var DurableService = class {
42733
42918
  findTask(taskId) {
42734
42919
  return this.taskRegistry.find(taskId);
42735
42920
  }
42736
- async startExecution(task2, input, options) {
42737
- return this.executionManager.startExecution(task2, input, options);
42921
+ start(task2, input, options) {
42922
+ if (task2 === void 0) {
42923
+ this.pollingManager.start();
42924
+ return;
42925
+ }
42926
+ return this.executionManager.start(task2, input, options);
42738
42927
  }
42739
42928
  async cancelExecution(executionId, reason) {
42740
42929
  await this.executionManager.cancelExecution(executionId, reason);
42741
42930
  }
42742
- async execute(task2, input, options) {
42743
- return this.executionManager.execute(task2, input, options);
42744
- }
42745
- async executeStrict(task2, input, options) {
42746
- return this.executionManager.executeStrict(task2, input, options);
42931
+ async startAndWait(task2, input, options) {
42932
+ return this.executionManager.startAndWait(task2, input, options);
42747
42933
  }
42748
42934
  wait(executionId, options) {
42749
42935
  return this.waitManager.waitForResult(executionId, options);
@@ -42762,9 +42948,6 @@ var DurableService = class {
42762
42948
  }
42763
42949
  }
42764
42950
  }
42765
- start() {
42766
- this.pollingManager.start();
42767
- }
42768
42951
  async stop() {
42769
42952
  await this.pollingManager.stop();
42770
42953
  }
@@ -42900,10 +43083,7 @@ async function createRunnerDurableRuntime(config, deps) {
42900
43083
  },
42901
43084
  taskExecutor: {
42902
43085
  run: /* @__PURE__ */ __name(async (task2, input) => {
42903
- const outputPromise = await deps.taskRunner.run(
42904
- task2,
42905
- input
42906
- );
43086
+ const outputPromise = await deps.taskRunner.run(task2, input);
42907
43087
  if (outputPromise === void 0) {
42908
43088
  throw new Error(
42909
43089
  `Durable task '${task2.id}' completed without a result promise.`
@@ -42931,7 +43111,7 @@ async function createRunnerDurableRuntime(config, deps) {
42931
43111
  __name(createRunnerDurableRuntime, "createRunnerDurableRuntime");
42932
43112
 
42933
43113
  // src/node/durable/core/resource.ts
42934
- var durableResource = r.resource("base.durable").register(durableEventsArray).dependencies({
43114
+ var durableResource = r.resource("base.durable").register([durableWorkflowTag, ...durableEventsArray]).dependencies({
42935
43115
  taskRunner: globals.resources.taskRunner,
42936
43116
  eventManager: globals.resources.eventManager,
42937
43117
  runnerStore: globals.resources.store
@@ -44073,7 +44253,7 @@ async function waitUntil(predicate, options) {
44073
44253
  __name(waitUntil, "waitUntil");
44074
44254
 
44075
44255
  // src/node/durable/resources/memoryDurableResource.ts
44076
- var memoryDurableResource = r.resource("base.durable.memory").register(durableEventsArray).dependencies({
44256
+ var memoryDurableResource = r.resource("base.durable.memory").register([durableWorkflowTag, ...durableEventsArray]).dependencies({
44077
44257
  taskRunner: globals.resources.taskRunner,
44078
44258
  eventManager: globals.resources.eventManager,
44079
44259
  runnerStore: globals.resources.store
@@ -44132,7 +44312,7 @@ function deriveDurableIsolation(params) {
44132
44312
  __name(deriveDurableIsolation, "deriveDurableIsolation");
44133
44313
 
44134
44314
  // src/node/durable/resources/redisDurableResource.ts
44135
- var redisDurableResource = r.resource("base.durable.redis").register(durableEventsArray).dependencies({
44315
+ var redisDurableResource = r.resource("base.durable.redis").register([durableWorkflowTag, ...durableEventsArray]).dependencies({
44136
44316
  taskRunner: globals.resources.taskRunner,
44137
44317
  eventManager: globals.resources.eventManager,
44138
44318
  runnerStore: globals.resources.store
@@ -44510,6 +44690,6 @@ serve-static/index.js:
44510
44690
  *)
44511
44691
  */
44512
44692
 
44513
- export { DependencyProcessor, DurableAuditEntryKind, DurableContext, DurableExecutionError, DurableOperator, DurableResource, DurableService, DurableWorker, errors_exports as Errors, EventManager, ExecutionStatus, LogPrinter, Logger, MemoryEventBus, MemoryQueue, MemoryStore, MiddlewareManager, NoopEventBus, PlatformAdapter, Queue, RabbitMQQueue, RedisEventBus, RedisStore, ResourceInitializer, RunResult, ScheduleStatus, ScheduleType, Semaphore, Serializer, StepBuilder, Store, SuspensionSignal, SymbolPolicy, SymbolPolicyErrorMessage, TaskRunner, TimerStatus, TimerType, allFalse, defineAsyncContext as asyncContext, bindProcessErrorHandler, createContext2 as createContext, createDashboardMiddleware, createDefaultUnhandledError, createDurableAuditEntryId, createDurableRunnerAuditEmitter, createDurableStepId, createDurableTestSetup, createExposureFetch, createHttpClient, createHttpMixedClient, createHttpSmartClient, createNodeFile, createRunnerDurableRuntime, createTestResource, debug, debugLevels, defs_exports as definitions, disposeDurableService, durableEvents, durableEventsArray, durableResource, defineEvent as event, getConfig, globals2 as globals, hasExposureContext, defineHook as hook, initDurableService, initDurableWorker, isDurableInternalStepId, journal, levelNormal, levelVerbose, memoryDurableResource, nodeExposure, normalizeError, defineOverride as override, r, readInputFileToBuffer, redisDurableResource, defineResource as resource, defineResourceMiddleware as resourceMiddleware, run2 as run, safeReportUnhandledError, setPlatform, defineTag as tag, defineTask as task, defineTaskMiddleware as taskMiddleware, useExposureContext, waitUntil, writeInputFileToPath };
44693
+ export { DependencyProcessor, DurableAuditEntryKind, DurableContext, DurableExecutionError, DurableOperator, DurableResource, DurableService, DurableWorker, errors_exports as Errors, EventManager, ExecutionStatus, LogPrinter, Logger, MemoryEventBus, MemoryQueue, MemoryStore, MiddlewareManager, NodeInputFile, NoopEventBus, PlatformAdapter, Queue, RabbitMQQueue, RedisEventBus, RedisStore, ResourceInitializer, RunResult, RunnerError, ScheduleStatus, ScheduleType, Semaphore, Serializer, StepBuilder, Store, SuspensionSignal, SymbolPolicy, SymbolPolicyErrorMessage, TaskRunner, TimerStatus, TimerType, allFalse, defineAsyncContext as asyncContext, bindProcessErrorHandler, createContext2 as createContext, createDashboardMiddleware, createDefaultUnhandledError, createDurableAuditEntryId, createDurableRunnerAuditEmitter, createDurableStepId, createDurableTestSetup, createExposureFetch, createHttpClient, createHttpMixedClient, createHttpSmartClient, createNodeFile, createRunnerDurableRuntime, createTestResource, debug, debugLevels, defs_exports as definitions, disposeDurableService, durableEvents, durableEventsArray, durableResource, durableWorkflowTag, defineEvent as event, getConfig, globals2 as globals, hasExposureContext, defineHook as hook, initDurableService, initDurableWorker, isDurableInternalStepId, journal, levelNormal, levelVerbose, memoryDurableResource, nodeExposure, normalizeError, defineOverride as override, r, readInputFileToBuffer, redisDurableResource, defineResource as resource, defineResourceMiddleware as resourceMiddleware, run2 as run, safeReportUnhandledError, setPlatform, defineTag as tag, defineTask as task, defineTaskMiddleware as taskMiddleware, useExposureContext, waitUntil, writeInputFileToPath };
44514
44694
  //# sourceMappingURL=node.mjs.map
44515
44695
  //# sourceMappingURL=node.mjs.map