@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
@@ -221,6 +221,7 @@ function toTunnelError(input, fallbackMessage) {
221
221
  const pe = input;
222
222
  const msg = pe.message || fallbackMessage || "Tunnel error";
223
223
  return new TunnelError(pe.code, msg, pe.details, {
224
+ httpCode: pe.httpCode,
224
225
  id: pe.id,
225
226
  data: pe.data
226
227
  });
@@ -266,6 +267,7 @@ var init_protocol = __esm({
266
267
  this.name = "TunnelError";
267
268
  this.code = code;
268
269
  this.details = details;
270
+ this.httpCode = extras?.httpCode;
269
271
  this.id = extras?.id;
270
272
  this.data = extras?.data;
271
273
  }
@@ -28615,9 +28617,7 @@ var symbolPhantomTask = Symbol.for(
28615
28617
  "runner.task.phantom"
28616
28618
  );
28617
28619
  var symbolResource = Symbol.for("runner.resource");
28618
- var symbolResourceForkedFrom = Symbol.for(
28619
- "runner.resourceForkedFrom"
28620
- );
28620
+ var symbolForkedFrom = Symbol.for("runner.forkedFrom");
28621
28621
  var symbolResourceWithConfig = Symbol.for(
28622
28622
  "runner.resourceWithConfig"
28623
28623
  );
@@ -28684,13 +28684,13 @@ __export(defs_exports, {
28684
28684
  symbolError: () => symbolError,
28685
28685
  symbolEvent: () => symbolEvent,
28686
28686
  symbolFilePath: () => symbolFilePath,
28687
+ symbolForkedFrom: () => symbolForkedFrom,
28687
28688
  symbolHook: () => symbolHook,
28688
28689
  symbolMiddleware: () => symbolMiddleware,
28689
28690
  symbolMiddlewareConfigured: () => symbolMiddlewareConfigured,
28690
28691
  symbolOptionalDependency: () => symbolOptionalDependency,
28691
28692
  symbolPhantomTask: () => symbolPhantomTask,
28692
28693
  symbolResource: () => symbolResource,
28693
- symbolResourceForkedFrom: () => symbolResourceForkedFrom,
28694
28694
  symbolResourceMiddleware: () => symbolResourceMiddleware,
28695
28695
  symbolResourceWithConfig: () => symbolResourceWithConfig,
28696
28696
  symbolTag: () => symbolTag,
@@ -28895,12 +28895,26 @@ __export(errors_exports, {
28895
28895
  });
28896
28896
 
28897
28897
  // src/definers/defineError.ts
28898
+ var isValidHttpCode = /* @__PURE__ */ __name((value) => Number.isInteger(value) && value >= 100 && value <= 599, "isValidHttpCode");
28899
+ var assertHttpCode = /* @__PURE__ */ __name((value) => {
28900
+ if (!isValidHttpCode(value)) {
28901
+ throw new Error(
28902
+ `Error httpCode must be an integer between 100 and 599. Received: ${value}`
28903
+ );
28904
+ }
28905
+ }, "assertHttpCode");
28898
28906
  var RunnerError = class extends Error {
28899
- constructor(id2, message, data) {
28900
- super(message);
28907
+ constructor(id2, message, data, httpCode, remediation) {
28908
+ super(
28909
+ remediation !== void 0 ? `${message}
28910
+
28911
+ Remediation: ${remediation}` : message
28912
+ );
28901
28913
  this.id = id2;
28902
28914
  this.data = data;
28903
28915
  this.name = id2;
28916
+ this.httpCode = httpCode;
28917
+ this.remediation = remediation;
28904
28918
  }
28905
28919
  static {
28906
28920
  __name(this, "RunnerError");
@@ -28920,10 +28934,20 @@ var ErrorHelper = class {
28920
28934
  get id() {
28921
28935
  return this.definition.id;
28922
28936
  }
28937
+ get httpCode() {
28938
+ return this.definition.httpCode;
28939
+ }
28923
28940
  throw(data) {
28924
28941
  const parsed = this.definition.dataSchema ? this.definition.dataSchema.parse(data) : data;
28925
28942
  const message = this.definition.format(parsed);
28926
- throw new RunnerError(this.definition.id, message, parsed);
28943
+ const remediation = typeof this.definition.remediation === "function" ? this.definition.remediation(parsed) : this.definition.remediation;
28944
+ throw new RunnerError(
28945
+ this.definition.id,
28946
+ message,
28947
+ parsed,
28948
+ this.definition.httpCode,
28949
+ remediation
28950
+ );
28927
28951
  }
28928
28952
  is(error2) {
28929
28953
  return error2 instanceof RunnerError && error2.name === this.definition.id;
@@ -28936,6 +28960,9 @@ var ErrorHelper = class {
28936
28960
  }
28937
28961
  };
28938
28962
  function defineError(definition, filePath) {
28963
+ if (definition.httpCode !== void 0) {
28964
+ assertHttpCode(definition.httpCode);
28965
+ }
28939
28966
  if (!definition.format) {
28940
28967
  definition.format = (data) => `${JSON.stringify(data)}`;
28941
28968
  }
@@ -28957,9 +28984,22 @@ function clone(s, patch) {
28957
28984
  __name(clone, "clone");
28958
28985
 
28959
28986
  // src/definers/builders/error/fluent-builder.ts
28987
+ var isValidHttpCode2 = /* @__PURE__ */ __name((value) => Number.isInteger(value) && value >= 100 && value <= 599, "isValidHttpCode");
28988
+ var assertHttpCode2 = /* @__PURE__ */ __name((value) => {
28989
+ if (!isValidHttpCode2(value)) {
28990
+ throw new Error(
28991
+ `Error httpCode must be an integer between 100 and 599. Received: ${value}`
28992
+ );
28993
+ }
28994
+ }, "assertHttpCode");
28960
28995
  function makeErrorBuilder(state) {
28961
28996
  const builder = {
28962
28997
  id: state.id,
28998
+ httpCode(code) {
28999
+ assertHttpCode2(code);
29000
+ const next = clone(state, { httpCode: code });
29001
+ return makeErrorBuilder(next);
29002
+ },
28963
29003
  serialize(fn) {
28964
29004
  const next = clone(state, { serialize: fn });
28965
29005
  return makeErrorBuilder(next);
@@ -28976,6 +29016,10 @@ function makeErrorBuilder(state) {
28976
29016
  const next = clone(state, { format: fn });
28977
29017
  return makeErrorBuilder(next);
28978
29018
  },
29019
+ remediation(advice) {
29020
+ const next = clone(state, { remediation: advice });
29021
+ return makeErrorBuilder(next);
29022
+ },
28979
29023
  meta(m) {
28980
29024
  const next = clone(state, { meta: m });
28981
29025
  return makeErrorBuilder(next);
@@ -28984,10 +29028,12 @@ function makeErrorBuilder(state) {
28984
29028
  return defineError(
28985
29029
  {
28986
29030
  id: state.id,
29031
+ httpCode: state.httpCode,
28987
29032
  serialize: state.serialize,
28988
29033
  parse: state.parse,
28989
29034
  dataSchema: state.dataSchema,
28990
29035
  format: state.format,
29036
+ remediation: state.remediation,
28991
29037
  meta: state.meta
28992
29038
  },
28993
29039
  state.filePath
@@ -29004,6 +29050,7 @@ function errorBuilder(id2) {
29004
29050
  const initial = Object.freeze({
29005
29051
  id: id2,
29006
29052
  filePath,
29053
+ httpCode: void 0,
29007
29054
  serialize: void 0,
29008
29055
  parse: void 0,
29009
29056
  dataSchema: void 0,
@@ -29012,7 +29059,13 @@ function errorBuilder(id2) {
29012
29059
  return makeErrorBuilder(initial);
29013
29060
  }
29014
29061
  __name(errorBuilder, "errorBuilder");
29015
- var error = errorBuilder;
29062
+ function isRunnerError(error2) {
29063
+ return error2 instanceof RunnerError;
29064
+ }
29065
+ __name(isRunnerError, "isRunnerError");
29066
+ var error = Object.assign(errorBuilder, {
29067
+ is: isRunnerError
29068
+ });
29016
29069
 
29017
29070
  // src/platform/adapters/node-als.ts
29018
29071
  async function loadAsyncLocalStorageClass() {
@@ -29443,9 +29496,13 @@ var PlatformAdapter = class {
29443
29496
  // src/errors.ts
29444
29497
  var duplicateRegistrationError = error("runner.errors.duplicateRegistration").format(
29445
29498
  ({ 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.`
29499
+ ).remediation(
29500
+ ({ 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.`
29446
29501
  ).build();
29447
29502
  var dependencyNotFoundError = error("runner.errors.dependencyNotFound").format(
29448
29503
  ({ key }) => `Dependency ${key.toString()} not found. Did you forget to register it through a resource?`
29504
+ ).remediation(
29505
+ ({ key }) => `Register the dependency "${key.toString()}" in a parent resource using .register([${key.toString()}]). If the dependency is optional, use .optional() when declaring it.`
29449
29506
  ).build();
29450
29507
  var unknownItemTypeError = error(
29451
29508
  "runner.errors.unknownItemType"
@@ -29453,10 +29510,14 @@ var unknownItemTypeError = error(
29453
29510
  ({ item }) => `Unknown item type: ${String(
29454
29511
  item
29455
29512
  )}. Please ensure you are not using different versions of '@bluelibs/runner'`
29513
+ ).remediation(
29514
+ "Check that all packages depend on the same version of '@bluelibs/runner'. Run 'npm ls @bluelibs/runner' to detect duplicates."
29456
29515
  ).build();
29457
29516
  var contextError = error(
29458
29517
  "runner.errors.context"
29459
- ).format(({ details }) => details ?? "Context error").build();
29518
+ ).format(({ details }) => details ?? "Context error").remediation(
29519
+ "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."
29520
+ ).build();
29460
29521
  var circularDependenciesError = error("runner.errors.circularDependencies").format(({ cycles }) => {
29461
29522
  const cycleDetails = cycles.map((cycle) => ` \u2022 ${cycle}`).join("\n");
29462
29523
  const hasMiddleware = cycles.some((cycle) => cycle.includes("middleware"));
@@ -29469,36 +29530,54 @@ var circularDependenciesError = error("runner.errors.circularDependencies").form
29469
29530
  }
29470
29531
  return `Circular dependencies detected:
29471
29532
  ${cycleDetails}${guidance}`;
29472
- }).build();
29533
+ }).remediation(
29534
+ "Break the cycle by extracting shared state into a new resource that both sides depend on, or use events for indirect communication."
29535
+ ).build();
29473
29536
  var eventNotFoundError = error(
29474
29537
  "runner.errors.eventNotFound"
29475
29538
  ).format(
29476
29539
  ({ id: id2 }) => `Event "${id2.toString()}" not found. Did you forget to register it?`
29540
+ ).remediation(
29541
+ ({ 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().`
29477
29542
  ).build();
29478
29543
  var resourceNotFoundError = error(
29479
29544
  "runner.errors.resourceNotFound"
29480
29545
  ).format(
29481
29546
  ({ id: id2 }) => `Resource "${id2.toString()}" not found. Did you forget to register it or are you using the correct id?`
29547
+ ).remediation(
29548
+ ({ id: id2 }) => `Register the resource "${id2.toString()}" in a parent resource via .register([yourResource]). Verify the id string matches exactly (ids are case-sensitive).`
29482
29549
  ).build();
29483
29550
  var middlewareNotRegisteredError = error("runner.errors.middlewareNotRegistered").format(
29484
29551
  ({ type, source, middlewareId }) => `Middleware inside ${type} "${source}" depends on "${middlewareId}" but it's not registered. Did you forget to register it?`
29552
+ ).remediation(
29553
+ ({ middlewareId }) => `Register the middleware "${middlewareId}" alongside its consumer in a parent resource via .register([yourMiddleware]).`
29485
29554
  ).build();
29486
29555
  var tagNotFoundError = error(
29487
29556
  "runner.errors.tagNotFound"
29488
29557
  ).format(
29489
29558
  ({ id: id2 }) => `Tag "${id2}" not registered. Did you forget to register it inside a resource?`
29559
+ ).remediation(
29560
+ ({ id: id2 }) => `Register the tag "${id2}" in a parent resource via .register([yourTag]). Tags must be registered before they can be queried.`
29490
29561
  ).build();
29491
29562
  var lockedError = error(
29492
29563
  "runner.errors.locked"
29493
29564
  ).format(
29494
29565
  ({ what }) => `Cannot modify the ${what.toString()} when it is locked.`
29566
+ ).remediation(
29567
+ ({ what }) => `The ${what.toString()} is locked after initialization. Perform all modifications before calling run().`
29495
29568
  ).build();
29496
29569
  var storeAlreadyInitializedError = error(
29497
29570
  "runner.errors.storeAlreadyInitialized"
29498
- ).format(() => "Store already initialized. Cannot reinitialize.").build();
29571
+ ).format(() => "Store already initialized. Cannot reinitialize.").remediation(
29572
+ "Do not call run() more than once on the same resource tree. Create a fresh resource if you need a separate runtime."
29573
+ ).build();
29499
29574
  var validationError = error("runner.errors.validation").format(({ subject, id: id2, originalError }) => {
29500
29575
  const errorMessage2 = originalError instanceof Error ? originalError.message : String(originalError);
29501
29576
  return `${subject} validation failed for ${id2.toString()}: ${errorMessage2}`;
29577
+ }).remediation(({ subject, id: id2 }) => {
29578
+ const lower = subject.toLowerCase();
29579
+ const schemaHint = lower.includes("input") ? "inputSchema" : lower.includes("config") ? "configSchema" : lower.includes("result") ? "resultSchema" : "schema";
29580
+ return `Check the ${subject} passed to "${id2.toString()}". Ensure it matches the schema defined via .${schemaHint}().`;
29502
29581
  }).build();
29503
29582
  var eventCycleError = error("runner.errors.eventCycle").format(({ path: path4 }) => {
29504
29583
  const chain = path4.map((p) => `${p.id}\u2190${p.source}`).join(" -> ");
@@ -29506,33 +29585,49 @@ var eventCycleError = error("runner.errors.eventCycle").format(({ path: path4 })
29506
29585
  ${chain}
29507
29586
 
29508
29587
  Break the cycle by changing hook logic (avoid mutual emits) or gate with conditions/tags.`;
29509
- }).build();
29588
+ }).remediation(
29589
+ "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."
29590
+ ).build();
29510
29591
  var eventEmissionCycleError = error("runner.errors.eventEmissionCycle").format(({ cycles }) => {
29511
29592
  const list = cycles.map((c) => ` \u2022 ${c}`).join("\n");
29512
29593
  return `Event emission cycles detected between hooks and events:
29513
29594
  ${list}
29514
29595
 
29515
29596
  This was detected at compile time (dry-run). Break the cycle by avoiding mutual emits between hooks or scoping hooks using tags.`;
29516
- }).build();
29597
+ }).remediation(
29598
+ "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."
29599
+ ).build();
29517
29600
  var platformUnsupportedFunctionError = error("runner.errors.platformUnsupportedFunction").format(
29518
29601
  ({ functionName }) => `Platform function not supported in this environment: ${functionName}. Detected platform: ${detectEnvironment()}.`
29602
+ ).remediation(
29603
+ ({ 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.`
29519
29604
  ).build();
29520
29605
  var cancellationError = error(
29521
29606
  "runner.errors.cancellation"
29522
- ).format(({ reason }) => reason || "Operation cancelled").build();
29607
+ ).format(({ reason }) => reason || "Operation cancelled").remediation(
29608
+ "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."
29609
+ ).build();
29523
29610
  var tunnelOwnershipConflictError = error("runner.errors.tunnelOwnershipConflict").format(
29524
29611
  ({ 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.`
29612
+ ).remediation(
29613
+ ({ 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.`
29525
29614
  ).build();
29526
29615
  var phantomTaskNotRoutedError = error("runner.errors.phantomTaskNotRouted").format(
29527
29616
  ({ 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).`
29617
+ ).remediation(
29618
+ ({ 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.`
29528
29619
  ).build();
29529
29620
  var taskNotRegisteredError = error("runner.errors.taskNotRegistered").format(
29530
29621
  ({ taskId }) => `Task "${taskId}" is not registered in the Store. This is an internal error\u2014ensure the task is registered before execution.`
29622
+ ).remediation(
29623
+ ({ 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.`
29531
29624
  ).build();
29532
29625
  var builderIncompleteError = error("runner.errors.builderIncomplete").format(({ type, builderId, missingFields }) => {
29533
29626
  const typeLabel = type === "hook" ? "Hook" : type === "task-middleware" ? "Task middleware" : "Resource middleware";
29534
29627
  return `${typeLabel} "${builderId}" is incomplete. Missing required: ${missingFields.join(", ")}. Call ${missingFields.map((f) => `.${f}()`).join(" and ")} before .build().`;
29535
- }).build();
29628
+ }).remediation(
29629
+ ({ missingFields }) => `Add the missing builder steps: ${missingFields.map((f) => `.${f}()`).join(", ")} before calling .build().`
29630
+ ).build();
29536
29631
  function isCancellationError(err) {
29537
29632
  return cancellationError.is(err);
29538
29633
  }
@@ -29766,6 +29861,7 @@ function defineResource(constConfig) {
29766
29861
  };
29767
29862
  },
29768
29863
  fork(newId, options) {
29864
+ const forkCallerFilePath = getCallerFile();
29769
29865
  const forkedParts = resolveForkedRegisterAndDependencies({
29770
29866
  register: constConfig.register,
29771
29867
  dependencies: constConfig.dependencies,
@@ -29777,11 +29873,10 @@ function defineResource(constConfig) {
29777
29873
  id: newId,
29778
29874
  register: forkedParts.register,
29779
29875
  dependencies: forkedParts.dependencies,
29780
- [symbolFilePath]: filePath
29876
+ [symbolFilePath]: forkCallerFilePath
29781
29877
  });
29782
- forked[symbolResourceForkedFrom] = {
29783
- fromId: id2,
29784
- forkedAtFilePath: getCallerFile()
29878
+ forked[symbolForkedFrom] = {
29879
+ fromId: id2
29785
29880
  };
29786
29881
  return forked;
29787
29882
  }
@@ -30012,7 +30107,7 @@ __name(defineTag, "defineTag");
30012
30107
 
30013
30108
  // src/globals/middleware/requireContext.middleware.ts
30014
30109
  var requireContextTaskMiddleware = defineTaskMiddleware({
30015
- id: "globals.middleware.requireContext",
30110
+ id: "globals.middleware.task.requireContext",
30016
30111
  async run({ task: task2, next }, _deps, config) {
30017
30112
  if (!config.context) {
30018
30113
  throw new Error(
@@ -31656,7 +31751,7 @@ var cacheFactoryTask = defineTask({
31656
31751
  });
31657
31752
  var journalKeys = {
31658
31753
  /** Whether the result was served from cache (true) or freshly computed (false) */
31659
- hit: journal.createKey("globals.middleware.cache.hit")
31754
+ hit: journal.createKey("globals.middleware.task.cache.hit")
31660
31755
  };
31661
31756
  var cacheResource = defineResource({
31662
31757
  id: "globals.resources.cache",
@@ -31686,7 +31781,7 @@ var cacheResource = defineResource({
31686
31781
  });
31687
31782
  var defaultKeyBuilder = /* @__PURE__ */ __name((taskId, input) => `${taskId}-${JSON.stringify(input)}`, "defaultKeyBuilder");
31688
31783
  var cacheMiddleware = defineTaskMiddleware({
31689
- id: "globals.middleware.cache",
31784
+ id: "globals.middleware.task.cache",
31690
31785
  dependencies: { cache: cacheResource },
31691
31786
  async run({ task: task2, next, journal: journal2 }, deps, config) {
31692
31787
  const { cache } = deps;
@@ -31737,11 +31832,11 @@ var CircuitBreakerOpenError = class extends Error {
31737
31832
  var journalKeys2 = {
31738
31833
  /** Current state of the circuit breaker (CLOSED, OPEN, or HALF_OPEN) */
31739
31834
  state: journal.createKey(
31740
- "globals.middleware.circuitBreaker.state"
31835
+ "globals.middleware.task.circuitBreaker.state"
31741
31836
  ),
31742
31837
  /** Current failure count */
31743
31838
  failures: journal.createKey(
31744
- "globals.middleware.circuitBreaker.failures"
31839
+ "globals.middleware.task.circuitBreaker.failures"
31745
31840
  )
31746
31841
  };
31747
31842
  var circuitBreakerResource = defineResource({
@@ -31754,7 +31849,7 @@ var circuitBreakerResource = defineResource({
31754
31849
  }, "init")
31755
31850
  });
31756
31851
  var circuitBreakerMiddleware = defineTaskMiddleware({
31757
- id: "globals.middleware.circuitBreaker",
31852
+ id: "globals.middleware.task.circuitBreaker",
31758
31853
  dependencies: { state: circuitBreakerResource },
31759
31854
  async run({ task: task2, next, journal: journal2 }, { state }, config) {
31760
31855
  const taskId = task2.definition.id;
@@ -32334,12 +32429,12 @@ var EventManager = class {
32334
32429
 
32335
32430
  // src/models/Semaphore.ts
32336
32431
  var SemaphoreEvents = {
32337
- queued: defineEvent({ id: "semaphore.queued" }),
32338
- acquired: defineEvent({ id: "semaphore.acquired" }),
32339
- released: defineEvent({ id: "semaphore.released" }),
32340
- timeout: defineEvent({ id: "semaphore.timeout" }),
32341
- aborted: defineEvent({ id: "semaphore.aborted" }),
32342
- disposed: defineEvent({ id: "semaphore.disposed" })
32432
+ queued: defineEvent({ id: "semaphore.events.queued" }),
32433
+ acquired: defineEvent({ id: "semaphore.events.acquired" }),
32434
+ released: defineEvent({ id: "semaphore.events.released" }),
32435
+ timeout: defineEvent({ id: "semaphore.events.timeout" }),
32436
+ aborted: defineEvent({ id: "semaphore.events.aborted" }),
32437
+ disposed: defineEvent({ id: "semaphore.events.disposed" })
32343
32438
  };
32344
32439
  var Semaphore = class {
32345
32440
  constructor(maxPermits) {
@@ -32612,7 +32707,7 @@ var concurrencyResource = defineResource({
32612
32707
  }), "init")
32613
32708
  });
32614
32709
  var concurrencyTaskMiddleware = defineTaskMiddleware({
32615
- id: "globals.middleware.concurrency",
32710
+ id: "globals.middleware.task.concurrency",
32616
32711
  dependencies: { state: concurrencyResource },
32617
32712
  async run({ task: task2, next }, { state }, config) {
32618
32713
  let semaphore = config.semaphore;
@@ -32691,14 +32786,16 @@ var RateLimitError = class extends Error {
32691
32786
  var journalKeys3 = {
32692
32787
  /** Number of remaining requests in the current window */
32693
32788
  remaining: journal.createKey(
32694
- "globals.middleware.rateLimit.remaining"
32789
+ "globals.middleware.task.rateLimit.remaining"
32695
32790
  ),
32696
32791
  /** Timestamp when the current window resets */
32697
32792
  resetTime: journal.createKey(
32698
- "globals.middleware.rateLimit.resetTime"
32793
+ "globals.middleware.task.rateLimit.resetTime"
32699
32794
  ),
32700
32795
  /** Maximum requests allowed per window */
32701
- limit: journal.createKey("globals.middleware.rateLimit.limit")
32796
+ limit: journal.createKey(
32797
+ "globals.middleware.task.rateLimit.limit"
32798
+ )
32702
32799
  };
32703
32800
  var rateLimitResource = defineResource({
32704
32801
  id: "globals.resources.rateLimit",
@@ -32710,7 +32807,7 @@ var rateLimitResource = defineResource({
32710
32807
  }, "init")
32711
32808
  });
32712
32809
  var rateLimitTaskMiddleware = defineTaskMiddleware({
32713
- id: "globals.middleware.rateLimit",
32810
+ id: "globals.middleware.task.rateLimit",
32714
32811
  configSchema: rateLimitConfigSchema,
32715
32812
  dependencies: { state: rateLimitResource },
32716
32813
  async run({ task: task2, next, journal: journal2 }, { state }, config) {
@@ -32758,7 +32855,7 @@ var temporalResource = defineResource({
32758
32855
  }, "init")
32759
32856
  });
32760
32857
  var debounceTaskMiddleware = defineTaskMiddleware({
32761
- id: "globals.middleware.debounce",
32858
+ id: "globals.middleware.task.debounce",
32762
32859
  dependencies: { state: temporalResource },
32763
32860
  async run({ task: task2, next }, { state }, config) {
32764
32861
  const { debounceStates } = state;
@@ -32795,7 +32892,7 @@ var debounceTaskMiddleware = defineTaskMiddleware({
32795
32892
  }
32796
32893
  });
32797
32894
  var throttleTaskMiddleware = defineTaskMiddleware({
32798
- id: "globals.middleware.throttle",
32895
+ id: "globals.middleware.task.throttle",
32799
32896
  dependencies: { state: temporalResource },
32800
32897
  async run({ task: task2, next }, { state }, config) {
32801
32898
  const { throttleStates } = state;
@@ -32867,12 +32964,12 @@ var throttleTaskMiddleware = defineTaskMiddleware({
32867
32964
 
32868
32965
  // src/models/Queue.ts
32869
32966
  var QueueEvents = {
32870
- enqueue: defineEvent({ id: "queue.enqueue" }),
32871
- start: defineEvent({ id: "queue.start" }),
32872
- finish: defineEvent({ id: "queue.finish" }),
32873
- error: defineEvent({ id: "queue.error" }),
32874
- cancel: defineEvent({ id: "queue.cancel" }),
32875
- disposed: defineEvent({ id: "queue.disposed" })
32967
+ enqueue: defineEvent({ id: "queue.events.enqueue" }),
32968
+ start: defineEvent({ id: "queue.events.start" }),
32969
+ finish: defineEvent({ id: "queue.events.finish" }),
32970
+ error: defineEvent({ id: "queue.events.error" }),
32971
+ cancel: defineEvent({ id: "queue.events.cancel" }),
32972
+ disposed: defineEvent({ id: "queue.events.disposed" })
32876
32973
  };
32877
32974
  var Queue = class {
32878
32975
  constructor() {
@@ -33481,13 +33578,15 @@ var retryResourceMiddleware = defineResourceMiddleware({
33481
33578
  var journalKeys6 = {
33482
33579
  /** Whether the fallback path was taken (true) or primary succeeded (false) */
33483
33580
  active: journal.createKey(
33484
- "globals.middleware.fallback.active"
33581
+ "globals.middleware.task.fallback.active"
33485
33582
  ),
33486
33583
  /** The error that triggered the fallback (only set when active is true) */
33487
- error: journal.createKey("globals.middleware.fallback.error")
33584
+ error: journal.createKey(
33585
+ "globals.middleware.task.fallback.error"
33586
+ )
33488
33587
  };
33489
33588
  var fallbackTaskMiddleware = defineTaskMiddleware({
33490
- id: "globals.middleware.fallback",
33589
+ id: "globals.middleware.task.fallback",
33491
33590
  dependencies: {
33492
33591
  taskRunner: globalResources.taskRunner
33493
33592
  },
@@ -36167,21 +36266,11 @@ var Store = class {
36167
36266
  storeGenericItem(item) {
36168
36267
  return this.registry.storeGenericItem(item);
36169
36268
  }
36170
- /**
36171
- * Returns all tasks with the given tag.
36172
- * @param tag - The tag to filter by.
36173
- * @returns The tasks with the given tag.
36174
- */
36175
36269
  getTasksWithTag(tag2) {
36176
- return this.registry.getTasksWithTag(tag2);
36270
+ return typeof tag2 === "string" ? this.registry.getTasksWithTag(tag2) : this.registry.getTasksWithTag(tag2);
36177
36271
  }
36178
- /**
36179
- * Returns all resources with the given tag.
36180
- * @param tag - The tag to filter by.
36181
- * @returns The resources with the given tag.
36182
- */
36183
36272
  getResourcesWithTag(tag2) {
36184
- return this.registry.getResourcesWithTag(tag2);
36273
+ return typeof tag2 === "string" ? this.registry.getResourcesWithTag(tag2) : this.registry.getResourcesWithTag(tag2);
36185
36274
  }
36186
36275
  };
36187
36276
 
@@ -39418,6 +39507,7 @@ var UNSAFE_ERROR_FIELDS = /* @__PURE__ */ new Set([
39418
39507
  var isRecord2 = /* @__PURE__ */ __name((value) => !!value && typeof value === "object", "isRecord");
39419
39508
  var isJsonBody = /* @__PURE__ */ __name((value) => isRecord2(value) && typeof value.ok === "boolean", "isJsonBody");
39420
39509
  var toErrorRecord = /* @__PURE__ */ __name((value) => isRecord2(value) ? value : void 0, "toErrorRecord");
39510
+ var isValidHttpCode3 = /* @__PURE__ */ __name((value) => typeof value === "number" && Number.isInteger(value) && value >= 100 && value <= 599, "isValidHttpCode");
39421
39511
  var sanitizeErrorResponse = /* @__PURE__ */ __name((response) => {
39422
39512
  const asRecord = isRecord2(response) ? response : void 0;
39423
39513
  const statusRaw = asRecord?.status ?? asRecord?.statusCode;
@@ -39459,6 +39549,9 @@ var sanitizeErrorResponse = /* @__PURE__ */ __name((response) => {
39459
39549
  if (bodyError.data !== void 0) {
39460
39550
  safeError.data = bodyError.data;
39461
39551
  }
39552
+ if (isValidHttpCode3(bodyError.httpCode)) {
39553
+ safeError.httpCode = bodyError.httpCode;
39554
+ }
39462
39555
  return {
39463
39556
  ok: false,
39464
39557
  error: safeError
@@ -39480,11 +39573,19 @@ var resolveAppErrorExtra = /* @__PURE__ */ __name((store2, error2) => {
39480
39573
  try {
39481
39574
  for (const helper of store2.errors.values()) {
39482
39575
  if (helper.is(error2)) {
39483
- if (!isRecord2(error2)) return { id: void 0, data: void 0 };
39576
+ if (!isRecord2(error2)) {
39577
+ return {
39578
+ id: void 0,
39579
+ data: void 0,
39580
+ httpCode: isValidHttpCode3(helper.httpCode) ? helper.httpCode : void 0
39581
+ };
39582
+ }
39484
39583
  const name = error2["name" /* Name */];
39485
39584
  const id2 = typeof name === "string" ? name : void 0;
39486
39585
  const data = error2["data" /* Data */];
39487
- return { id: id2, data };
39586
+ const runtimeHttpCode = error2["httpCode" /* HttpCode */];
39587
+ const httpCode = isValidHttpCode3(runtimeHttpCode) ? runtimeHttpCode : isValidHttpCode3(helper.httpCode) ? helper.httpCode : void 0;
39588
+ return { id: id2, data, httpCode };
39488
39589
  }
39489
39590
  }
39490
39591
  } catch {
@@ -39494,6 +39595,7 @@ var resolveAppErrorExtra = /* @__PURE__ */ __name((store2, error2) => {
39494
39595
  var handleRequestError = /* @__PURE__ */ __name((options) => {
39495
39596
  const { error: error2, req, res, store: store2, logger, cors, serializer: serializer3, logKey } = options;
39496
39597
  const appErrorExtra = resolveAppErrorExtra(store2, error2);
39598
+ const responseStatus = appErrorExtra?.httpCode ?? 500;
39497
39599
  const displayMessage = appErrorExtra && error2 instanceof Error && error2.message ? error2.message : "Internal Error" /* InternalError */;
39498
39600
  safeLogError(logger, logKey, { error: errorMessage(error2) });
39499
39601
  applyCorsActual(req, res, cors);
@@ -39501,7 +39603,7 @@ var handleRequestError = /* @__PURE__ */ __name((options) => {
39501
39603
  res,
39502
39604
  sanitizeErrorResponse(
39503
39605
  jsonErrorResponse(
39504
- 500,
39606
+ responseStatus,
39505
39607
  displayMessage,
39506
39608
  "INTERNAL_ERROR" /* InternalError */,
39507
39609
  appErrorExtra
@@ -39513,7 +39615,7 @@ var handleRequestError = /* @__PURE__ */ __name((options) => {
39513
39615
 
39514
39616
  // src/node/exposure/requestContext.ts
39515
39617
  var ExposureRequestContext = defineAsyncContext({
39516
- id: "platform.node.exposure.request"
39618
+ id: "platform.node.ctx.exposureRequest"
39517
39619
  });
39518
39620
  function useExposureContext() {
39519
39621
  return ExposureRequestContext.use();
@@ -40460,6 +40562,15 @@ async function recordFlowShape(descriptor) {
40460
40562
  }
40461
40563
  __name(recordFlowShape, "recordFlowShape");
40462
40564
 
40565
+ // src/node/durable/tags/durableWorkflow.tag.ts
40566
+ var durableWorkflowTag = defineTag({
40567
+ id: "globals.tags.durableWorkflow",
40568
+ meta: {
40569
+ title: "Durable Workflow",
40570
+ description: "Marks tasks intended to run as durable workflows so they can be discovered at runtime."
40571
+ }
40572
+ });
40573
+
40463
40574
  // src/node/durable/core/DurableResource.ts
40464
40575
  var DurableResource = class _DurableResource {
40465
40576
  constructor(service, contextStorage, store2, runnerStore) {
@@ -40516,6 +40627,14 @@ var DurableResource = class _DurableResource {
40516
40627
  await effectiveTask.run(input, depsWithRecorder);
40517
40628
  });
40518
40629
  }
40630
+ getWorkflows() {
40631
+ if (!this.runnerStore) {
40632
+ throw new Error(
40633
+ "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."
40634
+ );
40635
+ }
40636
+ return this.runnerStore.getTasksWithTag(durableWorkflowTag);
40637
+ }
40519
40638
  injectRecorderIntoDurableDeps(deps, ctx) {
40520
40639
  const next = { ...deps };
40521
40640
  for (const [key, value] of Object.entries(deps)) {
@@ -40533,8 +40652,11 @@ var DurableResource = class _DurableResource {
40533
40652
  }
40534
40653
  return next;
40535
40654
  }
40536
- startExecution(task2, input, options) {
40537
- return this.service.startExecution(task2, input, options);
40655
+ start(task2, input, options) {
40656
+ if (typeof task2 === "string") {
40657
+ return this.service.start(task2, input, options);
40658
+ }
40659
+ return this.service.start(task2, input, options);
40538
40660
  }
40539
40661
  cancelExecution(executionId, reason) {
40540
40662
  return this.service.cancelExecution(executionId, reason);
@@ -40542,16 +40664,22 @@ var DurableResource = class _DurableResource {
40542
40664
  wait(executionId, options) {
40543
40665
  return this.service.wait(executionId, options);
40544
40666
  }
40545
- execute(task2, input, options) {
40546
- return this.service.execute(task2, input, options);
40547
- }
40548
- executeStrict(task2, input, options) {
40549
- return this.service.executeStrict(task2, input, options);
40667
+ startAndWait(task2, input, options) {
40668
+ if (typeof task2 === "string") {
40669
+ return this.service.startAndWait(task2, input, options);
40670
+ }
40671
+ return this.service.startAndWait(task2, input, options);
40550
40672
  }
40551
40673
  schedule(task2, input, options) {
40674
+ if (typeof task2 === "string") {
40675
+ return this.service.schedule(task2, input, options);
40676
+ }
40552
40677
  return this.service.schedule(task2, input, options);
40553
40678
  }
40554
40679
  ensureSchedule(task2, input, options) {
40680
+ if (typeof task2 === "string") {
40681
+ return this.service.ensureSchedule(task2, input, options);
40682
+ }
40555
40683
  return this.service.ensureSchedule(task2, input, options);
40556
40684
  }
40557
40685
  pauseSchedule(scheduleId) {
@@ -41074,10 +41202,11 @@ var ScheduleManager = class {
41074
41202
  static {
41075
41203
  __name(this, "ScheduleManager");
41076
41204
  }
41077
- async ensureSchedule(task2, input, options) {
41205
+ async ensureSchedule(taskRef, input, options) {
41078
41206
  if (!options.cron && options.interval === void 0) {
41079
41207
  throw new Error("ensureSchedule() requires cron or interval");
41080
41208
  }
41209
+ const task2 = this.resolveTaskReference(taskRef, "ensureSchedule");
41081
41210
  this.taskRegistry.register(task2);
41082
41211
  const scheduleId = options.id;
41083
41212
  const lockTtlMs = 1e4;
@@ -41140,7 +41269,8 @@ var ScheduleManager = class {
41140
41269
  }
41141
41270
  }
41142
41271
  }
41143
- async schedule(task2, input, options) {
41272
+ async schedule(taskRef, input, options) {
41273
+ const task2 = this.resolveTaskReference(taskRef, "schedule");
41144
41274
  this.taskRegistry.register(task2);
41145
41275
  const id2 = options.id ?? createExecutionId();
41146
41276
  if (options.cron || options.interval !== void 0) {
@@ -41223,6 +41353,18 @@ var ScheduleManager = class {
41223
41353
  async remove(id2) {
41224
41354
  await this.store.deleteSchedule(id2);
41225
41355
  }
41356
+ resolveTaskReference(taskRef, apiMethod) {
41357
+ if (typeof taskRef !== "string") {
41358
+ return taskRef;
41359
+ }
41360
+ const resolved = this.taskRegistry.find(taskRef);
41361
+ if (!resolved) {
41362
+ throw new Error(
41363
+ `DurableService.${apiMethod}() could not resolve task id "${taskRef}". Ensure the task is registered in the runtime store.`
41364
+ );
41365
+ }
41366
+ return resolved;
41367
+ }
41226
41368
  };
41227
41369
 
41228
41370
  // src/node/durable/core/managers/SignalHandler.ts
@@ -42113,99 +42255,117 @@ var ExecutionManager = class {
42113
42255
  static {
42114
42256
  __name(this, "ExecutionManager");
42115
42257
  }
42116
- async startExecution(task2, input, options) {
42258
+ async start(taskRef, input, options) {
42259
+ const task2 = this.resolveTaskReference(taskRef, "start");
42117
42260
  this.taskRegistry.register(task2);
42261
+ this.assertCanExecute();
42262
+ if (options?.idempotencyKey) {
42263
+ return this.startWithIdempotencyKey(
42264
+ task2,
42265
+ input,
42266
+ options.idempotencyKey,
42267
+ options
42268
+ );
42269
+ }
42270
+ const executionId = await this.persistNewExecution(task2, input, options);
42271
+ await this.kickoffWithFailsafe(executionId);
42272
+ return executionId;
42273
+ }
42274
+ // ─── Idempotent start ──────────────────────────────────────────────────────
42275
+ /**
42276
+ * Start a workflow with deduplication: if the same (taskId, idempotencyKey)
42277
+ * was already started, returns the existing executionId instead of creating
42278
+ * a duplicate. Uses a distributed lock to prevent concurrent races.
42279
+ */
42280
+ async startWithIdempotencyKey(task2, input, idempotencyKey, options) {
42281
+ this.assertStoreSupportsIdempotency();
42282
+ return this.withIdempotencyLock(task2.id, idempotencyKey, async () => {
42283
+ const existingId = await this.config.store.getExecutionIdByIdempotencyKey({
42284
+ taskId: task2.id,
42285
+ idempotencyKey
42286
+ });
42287
+ if (existingId) return existingId;
42288
+ const executionId = createExecutionId();
42289
+ const claimed = await this.config.store.setExecutionIdByIdempotencyKey({
42290
+ taskId: task2.id,
42291
+ idempotencyKey,
42292
+ executionId
42293
+ });
42294
+ if (!claimed) {
42295
+ return this.resolveRacedIdempotencyKey(task2.id, idempotencyKey);
42296
+ }
42297
+ await this.persistNewExecution(task2, input, options, executionId);
42298
+ await this.kickoffExecution(executionId);
42299
+ return executionId;
42300
+ });
42301
+ }
42302
+ assertCanExecute() {
42118
42303
  if (!this.config.queue && !this.config.taskExecutor) {
42119
42304
  throw new Error(
42120
42305
  "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."
42121
42306
  );
42122
42307
  }
42123
- const idempotencyKey = options?.idempotencyKey;
42124
- if (idempotencyKey) {
42125
- if (!this.config.store.getExecutionIdByIdempotencyKey || !this.config.store.setExecutionIdByIdempotencyKey) {
42126
- throw new Error(
42127
- "Durable store does not support execution idempotency keys. Implement getExecutionIdByIdempotencyKey/setExecutionIdByIdempotencyKey on the store to use ExecuteOptions.idempotencyKey."
42128
- );
42129
- }
42130
- const lockTtlMs = 1e4;
42131
- const lockResource = `idempotency:${task2.id}:${idempotencyKey}`;
42132
- const canLock = !!this.config.store.acquireLock && !!this.config.store.releaseLock;
42133
- let lockId = null;
42134
- if (canLock) {
42135
- const maxAttempts = 50;
42136
- for (let attempt = 0; attempt < maxAttempts; attempt += 1) {
42137
- lockId = await this.config.store.acquireLock(
42138
- lockResource,
42139
- lockTtlMs
42140
- );
42141
- if (lockId !== null) break;
42142
- await sleepMs(5);
42143
- }
42144
- if (lockId === null) {
42145
- throw new Error(
42146
- `Failed to acquire idempotency lock for '${task2.id}:${idempotencyKey}'`
42147
- );
42148
- }
42149
- }
42308
+ }
42309
+ assertStoreSupportsIdempotency() {
42310
+ if (!this.config.store.getExecutionIdByIdempotencyKey || !this.config.store.setExecutionIdByIdempotencyKey) {
42311
+ throw new Error(
42312
+ "Durable store does not support execution idempotency keys. Implement getExecutionIdByIdempotencyKey/setExecutionIdByIdempotencyKey on the store to use ExecuteOptions.idempotencyKey."
42313
+ );
42314
+ }
42315
+ }
42316
+ /**
42317
+ * Acquires a distributed lock around the idempotency check-and-set,
42318
+ * falling back to lock-free operation when the store has no locking support.
42319
+ */
42320
+ async withIdempotencyLock(taskId, idempotencyKey, fn) {
42321
+ const canLock = !!this.config.store.acquireLock && !!this.config.store.releaseLock;
42322
+ if (!canLock) return fn();
42323
+ const lockResource = `idempotency:${taskId}:${idempotencyKey}`;
42324
+ const lockTtlMs = 1e4;
42325
+ const maxAttempts = 50;
42326
+ let lockId = null;
42327
+ for (let attempt = 0; attempt < maxAttempts; attempt += 1) {
42328
+ lockId = await this.config.store.acquireLock(lockResource, lockTtlMs);
42329
+ if (lockId !== null) break;
42330
+ await sleepMs(5);
42331
+ }
42332
+ if (lockId === null) {
42333
+ throw new Error(
42334
+ `Failed to acquire idempotency lock for '${taskId}:${idempotencyKey}'`
42335
+ );
42336
+ }
42337
+ try {
42338
+ return await fn();
42339
+ } finally {
42150
42340
  try {
42151
- const existing = await this.config.store.getExecutionIdByIdempotencyKey(
42152
- {
42153
- taskId: task2.id,
42154
- idempotencyKey
42155
- }
42156
- );
42157
- if (existing) return existing;
42158
- const executionId2 = createExecutionId();
42159
- const setOk = await this.config.store.setExecutionIdByIdempotencyKey({
42160
- taskId: task2.id,
42161
- idempotencyKey,
42162
- executionId: executionId2
42163
- });
42164
- if (!setOk) {
42165
- const raced = await this.config.store.getExecutionIdByIdempotencyKey({
42166
- taskId: task2.id,
42167
- idempotencyKey
42168
- });
42169
- if (raced) return raced;
42170
- throw new Error(
42171
- "Failed to set idempotency mapping but no existing mapping found."
42172
- );
42173
- }
42174
- const execution2 = {
42175
- id: executionId2,
42176
- taskId: task2.id,
42177
- input,
42178
- status: ExecutionStatus.Pending,
42179
- attempt: 1,
42180
- maxAttempts: this.config.execution?.maxAttempts ?? 3,
42181
- timeout: options?.timeout ?? this.config.execution?.timeout,
42182
- createdAt: /* @__PURE__ */ new Date(),
42183
- updatedAt: /* @__PURE__ */ new Date()
42184
- };
42185
- await this.config.store.saveExecution(execution2);
42186
- await this.auditLogger.log({
42187
- kind: DurableAuditEntryKind.ExecutionStatusChanged,
42188
- executionId: executionId2,
42189
- taskId: task2.id,
42190
- attempt: execution2.attempt,
42191
- from: null,
42192
- to: ExecutionStatus.Pending,
42193
- reason: "created"
42194
- });
42195
- await this.kickoffExecution(executionId2);
42196
- return executionId2;
42197
- } finally {
42198
- if (canLock && lockId !== null) {
42199
- try {
42200
- await this.config.store.releaseLock(lockResource, lockId);
42201
- } catch {
42202
- }
42203
- }
42341
+ await this.config.store.releaseLock(lockResource, lockId);
42342
+ } catch {
42204
42343
  }
42205
42344
  }
42206
- const executionId = createExecutionId();
42345
+ }
42346
+ /**
42347
+ * Recovery path: `setExecutionIdByIdempotencyKey` returned false (another
42348
+ * writer won the race), so we re-read the mapping to get their executionId.
42349
+ */
42350
+ async resolveRacedIdempotencyKey(taskId, idempotencyKey) {
42351
+ const racedId = await this.config.store.getExecutionIdByIdempotencyKey({
42352
+ taskId,
42353
+ idempotencyKey
42354
+ });
42355
+ if (racedId) return racedId;
42356
+ throw new Error(
42357
+ "Failed to set idempotency mapping but no existing mapping found."
42358
+ );
42359
+ }
42360
+ // ─── Execution persistence ─────────────────────────────────────────────────
42361
+ /**
42362
+ * Creates a new execution record, persists it to the store, and logs an
42363
+ * audit entry. Returns the executionId.
42364
+ */
42365
+ async persistNewExecution(task2, input, options, executionId) {
42366
+ const id2 = executionId ?? createExecutionId();
42207
42367
  const execution = {
42208
- id: executionId,
42368
+ id: id2,
42209
42369
  taskId: task2.id,
42210
42370
  input,
42211
42371
  status: ExecutionStatus.Pending,
@@ -42218,37 +42378,40 @@ var ExecutionManager = class {
42218
42378
  await this.config.store.saveExecution(execution);
42219
42379
  await this.auditLogger.log({
42220
42380
  kind: DurableAuditEntryKind.ExecutionStatusChanged,
42221
- executionId,
42381
+ executionId: id2,
42222
42382
  taskId: task2.id,
42223
42383
  attempt: execution.attempt,
42224
42384
  from: null,
42225
42385
  to: ExecutionStatus.Pending,
42226
42386
  reason: "created"
42227
42387
  });
42228
- const kickoffTimerId = `kickoff:${executionId}`;
42229
- const kickoffFailsafeDelayMs = this.config.execution?.kickoffFailsafeDelayMs ?? 1e4;
42230
- const shouldArmKickoffFailsafe = Boolean(this.config.queue) && kickoffFailsafeDelayMs > 0;
42231
- if (shouldArmKickoffFailsafe) {
42388
+ return id2;
42389
+ }
42390
+ /**
42391
+ * Kicks off an execution with a failsafe timer for queue mode.
42392
+ * If the queue enqueue succeeds, the timer is cleaned up immediately.
42393
+ * If enqueue fails, the timer remains so the polling loop can retry later.
42394
+ */
42395
+ async kickoffWithFailsafe(executionId) {
42396
+ const failsafeDelayMs = this.config.execution?.kickoffFailsafeDelayMs ?? 1e4;
42397
+ const shouldArmFailsafe = Boolean(this.config.queue) && failsafeDelayMs > 0;
42398
+ if (shouldArmFailsafe) {
42399
+ const timerId = `kickoff:${executionId}`;
42232
42400
  await this.config.store.createTimer({
42233
- id: kickoffTimerId,
42401
+ id: timerId,
42234
42402
  executionId,
42235
42403
  type: TimerType.Retry,
42236
- fireAt: new Date(Date.now() + kickoffFailsafeDelayMs),
42404
+ fireAt: new Date(Date.now() + failsafeDelayMs),
42237
42405
  status: TimerStatus.Pending
42238
42406
  });
42239
- }
42240
- try {
42241
42407
  await this.kickoffExecution(executionId);
42242
- if (shouldArmKickoffFailsafe) {
42243
- try {
42244
- await this.config.store.deleteTimer(kickoffTimerId);
42245
- } catch {
42246
- }
42408
+ try {
42409
+ await this.config.store.deleteTimer(timerId);
42410
+ } catch {
42247
42411
  }
42248
- } catch (error2) {
42249
- throw error2;
42412
+ return;
42250
42413
  }
42251
- return executionId;
42414
+ await this.kickoffExecution(executionId);
42252
42415
  }
42253
42416
  async cancelExecution(executionId, reason) {
42254
42417
  const execution = await this.config.store.getExecution(executionId);
@@ -42279,16 +42442,16 @@ var ExecutionManager = class {
42279
42442
  timestamp: /* @__PURE__ */ new Date()
42280
42443
  });
42281
42444
  }
42282
- async execute(task2, input, options) {
42283
- const executionId = await this.startExecution(task2, input, options);
42284
- return await this.waitManager.waitForResult(executionId, {
42445
+ async startAndWait(taskRef, input, options) {
42446
+ const executionId = await this.start(taskRef, input, options);
42447
+ const data = await this.waitManager.waitForResult(executionId, {
42285
42448
  timeout: options?.timeout,
42286
42449
  waitPollIntervalMs: options?.waitPollIntervalMs
42287
42450
  });
42288
- }
42289
- async executeStrict(task2, input, options) {
42290
- const actualTask = task2;
42291
- return await this.execute(actualTask, input, options);
42451
+ return {
42452
+ durable: { executionId },
42453
+ data
42454
+ };
42292
42455
  }
42293
42456
  async processExecution(executionId) {
42294
42457
  const execution = await this.config.store.getExecution(executionId);
@@ -42497,6 +42660,18 @@ var ExecutionManager = class {
42497
42660
  });
42498
42661
  }
42499
42662
  }
42663
+ resolveTaskReference(taskRef, apiMethod) {
42664
+ if (typeof taskRef !== "string") {
42665
+ return taskRef;
42666
+ }
42667
+ const resolved = this.taskRegistry.find(taskRef);
42668
+ if (!resolved) {
42669
+ throw new Error(
42670
+ `DurableService.${apiMethod}() could not resolve task id "${taskRef}". Ensure the task is registered in the runtime store.`
42671
+ );
42672
+ }
42673
+ return resolved;
42674
+ }
42500
42675
  };
42501
42676
  var PollingManager = class {
42502
42677
  constructor(workerId, config, store2, queue, maxAttempts, defaultTimeout, taskRegistry, auditLogger, scheduleManager, callbacks) {
@@ -42691,16 +42866,26 @@ var DurableService = class {
42691
42866
  this.config = config;
42692
42867
  this.workerId = config.workerId ?? createExecutionId();
42693
42868
  this.taskRegistry = new TaskRegistry(config.taskResolver);
42694
- if (config.schedules) {
42695
- for (const schedule of config.schedules) {
42696
- this.taskRegistry.register(schedule.task);
42697
- }
42698
- }
42699
42869
  if (config.tasks) {
42700
42870
  for (const task2 of config.tasks) {
42701
42871
  this.taskRegistry.register(task2);
42702
42872
  }
42703
42873
  }
42874
+ if (config.schedules) {
42875
+ for (const schedule of config.schedules) {
42876
+ if (typeof schedule.task === "string") {
42877
+ const resolved = this.taskRegistry.find(schedule.task);
42878
+ if (!resolved) {
42879
+ throw new Error(
42880
+ `Cannot initialize durable schedule "${schedule.id}": task "${schedule.task}" is not registered.`
42881
+ );
42882
+ }
42883
+ this.taskRegistry.register(resolved);
42884
+ continue;
42885
+ }
42886
+ this.taskRegistry.register(schedule.task);
42887
+ }
42888
+ }
42704
42889
  this.auditLogger = new AuditLogger(
42705
42890
  { enabled: config.audit?.enabled, emitter: config.audit?.emitter },
42706
42891
  config.store
@@ -42760,17 +42945,18 @@ var DurableService = class {
42760
42945
  findTask(taskId) {
42761
42946
  return this.taskRegistry.find(taskId);
42762
42947
  }
42763
- async startExecution(task2, input, options) {
42764
- return this.executionManager.startExecution(task2, input, options);
42948
+ start(task2, input, options) {
42949
+ if (task2 === void 0) {
42950
+ this.pollingManager.start();
42951
+ return;
42952
+ }
42953
+ return this.executionManager.start(task2, input, options);
42765
42954
  }
42766
42955
  async cancelExecution(executionId, reason) {
42767
42956
  await this.executionManager.cancelExecution(executionId, reason);
42768
42957
  }
42769
- async execute(task2, input, options) {
42770
- return this.executionManager.execute(task2, input, options);
42771
- }
42772
- async executeStrict(task2, input, options) {
42773
- return this.executionManager.executeStrict(task2, input, options);
42958
+ async startAndWait(task2, input, options) {
42959
+ return this.executionManager.startAndWait(task2, input, options);
42774
42960
  }
42775
42961
  wait(executionId, options) {
42776
42962
  return this.waitManager.waitForResult(executionId, options);
@@ -42789,9 +42975,6 @@ var DurableService = class {
42789
42975
  }
42790
42976
  }
42791
42977
  }
42792
- start() {
42793
- this.pollingManager.start();
42794
- }
42795
42978
  async stop() {
42796
42979
  await this.pollingManager.stop();
42797
42980
  }
@@ -42927,10 +43110,7 @@ async function createRunnerDurableRuntime(config, deps) {
42927
43110
  },
42928
43111
  taskExecutor: {
42929
43112
  run: /* @__PURE__ */ __name(async (task2, input) => {
42930
- const outputPromise = await deps.taskRunner.run(
42931
- task2,
42932
- input
42933
- );
43113
+ const outputPromise = await deps.taskRunner.run(task2, input);
42934
43114
  if (outputPromise === void 0) {
42935
43115
  throw new Error(
42936
43116
  `Durable task '${task2.id}' completed without a result promise.`
@@ -42958,7 +43138,7 @@ async function createRunnerDurableRuntime(config, deps) {
42958
43138
  __name(createRunnerDurableRuntime, "createRunnerDurableRuntime");
42959
43139
 
42960
43140
  // src/node/durable/core/resource.ts
42961
- var durableResource = r.resource("base.durable").register(durableEventsArray).dependencies({
43141
+ var durableResource = r.resource("base.durable").register([durableWorkflowTag, ...durableEventsArray]).dependencies({
42962
43142
  taskRunner: globals.resources.taskRunner,
42963
43143
  eventManager: globals.resources.eventManager,
42964
43144
  runnerStore: globals.resources.store
@@ -44100,7 +44280,7 @@ async function waitUntil(predicate, options) {
44100
44280
  __name(waitUntil, "waitUntil");
44101
44281
 
44102
44282
  // src/node/durable/resources/memoryDurableResource.ts
44103
- var memoryDurableResource = r.resource("base.durable.memory").register(durableEventsArray).dependencies({
44283
+ var memoryDurableResource = r.resource("base.durable.memory").register([durableWorkflowTag, ...durableEventsArray]).dependencies({
44104
44284
  taskRunner: globals.resources.taskRunner,
44105
44285
  eventManager: globals.resources.eventManager,
44106
44286
  runnerStore: globals.resources.store
@@ -44159,7 +44339,7 @@ function deriveDurableIsolation(params) {
44159
44339
  __name(deriveDurableIsolation, "deriveDurableIsolation");
44160
44340
 
44161
44341
  // src/node/durable/resources/redisDurableResource.ts
44162
- var redisDurableResource = r.resource("base.durable.redis").register(durableEventsArray).dependencies({
44342
+ var redisDurableResource = r.resource("base.durable.redis").register([durableWorkflowTag, ...durableEventsArray]).dependencies({
44163
44343
  taskRunner: globals.resources.taskRunner,
44164
44344
  eventManager: globals.resources.eventManager,
44165
44345
  runnerStore: globals.resources.store
@@ -44554,6 +44734,7 @@ exports.MemoryEventBus = MemoryEventBus;
44554
44734
  exports.MemoryQueue = MemoryQueue;
44555
44735
  exports.MemoryStore = MemoryStore;
44556
44736
  exports.MiddlewareManager = MiddlewareManager;
44737
+ exports.NodeInputFile = NodeInputFile;
44557
44738
  exports.NoopEventBus = NoopEventBus;
44558
44739
  exports.PlatformAdapter = PlatformAdapter;
44559
44740
  exports.Queue = Queue;
@@ -44562,6 +44743,7 @@ exports.RedisEventBus = RedisEventBus;
44562
44743
  exports.RedisStore = RedisStore;
44563
44744
  exports.ResourceInitializer = ResourceInitializer;
44564
44745
  exports.RunResult = RunResult;
44746
+ exports.RunnerError = RunnerError;
44565
44747
  exports.ScheduleStatus = ScheduleStatus;
44566
44748
  exports.ScheduleType = ScheduleType;
44567
44749
  exports.Semaphore = Semaphore;
@@ -44598,6 +44780,7 @@ exports.disposeDurableService = disposeDurableService;
44598
44780
  exports.durableEvents = durableEvents;
44599
44781
  exports.durableEventsArray = durableEventsArray;
44600
44782
  exports.durableResource = durableResource;
44783
+ exports.durableWorkflowTag = durableWorkflowTag;
44601
44784
  exports.event = defineEvent;
44602
44785
  exports.getConfig = getConfig;
44603
44786
  exports.globals = globals2;