@zudojs/events 0.0.1 → 1.0.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 (118) hide show
  1. package/README.md +70 -11
  2. package/dist/eventBus/eventBus.core.d.ts +28 -2
  3. package/dist/eventBus/eventBus.core.js +104 -31
  4. package/dist/eventBus/eventBus.publish.d.ts +23 -5
  5. package/dist/eventBus/eventBus.publish.js +72 -32
  6. package/dist/eventBus/eventBus.registration.d.ts +24 -13
  7. package/dist/eventBus/eventBus.registration.js +31 -28
  8. package/dist/eventBus/eventBus.type.d.ts +75 -3
  9. package/dist/eventBus/eventBus.type.js +9 -0
  10. package/dist/eventBus/index.d.ts +2 -0
  11. package/dist/eventBus/index.js +2 -0
  12. package/dist/eventEmitter/eventEmitter.abort.d.ts +11 -2
  13. package/dist/eventEmitter/eventEmitter.abort.js +20 -5
  14. package/dist/eventEmitter/eventEmitter.core.d.ts +14 -3
  15. package/dist/eventEmitter/eventEmitter.core.js +63 -55
  16. package/dist/eventEmitter/eventEmitter.parallel.d.ts +7 -1
  17. package/dist/eventEmitter/eventEmitter.parallel.js +31 -13
  18. package/dist/eventEmitter/eventEmitter.sequential.d.ts +17 -1
  19. package/dist/eventEmitter/eventEmitter.sequential.js +19 -12
  20. package/dist/eventEmitter/eventEmitter.type.d.ts +60 -0
  21. package/dist/eventErrors/eventError.base.d.ts +50 -3
  22. package/dist/eventErrors/eventError.base.js +64 -3
  23. package/dist/eventHandler/eventHandler.core.d.ts +19 -0
  24. package/dist/eventHandler/eventHandler.core.js +67 -4
  25. package/dist/eventMiddleware/eventMiddleware.builder.js +5 -11
  26. package/dist/eventMiddleware/eventMiddleware.helper.js +0 -1
  27. package/dist/eventMiddleware/eventMiddleware.pipeline.d.ts +5 -0
  28. package/dist/eventMiddleware/eventMiddleware.pipeline.js +33 -9
  29. package/dist/eventRegistry/eventRegistry.lifecycle.d.ts +10 -5
  30. package/dist/eventRegistry/eventRegistry.lifecycle.js +23 -10
  31. package/dist/eventRegistry/eventRegistry.queries.d.ts +16 -7
  32. package/dist/eventRegistry/eventRegistry.queries.js +22 -13
  33. package/dist/eventRegistry/eventRegistry.registration.d.ts +14 -7
  34. package/dist/eventRegistry/eventRegistry.registration.js +120 -41
  35. package/dist/eventRegistry/eventRegistry.store.d.ts +12 -5
  36. package/dist/eventRegistry/eventRegistry.store.js +37 -12
  37. package/dist/eventRegistry/eventRegistry.type.d.ts +87 -2
  38. package/dist/eventRegistry/eventRegistry.type.js +5 -0
  39. package/dist/eventSubscription/eventSubscription.core.d.ts +5 -0
  40. package/dist/eventSubscription/eventSubscription.core.js +17 -3
  41. package/dist/eventTypes/eventDefinition.type.d.ts +11 -4
  42. package/dist/eventTypes/eventDefinition.type.js +49 -17
  43. package/dist/eventTypes/eventPayload.type.d.ts +5 -0
  44. package/dist/eventTypes/eventPayload.type.js +56 -15
  45. package/dist/eventTypes/eventType.type.d.ts +22 -5
  46. package/dist/eventTypes/eventType.type.js +33 -11
  47. package/dist/eventTypes/index.d.ts +2 -2
  48. package/dist/eventTypes/index.js +2 -2
  49. package/package.json +15 -7
  50. package/dist/.tsbuildinfo +0 -1
  51. package/dist/eventBus/eventBus.core.d.ts.map +0 -1
  52. package/dist/eventBus/eventBus.core.js.map +0 -1
  53. package/dist/eventBus/eventBus.factory.d.ts.map +0 -1
  54. package/dist/eventBus/eventBus.factory.js.map +0 -1
  55. package/dist/eventBus/eventBus.publish.d.ts.map +0 -1
  56. package/dist/eventBus/eventBus.publish.js.map +0 -1
  57. package/dist/eventBus/eventBus.registration.d.ts.map +0 -1
  58. package/dist/eventBus/eventBus.registration.js.map +0 -1
  59. package/dist/eventBus/eventBus.type.d.ts.map +0 -1
  60. package/dist/eventBus/eventBus.type.js.map +0 -1
  61. package/dist/eventBus/index.d.ts.map +0 -1
  62. package/dist/eventBus/index.js.map +0 -1
  63. package/dist/eventEmitter/eventEmitter.abort.d.ts.map +0 -1
  64. package/dist/eventEmitter/eventEmitter.abort.js.map +0 -1
  65. package/dist/eventEmitter/eventEmitter.core.d.ts.map +0 -1
  66. package/dist/eventEmitter/eventEmitter.core.js.map +0 -1
  67. package/dist/eventEmitter/eventEmitter.parallel.d.ts.map +0 -1
  68. package/dist/eventEmitter/eventEmitter.parallel.js.map +0 -1
  69. package/dist/eventEmitter/eventEmitter.sequential.d.ts.map +0 -1
  70. package/dist/eventEmitter/eventEmitter.sequential.js.map +0 -1
  71. package/dist/eventEmitter/eventEmitter.type.d.ts.map +0 -1
  72. package/dist/eventEmitter/eventEmitter.type.js.map +0 -1
  73. package/dist/eventEmitter/index.d.ts.map +0 -1
  74. package/dist/eventEmitter/index.js.map +0 -1
  75. package/dist/eventErrors/eventError.base.d.ts.map +0 -1
  76. package/dist/eventErrors/eventError.base.js.map +0 -1
  77. package/dist/eventErrors/index.d.ts.map +0 -1
  78. package/dist/eventErrors/index.js.map +0 -1
  79. package/dist/eventHandler/eventHandler.core.d.ts.map +0 -1
  80. package/dist/eventHandler/eventHandler.core.js.map +0 -1
  81. package/dist/eventHandler/index.d.ts.map +0 -1
  82. package/dist/eventHandler/index.js.map +0 -1
  83. package/dist/eventMiddleware/eventMiddleware.builder.d.ts.map +0 -1
  84. package/dist/eventMiddleware/eventMiddleware.builder.js.map +0 -1
  85. package/dist/eventMiddleware/eventMiddleware.helper.d.ts.map +0 -1
  86. package/dist/eventMiddleware/eventMiddleware.helper.js.map +0 -1
  87. package/dist/eventMiddleware/eventMiddleware.pipeline.d.ts.map +0 -1
  88. package/dist/eventMiddleware/eventMiddleware.pipeline.js.map +0 -1
  89. package/dist/eventMiddleware/eventMiddleware.type.d.ts.map +0 -1
  90. package/dist/eventMiddleware/eventMiddleware.type.js.map +0 -1
  91. package/dist/eventMiddleware/index.d.ts.map +0 -1
  92. package/dist/eventMiddleware/index.js.map +0 -1
  93. package/dist/eventRegistry/eventRegistry.lifecycle.d.ts.map +0 -1
  94. package/dist/eventRegistry/eventRegistry.lifecycle.js.map +0 -1
  95. package/dist/eventRegistry/eventRegistry.queries.d.ts.map +0 -1
  96. package/dist/eventRegistry/eventRegistry.queries.js.map +0 -1
  97. package/dist/eventRegistry/eventRegistry.registration.d.ts.map +0 -1
  98. package/dist/eventRegistry/eventRegistry.registration.js.map +0 -1
  99. package/dist/eventRegistry/eventRegistry.store.d.ts.map +0 -1
  100. package/dist/eventRegistry/eventRegistry.store.js.map +0 -1
  101. package/dist/eventRegistry/eventRegistry.type.d.ts.map +0 -1
  102. package/dist/eventRegistry/eventRegistry.type.js.map +0 -1
  103. package/dist/eventRegistry/index.d.ts.map +0 -1
  104. package/dist/eventRegistry/index.js.map +0 -1
  105. package/dist/eventSubscription/eventSubscription.core.d.ts.map +0 -1
  106. package/dist/eventSubscription/eventSubscription.core.js.map +0 -1
  107. package/dist/eventSubscription/index.d.ts.map +0 -1
  108. package/dist/eventSubscription/index.js.map +0 -1
  109. package/dist/eventTypes/eventDefinition.type.d.ts.map +0 -1
  110. package/dist/eventTypes/eventDefinition.type.js.map +0 -1
  111. package/dist/eventTypes/eventPayload.type.d.ts.map +0 -1
  112. package/dist/eventTypes/eventPayload.type.js.map +0 -1
  113. package/dist/eventTypes/eventType.type.d.ts.map +0 -1
  114. package/dist/eventTypes/eventType.type.js.map +0 -1
  115. package/dist/eventTypes/index.d.ts.map +0 -1
  116. package/dist/eventTypes/index.js.map +0 -1
  117. package/dist/index.d.ts.map +0 -1
  118. package/dist/index.js.map +0 -1
