@bluelibs/runner 4.8.6 → 4.9.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 (173) hide show
  1. package/AI.md +8 -0
  2. package/README.md +39 -4
  3. package/dist/browser/index.cjs +543 -418
  4. package/dist/browser/index.cjs.map +1 -1
  5. package/dist/browser/index.d.mts +2221 -0
  6. package/dist/browser/index.d.ts +2221 -0
  7. package/dist/browser/index.mjs +543 -418
  8. package/dist/browser/index.mjs.map +1 -1
  9. package/dist/edge/index.cjs +543 -418
  10. package/dist/edge/index.cjs.map +1 -1
  11. package/dist/edge/index.d.mts +2221 -0
  12. package/dist/edge/index.d.ts +2221 -0
  13. package/dist/edge/index.mjs +543 -418
  14. package/dist/edge/index.mjs.map +1 -1
  15. package/dist/node/node.cjs +584 -436
  16. package/dist/node/node.cjs.map +1 -1
  17. package/dist/node/node.d.mts +2472 -0
  18. package/dist/node/node.d.ts +2425 -55
  19. package/dist/node/node.mjs +584 -436
  20. package/dist/node/node.mjs.map +1 -1
  21. package/dist/universal/index.cjs +541 -418
  22. package/dist/universal/index.cjs.map +1 -1
  23. package/dist/universal/index.d.mts +2221 -0
  24. package/dist/universal/index.d.ts +2221 -0
  25. package/dist/universal/index.mjs +541 -418
  26. package/dist/universal/index.mjs.map +1 -1
  27. package/package.json +5 -5
  28. package/dist/define.d.ts +0 -9
  29. package/dist/definers/builders/asyncContext.d.ts +0 -13
  30. package/dist/definers/builders/core.d.ts +0 -30
  31. package/dist/definers/builders/error.d.ts +0 -15
  32. package/dist/definers/builders/event.d.ts +0 -12
  33. package/dist/definers/builders/hook.d.ts +0 -20
  34. package/dist/definers/builders/middleware.d.ts +0 -39
  35. package/dist/definers/builders/resource.d.ts +0 -40
  36. package/dist/definers/builders/tag.d.ts +0 -10
  37. package/dist/definers/builders/task.d.ts +0 -37
  38. package/dist/definers/builders/task.phantom.d.ts +0 -27
  39. package/dist/definers/builders/utils.d.ts +0 -4
  40. package/dist/definers/defineAsyncContext.d.ts +0 -15
  41. package/dist/definers/defineError.d.ts +0 -26
  42. package/dist/definers/defineEvent.d.ts +0 -2
  43. package/dist/definers/defineHook.d.ts +0 -6
  44. package/dist/definers/defineOverride.d.ts +0 -17
  45. package/dist/definers/defineResource.d.ts +0 -2
  46. package/dist/definers/defineResourceMiddleware.d.ts +0 -2
  47. package/dist/definers/defineTag.d.ts +0 -12
  48. package/dist/definers/defineTask.d.ts +0 -18
  49. package/dist/definers/defineTaskMiddleware.d.ts +0 -2
  50. package/dist/definers/tools.d.ts +0 -53
  51. package/dist/defs.d.ts +0 -31
  52. package/dist/errors.d.ts +0 -62
  53. package/dist/globals/debug.d.ts +0 -10
  54. package/dist/globals/globalEvents.d.ts +0 -8
  55. package/dist/globals/globalMiddleware.d.ts +0 -31
  56. package/dist/globals/globalResources.d.ts +0 -41
  57. package/dist/globals/globalTags.d.ts +0 -11
  58. package/dist/globals/middleware/cache.middleware.d.ts +0 -27
  59. package/dist/globals/middleware/requireContext.middleware.d.ts +0 -6
  60. package/dist/globals/middleware/retry.middleware.d.ts +0 -21
  61. package/dist/globals/middleware/timeout.middleware.d.ts +0 -9
  62. package/dist/globals/middleware/tunnel.middleware.d.ts +0 -2
  63. package/dist/globals/resources/debug/debug.resource.d.ts +0 -7
  64. package/dist/globals/resources/debug/debug.tag.d.ts +0 -2
  65. package/dist/globals/resources/debug/debugConfig.resource.d.ts +0 -22
  66. package/dist/globals/resources/debug/executionTracker.middleware.d.ts +0 -50
  67. package/dist/globals/resources/debug/globalEvent.hook.d.ts +0 -27
  68. package/dist/globals/resources/debug/hook.hook.d.ts +0 -30
  69. package/dist/globals/resources/debug/index.d.ts +0 -6
  70. package/dist/globals/resources/debug/middleware.hook.d.ts +0 -30
  71. package/dist/globals/resources/debug/types.d.ts +0 -25
  72. package/dist/globals/resources/debug/utils.d.ts +0 -2
  73. package/dist/globals/resources/httpClientFactory.resource.d.ts +0 -28
  74. package/dist/globals/resources/queue.resource.d.ts +0 -10
  75. package/dist/globals/resources/tunnel/ejson-extensions.d.ts +0 -1
  76. package/dist/globals/resources/tunnel/error-utils.d.ts +0 -1
  77. package/dist/globals/resources/tunnel/plan.d.ts +0 -19
  78. package/dist/globals/resources/tunnel/protocol.d.ts +0 -47
  79. package/dist/globals/resources/tunnel/serializer.d.ts +0 -9
  80. package/dist/globals/resources/tunnel/tunnel.policy.tag.d.ts +0 -18
  81. package/dist/globals/resources/tunnel/tunnel.tag.d.ts +0 -2
  82. package/dist/globals/resources/tunnel/types.d.ts +0 -42
  83. package/dist/globals/tunnels/index.d.ts +0 -23
  84. package/dist/globals/types.d.ts +0 -4
  85. package/dist/http-client.d.ts +0 -25
  86. package/dist/http-fetch-tunnel.resource.d.ts +0 -11
  87. package/dist/index.d.ts +0 -117
  88. package/dist/models/DependencyProcessor.d.ts +0 -48
  89. package/dist/models/EventManager.d.ts +0 -153
  90. package/dist/models/LogPrinter.d.ts +0 -55
  91. package/dist/models/Logger.d.ts +0 -85
  92. package/dist/models/MiddlewareManager.d.ts +0 -75
  93. package/dist/models/OverrideManager.d.ts +0 -13
  94. package/dist/models/Queue.d.ts +0 -26
  95. package/dist/models/ResourceInitializer.d.ts +0 -20
  96. package/dist/models/RunResult.d.ts +0 -35
  97. package/dist/models/Semaphore.d.ts +0 -61
  98. package/dist/models/Store.d.ts +0 -73
  99. package/dist/models/StoreRegistry.d.ts +0 -49
  100. package/dist/models/StoreValidator.d.ts +0 -8
  101. package/dist/models/TaskRunner.d.ts +0 -27
  102. package/dist/models/UnhandledError.d.ts +0 -11
  103. package/dist/models/index.d.ts +0 -11
  104. package/dist/models/middleware/InterceptorRegistry.d.ts +0 -56
  105. package/dist/models/middleware/MiddlewareResolver.d.ts +0 -31
  106. package/dist/models/middleware/ResourceMiddlewareComposer.d.ts +0 -34
  107. package/dist/models/middleware/TaskMiddlewareComposer.d.ts +0 -43
  108. package/dist/models/middleware/ValidationHelper.d.ts +0 -20
  109. package/dist/models/middleware/index.d.ts +0 -6
  110. package/dist/models/middleware/types.d.ts +0 -10
  111. package/dist/models/utils/findCircularDependencies.d.ts +0 -16
  112. package/dist/models/utils/safeStringify.d.ts +0 -3
  113. package/dist/node/exposure/allowList.d.ts +0 -3
  114. package/dist/node/exposure/authenticator.d.ts +0 -6
  115. package/dist/node/exposure/cors.d.ts +0 -4
  116. package/dist/node/exposure/createNodeExposure.d.ts +0 -2
  117. package/dist/node/exposure/exposureServer.d.ts +0 -18
  118. package/dist/node/exposure/httpResponse.d.ts +0 -10
  119. package/dist/node/exposure/logging.d.ts +0 -4
  120. package/dist/node/exposure/multipart.d.ts +0 -27
  121. package/dist/node/exposure/requestBody.d.ts +0 -11
  122. package/dist/node/exposure/requestContext.d.ts +0 -17
  123. package/dist/node/exposure/requestHandlers.d.ts +0 -24
  124. package/dist/node/exposure/resourceTypes.d.ts +0 -60
  125. package/dist/node/exposure/router.d.ts +0 -17
  126. package/dist/node/exposure/serverLifecycle.d.ts +0 -13
  127. package/dist/node/exposure/types.d.ts +0 -31
  128. package/dist/node/exposure/utils.d.ts +0 -17
  129. package/dist/node/exposure.resource.d.ts +0 -12
  130. package/dist/node/files.d.ts +0 -9
  131. package/dist/node/http-mixed-client.d.ts +0 -30
  132. package/dist/node/http-smart-client.model.d.ts +0 -24
  133. package/dist/node/index.d.ts +0 -1
  134. package/dist/node/inputFile.model.d.ts +0 -22
  135. package/dist/node/inputFile.utils.d.ts +0 -14
  136. package/dist/node/platform/createFile.d.ts +0 -9
  137. package/dist/node/resources/http-mixed-client.factory.resource.d.ts +0 -17
  138. package/dist/node/resources/http-smart-client.factory.resource.d.ts +0 -16
  139. package/dist/node/tunnel.allowlist.d.ts +0 -7
  140. package/dist/node/upload/manifest.d.ts +0 -22
  141. package/dist/platform/adapters/browser.d.ts +0 -14
  142. package/dist/platform/adapters/edge.d.ts +0 -5
  143. package/dist/platform/adapters/node-als.d.ts +0 -1
  144. package/dist/platform/adapters/node.d.ts +0 -15
  145. package/dist/platform/adapters/universal-generic.d.ts +0 -14
  146. package/dist/platform/adapters/universal.d.ts +0 -17
  147. package/dist/platform/createFile.d.ts +0 -10
  148. package/dist/platform/createWebFile.d.ts +0 -11
  149. package/dist/platform/factory.d.ts +0 -2
  150. package/dist/platform/index.d.ts +0 -27
  151. package/dist/platform/types.d.ts +0 -29
  152. package/dist/processHooks.d.ts +0 -2
  153. package/dist/run.d.ts +0 -14
  154. package/dist/testing.d.ts +0 -25
  155. package/dist/tools/getCallerFile.d.ts +0 -1
  156. package/dist/tunnels/buildUniversalManifest.d.ts +0 -24
  157. package/dist/types/asyncContext.d.ts +0 -41
  158. package/dist/types/contracts.d.ts +0 -63
  159. package/dist/types/error.d.ts +0 -36
  160. package/dist/types/event.d.ts +0 -74
  161. package/dist/types/hook.d.ts +0 -23
  162. package/dist/types/inputFile.d.ts +0 -34
  163. package/dist/types/meta.d.ts +0 -22
  164. package/dist/types/resource.d.ts +0 -87
  165. package/dist/types/resourceMiddleware.d.ts +0 -47
  166. package/dist/types/runner.d.ts +0 -68
  167. package/dist/types/storeTypes.d.ts +0 -40
  168. package/dist/types/symbols.d.ts +0 -32
  169. package/dist/types/tag.d.ts +0 -46
  170. package/dist/types/task.d.ts +0 -54
  171. package/dist/types/taskMiddleware.d.ts +0 -48
  172. package/dist/types/utilities.d.ts +0 -113
  173. package/dist/utils/detectRunnerMode.d.ts +0 -9
