@holo-js/events 0.3.28 → 0.4.1

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.
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Holo-JS contributors
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
@@ -0,0 +1 @@
1
+ 3fc9dd4981443d3306e60b3ff6271a7d1d56721340f20c03d22c91df21ae7fe9
package/dist/index.d.ts CHANGED
@@ -1,4 +1,4 @@
1
- import { QueueJobDefinition, QueueJsonValue, QueueRegisteredJob } from '@holo-js/queue';
1
+ import { QueueJobDefinition, QueueJsonValue, QueueRegisteredJob, QueueJobContext } from '@holo-js/queue';
2
2
 
3
3
  type EventDelayValue = number | Date;
4
4
  type EventDefinitionDispatcher = <TPayload>(event: EventDefinition<TPayload, string | undefined>, payload: TPayload) => EventPendingDispatch<TPayload>;
@@ -50,6 +50,8 @@ interface ListenerDefinition<TInput extends EventReferenceInput = EventReference
50
50
  readonly queueName?: string;
51
51
  readonly delay?: EventDelayValue;
52
52
  readonly afterCommit?: boolean;
53
+ readonly tries?: number;
54
+ readonly backoff?: number | readonly number[];
53
55
  handle(event: ListenerHandledEvent<TInput>): TResult | Promise<TResult>;
54
56
  }