@@ -146,17 +146,31 @@ export class EventSubscriptionGroup {
146
146
  }
147
147
  /**
148
148
  * Unsubscribes every subscription in the group.
149
+ *
150
+ * Every subscription is attempted even if one throws; the
151
+ * failures are then re-thrown together as an AggregateError and
152
+ * the failed subscriptions stay in the group so the call can be
153
+ * retried.
149
154
  */
150
155
  unsubscribe() {
151
156
  if (!this.active) {
152
157
  return;
153
158
  }
154
- this.currentState = EventSubscriptionState.CANCELLED;
155
159
  const subscriptions = [...this.subscriptions];
156
- this.subscriptions.clear();
160
+ const failures = [];
157
161
  for (const subscription of subscriptions) {
158
- subscription.unsubscribe();
162
+ try {
163
+ subscription.unsubscribe();
164
+ this.subscriptions.delete(subscription);
165
+ }
166
+ catch (error) {
167
+ failures.push(error);
168
+ }
159
169
  }
170
+ if (failures.length > 0) {
171
+ throw new AggregateError(failures, `${failures.length} of ${subscriptions.length} subscriptions failed to unsubscribe.`);
172
+ }
173
+ this.currentState = EventSubscriptionState.CANCELLED;
160
174
  }
161
175
  /**
162
176
  * Returns all subscriptions in the group.
@@ -95,9 +95,10 @@ export interface Event<TPayload = EventPayload> {
95
95
  */
