@theelena/shared 1.0.1 → 1.0.2

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/package.json CHANGED
@@ -1,9 +1,9 @@
1
1
  {
2
2
  "name": "@theelena/shared",
3
- "version": "1.0.1",
3
+ "version": "1.0.2",
4
4
  "type": "module",
5
- "main": "./index.js",
6
- "types": "./index.ts",
5
+ "main": "./src/index.ts",
6
+ "types": "./src/index.ts",
7
7
  "exports": {
8
8
  ".": {
9
9
  "types": "./src/index.ts",
@@ -17,6 +17,9 @@
17
17
  }
18
18
  },
19
19
  "files": [
20
- "dist"
21
- ]
20
+ "src"
21
+ ],
22
+ "devDependencies": {
23
+ "typescript": "^5"
24
+ }
22
25
  }
@@ -0,0 +1,153 @@
1
+ /**
2
+ * Event scope types for channel routing
3
+ */
4
+ export type EventScope = "restaurant" | "user" | "global";
5
+
6
+ /**
7
+ * Context required for channel resolution
8
+ */
9
+ export interface EventContext {
10
+ restaurantId: string;
11
+ userId: string;
12
+ }
13
+
14
+ /**
15
+ * Serialized event format for SSE transmission
16
+ */
17
+ export interface SerializedEvent<TData = unknown> {
18
+ type: string;
19
+ data: TData;
20
+ timestamp: number;
21
+ }
22
+
23
+ /**
24
+ * Channel name generators for event routing
25
+ */
26
+ export const EventChannels = {
27
+ restaurant: (restaurantId: string) => `restaurant:${restaurantId}`,
28
+ user: (userId: string) => `user:${userId}`,
29
+ global: () => "global",
30
+ } as const;
31
+
32
+ /**
33
+ * Base class for all application events
34
+ *
35
+ * Scope is automatically deduced from event type prefix:
36
+ * - 'order.*', 'product.*', 'sale.*', 'customer.*' → restaurant scope
37
+ * - 'notification.*', 'system.*' → user scope (owner messages)
38
+ * - default → global scope
39
+ *
40
+ * @example
41
+ * ```typescript
42
+ * class OrderCreatedEvent extends BaseEvent<OrderCreatedData> {
43
+ * readonly type = 'order.created' as const;
44
+ * }
45
+ *
46
+ * const event = new OrderCreatedEvent({ orderId: '123', ... });
47
+ * const channel = event.getChannel({ restaurantId: 'abc', userId: 'xyz' });
48
+ * // Returns: 'restaurant:abc'
49
+ * ```
50
+ */
51
+ export abstract class BaseEvent<TData = unknown> {
52
+ /**
53
+ * Event type identifier - used for routing and filtering
54
+ * Convention: '{domain}.{action}' e.g., 'order.created', 'notification.sent'
55
+ */
56
+ abstract readonly type: string;
57
+
58
+ /**
59
+ * Event payload data
60
+ */
61
+ readonly data: TData;
62
+
63
+ /**
64
+ * Event creation timestamp (milliseconds since epoch)
65
+ */
66
+ readonly timestamp: number;
67
+
68
+ constructor(data: TData) {
69
+ this.data = data;
70
+ this.timestamp = Date.now();
71
+ }
72
+
73
+ /**
74
+ * Deduce scope from event type prefix
75
+ * Override in subclass if custom scope logic is needed
76
+ */
77
+ protected getScope(): EventScope {
78
+ const domain = this.type.split(".")[0] ?? "";
79
+
80
+ const restaurantDomains = ["order", "product", "sale", "customer", "table", "category"];
81
+ if (restaurantDomains.includes(domain)) {
82
+ return "restaurant";
83
+ }
84
+
85
+ const userDomains = ["notification", "system", "alert"];
86
+ if (userDomains.includes(domain)) {
87
+ return "user";
88
+ }
89
+
90
+ return "global";
91
+ }
92
+
93
+ /**
94
+ * Get the Redis channel for this event based on context
95
+ */
96
+ getChannel(context: EventContext): string {
97
+ const scope = this.getScope();
98
+
99
+ switch (scope) {
100
+ case "restaurant":
101
+ return EventChannels.restaurant(context.restaurantId);
102
+ case "user":
103
+ return EventChannels.user(context.userId);
104
+ case "global":
105
+ return EventChannels.global();
106
+ }
107
+ }
108
+
109
+ /**
110
+ * Get all channels this event should be published to
111
+ * By default, only returns the primary channel
112
+ * Override for multi-channel publishing
113
+ */
114
+ getChannels(context: EventContext): string[] {
115
+ return [this.getChannel(context)];
116
+ }
117
+
118
+ /**
119
+ * Serialize event for transmission
120
+ */
121
+ toJSON(): SerializedEvent<TData> {
122
+ return {
123
+ type: this.type,
124
+ data: this.data,
125
+ timestamp: this.timestamp,
126
+ };
127
+ }
128
+
129
+ /**
130
+ * Create event instance from serialized data
131
+ * Must be implemented by concrete event classes for deserialization
132
+ */
133
+ static fromJSON<T extends BaseEvent>(
134
+ this: new (data: any) => T,
135
+ json: SerializedEvent
136
+ ): T {
137
+ return new this(json.data);
138
+ }
139
+ }
140
+
141
+ /**
142
+ * Type helper to extract data type from event class
143
+ */
144
+ export type EventData<E extends BaseEvent> = E extends BaseEvent<infer D>
145
+ ? D
146
+ : never;
147
+
148
+ /**
149
+ * Type helper for event class constructor
150
+ */
151
+ export type EventClass<E extends BaseEvent = BaseEvent> = new (
152
+ data: EventData<E>
153
+ ) => E;
@@ -0,0 +1,3 @@
1
+ export * from "./base";
2
+ export * from "./order.events";
3
+ export * from "./notification.events";
@@ -0,0 +1,23 @@
1
+ import { BaseEvent } from "./base";
2
+
3
+ export interface SystemNotificationData {
4
+ title: string;
5
+ message: string;
6
+ severity: "info" | "warning" | "error" | "success";
7
+ actionUrl?: string;
8
+ }
9
+
10
+ export interface AlertData {
11
+ alertId: string;
12
+ alertType: string;
13
+ message: string;
14
+ metadata?: Record<string, unknown>;
15
+ }
16
+
17
+ export class SystemNotificationEvent extends BaseEvent<SystemNotificationData> {
18
+ readonly type = "system.notification" as const;
19
+ }
20
+
21
+ export class AlertEvent extends BaseEvent<AlertData> {
22
+ readonly type = "alert.triggered" as const;
23
+ }
@@ -0,0 +1,25 @@
1
+ import { BaseEvent } from "./base";
2
+
3
+ export interface OrderCreatedData {
4
+ orderId: string;
5
+ orderNumber: string;
6
+ orderType: "takeout" | "dine_in" | "reservation";
7
+ customerName?: string;
8
+ tableName?: string;
9
+ }
10
+
11
+ export interface OrderStatusChangedData {
12
+ orderId: string;
13
+ orderNumber: string;
14
+ oldStatus: string;
15
+ newStatus: string;
16
+ updatedBy: string;
17
+ }
18
+
19
+ export class OrderCreatedEvent extends BaseEvent<OrderCreatedData> {
20
+ readonly type = "order.created" as const;
21
+ }
22
+
23
+ export class OrderStatusChangedEvent extends BaseEvent<OrderStatusChangedData> {
24
+ readonly type = "order.status_changed" as const;
25
+ }
package/src/index.ts ADDED
@@ -0,0 +1 @@
1
+ export * from "./events";