55
57
  type ListenerDefinitionInput<TInput extends EventReferenceInput = EventReferenceInput, TResult = unknown> = Omit<ListenerDefinition<TInput, TResult>, 'listensTo'> & {
@@ -94,6 +96,7 @@ interface EventQueuedListenerDispatch {
94
96
  readonly connection?: string;
95
97
  readonly queueName?: string;
96
98
  readonly delay?: EventDelayValue;
99
+ readonly tries?: number;
97
100
  }
98
101
  interface EventDeferredDispatchContext {
99
102
  readonly eventName: string;
@@ -196,7 +199,9 @@ declare module '@holo-js/queue' {
196
199
  }
197
200
  type QueueModule = {
198
201
  defineJob(definition: QueueJobDefinition<EventsInvokeListenerPayload, void>): QueueJobDefinition<EventsInvokeListenerPayload, void>;
199
- dispatch(jobName: string, payload: EventsInvokeListenerPayload): QueuePendingDispatchChain;
202
+ dispatch(jobName: string, payload: EventsInvokeListenerPayload, options?: {
203
+ readonly tries?: number;
204
+ }): QueuePendingDispatchChain;
200
205
  getRegisteredQueueJob(name: string): QueueRegisteredJob | undefined;
201
206
  registerQueueJob(definition: QueueJobDefinition<EventsInvokeListenerPayload, void>, options: {
202
207
  name: string;
@@ -212,7 +217,7 @@ declare function loadQueueModule(): Promise<QueueModule>;
212
217
  declare function createQueuedListenerEventEnvelope(payload: EventsInvokeListenerPayload): EventEnvelope<string, QueueJsonValue>;
213
218
  declare function requireQueuedListener(listenerId: string): RegisteredListener;
214
219
  declare function assertQueuedListenerMatchesEvent(listener: RegisteredListener, eventName: string): void;
215
- declare function runQueuedListenerInvocation(payload: EventsInvokeListenerPayload): Promise<void>;
220
+ declare function runQueuedListenerInvocation(payload: EventsInvokeListenerPayload, context?: QueueJobContext): Promise<void>;
216
221
  declare function ensureEventsQueueJobRegistered(): void;
217
222
  declare function ensureEventsQueueJobRegisteredAsync(): Promise<void>;
218
223
  declare const eventQueueInternals: {
@@ -234,12 +239,6 @@ type EventPayloadValidationState = {
234
239
  type RuntimeState = {
235
240
  hooks: EventRuntimeHooks;
236
241
  };
237
- type ListenerExecutionGroups = {
238
- readonly immediateSyncListeners: readonly RegisteredListener[];
239
- readonly immediateQueuedListeners: readonly RegisteredListener[];
240
- readonly deferredSyncListeners: readonly RegisteredListener[];
241
- readonly deferredQueuedListeners: readonly RegisteredListener[];
242
- };
243
242
  declare function isPlainObject(value: unknown): value is Record<string, unknown>;
244
243
  declare function assertEventJsonValue(value: unknown, path: string, state: EventPayloadValidationState): void;
245
244
  declare function validateQueuedEventPayload<TPayload>(payload: TPayload): void;
@@ -249,19 +248,14 @@ declare function normalizeEventName(name: string): string;
249
248
  declare function resolveDispatchedEventName<TPayload>(event: string | EventDefinition<TPayload, string | undefined>): string;
250
249
  declare function requireRegisteredEvent(name: string): RegisteredEvent;
251
250
  declare function createEventEnvelope<TPayload>(eventName: string, payload: TPayload, occurredAt?: number): EventEnvelope<string, TPayload>;
252
- declare function splitRegisteredListeners(listeners: readonly RegisteredListener[]): {
253
- readonly syncListeners: readonly RegisteredListener[];
254
- readonly queuedListeners: readonly RegisteredListener[];
251
+ declare function splitListenersByCommitTiming(listeners: readonly RegisteredListener[], shouldDeferAll: boolean): {
252
+ readonly immediateListeners: readonly RegisteredListener[];
253
+ readonly deferredListeners: readonly RegisteredListener[];
255
254
  };
256
- declare function executeSyncListeners<TPayload>(listeners: readonly RegisteredListener[], event: EventEnvelope<string, TPayload>): Promise<number>;
257
- declare function executeListenerGroups<TPayload>(syncListeners: readonly RegisteredListener[], queuedListeners: readonly RegisteredListener[], event: EventEnvelope<string, TPayload>, options: PendingOptions): Promise<{
258
- readonly syncCount: number;
259
- readonly queuedCount: number;
260
- }>;
261
255
  declare function executeListener<TPayload>(listener: RegisteredListener, event: EventEnvelope<string, TPayload>): Promise<void>;
262
256
  declare function createQueuedListenerDispatch(listener: RegisteredListener, event: EventEnvelope, options: PendingOptions): EventQueuedListenerDispatch;
263
- declare function dispatchQueuedListeners<TPayload>(listeners: readonly RegisteredListener[], event: EventEnvelope<string, TPayload>, options: PendingOptions): Promise<number>;
264
- declare function splitListenersForExecution(listeners: readonly RegisteredListener[], forceAfterCommit: boolean): ListenerExecutionGroups;
257
+ declare function dispatchQueuedListener<TPayload>(listener: RegisteredListener, event: EventEnvelope<string, TPayload>, options: PendingOptions): Promise<void>;
258
+ declare function runListenersInOrder<TPayload>(listeners: readonly RegisteredListener[], event: EventEnvelope<string, TPayload>, options: PendingOptions): Promise<void>;
265
259
  declare function scheduleDeferredDispatch(callback: () => Promise<void>, eventName: string): boolean;
266
260
  declare function executeDispatch<TPayload>(event: string | EventDefinition<TPayload, string | undefined>, payload: TPayload, options?: PendingOptions): Promise<EventDispatchResult>;
267
261
  declare function configureEventsRuntime(hooks?: EventRuntimeHooks): void;
@@ -276,20 +270,19 @@ declare const eventRuntimeInternals: {
276
270
  createEventEnvelope: typeof createEventEnvelope;
277
271
  createQueuedListenerDispatch: typeof createQueuedListenerDispatch;
278
272
  createRuntimeBinding: typeof createRuntimeBinding;
279
- dispatchQueuedListeners: typeof dispatchQueuedListeners;
280
- executeListenerGroups: typeof executeListenerGroups;
273
+ dispatchQueuedListener: typeof dispatchQueuedListener;
281
274
  executeDispatch: typeof executeDispatch;
282
275
  executeListener: typeof executeListener;
283
- executeSyncListeners: typeof executeSyncListeners;
276
+ runListenersInOrder: typeof runListenersInOrder;
284
277
  getRuntimeState: typeof getRuntimeState;
285
278
  isPlainObject: typeof isPlainObject;
286
279
  normalizeEventName: typeof normalizeEventName;
287
280
  requireRegisteredEvent: typeof requireRegisteredEvent;
288
281
  resolveDispatchedEventName: typeof resolveDispatchedEventName;
289
282
  scheduleDeferredDispatch: typeof scheduleDeferredDispatch;
290
- splitRegisteredListeners: typeof splitRegisteredListeners;
291
- splitListenersForExecution: typeof splitListenersForExecution;
283
+ splitListenersByCommitTiming: typeof splitListenersByCommitTiming;
292
284
  validateQueuedEventPayload: typeof validateQueuedEventPayload;
293
285
  };
294
286
 
295
- export { type DefinedEventDefinition, EVENTS_INVOKE_LISTENER_JOB, Event, type EventDeferredDispatchContext, type EventDefinition, type EventDelayValue, type EventDispatchResult, type EventEnvelope, type EventEnvelopeFor, type EventFacade, type EventPayloadFor, type EventPendingDispatch, type EventQueuedListenerDispatch, type EventReference, type EventReferenceInput, type EventRuntimeBinding, type EventRuntimeHooks, type EventsInvokeListenerPayload, type ExportedEventDefinition, type HoloEventRegistry, type HoloListenerRegistry, type ListenerDefinition, type ListenerDefinitionInput, type ListenerHandledEvent, type RegisterEventOptions, type RegisterListenerOptions, type RegisteredEvent, type RegisteredListener, configureEventsRuntime, deferEventDispatchToDatabaseCommit, defineEvent, defineListener, dispatchEvent, ensureEventsQueueJobRegistered, ensureEventsQueueJobRegisteredAsync, eventInternals, eventQueueInternals, eventRegistryInternals, eventRuntimeInternals, getEventsRuntime, getRegisteredEvent, getRegisteredListener, isEventDefinition, isListenerDefinition, listRegisteredEvents, listRegisteredListeners, listRegisteredListenersForEvent, normalizeEventDefinition, normalizeListenerDefinition, registerEvent, registerEvents, registerListener, registerListeners, resetEventRegistry, resetEventsRegistry, resetEventsRuntime, resetListenerRegistry, runQueuedListenerInvocation, unregisterEvent, unregisterListener };
287
+ export { EVENTS_INVOKE_LISTENER_JOB, Event, configureEventsRuntime, deferEventDispatchToDatabaseCommit, defineEvent, defineListener, dispatchEvent, ensureEventsQueueJobRegistered, ensureEventsQueueJobRegisteredAsync, eventInternals, eventQueueInternals, eventRegistryInternals, eventRuntimeInternals, getEventsRuntime, getRegisteredEvent, getRegisteredListener, isEventDefinition, isListenerDefinition, listRegisteredEvents, listRegisteredListeners, listRegisteredListenersForEvent, normalizeEventDefinition, normalizeListenerDefinition, registerEvent, registerEvents, registerListener, registerListeners, resetEventRegistry, resetEventsRegistry, resetEventsRuntime, resetListenerRegistry, runQueuedListenerInvocation, unregisterEvent, unregisterListener };
288
+ export type { DefinedEventDefinition, EventDeferredDispatchContext, EventDefinition, EventDelayValue, EventDispatchResult, EventEnvelope, EventEnvelopeFor, EventFacade, EventPayloadFor, EventPendingDispatch, EventQueuedListenerDispatch, EventReference, EventReferenceInput, EventRuntimeBinding, EventRuntimeHooks, EventsInvokeListenerPayload, ExportedEventDefinition, HoloEventRegistry, HoloListenerRegistry, ListenerDefinition, ListenerDefinitionInput, ListenerHandledEvent, RegisterEventOptions, RegisterListenerOptions, RegisteredEvent, RegisteredListener };
package/dist/index.mjs CHANGED
@@ -37,6 +37,30 @@ function normalizeOptionalBoolean(value, label) {
37
37
  }
38
38
  return value;
39
39
  }
40
+ function normalizeOptionalTries(value) {
41
+ if (typeof value === "undefined") {
42
+ return void 0;
43
+ }
44
+ if (!Number.isInteger(value) || value < 1) {
45
+ throw new Error("[Holo Events] Listener tries must be an integer greater than or equal to 1.");
46
+ }
47
+ return value;
48
+ }
49
+ function normalizeOptionalBackoff(value) {
50
+ if (typeof value === "undefined") {
51
+ return void 0;
52
+ }
53
+ if (typeof value === "number") {
54
+ if (!Number.isInteger(value) || value < 0) {
55
+ throw new Error("[Holo Events] Listener backoff must be an integer greater than or equal to 0 or a non-empty array of them.");
56
+ }
57
+ return value;
58
+ }
59
+ if (!Array.isArray(value) || value.length === 0 || value.some((entry) => !Number.isInteger(entry) || entry < 0)) {
60
+ throw new Error("[Holo Events] Listener backoff must be an integer greater than or equal to 0 or a non-empty array of them.");
61
+ }
62
+ return Object.freeze([...value]);
63
+ }
40
64
  function normalizeOptionalDelay(value) {
41
65
  if (typeof value === "undefined") {
42
66
  return void 0;
@@ -139,7 +163,9 @@ function normalizeListenerDefinition(listener) {
139
163
  const queueName = normalizeOptionalString(listener.queueName, "Listener queue name");
140
164
  const delay = normalizeOptionalDelay(listener.delay);
141
165
  const afterCommit = normalizeOptionalBoolean(listener.afterCommit, "Listener afterCommit");
142
- if (queue !== true && (connection || queueName || typeof delay !== "undefined")) {
166
+ const tries = normalizeOptionalTries(listener.tries);
167
+ const backoff = normalizeOptionalBackoff(listener.backoff);
168
+ if (queue !== true && (connection || queueName || typeof delay !== "undefined" || typeof tries !== "undefined" || typeof backoff !== "undefined")) {
143
169
  throw new Error("[Holo Events] Listener queue metadata requires queue: true.");
144
170
  }
145
171
  return {
@@ -150,7 +176,9 @@ function normalizeListenerDefinition(listener) {
150
176
  ...typeof connection === "undefined" ? {} : { connection },
151
177
  ...typeof queueName === "undefined" ? {} : { queueName },
152
178
  ...typeof delay === "undefined" ? {} : { delay },
153
- ...typeof afterCommit === "undefined" ? {} : { afterCommit }
179
+ ...typeof afterCommit === "undefined" ? {} : { afterCommit },
180
+ ...typeof tries === "undefined" ? {} : { tries },
181
+ ...typeof backoff === "undefined" ? {} : { backoff }
154
182
  };
155
183
  }
156
184
  function defineListener(listener) {
@@ -411,6 +439,7 @@ function deferEventDispatchToDatabaseCommit(callback, _context) {
411
439
 
412
440
  // src/queue.ts
413
441
  import { createRequire } from "module";
442
+ import { holoRuntimeInternals } from "@holo-js/db";
414
443
  var EVENTS_INVOKE_LISTENER_JOB = "holo.events.invoke-listener";
415
444
  var require2 = createRequire(import.meta.url);
416
445
  var queueSpecifier = "@holo-js/queue";
@@ -455,10 +484,29 @@ function assertQueuedListenerMatchesEvent(listener, eventName) {
455
484
  );
456
485
  }
457
486
  }
458
- async function runQueuedListenerInvocation(payload) {
487
+ function resolveListenerRetryDelaySeconds(backoff, attempt) {
488
+ if (typeof backoff === "number") {
489
+ return backoff;
490
+ }
491
+ return backoff[Math.min(Math.max(attempt - 1, 0), backoff.length - 1)] ?? 0;
492
+ }
493
+ async function runQueuedListenerInvocation(payload, context) {
459
494
  const listener = requireQueuedListener(payload.listenerId);
460
495
  assertQueuedListenerMatchesEvent(listener, payload.eventName);
461
- await listener.definition.handle(createQueuedListenerEventEnvelope(payload));
496
+ try {
497
+ await listener.definition.handle(createQueuedListenerEventEnvelope(payload));
498
+ } catch (error) {
499
+ const { backoff } = listener.definition;
500
+ if (!context || typeof backoff === "undefined" || context.attempt >= context.maxAttempts) {
501
+ throw error;
502
+ }
503
+ try {
504
+ await context.release(resolveListenerRetryDelaySeconds(backoff, context.attempt));
505
+ } catch (releaseError) {
506
+ holoRuntimeInternals.reportError(releaseError, { capability: "events", operation: "listener.releaseRetry" });
507
+ throw error;
508
+ }
509
+ }
462
510
  }
463
511
  function registerEventsQueueJob(queue) {
464
512
  if (queue.getRegisteredQueueJob(EVENTS_INVOKE_LISTENER_JOB)) {
@@ -482,7 +530,7 @@ async function dispatchQueuedListenerViaQueue(dispatch) {
482
530
  eventName: dispatch.event.name,
483
531
  occurredAt: dispatch.event.occurredAt,
484
532
  payload: dispatch.event.payload
485
- });
533
+ }, typeof dispatch.tries === "undefined" ? {} : { tries: dispatch.tries });
486
534
  if (typeof dispatch.connection !== "undefined") {
487
535
  pending = pending.onConnection(dispatch.connection);
488
536
  }
@@ -599,33 +647,19 @@ function createEventEnvelope(eventName, payload, occurredAt = Date.now()) {
599
647
  occurredAt
600
648
  });
601
649
  }
602
- function splitRegisteredListeners(listeners) {
603
- const syncListeners = [];
604
- const queuedListeners = [];
650
+ function splitListenersByCommitTiming(listeners, shouldDeferAll) {
651
+ const immediateListeners = [];
652
+ const deferredListeners = [];
605
653
  for (const listener of listeners) {
606
- if (listener.definition.queue === true) {
607
- queuedListeners.push(listener);
654
+ if (shouldDeferAll || listener.definition.afterCommit === true) {
655
+ deferredListeners.push(listener);
608
656
  continue;
609
657
  }
610
- syncListeners.push(listener);
611
- }
612
- return Object.freeze({
613
- syncListeners: Object.freeze(syncListeners),
614
- queuedListeners: Object.freeze(queuedListeners)
615
- });
616
- }
617
- async function executeSyncListeners(listeners, event) {
618
- for (const listener of listeners) {
619
- await executeListener(listener, event);
658
+ immediateListeners.push(listener);
620
659
  }
621
- return listeners.length;
622
- }
623
- async function executeListenerGroups(syncListeners, queuedListeners, event, options) {
624
- const syncCount = await executeSyncListeners(syncListeners, event);
625
- const queuedCount = await dispatchQueuedListeners(queuedListeners, event, options);
626
660
  return Object.freeze({
627
- syncCount,
628
- queuedCount
661
+ immediateListeners: Object.freeze(immediateListeners),
662
+ deferredListeners: Object.freeze(deferredListeners)
629
663
  });
630
664
  }
631
665
  async function executeListener(listener, event) {
@@ -637,53 +671,22 @@ function createQueuedListenerDispatch(listener, event, options) {
637
671
  event,
638
672
  ...typeof (options.connection ?? listener.definition.connection) === "undefined" ? {} : { connection: options.connection ?? listener.definition.connection },
639
673
  ...typeof (options.queueName ?? listener.definition.queueName) === "undefined" ? {} : { queueName: options.queueName ?? listener.definition.queueName },
640
- ...typeof (options.delay ?? listener.definition.delay) === "undefined" ? {} : { delay: options.delay ?? listener.definition.delay }
674
+ ...typeof (options.delay ?? listener.definition.delay) === "undefined" ? {} : { delay: options.delay ?? listener.definition.delay },
675
+ ...typeof listener.definition.tries === "undefined" ? {} : { tries: listener.definition.tries }
641
676
  });
642
677
  }
643
- async function dispatchQueuedListeners(listeners, event, options) {
644
- if (listeners.length === 0) {
645
- return 0;
646
- }
647
- validateQueuedEventPayload(event.payload);
678
+ async function dispatchQueuedListener(listener, event, options) {
648
679
  const dispatcher = getRuntimeState().hooks.dispatchQueuedListener ?? dispatchQueuedListenerViaQueue;
649
- for (const listener of listeners) {
650
- await dispatcher(createQueuedListenerDispatch(listener, event, options));
651
- }
652
- return listeners.length;
680
+ await dispatcher(createQueuedListenerDispatch(listener, event, options));
653
681
  }
654
- function splitListenersForExecution(listeners, forceAfterCommit) {
655
- const immediateSyncListeners = [];
656
- const immediateQueuedListeners = [];
657
- const deferredSyncListeners = [];
658
- const deferredQueuedListeners = [];
682
+ async function runListenersInOrder(listeners, event, options) {
659
683
  for (const listener of listeners) {
660
- const shouldDefer = forceAfterCommit || listener.definition.afterCommit === true;
661
- if (shouldDefer) {
662
- if (listener.definition.queue === true) {
663
- deferredQueuedListeners.push(listener);
664
- continue;
665
- }
666
- deferredSyncListeners.push(listener);
667
- continue;
668
- }
669
684
  if (listener.definition.queue === true) {
670
- immediateQueuedListeners.push(listener);
685
+ await dispatchQueuedListener(listener, event, options);
671
686
  continue;
672
687
  }
673
- immediateSyncListeners.push(listener);
674
- }
675
- return Object.freeze({
676
- immediateSyncListeners: Object.freeze(immediateSyncListeners),
677
- immediateQueuedListeners: Object.freeze(immediateQueuedListeners),
678
- deferredSyncListeners: Object.freeze(deferredSyncListeners),
679
- deferredQueuedListeners: Object.freeze(deferredQueuedListeners)
680
- });
681
- }
682
- function validateQueuedPayloadForExecutionGroups(groups, payload) {
683
- if (groups.immediateQueuedListeners.length === 0 && groups.deferredQueuedListeners.length === 0) {
684
- return;
688
+ await executeListener(listener, event);
685
689
  }
686
- validateQueuedEventPayload(payload);
687
690
  }
688
691
  function scheduleDeferredDispatch(callback, eventName) {
689
692
  const defer = getRuntimeState().hooks.defer ?? deferEventDispatchToDatabaseCommit;
@@ -697,61 +700,38 @@ async function executeDispatch(event, payload, options = {}) {
697
700
  requireRegisteredEvent(eventName);
698
701
  const envelope = createEventEnvelope(eventName, payload);
699
702
  const listeners = listRegisteredListenersForEvent(eventName);
700
- const groups = splitListenersForExecution(listeners, options.afterCommit === true);
701
- validateQueuedPayloadForExecutionGroups(groups, envelope.payload);
702
- if (options.afterCommit) {
703
- const scheduled = scheduleDeferredDispatch(async () => {
704
- await executeListenerGroups(
705
- [...groups.immediateSyncListeners, ...groups.deferredSyncListeners],
706
- [...groups.immediateQueuedListeners, ...groups.deferredQueuedListeners],
707
- envelope,
708
- options
709
- );
710
- }, eventName);
711
- if (scheduled) {
712
- return Object.freeze({
713
- eventName,
714
- occurredAt: envelope.occurredAt,
715
- deferred: true,
716
- syncListeners: groups.immediateSyncListeners.length + groups.deferredSyncListeners.length,
717
- queuedListeners: groups.immediateQueuedListeners.length + groups.deferredQueuedListeners.length
718
- });
719
- }
720
- }
721
- const immediate = await executeListenerGroups(
722
- groups.immediateSyncListeners,
723
- groups.immediateQueuedListeners,
724
- envelope,
725
- options
726
- );
727
- let deferred = false;
728
- if (groups.deferredSyncListeners.length > 0 || groups.deferredQueuedListeners.length > 0) {
729
- const scheduled = scheduleDeferredDispatch(async () => {
730
- await executeListenerGroups(
731
- groups.deferredSyncListeners,
732
- groups.deferredQueuedListeners,
733
- envelope,
734
- options
735
- );
736
- }, eventName);
737
- if (scheduled) {
738
- deferred = true;
739
- } else {
740
- await executeListenerGroups(
741
- groups.deferredSyncListeners,
742
- groups.deferredQueuedListeners,
743
- envelope,
744
- options
745
- );
746
- }
703
+ const queuedListeners = listeners.filter((listener) => listener.definition.queue === true).length;
704
+ if (queuedListeners > 0) {
705
+ validateQueuedEventPayload(envelope.payload);
747
706
  }
748
- return Object.freeze({
707
+ const createResult = (deferred) => Object.freeze({
749
708
  eventName,
750
709
  occurredAt: envelope.occurredAt,
751
710
  deferred,
752
- syncListeners: immediate.syncCount + groups.deferredSyncListeners.length,
753
- queuedListeners: immediate.queuedCount + groups.deferredQueuedListeners.length
711
+ syncListeners: listeners.length - queuedListeners,
712
+ queuedListeners
754
713
  });
714
+ const { immediateListeners, deferredListeners } = splitListenersByCommitTiming(
715
+ listeners,
716
+ options.afterCommit === true
717
+ );
718
+ if (deferredListeners.length === 0) {
719
+ await runListenersInOrder(listeners, envelope, options);
720
+ return createResult(false);
721
+ }
722
+ let immediatePhaseCompleted = false;
723
+ const scheduled = scheduleDeferredDispatch(async () => {
724
+ if (immediatePhaseCompleted) {
725
+ await runListenersInOrder(deferredListeners, envelope, options);
726
+ }
727
+ }, eventName);
728
+ if (!scheduled) {
729
+ await runListenersInOrder(listeners, envelope, options);
730
+ return createResult(false);
731
+ }
732
+ await runListenersInOrder(immediateListeners, envelope, options);
733
+ immediatePhaseCompleted = true;
734
+ return createResult(true);
755
735
  }
756
736
  var PendingEventDispatch = class _PendingEventDispatch {
757
737
  constructor(event, payload, options = {}) {
@@ -759,6 +739,8 @@ var PendingEventDispatch = class _PendingEventDispatch {
759
739
  this.payload = payload;
760
740
  this.options = options;
761
741
  }
742
+ event;
743
+ payload;
762
744
  options;
763
745
  executionPromise;
764
746
  afterCommit() {
@@ -826,19 +808,17 @@ var eventRuntimeInternals = {
826
808
  createEventEnvelope,
827
809
  createQueuedListenerDispatch,
828
810
  createRuntimeBinding,
829
- dispatchQueuedListeners,
830
- executeListenerGroups,
811
+ dispatchQueuedListener,
831
812
  executeDispatch,
832
813
  executeListener,
833
- executeSyncListeners,
814
+ runListenersInOrder,
834
815
  getRuntimeState,
835
816
  isPlainObject: isPlainObject2,
836
817
  normalizeEventName,
837
818
  requireRegisteredEvent,
838
819
  resolveDispatchedEventName,
839
820
  scheduleDeferredDispatch,
840
- splitRegisteredListeners,
841
- splitListenersForExecution,
821
+ splitListenersByCommitTiming,
842
822
  validateQueuedEventPayload
843
823
  };
844
824
  export {
package/package.json CHANGED
@@ -1,9 +1,14 @@
1
1
  {
2
2
  "name": "@holo-js/events",
3
- "version": "0.3.28",
3
+ "version": "0.4.1",
4
4
  "description": "Holo-JS Framework - event contracts and runtime surface",
5
5
  "type": "module",
6
6
  "license": "MIT",
7
+ "repository": {
8
+ "type": "git",
9
+ "url": "git+https://github.com/cobraprojects/holo-js.git",
10
+ "directory": "packages/events"
11
+ },
7
12
  "exports": {
8
13
  ".": {
9
14
  "types": "./dist/index.d.ts",
@@ -14,7 +19,8 @@
14
19
  "main": "./dist/index.mjs",
15
20
  "types": "./dist/index.d.ts",
16
21
  "files": [
17
- "dist"
22
+ "dist",
23
+ "LICENSE"
18
24
  ],
19
25
  "scripts": {
20
26
  "build": "tsup",
@@ -23,10 +29,10 @@
23
29
  "test": "vitest --run"
24
30
  },
25
31
  "dependencies": {
26
- "@holo-js/db": "^0.3.28"
32
+ "@holo-js/db": "^0.4.1"
27
33
  },
28
34
  "peerDependencies": {
29
- "@holo-js/queue": "^0.3.28"
35
+ "@holo-js/queue": "^0.4.1"
30
36
  },
31
37
  "peerDependenciesMeta": {
32
38
  "@holo-js/queue": {
@@ -34,13 +40,13 @@
34
40
  }
35
41
  },
36
42
  "devDependencies": {
37
- "@holo-js/queue": "^0.3.28",
38
- "@types/node": "^22.10.2",
39
- "tsup": "^8.3.5",
40
- "typescript": "^5.7.2",
41
- "vitest": "^4.1.5"
43
+ "@holo-js/queue": "^0.4.1",
44
+ "@types/node": "^22.20.5",
45
+ "tsup": "^9.0.0",
46
+ "typescript": "^6.0.3",
47
+ "vitest": "^5.0.5"
42
48
  },
43
49
  "engines": {
44
- "node": "^22.12.0 || ^24.0.0 || ^26.0.0"
50
+ "node": "^22.18.0 || ^24.0.0 || ^26.0.0"
45
51
  }
46
52
  }