96
96
  export interface EventInput<TPayload = EventPayload> {
97
97
  /**
98
- * Optional event identifier.
98
+ * Optional event identifier. Plain strings are accepted and
99
+ * branded on the created event.
99
100
  */
100
- readonly id?: EventId;
101
+ readonly id?: EventId | string;
101
102
  /**
102
103
  * Event type.
103
104
  */
@@ -115,9 +116,9 @@ export interface EventInput<TPayload = EventPayload> {
115
116
  */
116
117
  readonly source?: EventSource;
117
118
  /**
118
- * Optional correlation identifier.
119
+ * Optional correlation identifier. Plain strings are accepted.
119
120
  */
120
- readonly correlationId?: EventCorrelationId;
121
+ readonly correlationId?: EventCorrelationId | string;
121
122
  /**
122
123
  * Optional causation identifier.
123
124
  */
@@ -148,6 +149,12 @@ export declare function isEvent(value: unknown): value is Event;
148
149
  export declare function createEventId(): EventId;
149
150
  /**
150
151
  * Creates an immutable event.
152
+ *
153
+ * The event type is normalized (trimmed, lower-cased, separators
154
+ * collapsed) and validated; invalid types, ids and timestamps
155
+ * throw InvalidEventError. The top-level event object is frozen;
156
+ * the payload is left as supplied (use deepFreeze / the emitter's
157
+ * freezeEvents option for deep immutability).
151
158
  */
152
159
  export declare function createEvent<TPayload = EventPayload>(input: EventInput<TPayload>): Event<TPayload>;
153
160
  /**
@@ -6,6 +6,8 @@
6
6
  *
7
7
  * This module intentionally contains no dispatching logic.
8
8
  */
9
+ import { InvalidEventError } from "../eventErrors/eventError.base.js";
10
+ import { normalizeEventType } from "./eventType.type.js";
9
11
  /**
10
12
  * Determines whether an unknown value satisfies the Event
11
13
  * contract.
@@ -32,17 +34,39 @@ export function createEventId() {
32
34
  * Normalizes an event timestamp.
33
35
  */
34
36
  function normalizeTimestamp(timestamp) {
35
- if (timestamp instanceof Date) {
36
- return new Date(timestamp.getTime());
37
+ if (timestamp === undefined) {
38
+ return new Date();
37
39
  }
38
- if (typeof timestamp === "number") {
39
- const date = new Date(timestamp);
40
- if (Number.isNaN(date.getTime())) {
41
- throw new TypeError("Invalid event timestamp.");
42
- }
43
- return date;
40
+ const date = timestamp instanceof Date
41
+ ? new Date(timestamp.getTime())
42
+ : typeof timestamp === "number"
43
+ ? new Date(timestamp)
44
+ : undefined;
45
+ if (date === undefined || Number.isNaN(date.getTime())) {
46
+ throw new InvalidEventError("Invalid event timestamp.");
47
+ }
48
+ return date;
49
+ }
50
+ /**
51
+ * Normalizes and validates an event type, converting validation
52
+ * failures into InvalidEventError.
53
+ */
54
+ function normalizeInputType(type, eventId) {
55
+ if (typeof type !== "string" || type.trim().length === 0) {
56
+ throw new InvalidEventError("Event type must be a non-empty string.", {
57
+ eventId,
58
+ });
59
+ }
60
+ try {
61
+ return normalizeEventType(type);
62
+ }
63
+ catch (error) {
64
+ throw new InvalidEventError(`Invalid event type "${type}".`, {
65
+ eventType: type,
66
+ eventId,
67
+ cause: error,
68
+ });
44
69
  }
45
- return new Date();
46
70
  }
47
71
  /**
48
72
  * Freezes event metadata.
@@ -57,14 +81,24 @@ function normalizeMetadata(metadata) {
57
81
  }
58
82
  /**
59
83
  * Creates an immutable event.
84
+ *
85
+ * The event type is normalized (trimmed, lower-cased, separators
86
+ * collapsed) and validated; invalid types, ids and timestamps
87
+ * throw InvalidEventError. The top-level event object is frozen;
88
+ * the payload is left as supplied (use deepFreeze / the emitter's
89
+ * freezeEvents option for deep immutability).
60
90
  */
61
91
  export function createEvent(input) {
62
- if (typeof input.type !== "string" || input.type.trim().length === 0) {
63
- throw new TypeError("Event type must be a non-empty string.");
92
+ if (typeof input !== "object" || input === null) {
93
+ throw new InvalidEventError("Event input must be an object.");
94
+ }
95
+ if (input.id !== undefined && typeof input.id !== "string") {
96
+ throw new InvalidEventError("Event id must be a string.");
64
97
  }
98
+ const type = normalizeInputType(input.type, input.id);
65
99
  const event = {
66
100
  id: input.id ?? createEventId(),
67
- type: input.type,
101
+ type,
68
102
  payload: input.payload,
69
103
  timestamp: normalizeTimestamp(input.timestamp),
70
104
  source: input.source,
@@ -78,15 +112,13 @@ export function createEvent(input) {
78
112
  * Creates a typed event definition.
79
113
  */
80
114
  export function defineEvent(type) {
81
- if (type.trim().length === 0) {
82
- throw new TypeError("Event type must be a non-empty string.");
83
- }
115
+ const normalized = normalizeInputType(type);
84
116
  return Object.freeze({
85
- type,
117
+ type: normalized,
86
118
  create(payload, options = {}) {
87
119
  return createEvent({
88
120
  ...options,
89
- type,
121
+ type: normalized,
90
122
  payload,
91
123
  });
92
124
  },
@@ -93,6 +93,11 @@ export declare function createJsonEventPayload<const TPayload extends JsonEventP
93
93
  export declare function cloneEventPayload<TPayload extends EventPayload>(payload: TPayload): TPayload;
94
94
  /**
95
95
  * Deeply freezes an event payload.
96
+ *
97
+ * Cyclic and shared references are handled (each object is
98
+ * visited once), and an already frozen parent is still traversed
99
+ * so that unfrozen children are frozen too. ArrayBuffer views are
100
+ * left untouched because they cannot be frozen.
96
101
  */
97
102
  export declare function deepFreeze<T>(value: T): T;
98
103
  /**
@@ -24,6 +24,17 @@ export function isObjectEventPayload(value) {
24
24
  * event payload.
25
25
  */
26
26
  export function isJsonEventPayload(value) {
27
+ return isJsonValue(value, new WeakSet(), 0);
28
+ }
29
+ /**
30
+ * Maximum nesting depth accepted by isJsonEventPayload.
31
+ */
32
+ const MAX_JSON_PAYLOAD_DEPTH = 256;
33
+ function isPlainObject(value) {
34
+ const prototype = Object.getPrototypeOf(value);
35
+ return prototype === Object.prototype || prototype === null;
36
+ }
37
+ function isJsonValue(value, visited, depth) {
27
38
  if (value === null ||
28
39
  typeof value === "string" ||
29
40
  typeof value === "boolean") {
@@ -32,13 +43,31 @@ export function isJsonEventPayload(value) {
32
43
  if (typeof value === "number") {
33
44
  return Number.isFinite(value);
34
45
  }
35
- if (Array.isArray(value)) {
36
- return value.every((item) => isJsonEventPayload(item));
46
+ if (typeof value !== "object") {
47
+ return false;
48
+ }
49
+ if (depth > MAX_JSON_PAYLOAD_DEPTH) {
50
+ return false;
51
+ }
52
+ if (visited.has(value)) {
53
+ // A cycle can never be serialized to JSON.
54
+ return false;
37
55
  }
38
- if (typeof value === "object") {
39
- return Object.values(value).every((item) => isJsonEventPayload(item));
56
+ visited.add(value);
57
+ try {
58
+ if (Array.isArray(value)) {
59
+ return value.every((item) => isJsonValue(item, visited, depth + 1));
60
+ }
61
+ if (!isPlainObject(value)) {
62
+ // Date, Map, Set, class instances, typed arrays, ... are not
63
+ // JSON values even though JSON.stringify may accept them.
64
+ return false;
65
+ }
66
+ return Object.values(value).every((item) => isJsonValue(item, visited, depth + 1));
67
+ }
68
+ finally {
69
+ visited.delete(value);
40
70
  }
41
- return false;
42
71
  }
43
72
  /**
44
73
  * Creates a payload object from an input object.
@@ -87,24 +116,36 @@ export function cloneEventPayload(payload) {
87
116
  }
88
117
  /**
89
118
  * Deeply freezes an event payload.
119
+ *
120
+ * Cyclic and shared references are handled (each object is
121
+ * visited once), and an already frozen parent is still traversed
122
+ * so that unfrozen children are frozen too. ArrayBuffer views are
123
+ * left untouched because they cannot be frozen.
90
124
  */
91
125
  export function deepFreeze(value) {
92
- if (value === null || typeof value !== "object") {
93
- return value;
126
+ freezeRecursively(value, new WeakSet());
127
+ return value;
128
+ }
129
+ function freezeRecursively(value, visited) {
130
+ if ((typeof value !== "object" && typeof value !== "function") ||
131
+ value === null) {
132
+ return;
133
+ }
134
+ if (visited.has(value)) {
135
+ return;
94
136
  }
95
- if (Object.isFrozen(value)) {
96
- return value;
137
+ visited.add(value);
138
+ if (ArrayBuffer.isView(value)) {
139
+ return;
97
140
  }
98
141
  const object = value;
99
142
  for (const key of Reflect.ownKeys(object)) {
100
- const child = object[key];
101
- if (child !== null &&
102
- typeof child === "object" &&
103
- !Object.isFrozen(child)) {
104
- deepFreeze(child);
143
+ const descriptor = Object.getOwnPropertyDescriptor(object, key);
144
+ if (descriptor && "value" in descriptor) {
145
+ freezeRecursively(descriptor.value, visited);
105
146
  }
106
147
  }
107
- return Object.freeze(value);
148
+ Object.freeze(value);
108
149
  }
109
150
  /**
110
151
  * Removes undefined properties from an object payload.
@@ -6,6 +6,8 @@
6
6
  * and strongly typed event-type helpers.
7
7
  */
8
8
  import type { Event, EventType } from "../eventTypes/eventDefinition.type.js";
9
+ import type { EventPayloadMap } from "./eventPayload.type.js";
10
+ export type { EventPayloadMap } from "./eventPayload.type.js";
9
11
  /**
10
12
  * A collection of event types.
11
13
  */
@@ -20,10 +22,6 @@ export type EventTypeList = readonly EventType[];
20
22
  * "*"
21
23
  */
22
24
  export type EventTypePattern = EventType | `${string}.*` | "*";
23
- /**
24
- * Type-safe mapping between event types and payloads.
25
- */
26
- export type EventPayloadMap = Record<EventType, unknown>;
27
25
  /**
28
26
  * Extracts the event type keys from a payload map.
29
27
  */
@@ -39,7 +37,9 @@ export type EventUnion<TMap extends EventPayloadMap> = {
39
37
  /**
40
38
  * Validates an event type string.
41
39
  *
42
- * Zudojs event types use dot-separated lowercase names.
40
+ * Zudojs event types use dot-separated lowercase names. Wildcards
41
+ * are not part of an event type; they are only valid in patterns
42
+ * (see isValidEventTypePattern).
43
43
  *
44
44
  * Examples:
45
45
  *
@@ -51,6 +51,10 @@ export type EventUnion<TMap extends EventPayloadMap> = {
51
51
  export declare function isValidEventType(value: unknown): value is EventType;
52
52
  /**
53
53
  * Validates an event type pattern.
54
+ *
55
+ * Supported forms are an exact event type, a namespace wildcard
56
+ * ("user.*") and the catch-all "*". Wildcards in any other
57
+ * position ("user.*.created", "user.cre*") are rejected.
54
58
  */
55
59
  export declare function isValidEventTypePattern(value: unknown): value is EventTypePattern;
56
60
  /**
@@ -92,6 +96,13 @@ export declare function getEventAction(type: EventType): string;
92
96
  * Returns all segments in an event type.
93
97
  */
94
98
  export declare function getEventTypeSegments(type: EventType): readonly string[];
99
+ /**
100
+ * Normalizes an event type pattern.
101
+ *
102
+ * "*" is returned unchanged, "User.*" becomes "user.*" and any
103
+ * other value is normalized as an event type.
104
+ */
105
+ export declare function normalizeEventTypePattern(pattern: string): EventTypePattern;
95
106
  /**
96
107
  * Checks whether an event type matches a pattern.
97
108
  *
@@ -99,8 +110,14 @@ export declare function getEventTypeSegments(type: EventType): readonly string[]
99
110
  *
100
111
  * "user.created" matches "user.created"
101
112
  * "user.created" matches "user.*"
113
+ * "user" matches "user.*" (a namespace pattern also
114
+ * matches the bare namespace event)
102
115
  * "user.created" matches "*"
103
116
  * "order.created" does not match "user.*"
117
+ *
118
+ * Both arguments are expected to be normalized (see
119
+ * normalizeEventType / normalizeEventTypePattern); no
120
+ * normalization happens here.
104
121
  */
105
122
  export declare function matchesEventType(type: EventType, pattern: EventTypePattern): boolean;
106
123
  /**
@@ -8,7 +8,9 @@
8
8
  /**
9
9
  * Validates an event type string.
10
10
  *
11
- * Zudojs event types use dot-separated lowercase names.
11
+ * Zudojs event types use dot-separated lowercase names. Wildcards
12
+ * are not part of an event type; they are only valid in patterns
13
+ * (see isValidEventTypePattern).
12
14
  *
13
15
  * Examples:
14
16
  *
@@ -28,10 +30,14 @@ export function isValidEventType(value) {
28
30
  if (normalized.length > 255) {
29
31
  return false;
30
32
  }
31
- return /^[a-z0-9_]+(?:[.-][a-z0-9_*]+)*$/.test(normalized);
33
+ return /^[a-z0-9_]+(?:[.-][a-z0-9_]+)*$/.test(normalized);
32
34
  }
33
35
  /**
34
36
  * Validates an event type pattern.
37
+ *
38
+ * Supported forms are an exact event type, a namespace wildcard
39
+ * ("user.*") and the catch-all "*". Wildcards in any other
40
+ * position ("user.*.created", "user.cre*") are rejected.
35
41
  */
36
42
  export function isValidEventTypePattern(value) {
37
43
  if (value === "*") {
@@ -56,26 +62,21 @@ export function isValidEventTypePattern(value) {
56
62
  * 4. Removes leading/trailing separators
57
63
  */
58
64
  export function normalizeEventType(type) {
65
+ if (typeof type !== "string") {
66
+ throw new TypeError(`Invalid event type: "${String(type)}".`);
67
+ }
59
68
  const lower = type.trim().toLowerCase();
60
69
  let result = "";
61
70
  let lastWasDot = false;
62
- let leadingDots = true;
63
71
  for (let i = 0; i < lower.length; i++) {
64
72
  const ch = lower[i];
65
- if (ch === "/" || ch === "\\" || ch === ":") {
66
- if (!lastWasDot) {
67
- result += ".";
68
- lastWasDot = true;
69
- }
70
- }
71
- else if (ch === ".") {
73
+ if (ch === "/" || ch === "\\" || ch === ":" || ch === ".") {
72
74
  if (!lastWasDot) {
73
75
  result += ".";
74
76
  lastWasDot = true;
75
77
  }
76
78
  }
77
79
  else {
78
- leadingDots = false;
79
80
  result += ch;
80
81
  lastWasDot = false;
81
82
  }
@@ -139,6 +140,21 @@ export function getEventAction(type) {
139
140
  export function getEventTypeSegments(type) {
140
141
  return type.split(".");
141
142
  }
143
+ /**
144
+ * Normalizes an event type pattern.
145
+ *
146
+ * "*" is returned unchanged, "User.*" becomes "user.*" and any
147
+ * other value is normalized as an event type.
148
+ */
149
+ export function normalizeEventTypePattern(pattern) {
150
+ if (pattern === "*") {
151
+ return "*";
152
+ }
153
+ if (typeof pattern === "string" && pattern.trim().endsWith(".*")) {
154
+ return `${normalizeEventType(pattern.trim().slice(0, -2))}.*`;
155
+ }
156
+ return normalizeEventType(pattern);
157
+ }
142
158
  /**
143
159
  * Checks whether an event type matches a pattern.
144
160
  *
@@ -146,8 +162,14 @@ export function getEventTypeSegments(type) {
146
162
  *
147
163
  * "user.created" matches "user.created"
148
164
  * "user.created" matches "user.*"
165
+ * "user" matches "user.*" (a namespace pattern also
166
+ * matches the bare namespace event)
149
167
  * "user.created" matches "*"
150
168
  * "order.created" does not match "user.*"
169
+ *
170
+ * Both arguments are expected to be normalized (see
171
+ * normalizeEventType / normalizeEventTypePattern); no
172
+ * normalization happens here.
151
173
  */
152
174
  export function matchesEventType(type, pattern) {
153
175
  if (pattern === "*") {
@@ -4,6 +4,6 @@
4
4
  * Event type definitions and payload types.
5
5
  */
6
6
  export { type EventId, type EventType, type EventTimestamp, type EventSource, type EventCorrelationId, type EventCausationId, type EventPayload, type Event, type EventInput, type EventDefinition, isEvent, createEventId, createEvent, defineEvent, withEventMetadata, createDerivedEvent, getEventType, getEventPayload, describeEvent, } from "./eventDefinition.type.js";
7
- export { type ObjectEventPayload, type PrimitiveEventPayload, type JsonEventPayload, type PayloadOf, type PayloadMap, type EventPayloadFactory, type EventPayloadOptions, isPrimitiveEventPayload, isObjectEventPayload, isJsonEventPayload, createEventPayload, validateEventPayload, } from "./eventPayload.type.js";
8
- export { type EventTypeList, type EventTypePattern, type EventTypeOf, type EventUnion, isValidEventType, isValidEventTypePattern, normalizeEventType, createEventType, getEventNamespace, } from "./eventType.type.js";
7
+ export { type ObjectEventPayload, type PrimitiveEventPayload, type JsonEventPayload, type EventPayloadMap, type PayloadOf, type PayloadMap, type EventPayloadFactory, type EventPayloadOptions, isPrimitiveEventPayload, isObjectEventPayload, isJsonEventPayload, createEventPayload, createObjectEventPayload, createJsonEventPayload, cloneEventPayload, deepFreeze, stripUndefinedValues, mergeEventPayloads, staticPayload, definePayloadFactory, validateEventPayload, describeEventPayload, } from "./eventPayload.type.js";
8
+ export { type EventTypeList, type EventTypePattern, type EventTypeOf, type EventUnion, isValidEventType, isValidEventTypePattern, normalizeEventType, normalizeEventTypePattern, tryNormalizeEventType, assertEventType, createEventType, createEventTypePattern, getEventNamespace, getEventAction, getEventTypeSegments, matchesEventType, isSameEventNamespace, isChildEventType, eventMatchesType, filterEventsByType, defineEventTypes, defineEventType, } from "./eventType.type.js";
9
9
  //# sourceMappingURL=index.d.ts.map
@@ -4,6 +4,6 @@
4
4
  * Event type definitions and payload types.
5
5
  */
6
6
  export { isEvent, createEventId, createEvent, defineEvent, withEventMetadata, createDerivedEvent, getEventType, getEventPayload, describeEvent, } from "./eventDefinition.type.js";
7
- export { isPrimitiveEventPayload, isObjectEventPayload, isJsonEventPayload, createEventPayload, validateEventPayload, } from "./eventPayload.type.js";
8
- export { isValidEventType, isValidEventTypePattern, normalizeEventType, createEventType, getEventNamespace, } from "./eventType.type.js";
7
+ export { isPrimitiveEventPayload, isObjectEventPayload, isJsonEventPayload, createEventPayload, createObjectEventPayload, createJsonEventPayload, cloneEventPayload, deepFreeze, stripUndefinedValues, mergeEventPayloads, staticPayload, definePayloadFactory, validateEventPayload, describeEventPayload, } from "./eventPayload.type.js";
8
+ export { isValidEventType, isValidEventTypePattern, normalizeEventType, normalizeEventTypePattern, tryNormalizeEventType, assertEventType, createEventType, createEventTypePattern, getEventNamespace, getEventAction, getEventTypeSegments, matchesEventType, isSameEventNamespace, isChildEventType, eventMatchesType, filterEventsByType, defineEventTypes, defineEventType, } from "./eventType.type.js";
9
9
  //# sourceMappingURL=index.js.map
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@zudojs/events",
3
- "version": "0.0.1",
3
+ "version": "1.0.0",
4
4
  "description": "Event-driven architecture with event bus, emitter, middleware, and registry for decoupled communication.",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",
@@ -13,18 +13,22 @@
13
13
  }
14
14
  },
15
15
  "files": [
16
- "dist"
16
+ "dist",
17
+ "!dist/**/*.map",
18
+ "!dist/**/*.tsbuildinfo",
19
+ "!dist/.tsbuildinfo"
17
20
  ],
21
+ "sideEffects": false,
18
22
  "dependencies": {
19
- "@zudojs/errors": "0.0.1",
20
- "@zudojs/constants": "0.0.1"
23
+ "@zudojs/errors": "1.0.0",
24
+ "@zudojs/constants": "1.0.0"
21
25
  },
22
26
  "engines": {
23
27
  "node": ">=24.0.0"
24
28
  },
25
29
  "devDependencies": {
26
30
  "vitest": "^4.1.11",
27
- "typescript": "^7.0.2"
31
+ "typescript": "7.0.2"
28
32
  },
29
33
  "license": "MIT",
30
34
  "publishConfig": {
@@ -37,13 +41,17 @@
37
41
  "pubsub"
38
42
  ],
39
43
  "homepage": "https://github.com/oyinlola-tech/zudo#readme",
44
+ "bugs": {
45
+ "url": "https://github.com/oyinlola-tech/zudo/issues"
46
+ },
40
47
  "repository": {
41
48
  "type": "git",
42
- "url": "https://github.com/oyinlola-tech/zudo"
49
+ "url": "https://github.com/oyinlola-tech/zudo",
50
+ "directory": "packages/events"
43
51
  },
44
52
  "scripts": {
45
53
  "build": "tsc -p tsconfig.json",
46
- "typecheck": "tsc -p tsconfig.json --noEmit",
54
+ "typecheck": "tsc -p tsconfig.json --noEmit && tsc -p tsconfig.test.json --noEmit",
47
55
  "test": "vitest run",
48
56
  "test:watch": "vitest",
49
57
  "clean": "rm -rf dist"