@fjell/core 4.4.49 → 4.4.50
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/dist/event/emitter.d.ts +140 -0
- package/dist/event/events.d.ts +81 -0
- package/dist/event/index.d.ts +38 -0
- package/dist/event/matching.d.ts +54 -0
- package/dist/event/subscription.d.ts +74 -0
- package/dist/event/types.d.ts +186 -0
- package/dist/index.d.ts +2 -0
- package/dist/index.js +272 -0
- package/package.json +1 -1
- package/src/event/emitter.ts +247 -0
- package/src/event/events.ts +178 -0
- package/src/event/index.ts +130 -0
- package/src/event/matching.ts +264 -0
- package/src/event/subscription.ts +181 -0
- package/src/event/types.ts +282 -0
- package/src/index.ts +4 -0
|
@@ -0,0 +1,178 @@
|
|
|
1
|
+
import { ComKey, PriKey } from '../keys';
|
|
2
|
+
import { Item } from '../items';
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* Base event interface that all events extend.
|
|
6
|
+
* Provides core event properties with full type safety using the existing PriKey/ComKey system.
|
|
7
|
+
*/
|
|
8
|
+
export interface BaseEvent<
|
|
9
|
+
S extends string,
|
|
10
|
+
L1 extends string = never,
|
|
11
|
+
L2 extends string = never,
|
|
12
|
+
L3 extends string = never,
|
|
13
|
+
L4 extends string = never,
|
|
14
|
+
L5 extends string = never
|
|
15
|
+
> {
|
|
16
|
+
/** Type of event - "create", "update", "delete", etc. */
|
|
17
|
+
eventType: string;
|
|
18
|
+
|
|
19
|
+
/** The key of the item that was affected - maintains full type safety */
|
|
20
|
+
key: PriKey<S> | ComKey<S, L1, L2, L3, L4, L5>;
|
|
21
|
+
|
|
22
|
+
/** Which storage backend(s) generated this event - enables filtering by implementation */
|
|
23
|
+
scopes: string[];
|
|
24
|
+
|
|
25
|
+
/** When the event occurred */
|
|
26
|
+
timestamp: Date;
|
|
27
|
+
|
|
28
|
+
/** Optional: the full item content - fully typed, no loss of type information */
|
|
29
|
+
item?: Item<S, L1, L2, L3, L4, L5>;
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
/**
|
|
33
|
+
* Event emitted when an item is created.
|
|
34
|
+
* The item property is required since we always have the created item data.
|
|
35
|
+
*/
|
|
36
|
+
export interface CreateEvent<
|
|
37
|
+
S extends string,
|
|
38
|
+
L1 extends string = never,
|
|
39
|
+
L2 extends string = never,
|
|
40
|
+
L3 extends string = never,
|
|
41
|
+
L4 extends string = never,
|
|
42
|
+
L5 extends string = never
|
|
43
|
+
> extends BaseEvent<S, L1, L2, L3, L4, L5> {
|
|
44
|
+
eventType: 'create';
|
|
45
|
+
/** The created item - always available for create events */
|
|
46
|
+
item: Item<S, L1, L2, L3, L4, L5>;
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
/**
|
|
50
|
+
* Event emitted when an item is updated.
|
|
51
|
+
* Provides detailed change tracking with before/after states.
|
|
52
|
+
*/
|
|
53
|
+
export interface UpdateEvent<
|
|
54
|
+
S extends string,
|
|
55
|
+
L1 extends string = never,
|
|
56
|
+
L2 extends string = never,
|
|
57
|
+
L3 extends string = never,
|
|
58
|
+
L4 extends string = never,
|
|
59
|
+
L5 extends string = never
|
|
60
|
+
> extends BaseEvent<S, L1, L2, L3, L4, L5> {
|
|
61
|
+
eventType: 'update';
|
|
62
|
+
/** List of field names that were changed */
|
|
63
|
+
changes: string[];
|
|
64
|
+
/** Optional: item state before the update */
|
|
65
|
+
before?: Item<S, L1, L2, L3, L4, L5>;
|
|
66
|
+
/** Optional: item state after the update */
|
|
67
|
+
after?: Item<S, L1, L2, L3, L4, L5>;
|
|
68
|
+
// Note: item property (from BaseEvent) contains current state if provided
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
/**
|
|
72
|
+
* Event emitted when an item is deleted.
|
|
73
|
+
* May include the deleted item data for cleanup/undo operations.
|
|
74
|
+
*/
|
|
75
|
+
export interface DeleteEvent<
|
|
76
|
+
S extends string,
|
|
77
|
+
L1 extends string = never,
|
|
78
|
+
L2 extends string = never,
|
|
79
|
+
L3 extends string = never,
|
|
80
|
+
L4 extends string = never,
|
|
81
|
+
L5 extends string = never
|
|
82
|
+
> extends BaseEvent<S, L1, L2, L3, L4, L5> {
|
|
83
|
+
eventType: 'delete';
|
|
84
|
+
/** Optional: the deleted item content - useful for cleanup/undo */
|
|
85
|
+
item?: Item<S, L1, L2, L3, L4, L5>;
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
/**
|
|
89
|
+
* Event emitted when a custom action is performed on an item.
|
|
90
|
+
* Allows libraries to define custom event types beyond standard CRUD.
|
|
91
|
+
*/
|
|
92
|
+
export interface ActionEvent<
|
|
93
|
+
S extends string,
|
|
94
|
+
L1 extends string = never,
|
|
95
|
+
L2 extends string = never,
|
|
96
|
+
L3 extends string = never,
|
|
97
|
+
L4 extends string = never,
|
|
98
|
+
L5 extends string = never
|
|
99
|
+
> extends BaseEvent<S, L1, L2, L3, L4, L5> {
|
|
100
|
+
eventType: 'action';
|
|
101
|
+
/** Name of the action that was performed */
|
|
102
|
+
actionName: string;
|
|
103
|
+
/** Optional: action-specific data */
|
|
104
|
+
actionData?: Record<string, unknown>;
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
/**
|
|
108
|
+
* Union type of all standard event types.
|
|
109
|
+
* Libraries can extend this with custom events if needed.
|
|
110
|
+
*/
|
|
111
|
+
export type Event<
|
|
112
|
+
S extends string,
|
|
113
|
+
L1 extends string = never,
|
|
114
|
+
L2 extends string = never,
|
|
115
|
+
L3 extends string = never,
|
|
116
|
+
L4 extends string = never,
|
|
117
|
+
L5 extends string = never
|
|
118
|
+
> =
|
|
119
|
+
| CreateEvent<S, L1, L2, L3, L4, L5>
|
|
120
|
+
| UpdateEvent<S, L1, L2, L3, L4, L5>
|
|
121
|
+
| DeleteEvent<S, L1, L2, L3, L4, L5>
|
|
122
|
+
| ActionEvent<S, L1, L2, L3, L4, L5>;
|
|
123
|
+
|
|
124
|
+
/**
|
|
125
|
+
* Type guard to check if an event is a CreateEvent
|
|
126
|
+
*/
|
|
127
|
+
export function isCreateEvent<
|
|
128
|
+
S extends string,
|
|
129
|
+
L1 extends string = never,
|
|
130
|
+
L2 extends string = never,
|
|
131
|
+
L3 extends string = never,
|
|
132
|
+
L4 extends string = never,
|
|
133
|
+
L5 extends string = never
|
|
134
|
+
>(event: BaseEvent<S, L1, L2, L3, L4, L5>): event is CreateEvent<S, L1, L2, L3, L4, L5> {
|
|
135
|
+
return event.eventType === 'create';
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
/**
|
|
139
|
+
* Type guard to check if an event is an UpdateEvent
|
|
140
|
+
*/
|
|
141
|
+
export function isUpdateEvent<
|
|
142
|
+
S extends string,
|
|
143
|
+
L1 extends string = never,
|
|
144
|
+
L2 extends string = never,
|
|
145
|
+
L3 extends string = never,
|
|
146
|
+
L4 extends string = never,
|
|
147
|
+
L5 extends string = never
|
|
148
|
+
>(event: BaseEvent<S, L1, L2, L3, L4, L5>): event is UpdateEvent<S, L1, L2, L3, L4, L5> {
|
|
149
|
+
return event.eventType === 'update';
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
/**
|
|
153
|
+
* Type guard to check if an event is a DeleteEvent
|
|
154
|
+
*/
|
|
155
|
+
export function isDeleteEvent<
|
|
156
|
+
S extends string,
|
|
157
|
+
L1 extends string = never,
|
|
158
|
+
L2 extends string = never,
|
|
159
|
+
L3 extends string = never,
|
|
160
|
+
L4 extends string = never,
|
|
161
|
+
L5 extends string = never
|
|
162
|
+
>(event: BaseEvent<S, L1, L2, L3, L4, L5>): event is DeleteEvent<S, L1, L2, L3, L4, L5> {
|
|
163
|
+
return event.eventType === 'delete';
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
/**
|
|
167
|
+
* Type guard to check if an event is an ActionEvent
|
|
168
|
+
*/
|
|
169
|
+
export function isActionEvent<
|
|
170
|
+
S extends string,
|
|
171
|
+
L1 extends string = never,
|
|
172
|
+
L2 extends string = never,
|
|
173
|
+
L3 extends string = never,
|
|
174
|
+
L4 extends string = never,
|
|
175
|
+
L5 extends string = never
|
|
176
|
+
>(event: BaseEvent<S, L1, L2, L3, L4, L5>): event is ActionEvent<S, L1, L2, L3, L4, L5> {
|
|
177
|
+
return event.eventType === 'action';
|
|
178
|
+
}
|
|
@@ -0,0 +1,130 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @fileoverview Event System Public API
|
|
3
|
+
*
|
|
4
|
+
* This module exports all the public interfaces and utilities for the Fjell event system.
|
|
5
|
+
* The event system provides type-safe, item-level change events with full PriKey/ComKey integration.
|
|
6
|
+
*
|
|
7
|
+
* Key Features:
|
|
8
|
+
* - Full type safety using existing PriKey/ComKey system
|
|
9
|
+
* - Storage-agnostic event interfaces
|
|
10
|
+
* - Item-specific and location-based subscriptions
|
|
11
|
+
* - Separate EventEmitters per item type for optimal type safety
|
|
12
|
+
* - Real-time awareness for application needs (not reliable business execution)
|
|
13
|
+
*
|
|
14
|
+
* Usage:
|
|
15
|
+
* - Libraries implement EventEmitter/EventSubscriber interfaces
|
|
16
|
+
* - Applications subscribe to events through library instances
|
|
17
|
+
* - Events are delivered through callback functions with full type safety
|
|
18
|
+
*/
|
|
19
|
+
|
|
20
|
+
// Core Event Types
|
|
21
|
+
export {
|
|
22
|
+
BaseEvent,
|
|
23
|
+
CreateEvent,
|
|
24
|
+
UpdateEvent,
|
|
25
|
+
DeleteEvent,
|
|
26
|
+
ActionEvent,
|
|
27
|
+
Event,
|
|
28
|
+
isCreateEvent,
|
|
29
|
+
isUpdateEvent,
|
|
30
|
+
isDeleteEvent,
|
|
31
|
+
isActionEvent,
|
|
32
|
+
} from './events';
|
|
33
|
+
|
|
34
|
+
// Subscription Interfaces
|
|
35
|
+
export {
|
|
36
|
+
BaseSubscription,
|
|
37
|
+
ItemSubscription,
|
|
38
|
+
LocationSubscription,
|
|
39
|
+
Subscription,
|
|
40
|
+
SubscriptionOptions,
|
|
41
|
+
isItemSubscription,
|
|
42
|
+
isLocationSubscription,
|
|
43
|
+
generateSubscriptionId,
|
|
44
|
+
createItemSubscription,
|
|
45
|
+
createLocationSubscription,
|
|
46
|
+
} from './subscription';
|
|
47
|
+
|
|
48
|
+
// Event Emitter/Subscriber Interfaces
|
|
49
|
+
export {
|
|
50
|
+
EventEmitter,
|
|
51
|
+
ScopedEventEmitter,
|
|
52
|
+
EventSubscriber,
|
|
53
|
+
EventSystem,
|
|
54
|
+
EventSystemFactory,
|
|
55
|
+
// Type aliases for common patterns
|
|
56
|
+
UserEventEmitter,
|
|
57
|
+
UserEventSubscriber,
|
|
58
|
+
UserEventSystem,
|
|
59
|
+
MessageEventEmitter,
|
|
60
|
+
MessageEventSubscriber,
|
|
61
|
+
MessageEventSystem,
|
|
62
|
+
} from './emitter';
|
|
63
|
+
|
|
64
|
+
// Subscription Matching Logic
|
|
65
|
+
export {
|
|
66
|
+
doesEventMatchSubscription,
|
|
67
|
+
doesScopeMatch,
|
|
68
|
+
doesEventTypeMatch,
|
|
69
|
+
doesKeyMatch,
|
|
70
|
+
doesKeyMatchLocation,
|
|
71
|
+
doesLocationMatch,
|
|
72
|
+
findMatchingSubscriptions,
|
|
73
|
+
extractLocationValues,
|
|
74
|
+
compareLocationValues,
|
|
75
|
+
} from './matching';
|
|
76
|
+
|
|
77
|
+
// Shared Types and Utilities
|
|
78
|
+
export {
|
|
79
|
+
STANDARD_EVENT_TYPES,
|
|
80
|
+
StandardEventType,
|
|
81
|
+
STANDARD_SCOPES,
|
|
82
|
+
StandardScope,
|
|
83
|
+
SubscriptionStatus,
|
|
84
|
+
SubscriptionMetadata,
|
|
85
|
+
ManagedSubscription,
|
|
86
|
+
EventHandler,
|
|
87
|
+
SafeEventHandler,
|
|
88
|
+
EventBatch,
|
|
89
|
+
EventStats,
|
|
90
|
+
EventSystemConfig,
|
|
91
|
+
DEFAULT_EVENT_CONFIG,
|
|
92
|
+
EventSystemError,
|
|
93
|
+
SubscriptionError,
|
|
94
|
+
EventEmissionError,
|
|
95
|
+
EventMatchingError,
|
|
96
|
+
createEventSystemError,
|
|
97
|
+
isEventSystemError,
|
|
98
|
+
ExtractItemType,
|
|
99
|
+
ExtractEventTypes,
|
|
100
|
+
} from './types';
|
|
101
|
+
|
|
102
|
+
/**
|
|
103
|
+
* Version of the event system API.
|
|
104
|
+
* Used for compatibility checking and debugging.
|
|
105
|
+
*/
|
|
106
|
+
export const EVENT_SYSTEM_VERSION = '1.0.0';
|
|
107
|
+
|
|
108
|
+
/**
|
|
109
|
+
* Supported event types for reference.
|
|
110
|
+
* Libraries should use these standard types for consistency.
|
|
111
|
+
*/
|
|
112
|
+
export const SUPPORTED_EVENT_TYPES = [
|
|
113
|
+
'create',
|
|
114
|
+
'update',
|
|
115
|
+
'delete',
|
|
116
|
+
'action',
|
|
117
|
+
] as const;
|
|
118
|
+
|
|
119
|
+
/**
|
|
120
|
+
* Supported storage scopes for reference.
|
|
121
|
+
* Libraries should use these standard scopes for consistency.
|
|
122
|
+
*/
|
|
123
|
+
export const SUPPORTED_SCOPES = [
|
|
124
|
+
'firestore',
|
|
125
|
+
'sequelize',
|
|
126
|
+
'postgresql',
|
|
127
|
+
'mysql',
|
|
128
|
+
'mongodb',
|
|
129
|
+
'redis',
|
|
130
|
+
] as const;
|
|
@@ -0,0 +1,264 @@
|
|
|
1
|
+
import { ComKey, ItemTypeArray, LocKeyArray, PriKey } from '../keys';
|
|
2
|
+
import { isComKey, isPriKey } from '../operations/Operations';
|
|
3
|
+
import { BaseEvent } from './events';
|
|
4
|
+
import { isItemSubscription, isLocationSubscription, Subscription } from './subscription';
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* Core subscription matching logic.
|
|
8
|
+
* Determines whether an event should be delivered to a specific subscription.
|
|
9
|
+
*/
|
|
10
|
+
export function doesEventMatchSubscription<
|
|
11
|
+
S extends string,
|
|
12
|
+
L1 extends string = never,
|
|
13
|
+
L2 extends string = never,
|
|
14
|
+
L3 extends string = never,
|
|
15
|
+
L4 extends string = never,
|
|
16
|
+
L5 extends string = never
|
|
17
|
+
>(
|
|
18
|
+
event: BaseEvent<S, L1, L2, L3, L4, L5>,
|
|
19
|
+
subscription: Subscription<S, L1, L2, L3, L4, L5>
|
|
20
|
+
): boolean {
|
|
21
|
+
// Check scope compatibility first (most efficient filter)
|
|
22
|
+
if (!doesScopeMatch(event.scopes, subscription.scopes)) {
|
|
23
|
+
return false;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
// Check event type compatibility
|
|
27
|
+
if (!doesEventTypeMatch(event.eventType, subscription.eventTypes)) {
|
|
28
|
+
return false;
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
// Check key/location pattern matching
|
|
32
|
+
if (isItemSubscription(subscription)) {
|
|
33
|
+
return doesKeyMatch(event.key, subscription.key);
|
|
34
|
+
} else if (isLocationSubscription(subscription)) {
|
|
35
|
+
return doesKeyMatchLocation(event.key, subscription.kta, subscription.location);
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
return false;
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
/**
|
|
42
|
+
* Check if event scopes match subscription scope requirements.
|
|
43
|
+
*
|
|
44
|
+
* @param eventScopes - Scopes from the event (e.g., ["firestore"])
|
|
45
|
+
* @param subscriptionScopes - Optional scopes required by subscription
|
|
46
|
+
* @returns true if scopes are compatible
|
|
47
|
+
*/
|
|
48
|
+
export function doesScopeMatch(
|
|
49
|
+
eventScopes: string[],
|
|
50
|
+
subscriptionScopes?: string[]
|
|
51
|
+
): boolean {
|
|
52
|
+
// If subscription doesn't specify scopes, accept events from any scope
|
|
53
|
+
if (!subscriptionScopes || subscriptionScopes.length === 0) {
|
|
54
|
+
return true;
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
// Check if any of the event's scopes match any of the subscription's required scopes
|
|
58
|
+
return subscriptionScopes.some(requiredScope =>
|
|
59
|
+
eventScopes.includes(requiredScope)
|
|
60
|
+
);
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
/**
|
|
64
|
+
* Check if event type matches subscription event type requirements.
|
|
65
|
+
*
|
|
66
|
+
* @param eventType - Type from the event (e.g., "create", "update")
|
|
67
|
+
* @param subscriptionEventTypes - Optional event types required by subscription
|
|
68
|
+
* @returns true if event type is compatible
|
|
69
|
+
*/
|
|
70
|
+
export function doesEventTypeMatch(
|
|
71
|
+
eventType: string,
|
|
72
|
+
subscriptionEventTypes?: string[]
|
|
73
|
+
): boolean {
|
|
74
|
+
// If subscription doesn't specify event types, accept all event types
|
|
75
|
+
if (!subscriptionEventTypes || subscriptionEventTypes.length === 0) {
|
|
76
|
+
return true;
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
// Check if the event type is in the subscription's allowed types
|
|
80
|
+
return subscriptionEventTypes.includes(eventType);
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
/**
|
|
84
|
+
* Check if two keys are exactly equal.
|
|
85
|
+
* Used for item-based subscriptions that want events for a specific key.
|
|
86
|
+
*/
|
|
87
|
+
export function doesKeyMatch<
|
|
88
|
+
S extends string,
|
|
89
|
+
L1 extends string = never,
|
|
90
|
+
L2 extends string = never,
|
|
91
|
+
L3 extends string = never,
|
|
92
|
+
L4 extends string = never,
|
|
93
|
+
L5 extends string = never
|
|
94
|
+
>(
|
|
95
|
+
eventKey: PriKey<S> | ComKey<S, L1, L2, L3, L4, L5>,
|
|
96
|
+
subscriptionKey: PriKey<S> | ComKey<S, L1, L2, L3, L4, L5>
|
|
97
|
+
): boolean {
|
|
98
|
+
// Both must be the same type (PriKey or ComKey)
|
|
99
|
+
if (isPriKey(eventKey) && isPriKey(subscriptionKey)) {
|
|
100
|
+
return eventKey.pk === subscriptionKey.pk && eventKey.kt === subscriptionKey.kt;
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
if (isComKey(eventKey) && isComKey(subscriptionKey)) {
|
|
104
|
+
const eventComKey = eventKey as ComKey<S, L1, L2, L3, L4, L5>;
|
|
105
|
+
const subscriptionComKey = subscriptionKey as ComKey<S, L1, L2, L3, L4, L5>;
|
|
106
|
+
|
|
107
|
+
// Compare primary key and key type
|
|
108
|
+
if (eventComKey.pk !== subscriptionComKey.pk || eventComKey.kt !== subscriptionComKey.kt) {
|
|
109
|
+
return false;
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
// Compare location arrays
|
|
113
|
+
if (eventComKey.loc.length !== subscriptionComKey.loc.length) {
|
|
114
|
+
return false;
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
// Check each location key
|
|
118
|
+
return eventComKey.loc.every((eventLocKey, index) => {
|
|
119
|
+
const subLocKey = subscriptionComKey.loc[index];
|
|
120
|
+
return eventLocKey.lk === subLocKey.lk && eventLocKey.kt === subLocKey.kt;
|
|
121
|
+
});
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
return false; // Different key types don't match
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
/**
|
|
128
|
+
* Check if an event key matches a location-based subscription.
|
|
129
|
+
* This is more complex as it needs to determine if the event key is "within" the subscription location.
|
|
130
|
+
*/
|
|
131
|
+
export function doesKeyMatchLocation<
|
|
132
|
+
S extends string,
|
|
133
|
+
L1 extends string = never,
|
|
134
|
+
L2 extends string = never,
|
|
135
|
+
L3 extends string = never,
|
|
136
|
+
L4 extends string = never,
|
|
137
|
+
L5 extends string = never
|
|
138
|
+
>(
|
|
139
|
+
eventKey: PriKey<S> | ComKey<S, L1, L2, L3, L4, L5>,
|
|
140
|
+
subscriptionKta: ItemTypeArray<S, L1, L2, L3, L4, L5>,
|
|
141
|
+
subscriptionLocation: LocKeyArray<L1, L2, L3, L4, L5>
|
|
142
|
+
): boolean {
|
|
143
|
+
// First, check if the key type matches the target type in the KTA
|
|
144
|
+
const targetItemType = subscriptionKta[subscriptionKta.length - 1];
|
|
145
|
+
if (eventKey.kt !== targetItemType) {
|
|
146
|
+
return false;
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
// For PriKey events
|
|
150
|
+
if (isPriKey(eventKey)) {
|
|
151
|
+
// PriKey can only match location subscriptions with empty locations (root level)
|
|
152
|
+
return subscriptionLocation.length === 0;
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
// For ComKey events
|
|
156
|
+
if (isComKey(eventKey)) {
|
|
157
|
+
const comKey = eventKey as ComKey<S, L1, L2, L3, L4, L5>;
|
|
158
|
+
// The event's location must match the subscription location exactly or be a sub-location
|
|
159
|
+
return doesLocationMatch(comKey.loc, subscriptionLocation, subscriptionKta);
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
return false;
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
/**
|
|
166
|
+
* Check if an event's location keys match a subscription's location requirements.
|
|
167
|
+
* This implements the hierarchical location matching logic.
|
|
168
|
+
*/
|
|
169
|
+
export function doesLocationMatch<
|
|
170
|
+
L1 extends string = never,
|
|
171
|
+
L2 extends string = never,
|
|
172
|
+
L3 extends string = never,
|
|
173
|
+
L4 extends string = never,
|
|
174
|
+
L5 extends string = never
|
|
175
|
+
>(
|
|
176
|
+
eventLocation: LocKeyArray<L1, L2, L3, L4, L5>,
|
|
177
|
+
subscriptionLocation: LocKeyArray<L1, L2, L3, L4, L5>,
|
|
178
|
+
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
|
179
|
+
_subscriptionKta: ItemTypeArray<string, L1, L2, L3, L4, L5>
|
|
180
|
+
): boolean {
|
|
181
|
+
// If subscription location is empty, it matches all locations (root level subscription)
|
|
182
|
+
if (subscriptionLocation.length === 0) {
|
|
183
|
+
return true;
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
// Event location must be at least as deep as subscription location
|
|
187
|
+
if (eventLocation.length < subscriptionLocation.length) {
|
|
188
|
+
return false;
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
// Check that all subscription location keys match the corresponding event location keys
|
|
192
|
+
for (let i = 0; i < subscriptionLocation.length; i++) {
|
|
193
|
+
const eventLocKey = eventLocation[i];
|
|
194
|
+
const subLocKey = subscriptionLocation[i];
|
|
195
|
+
|
|
196
|
+
if (!eventLocKey || !subLocKey) {
|
|
197
|
+
return false;
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
if (eventLocKey.lk !== subLocKey.lk || eventLocKey.kt !== subLocKey.kt) {
|
|
201
|
+
return false;
|
|
202
|
+
}
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
return true;
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
/**
|
|
209
|
+
* Find all subscriptions that match a given event.
|
|
210
|
+
* Used by EventSubscriber implementations to determine which subscriptions should receive an event.
|
|
211
|
+
*/
|
|
212
|
+
export function findMatchingSubscriptions<
|
|
213
|
+
S extends string,
|
|
214
|
+
L1 extends string = never,
|
|
215
|
+
L2 extends string = never,
|
|
216
|
+
L3 extends string = never,
|
|
217
|
+
L4 extends string = never,
|
|
218
|
+
L5 extends string = never
|
|
219
|
+
>(
|
|
220
|
+
event: BaseEvent<S, L1, L2, L3, L4, L5>,
|
|
221
|
+
subscriptions: Subscription<S, L1, L2, L3, L4, L5>[]
|
|
222
|
+
): Subscription<S, L1, L2, L3, L4, L5>[] {
|
|
223
|
+
return subscriptions.filter(subscription =>
|
|
224
|
+
doesEventMatchSubscription(event, subscription)
|
|
225
|
+
);
|
|
226
|
+
}
|
|
227
|
+
|
|
228
|
+
/**
|
|
229
|
+
* Utility function to extract the location from a ComKey for comparison purposes.
|
|
230
|
+
* Returns the location key values as strings for easier comparison.
|
|
231
|
+
*/
|
|
232
|
+
export function extractLocationValues<
|
|
233
|
+
L1 extends string = never,
|
|
234
|
+
L2 extends string = never,
|
|
235
|
+
L3 extends string = never,
|
|
236
|
+
L4 extends string = never,
|
|
237
|
+
L5 extends string = never
|
|
238
|
+
>(location: LocKeyArray<L1, L2, L3, L4, L5>): string[] {
|
|
239
|
+
return location.map(locKey => String(locKey.lk));
|
|
240
|
+
}
|
|
241
|
+
|
|
242
|
+
/**
|
|
243
|
+
* Utility function to compare two location arrays by their values.
|
|
244
|
+
* Useful for debugging and testing location matching logic.
|
|
245
|
+
*/
|
|
246
|
+
export function compareLocationValues<
|
|
247
|
+
L1 extends string = never,
|
|
248
|
+
L2 extends string = never,
|
|
249
|
+
L3 extends string = never,
|
|
250
|
+
L4 extends string = never,
|
|
251
|
+
L5 extends string = never
|
|
252
|
+
>(
|
|
253
|
+
location1: LocKeyArray<L1, L2, L3, L4, L5>,
|
|
254
|
+
location2: LocKeyArray<L1, L2, L3, L4, L5>
|
|
255
|
+
): boolean {
|
|
256
|
+
if (location1.length !== location2.length) {
|
|
257
|
+
return false;
|
|
258
|
+
}
|
|
259
|
+
|
|
260
|
+
return location1.every((locKey1, index) => {
|
|
261
|
+
const locKey2 = location2[index];
|
|
262
|
+
return locKey1.lk === locKey2.lk && locKey1.kt === locKey2.kt;
|
|
263
|
+
});
|
|
264
|
+
}
|