@@ -171,78 +171,87 @@ var cacheMiddleware = defineTaskMiddleware({
171
171
  }
172
172
  });
173
173
 
174
- // src/tools/getCallerFile.ts
175
- function getCallerFile() {
176
- const originalFunc = Error.prepareStackTrace;
177
- try {
178
- const err = new Error();
179
- let callerfile;
180
- let currentfile;
181
- Error.prepareStackTrace = (err2, stack2) => stack2;
182
- const stack = err.stack;
183
- stack.shift();
184
- currentfile = stack.shift()?.getFileName?.();
185
- callerfile = stack.shift()?.getFileName?.();
186
- return callerfile;
187
- } finally {
188
- Error.prepareStackTrace = originalFunc;
189
- }
174
+ // src/platform/adapters/node-als.ts
175
+ async function loadAsyncLocalStorageClass() {
176
+ const mod = __require("async_hooks");
177
+ return mod.AsyncLocalStorage;
190
178
  }
191
- __name(getCallerFile, "getCallerFile");
179
+ __name(loadAsyncLocalStorageClass, "loadAsyncLocalStorageClass");
192
180
 
193
- // src/definers/defineTask.ts
194
- function defineTask(taskConfig) {
195
- const filePath = getCallerFile();
196
- const id2 = taskConfig.id;
197
- return {
198
- [symbolTask]: true,
199
- [symbolFilePath]: filePath,
200
- id: id2,
201
- dependencies: taskConfig.dependencies || {},
202
- middleware: taskConfig.middleware || [],
203
- run: taskConfig.run,
204
- inputSchema: taskConfig.inputSchema,
205
- resultSchema: taskConfig.resultSchema,
206
- meta: taskConfig.meta || {},
207
- tags: taskConfig.tags || [],
208
- // autorun,
209
- optional() {
210
- return {
211
- inner: this,
212
- [symbolOptionalDependency]: true
213
- };
214
- }
215
- };
216
- }
217
- __name(defineTask, "defineTask");
218
- defineTask.phantom = (taskConfig) => {
219
- const taskDef = defineTask({
220
- ...taskConfig,
221
- run: /* @__PURE__ */ __name(async (input) => {
222
- return void 0;
223
- }, "run")
224
- });
225
- taskDef[symbolPhantomTask] = true;
226
- return taskDef;
181
+ // src/platform/adapters/node.ts
182
+ var NodePlatformAdapter = class {
183
+ constructor() {
184
+ this.id = "node";
185
+ this.setTimeout = globalThis.setTimeout;
186
+ this.clearTimeout = globalThis.clearTimeout;
187
+ }
188
+ static {
189
+ __name(this, "NodePlatformAdapter");
190
+ }
191
+ async init() {
192
+ this.alsClass = await loadAsyncLocalStorageClass();
193
+ }
194
+ onUncaughtException(handler) {
195
+ process.on("uncaughtException", handler);
196
+ return () => process.off("uncaughtException", handler);
197
+ }
198
+ onUnhandledRejection(handler) {
199
+ const h = /* @__PURE__ */ __name((reason) => handler(reason), "h");
200
+ process.on("unhandledRejection", h);
201
+ return () => process.off("unhandledRejection", h);
202
+ }
203
+ onShutdownSignal(handler) {
204
+ process.on("SIGINT", handler);
205
+ process.on("SIGTERM", handler);
206
+ return () => {
207
+ process.off("SIGINT", handler);
208
+ process.off("SIGTERM", handler);
209
+ };
210
+ }
211
+ exit(code) {
212
+ process.exit(code);
213
+ }
214
+ getEnv(key) {
215
+ return process.env[key];
216
+ }
217
+ hasAsyncLocalStorage() {
218
+ return true;
219
+ }
220
+ createAsyncLocalStorage() {
221
+ let instance;
222
+ const ensure = /* @__PURE__ */ __name(() => {
223
+ if (!this.alsClass) {
224
+ let als;
225
+ const forceNoop = typeof process !== "undefined" && !!process.env?.RUNNER_FORCE_NOOP_ALS;
226
+ if (!forceNoop) {
227
+ try {
228
+ const mod = __require("async_hooks");
229
+ als = mod?.AsyncLocalStorage;
230
+ } catch (_) {
231
+ als = void 0;
232
+ }
233
+ }
234
+ this.alsClass = als ? als : class NoopAsyncLocalStorage {
235
+ static {
236
+ __name(this, "NoopAsyncLocalStorage");
237
+ }
238
+ getStore() {
239
+ return void 0;
240
+ }
241
+ run(_store, callback) {
242
+ return callback();
243
+ }
244
+ };
245
+ }
246
+ return instance ??= new this.alsClass();
247
+ }, "ensure");
248
+ return {
249
+ getStore: /* @__PURE__ */ __name(() => ensure().getStore(), "getStore"),
250
+ run: /* @__PURE__ */ __name((store2, callback) => ensure().run(store2, callback), "run")
251
+ };
252
+ }
227
253
  };
228
254
 
229
- // src/definers/defineHook.ts
230
- function defineHook(hookDef) {
231
- const filePath = getCallerFile();
232
- return {
233
- [symbolHook]: true,
234
- [symbolFilePath]: filePath,
235
- id: hookDef.id,
236
- dependencies: hookDef.dependencies || {},
237
- on: hookDef.on,
238
- order: hookDef.order,
239
- run: hookDef.run,
240
- meta: hookDef.meta || {},
241
- tags: hookDef.tags || []
242
- };
243
- }
244
- __name(defineHook, "defineHook");
245
-
246
255
  // src/errors.ts
247
256
  var errors_exports = {};
248
257
  __export(errors_exports, {
@@ -369,86 +378,93 @@ function errorBuilder(id2) {
369
378
  __name(errorBuilder, "errorBuilder");
370
379
  var error = errorBuilder;
371
380
 
372
- // src/platform/adapters/node-als.ts
373
- async function loadAsyncLocalStorageClass() {
374
- const mod = __require("async_hooks");
375
- return mod.AsyncLocalStorage;
376
- }
377
- __name(loadAsyncLocalStorageClass, "loadAsyncLocalStorageClass");
378
-
379
- // src/platform/adapters/node.ts
380
- var NodePlatformAdapter = class {
381
- constructor() {
382
- this.id = "node";
383
- this.setTimeout = globalThis.setTimeout;
384
- this.clearTimeout = globalThis.clearTimeout;
385
- }
386
- static {
387
- __name(this, "NodePlatformAdapter");
388
- }
389
- async init() {
390
- this.alsClass = await loadAsyncLocalStorageClass();
391
- }
392
- onUncaughtException(handler) {
393
- process.on("uncaughtException", handler);
394
- return () => process.off("uncaughtException", handler);
395
- }
396
- onUnhandledRejection(handler) {
397
- const h = /* @__PURE__ */ __name((reason) => handler(reason), "h");
398
- process.on("unhandledRejection", h);
399
- return () => process.off("unhandledRejection", h);
400
- }
401
- onShutdownSignal(handler) {
402
- process.on("SIGINT", handler);
403
- process.on("SIGTERM", handler);
404
- return () => {
405
- process.off("SIGINT", handler);
406
- process.off("SIGTERM", handler);
407
- };
408
- }
409
- exit(code) {
410
- process.exit(code);
411
- }
412
- getEnv(key) {
413
- return process.env[key];
414
- }
415
- hasAsyncLocalStorage() {
416
- return true;
417
- }
418
- createAsyncLocalStorage() {
419
- let instance;
420
- const ensure = /* @__PURE__ */ __name(() => {
421
- if (!this.alsClass) {
422
- let als;
423
- const forceNoop = typeof process !== "undefined" && !!process.env?.RUNNER_FORCE_NOOP_ALS;
424
- if (!forceNoop) {
425
- try {
426
- const mod = __require("async_hooks");
427
- als = mod?.AsyncLocalStorage;
428
- } catch (_) {
429
- als = void 0;
430
- }
431
- }
432
- this.alsClass = als ? als : class NoopAsyncLocalStorage {
433
- static {
434
- __name(this, "NoopAsyncLocalStorage");
435
- }
436
- getStore() {
437
- return void 0;
438
- }
439
- run(_store, callback) {
440
- return callback();
441
- }
442
- };
443
- }
444
- return instance ??= new this.alsClass();
445
- }, "ensure");
446
- return {
447
- getStore: /* @__PURE__ */ __name(() => ensure().getStore(), "getStore"),
448
- run: /* @__PURE__ */ __name((store2, callback) => ensure().run(store2, callback), "run")
449
- };
381
+ // src/errors.ts
382
+ var duplicateRegistrationError = error("runner.errors.duplicateRegistration").format(
383
+ ({ 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.`
384
+ ).build();
385
+ var dependencyNotFoundError = error("runner.errors.dependencyNotFound").format(
386
+ ({ key }) => `Dependency ${key.toString()} not found. Did you forget to register it through a resource?`
387
+ ).build();
388
+ var unknownItemTypeError = error(
389
+ "runner.errors.unknownItemType"
390
+ ).format(
391
+ ({ item }) => `Unknown item type: ${String(
392
+ item
393
+ )}. Please ensure you are not using different versions of '@bluelibs/runner'`
394
+ ).build();
395
+ var contextError = error(
396
+ "runner.errors.context"
397
+ ).format(({ details }) => details ?? "Context error").build();
398
+ var circularDependenciesError = error("runner.errors.circularDependencies").format(({ cycles }) => {
399
+ const cycleDetails = cycles.map((cycle) => ` \u2022 ${cycle}`).join("\n");
400
+ const hasMiddleware = cycles.some((cycle) => cycle.includes("middleware"));
401
+ let guidance = "\n\nTo resolve circular dependencies:";
402
+ guidance += "\n \u2022 Consider refactoring to reduce coupling between components";
403
+ guidance += "\n \u2022 Extract shared dependencies into separate resources";
404
+ if (hasMiddleware) {
405
+ guidance += "\n \u2022 For middleware: you can filter out tasks/resources using everywhere(fn)";
406
+ guidance += "\n \u2022 Consider using events for communication instead of direct dependencies";
450
407
  }
451
- };
408
+ return `Circular dependencies detected:
409
+ ${cycleDetails}${guidance}`;
410
+ }).build();
411
+ var eventNotFoundError = error(
412
+ "runner.errors.eventNotFound"
413
+ ).format(
414
+ ({ id: id2 }) => `Event "${id2.toString()}" not found. Did you forget to register it?`
415
+ ).build();
416
+ var resourceNotFoundError = error(
417
+ "runner.errors.resourceNotFound"
418
+ ).format(
419
+ ({ id: id2 }) => `Resource "${id2.toString()}" not found. Did you forget to register it or are you using the correct id?`
420
+ ).build();
421
+ var middlewareNotRegisteredError = error("runner.errors.middlewareNotRegistered").format(
422
+ ({ type, source, middlewareId }) => `Middleware inside ${type} "${source}" depends on "${middlewareId}" but it's not registered. Did you forget to register it?`
423
+ ).build();
424
+ var tagNotFoundError = error(
425
+ "runner.errors.tagNotFound"
426
+ ).format(
427
+ ({ id: id2 }) => `Tag "${id2}" not registered. Did you forget to register it inside a resource?`
428
+ ).build();
429
+ var lockedError = error(
430
+ "runner.errors.locked"
431
+ ).format(
432
+ ({ what }) => `Cannot modify the ${what.toString()} when it is locked.`
433
+ ).build();
434
+ var storeAlreadyInitializedError = error(
435
+ "runner.errors.storeAlreadyInitialized"
436
+ ).format(() => "Store already initialized. Cannot reinitialize.").build();
437
+ var validationError = error("runner.errors.validation").format(({ subject, id: id2, originalError }) => {
438
+ const errorMessage = originalError instanceof Error ? originalError.message : String(originalError);
439
+ return `${subject} validation failed for ${id2.toString()}: ${errorMessage}`;
440
+ }).build();
441
+ var eventCycleError = error("runner.errors.eventCycle").format(({ path }) => {
442
+ const chain = path.map((p) => `${p.id}\u2190${p.source}`).join(" -> ");
443
+ return `Event emission cycle detected:
444
+ ${chain}
445
+
446
+ Break the cycle by changing hook logic (avoid mutual emits) or gate with conditions/tags.`;
447
+ }).build();
448
+ var eventEmissionCycleError = error("runner.errors.eventEmissionCycle").format(({ cycles }) => {
449
+ const list = cycles.map((c) => ` \u2022 ${c}`).join("\n");
450
+ return `Event emission cycles detected between hooks and events:
451
+ ${list}
452
+
453
+ This was detected at compile time (dry-run). Break the cycle by avoiding mutual emits between hooks or scoping hooks using tags.`;
454
+ }).build();
455
+ var platformUnsupportedFunctionError = error("runner.errors.platformUnsupportedFunction").format(
456
+ ({ functionName }) => `Platform function not supported in this environment: ${functionName}. Detected platform: ${detectEnvironment()}.`
457
+ ).build();
458
+ var cancellationError = error(
459
+ "runner.errors.cancellation"
460
+ ).format(({ reason }) => reason || "Operation cancelled").build();
461
+ var tunnelOwnershipConflictError = error("runner.errors.tunnelOwnershipConflict").format(
462
+ ({ 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.`
463
+ ).build();
464
+ function isCancellationError(err) {
465
+ return cancellationError.is(err);
466
+ }
467
+ __name(isCancellationError, "isCancellationError");
452
468
 
453
469
  // src/platform/adapters/browser.ts
454
470
  var BrowserPlatformAdapter = class {
@@ -757,6 +773,12 @@ function setPlatform(adapter) {
757
773
  adapter.id;
758
774
  }
759
775
  __name(setPlatform, "setPlatform");
776
+ function isNode() {
777
+ {
778
+ return false;
779
+ }
780
+ }
781
+ __name(isNode, "isNode");
760
782
  var PlatformAdapter = class {
761
783
  constructor(env) {
762
784
  this.setTimeout = globalThis.setTimeout;
@@ -810,93 +832,79 @@ var PlatformAdapter = class {
810
832
  }
811
833
  };
812
834
 
813
- // src/errors.ts
814
- var duplicateRegistrationError = error("runner.errors.duplicateRegistration").format(
815
- ({ 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.`
816
- ).build();
817
- var dependencyNotFoundError = error("runner.errors.dependencyNotFound").format(
818
- ({ key }) => `Dependency ${key.toString()} not found. Did you forget to register it through a resource?`
819
- ).build();
820
- var unknownItemTypeError = error(
821
- "runner.errors.unknownItemType"
822
- ).format(
823
- ({ item }) => `Unknown item type: ${String(
824
- item
825
- )}. Please ensure you are not using different versions of '@bluelibs/runner'`
826
- ).build();
827
- var contextError = error(
828
- "runner.errors.context"
829
- ).format(({ details }) => details ?? "Context error").build();
830
- var circularDependenciesError = error("runner.errors.circularDependencies").format(({ cycles }) => {
831
- const cycleDetails = cycles.map((cycle) => ` \u2022 ${cycle}`).join("\n");
832
- const hasMiddleware = cycles.some((cycle) => cycle.includes("middleware"));
833
- let guidance = "\n\nTo resolve circular dependencies:";
834
- guidance += "\n \u2022 Consider refactoring to reduce coupling between components";
835
- guidance += "\n \u2022 Extract shared dependencies into separate resources";
836
- if (hasMiddleware) {
837
- guidance += "\n \u2022 For middleware: you can filter out tasks/resources using everywhere(fn)";
838
- guidance += "\n \u2022 Consider using events for communication instead of direct dependencies";
835
+ // src/tools/getCallerFile.ts
836
+ function getCallerFile() {
837
+ const originalPrepare = Error.prepareStackTrace;
838
+ try {
839
+ if (isNode()) {
840
+ const err = new Error();
841
+ Error.prepareStackTrace = (_err, stack2) => stack2;
842
+ const stack = err.stack;
843
+ stack.shift();
844
+ stack.shift();
845
+ const candidate = stack.shift();
846
+ const file = candidate?.getFileName?.();
847
+ return file;
848
+ }
849
+ return "unknown";
850
+ } finally {
851
+ Error.prepareStackTrace = originalPrepare;
839
852
  }
840
- return `Circular dependencies detected:
841
- ${cycleDetails}${guidance}`;
842
- }).build();
843
- var eventNotFoundError = error(
844
- "runner.errors.eventNotFound"
845
- ).format(
846
- ({ id: id2 }) => `Event "${id2.toString()}" not found. Did you forget to register it?`
847
- ).build();
848
- var resourceNotFoundError = error(
849
- "runner.errors.resourceNotFound"
850
- ).format(
851
- ({ id: id2 }) => `Resource "${id2.toString()}" not found. Did you forget to register it or are you using the correct id?`
852
- ).build();
853
- var middlewareNotRegisteredError = error("runner.errors.middlewareNotRegistered").format(
854
- ({ type, source, middlewareId }) => `Middleware inside ${type} "${source}" depends on "${middlewareId}" but it's not registered. Did you forget to register it?`
855
- ).build();
856
- var tagNotFoundError = error(
857
- "runner.errors.tagNotFound"
858
- ).format(
859
- ({ id: id2 }) => `Tag "${id2}" not registered. Did you forget to register it inside a resource?`
860
- ).build();
861
- var lockedError = error(
862
- "runner.errors.locked"
863
- ).format(
864
- ({ what }) => `Cannot modify the ${what.toString()} when it is locked.`
865
- ).build();
866
- var storeAlreadyInitializedError = error(
867
- "runner.errors.storeAlreadyInitialized"
868
- ).format(() => "Store already initialized. Cannot reinitialize.").build();
869
- var validationError = error("runner.errors.validation").format(({ subject, id: id2, originalError }) => {
870
- const errorMessage = originalError instanceof Error ? originalError.message : String(originalError);
871
- return `${subject} validation failed for ${id2.toString()}: ${errorMessage}`;
872
- }).build();
873
- var eventCycleError = error("runner.errors.eventCycle").format(({ path }) => {
874
- const chain = path.map((p) => `${p.id}\u2190${p.source}`).join(" -> ");
875
- return `Event emission cycle detected:
876
- ${chain}
877
-
878
- Break the cycle by changing hook logic (avoid mutual emits) or gate with conditions/tags.`;
879
- }).build();
880
- var eventEmissionCycleError = error("runner.errors.eventEmissionCycle").format(({ cycles }) => {
881
- const list = cycles.map((c) => ` \u2022 ${c}`).join("\n");
882
- return `Event emission cycles detected between hooks and events:
883
- ${list}
853
+ }
854
+ __name(getCallerFile, "getCallerFile");
884
855
 
885
- This was detected at compile time (dry-run). Break the cycle by avoiding mutual emits between hooks or scoping hooks using tags.`;
886
- }).build();
887
- var platformUnsupportedFunctionError = error("runner.errors.platformUnsupportedFunction").format(
888
- ({ functionName }) => `Platform function not supported in this environment: ${functionName}. Detected platform: ${detectEnvironment()}.`
889
- ).build();
890
- var cancellationError = error(
891
- "runner.errors.cancellation"
892
- ).format(({ reason }) => reason || "Operation cancelled").build();
893
- var tunnelOwnershipConflictError = error("runner.errors.tunnelOwnershipConflict").format(
894
- ({ 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.`
895
- ).build();
896
- function isCancellationError(err) {
897
- return cancellationError.is(err);
856
+ // src/definers/defineTask.ts
857
+ function defineTask(taskConfig) {
858
+ const filePath = getCallerFile();
859
+ const id2 = taskConfig.id;
860
+ return {
861
+ [symbolTask]: true,
862
+ [symbolFilePath]: filePath,
863
+ id: id2,
864
+ dependencies: taskConfig.dependencies || {},
865
+ middleware: taskConfig.middleware || [],
866
+ run: taskConfig.run,
867
+ inputSchema: taskConfig.inputSchema,
868
+ resultSchema: taskConfig.resultSchema,
869
+ meta: taskConfig.meta || {},
870
+ tags: taskConfig.tags || [],
871
+ // autorun,
872
+ optional() {
873
+ return {
874
+ inner: this,
875
+ [symbolOptionalDependency]: true
876
+ };
877
+ }
878
+ };
879
+ }
880
+ __name(defineTask, "defineTask");
881
+ defineTask.phantom = (taskConfig) => {
882
+ const taskDef = defineTask({
883
+ ...taskConfig,
884
+ run: /* @__PURE__ */ __name(async (input) => {
885
+ return void 0;
886
+ }, "run")
887
+ });
888
+ taskDef[symbolPhantomTask] = true;
889
+ return taskDef;
890
+ };
891
+
892
+ // src/definers/defineHook.ts
893
+ function defineHook(hookDef) {
894
+ const filePath = getCallerFile();
895
+ return {
896
+ [symbolHook]: true,
897
+ [symbolFilePath]: filePath,
898
+ id: hookDef.id,
899
+ dependencies: hookDef.dependencies || {},
900
+ on: hookDef.on,
901
+ order: hookDef.order,
902
+ run: hookDef.run,
903
+ meta: hookDef.meta || {},
904
+ tags: hookDef.tags || []
905
+ };
898
906
  }
899
- __name(isCancellationError, "isCancellationError");
907
+ __name(defineHook, "defineHook");
900
908
 
901
909
  // src/definers/defineResource.ts
902
910
  function defineResource(constConfig) {
@@ -957,6 +965,7 @@ function defineEvent(config) {
957
965
  [symbolEvent]: true,
958
966
  // This is a workaround
959
967
  tags: eventConfig.tags || [],
968
+ parallel: eventConfig.parallel,
960
969
  optional() {
961
970
  return {
962
971
  inner: this,
@@ -3487,30 +3496,279 @@ var DependencyProcessor = class {
3487
3496
  }
3488
3497
  };
3489
3498
 
3490
- // src/models/EventManager.ts
3499
+ // src/models/event/types.ts
3491
3500
  var HandlerOptionsDefaults = { order: 0 };
3492
- var EventManager = class {
3493
- constructor(options) {
3494
- // Core storage for event listeners
3501
+
3502
+ // src/models/event/ListenerRegistry.ts
3503
+ var ListenerRegistry = class {
3504
+ constructor(isExcludedFromGlobal) {
3495
3505
  this.listeners = /* @__PURE__ */ new Map();
3496
3506
  this.globalListeners = [];
3497
- // Caching system for merged listeners to improve performance
3498
3507
  this.cachedMergedListeners = /* @__PURE__ */ new Map();
3499
- this.globalListenersCacheValid = true;
3500
- // Interceptors storage
3508
+ this._globalListenersCacheValid = true;
3509
+ this.isExcludedFromGlobal = isExcludedFromGlobal ?? ((event2) => globalTags.excludeFromGlobalHooks.exists(event2));
3510
+ }
3511
+ static {
3512
+ __name(this, "ListenerRegistry");
3513
+ }
3514
+ get globalListenersCacheValid() {
3515
+ return this._globalListenersCacheValid;
3516
+ }
3517
+ addListener(eventId, newListener) {
3518
+ const listeners = this.listeners.get(eventId);
3519
+ if (listeners) {
3520
+ this.insertListener(listeners, newListener);
3521
+ } else {
3522
+ this.listeners.set(eventId, [newListener]);
3523
+ }
3524
+ this.invalidateCache(eventId);
3525
+ }
3526
+ addGlobalListener(newListener) {
3527
+ this.insertListener(this.globalListeners, newListener);
3528
+ this.invalidateCache();
3529
+ }
3530
+ getListenersForEmit(eventDefinition) {
3531
+ const excludeGlobal = this.isExcludedFromGlobal(eventDefinition);
3532
+ if (excludeGlobal) {
3533
+ return this.listeners.get(eventDefinition.id) || [];
3534
+ }
3535
+ return this.getCachedMergedListeners(eventDefinition.id);
3536
+ }
3537
+ hasListeners(eventDefinition) {
3538
+ const eventListeners = this.listeners.get(eventDefinition.id) || [];
3539
+ if (eventListeners.length > 0) {
3540
+ return true;
3541
+ }
3542
+ if (this.globalListeners.length === 0) {
3543
+ return false;
3544
+ }
3545
+ return !this.isExcludedFromGlobal(eventDefinition);
3546
+ }
3547
+ /**
3548
+ * Cached merge between event-specific and global listeners.
3549
+ * Exposed for backward compatibility with existing tests.
3550
+ */
3551
+ getCachedMergedListeners(eventId) {
3552
+ if (!this._globalListenersCacheValid) {
3553
+ this.cachedMergedListeners.clear();
3554
+ this._globalListenersCacheValid = true;
3555
+ }
3556
+ let cached = this.cachedMergedListeners.get(eventId);
3557
+ if (!cached) {
3558
+ const eventListeners = this.listeners.get(eventId) || [];
3559
+ if (eventListeners.length === 0 && this.globalListeners.length === 0) {
3560
+ cached = [];
3561
+ } else if (eventListeners.length === 0) {
3562
+ cached = this.globalListeners;
3563
+ } else if (this.globalListeners.length === 0) {
3564
+ cached = eventListeners;
3565
+ } else {
3566
+ cached = this.mergeSortedListeners(eventListeners, this.globalListeners);
3567
+ }
3568
+ this.cachedMergedListeners.set(eventId, cached);
3569
+ }
3570
+ return cached;
3571
+ }
3572
+ invalidateCache(eventId) {
3573
+ if (eventId) {
3574
+ this.cachedMergedListeners.delete(eventId);
3575
+ } else {
3576
+ this._globalListenersCacheValid = false;
3577
+ }
3578
+ }
3579
+ mergeSortedListeners(a, b) {
3580
+ const result = [];
3581
+ let i = 0;
3582
+ let j = 0;
3583
+ while (i < a.length && j < b.length) {
3584
+ if (a[i].order <= b[j].order) {
3585
+ result.push(a[i++]);
3586
+ } else {
3587
+ result.push(b[j++]);
3588
+ }
3589
+ }
3590
+ while (i < a.length) result.push(a[i++]);
3591
+ while (j < b.length) result.push(b[j++]);
3592
+ return result;
3593
+ }
3594
+ insertListener(listeners, newListener) {
3595
+ let low = 0;
3596
+ let high = listeners.length;
3597
+ while (low < high) {
3598
+ const mid = low + high >>> 1;
3599
+ if (listeners[mid].order < newListener.order) {
3600
+ low = mid + 1;
3601
+ } else {
3602
+ high = mid;
3603
+ }
3604
+ }
3605
+ listeners.splice(low, 0, newListener);
3606
+ }
3607
+ };
3608
+ function createListener(newListener) {
3609
+ return {
3610
+ handler: newListener.handler,
3611
+ order: newListener.order ?? HandlerOptionsDefaults.order,
3612
+ filter: newListener.filter,
3613
+ id: newListener.id,
3614
+ isGlobal: newListener.isGlobal ?? false
3615
+ };
3616
+ }
3617
+ __name(createListener, "createListener");
3618
+
3619
+ // src/models/event/InterceptorPipeline.ts
3620
+ function composeInterceptors(interceptors, base) {
3621
+ return interceptors.slice().reverse().reduce(
3622
+ (next, interceptor) => (...args) => interceptor(next, ...args),
3623
+ base
3624
+ );
3625
+ }
3626
+ __name(composeInterceptors, "composeInterceptors");
3627
+
3628
+ // src/models/event/EmissionExecutor.ts
3629
+ async function executeSequentially({
3630
+ listeners,
3631
+ event: event2,
3632
+ isPropagationStopped
3633
+ }) {
3634
+ for (const listener of listeners) {
3635
+ if (isPropagationStopped()) {
3636
+ break;
3637
+ }
3638
+ if (shouldExecuteListener(listener, event2)) {
3639
+ await listener.handler(event2);
3640
+ }
3641
+ }
3642
+ }
3643
+ __name(executeSequentially, "executeSequentially");
3644
+ async function executeInParallel({
3645
+ listeners,
3646
+ event: event2
3647
+ }) {
3648
+ if (listeners.length === 0 || event2.isPropagationStopped()) {
3649
+ return;
3650
+ }
3651
+ let currentOrder = listeners[0].order;
3652
+ let currentBatch = [];
3653
+ const executeBatch = /* @__PURE__ */ __name(async (batch) => {
3654
+ const results = await Promise.allSettled(
3655
+ batch.map(async (listener) => {
3656
+ if (shouldExecuteListener(listener, event2)) {
3657
+ await listener.handler(event2);
3658
+ }
3659
+ })
3660
+ );
3661
+ const errors = results.map((result, index) => ({ result, listener: batch[index] })).filter(
3662
+ (r2) => r2.result.status === "rejected"
3663
+ ).map(({ result, listener }) => {
3664
+ const reason = result.reason;
3665
+ const errObj = reason && typeof reason === "object" ? reason : new Error(String(reason));
3666
+ if (errObj.listenerId === void 0) {
3667
+ errObj.listenerId = listener.id;
3668
+ }
3669
+ if (errObj.listenerOrder === void 0) {
3670
+ errObj.listenerOrder = listener.order;
3671
+ }
3672
+ return errObj;
3673
+ });
3674
+ if (errors.length > 0) {
3675
+ if (errors.length === 1) {
3676
+ throw errors[0];
3677
+ }
3678
+ const aggregateError = new Error(
3679
+ `${errors.length} listeners failed in parallel batch`
3680
+ );
3681
+ aggregateError.errors = errors;
3682
+ aggregateError.name = "AggregateError";
3683
+ throw aggregateError;
3684
+ }
3685
+ }, "executeBatch");
3686
+ for (const listener of listeners) {
3687
+ if (listener.order !== currentOrder) {
3688
+ await executeBatch(currentBatch);
3689
+ currentBatch = [];
3690
+ currentOrder = listener.order;
3691
+ if (event2.isPropagationStopped()) {
3692
+ break;
3693
+ }
3694
+ }
3695
+ currentBatch.push(listener);
3696
+ }
3697
+ if (currentBatch.length > 0 && !event2.isPropagationStopped()) {
3698
+ await executeBatch(currentBatch);
3699
+ }
3700
+ }
3701
+ __name(executeInParallel, "executeInParallel");
3702
+ function shouldExecuteListener(listener, event2) {
3703
+ if (listener.id && listener.id === event2.source) {
3704
+ return false;
3705
+ }
3706
+ return !listener.filter || listener.filter(event2);
3707
+ }
3708
+ __name(shouldExecuteListener, "shouldExecuteListener");
3709
+
3710
+ // src/models/event/CycleContext.ts
3711
+ var CycleContext = class {
3712
+ static {
3713
+ __name(this, "CycleContext");
3714
+ }
3715
+ constructor(runtimeCycleDetection) {
3716
+ const platform3 = getPlatform();
3717
+ if (platform3.hasAsyncLocalStorage() && runtimeCycleDetection) {
3718
+ this.emissionStack = platform3.createAsyncLocalStorage();
3719
+ this.currentHookIdContext = platform3.createAsyncLocalStorage();
3720
+ this.isEnabled = true;
3721
+ } else {
3722
+ this.emissionStack = null;
3723
+ this.currentHookIdContext = null;
3724
+ this.isEnabled = false;
3725
+ }
3726
+ }
3727
+ runEmission(frame, source, processEmission) {
3728
+ if (!this.isEnabled || !this.emissionStack || !this.currentHookIdContext) {
3729
+ return processEmission();
3730
+ }
3731
+ const currentStack = this.emissionStack.getStore();
3732
+ if (currentStack) {
3733
+ const cycleStart = currentStack.findIndex(
3734
+ (f) => f.id === frame.id
3735
+ );
3736
+ if (cycleStart !== -1) {
3737
+ const top = currentStack[currentStack.length - 1];
3738
+ const currentHookId = this.currentHookIdContext.getStore();
3739
+ const safeReEmitBySameHook = top.id === frame.id && currentHookId && currentHookId === source;
3740
+ if (!safeReEmitBySameHook) {
3741
+ eventCycleError.throw({
3742
+ path: [...currentStack.slice(cycleStart), frame]
3743
+ });
3744
+ }
3745
+ }
3746
+ }
3747
+ const nextStack = currentStack ? [...currentStack, frame] : [frame];
3748
+ return this.emissionStack.run(nextStack, processEmission);
3749
+ }
3750
+ runHook(hookId, execute) {
3751
+ if (!this.isEnabled || !this.currentHookIdContext) {
3752
+ return execute();
3753
+ }
3754
+ return this.currentHookIdContext.run(hookId, execute);
3755
+ }
3756
+ };
3757
+
3758
+ // src/models/EventManager.ts
3759
+ var EventManager = class {
3760
+ constructor(options) {
3761
+ // Interceptors storage (tests access these directly)
3501
3762
  this.emissionInterceptors = [];
3502
3763
  this.hookInterceptors = [];
3503
3764
  // Locking mechanism to prevent modifications after initialization
3504
3765
  this.#isLocked = false;
3505
3766
  this.runtimeCycleDetection = options?.runtimeCycleDetection ?? true;
3506
- if (getPlatform().hasAsyncLocalStorage() && this.runtimeCycleDetection) {
3507
- this.emissionStack = getPlatform().createAsyncLocalStorage();
3508
- this.currentHookIdContext = getPlatform().createAsyncLocalStorage();
3509
- } else {
3510
- this.runtimeCycleDetection = false;
3511
- this.emissionStack = null;
3512
- this.currentHookIdContext = null;
3513
- }
3767
+ this.registry = new ListenerRegistry();
3768
+ this.cycleContext = new CycleContext(this.runtimeCycleDetection);
3769
+ this.listeners = this.registry.listeners;
3770
+ this.globalListeners = this.registry.globalListeners;
3771
+ this.cachedMergedListeners = this.registry.cachedMergedListeners;
3514
3772
  }
3515
3773
  static {
3516
3774
  __name(this, "EventManager");
@@ -3551,8 +3809,7 @@ var EventManager = class {
3551
3809
  }
3552
3810
  const frame = { id: eventDefinition.id, source };
3553
3811
  const processEmission = /* @__PURE__ */ __name(async () => {
3554
- const excludeFromGlobal = globalTags.excludeFromGlobalHooks.exists(eventDefinition);
3555
- const allListeners = excludeFromGlobal ? this.listeners.get(eventDefinition.id) || [] : this.getCachedMergedListeners(eventDefinition.id);
3812
+ const allListeners = this.registry.getListenersForEmit(eventDefinition);
3556
3813
  let propagationStopped = false;
3557
3814
  const event2 = {
3558
3815
  id: eventDefinition.id,
@@ -3570,49 +3827,23 @@ var EventManager = class {
3570
3827
  if (allListeners.length === 0) {
3571
3828
  return;
3572
3829
  }
3573
- this.isExcludedFromGlobal(eventToEmit);
3574
- for (const listener of allListeners) {
3575
- if (propagationStopped) {
3576
- break;
3577
- }
3578
- if (listener.id && listener.id === eventToEmit.source) {
3579
- continue;
3580
- }
3581
- if (!listener.filter || listener.filter(eventToEmit)) {
3582
- await listener.handler(eventToEmit);
3583
- }
3830
+ if (eventDefinition.parallel) {
3831
+ await executeInParallel({ listeners: allListeners, event: eventToEmit });
3832
+ } else {
3833
+ await executeSequentially({
3834
+ listeners: allListeners,
3835
+ event: eventToEmit,
3836
+ isPropagationStopped: /* @__PURE__ */ __name(() => propagationStopped, "isPropagationStopped")
3837
+ });
3584
3838
  }
3585
3839
  }, "baseEmit");
3586
- let emitWithInterceptors = baseEmit;
3587
- const reversedInterceptors = [...this.emissionInterceptors].reverse();
3588
- for (const interceptor of reversedInterceptors) {
3589
- const nextFunction = emitWithInterceptors;
3590
- emitWithInterceptors = /* @__PURE__ */ __name(async (eventToEmit) => interceptor(nextFunction, eventToEmit), "emitWithInterceptors");
3591
- }
3840
+ const emitWithInterceptors = composeInterceptors(
3841
+ this.emissionInterceptors,
3842
+ baseEmit
3843
+ );
3592
3844
  await emitWithInterceptors(event2);
3593
3845
  }, "processEmission");
3594
- if (this.runtimeCycleDetection && this.emissionStack && this.currentHookIdContext) {
3595
- const currentStack = this.emissionStack.getStore();
3596
- if (currentStack) {
3597
- const cycleStart = currentStack.findIndex(
3598
- (f) => f.id === frame.id
3599
- );
3600
- if (cycleStart !== -1) {
3601
- const top = currentStack[currentStack.length - 1];
3602
- const currentHookId = this.currentHookIdContext.getStore();
3603
- const safeReEmitBySameHook = top.id === frame.id && currentHookId && currentHookId === source;
3604
- if (!safeReEmitBySameHook) {
3605
- eventCycleError.throw({
3606
- path: [...currentStack.slice(cycleStart), frame]
3607
- });
3608
- }
3609
- }
3610
- }
3611
- const nextStack = currentStack ? [...currentStack, frame] : [frame];
3612
- await this.emissionStack.run(nextStack, processEmission);
3613
- } else {
3614
- await processEmission();
3615
- }
3846
+ await this.cycleContext.runEmission(frame, source, processEmission);
3616
3847
  }
3617
3848
  /**
3618
3849
  * Registers an event listener for specific event(s).
@@ -3624,24 +3855,18 @@ var EventManager = class {
3624
3855
  */
3625
3856
  addListener(event2, handler, options = HandlerOptionsDefaults) {
3626
3857
  this.checkLock();
3627
- const newListener = {
3858
+ const newListener = createListener({
3628
3859
  handler,
3629
- order: options.order || 0,
3860
+ order: options.order,
3630
3861
  filter: options.filter,
3631
3862
  id: options.id,
3632
3863
  isGlobal: false
3633
- };
3864
+ });
3634
3865
  if (Array.isArray(event2)) {
3635
3866
  event2.forEach((id2) => this.addListener(id2, handler, options));
3636
3867
  } else {
3637
3868
  const eventId = event2.id;
3638
- const listeners = this.listeners.get(eventId);
3639
- if (listeners) {
3640
- this.insertListener(listeners, newListener);
3641
- } else {
3642
- this.listeners.set(eventId, [newListener]);
3643
- }
3644
- this.invalidateCache(eventId);
3869
+ this.registry.addListener(eventId, newListener);
3645
3870
  }
3646
3871
  }
3647
3872
  /**
@@ -3653,15 +3878,14 @@ var EventManager = class {
3653
3878
  */
3654
3879
  addGlobalListener(handler, options = HandlerOptionsDefaults) {
3655
3880
  this.checkLock();
3656
- const newListener = {
3881
+ const newListener = createListener({
3657
3882
  handler,
3658
- order: options.order || 0,
3883
+ order: options.order,
3659
3884
  filter: options.filter,
3660
3885
  id: options.id,
3661
3886
  isGlobal: true
3662
- };
3663
- this.insertListener(this.globalListeners, newListener);
3664
- this.invalidateCache();
3887
+ });
3888
+ this.registry.addGlobalListener(newListener);
3665
3889
  }
3666
3890
  /**
3667
3891
  * Checks if there are any listeners registered for the given event
@@ -3670,15 +3894,7 @@ var EventManager = class {
3670
3894
  * @returns true if listeners exist, false otherwise
3671
3895
  */
3672
3896
  hasListeners(eventDefinition) {
3673
- const eventListeners = this.listeners.get(eventDefinition.id) || [];
3674
- if (eventListeners.length > 0) {
3675
- return true;
3676
- }
3677
- if (this.globalListeners.length === 0) {
3678
- return false;
3679
- }
3680
- const isExcludedFromGlobal = globalTags.excludeFromGlobalHooks.exists(eventDefinition);
3681
- return !isExcludedFromGlobal;
3897
+ return this.registry.hasListeners(eventDefinition);
3682
3898
  }
3683
3899
  /**
3684
3900
  * Adds an interceptor for all event emissions
@@ -3723,20 +3939,14 @@ var EventManager = class {
3723
3939
  throw err;
3724
3940
  }
3725
3941
  }, "baseExecute");
3726
- let executeWithInterceptors = baseExecute;
3727
- const reversedInterceptors = [...this.hookInterceptors].reverse();
3728
- for (const interceptor of reversedInterceptors) {
3729
- const nextFunction = executeWithInterceptors;
3730
- executeWithInterceptors = /* @__PURE__ */ __name(async (hookToExecute, eventForHook) => interceptor(nextFunction, hookToExecute, eventForHook), "executeWithInterceptors");
3731
- }
3732
- if (this.runtimeCycleDetection) {
3733
- return await this.currentHookIdContext?.run(
3734
- hook2.id,
3735
- async () => await executeWithInterceptors(hook2, event2)
3736
- );
3737
- } else {
3738
- return await executeWithInterceptors(hook2, event2);
3739
- }
3942
+ const executeWithInterceptors = composeInterceptors(
3943
+ this.hookInterceptors,
3944
+ baseExecute
3945
+ );
3946
+ return this.cycleContext.isEnabled ? await this.cycleContext.runHook(
3947
+ hook2.id,
3948
+ () => executeWithInterceptors(hook2, event2)
3949
+ ) : await executeWithInterceptors(hook2, event2);
3740
3950
  }
3741
3951
  // ==================== PRIVATE METHODS ====================
3742
3952
  /**
@@ -3747,102 +3957,12 @@ var EventManager = class {
3747
3957
  lockedError.throw({ what: "EventManager" });
3748
3958
  }
3749
3959
  }
3750
- /**
3751
- * Merges two sorted arrays of listeners while maintaining order.
3752
- * Used to combine event-specific listeners with global listeners.
3753
- *
3754
- * @param a - First array of listeners
3755
- * @param b - Second array of listeners
3756
- * @returns Merged and sorted array of listeners
3757
- */
3758
- mergeSortedListeners(a, b) {
3759
- const result = [];
3760
- let i = 0, j = 0;
3761
- while (i < a.length && j < b.length) {
3762
- if (a[i].order <= b[j].order) {
3763
- result.push(a[i++]);
3764
- } else {
3765
- result.push(b[j++]);
3766
- }
3767
- }
3768
- while (i < a.length) result.push(a[i++]);
3769
- while (j < b.length) result.push(b[j++]);
3770
- return result;
3771
- }
3772
- /**
3773
- * Inserts a new listener into a sorted array using binary search.
3774
- * Maintains order based on listener priority.
3775
- *
3776
- * @param listeners - Array to insert into
3777
- * @param newListener - Listener to insert
3778
- */
3779
- insertListener(listeners, newListener) {
3780
- let low = 0;
3781
- let high = listeners.length;
3782
- while (low < high) {
3783
- const mid = low + high >>> 1;
3784
- if (listeners[mid].order < newListener.order) {
3785
- low = mid + 1;
3786
- } else {
3787
- high = mid;
3788
- }
3789
- }
3790
- listeners.splice(low, 0, newListener);
3791
- }
3792
- /**
3793
- * Returns true if the given emission carries the tag that marks
3794
- * it as excluded from global ("*") listeners.
3795
- *
3796
- * @param event - The event emission to check
3797
- * @returns true if event should exclude global listeners
3798
- */
3799
- isExcludedFromGlobal(event2) {
3800
- return globalTags.excludeFromGlobalHooks.exists(event2);
3801
- }
3802
3960
  /**
3803
3961
  * Retrieves cached merged listeners for an event, or creates them if not cached.
3804
- * Combines event-specific listeners with global listeners and sorts them by priority.
3805
- *
3806
- * @param eventId - The event ID to get listeners for
3807
- * @returns Array of merged listeners sorted by priority
3962
+ * Kept for backward compatibility (tests spy on this).
3808
3963
  */
3809
3964
  getCachedMergedListeners(eventId) {
3810
- if (!this.globalListenersCacheValid) {
3811
- this.cachedMergedListeners.clear();
3812
- this.globalListenersCacheValid = true;
3813
- }
3814
- let cached = this.cachedMergedListeners.get(eventId);
3815
- if (!cached) {
3816
- const eventListeners = this.listeners.get(eventId) || [];
3817
- if (eventListeners.length === 0 && this.globalListeners.length === 0) {
3818
- cached = [];
3819
- } else if (eventListeners.length === 0) {
3820
- cached = this.globalListeners;
3821
- } else if (this.globalListeners.length === 0) {
3822
- cached = eventListeners;
3823
- } else {
3824
- cached = this.mergeSortedListeners(
3825
- eventListeners,
3826
- this.globalListeners
3827
- );
3828
- }
3829
- this.cachedMergedListeners.set(eventId, cached);
3830
- }
3831
- return cached;
3832
- }
3833
- /**
3834
- * Invalidates the cached merged listeners.
3835
- * If eventId is provided, only invalidates cache for that specific event.
3836
- * Otherwise, invalidates the global cache.
3837
- *
3838
- * @param eventId - Optional specific event ID to invalidate
3839
- */
3840
- invalidateCache(eventId) {
3841
- if (eventId) {
3842
- this.cachedMergedListeners.delete(eventId);
3843
- } else {
3844
- this.globalListenersCacheValid = false;
3845
- }
3965
+ return this.registry.getCachedMergedListeners(eventId);
3846
3966
  }
3847
3967
  };
3848
3968
 
@@ -5788,6 +5908,10 @@ function makeEventBuilder(state) {
5788
5908
  const next = clone5(state, { meta: m });
5789
5909
  return makeEventBuilder(next);
5790
5910
  },
5911
+ parallel(enabled = true) {
5912
+ const next = clone5(state, { parallel: enabled });
5913
+ return makeEventBuilder(next);
5914
+ },
5791
5915
  build() {
5792
5916
  const event2 = defineEvent({
5793
5917
  ...state
@@ -5806,7 +5930,8 @@ function eventBuilder(id2) {
5806
5930
  filePath,
5807
5931
  meta: {},
5808
5932
  payloadSchema: void 0,
5809
- tags: []
5933
+ tags: [],
5934
+ parallel: void 0
5810
5935
  });
5811
5936
  return makeEventBuilder(initial);
5812
5937
  }