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