@axova/shared 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 (112) hide show
  1. package/CONFIGURATION_GUIDE.md +1 -0
  2. package/README.md +384 -0
  3. package/SCHEMA_ORGANIZATION.md +209 -0
  4. package/dist/configs/index.d.ts +85 -0
  5. package/dist/configs/index.js +555 -0
  6. package/dist/events/kafka.d.ts +40 -0
  7. package/dist/events/kafka.js +311 -0
  8. package/dist/index.d.ts +13 -0
  9. package/dist/index.js +41 -0
  10. package/dist/interfaces/customer-events.d.ts +85 -0
  11. package/dist/interfaces/customer-events.js +2 -0
  12. package/dist/interfaces/inventory-events.d.ts +453 -0
  13. package/dist/interfaces/inventory-events.js +3 -0
  14. package/dist/interfaces/inventory-types.d.ts +894 -0
  15. package/dist/interfaces/inventory-types.js +3 -0
  16. package/dist/interfaces/order-events.d.ts +320 -0
  17. package/dist/interfaces/order-events.js +3 -0
  18. package/dist/lib/auditLogger.d.ts +162 -0
  19. package/dist/lib/auditLogger.js +626 -0
  20. package/dist/lib/authOrganization.d.ts +24 -0
  21. package/dist/lib/authOrganization.js +110 -0
  22. package/dist/lib/db.d.ts +6 -0
  23. package/dist/lib/db.js +88 -0
  24. package/dist/middleware/serviceAuth.d.ts +60 -0
  25. package/dist/middleware/serviceAuth.js +272 -0
  26. package/dist/middleware/storeOwnership.d.ts +15 -0
  27. package/dist/middleware/storeOwnership.js +156 -0
  28. package/dist/middleware/storeValidationMiddleware.d.ts +44 -0
  29. package/dist/middleware/storeValidationMiddleware.js +180 -0
  30. package/dist/middleware/userAuth.d.ts +27 -0
  31. package/dist/middleware/userAuth.js +218 -0
  32. package/dist/schemas/admin/admin-schema.d.ts +741 -0
  33. package/dist/schemas/admin/admin-schema.js +111 -0
  34. package/dist/schemas/ai-moderation/ai-moderation-schema.d.ts +648 -0
  35. package/dist/schemas/ai-moderation/ai-moderation-schema.js +88 -0
  36. package/dist/schemas/common/common-schemas.d.ts +436 -0
  37. package/dist/schemas/common/common-schemas.js +94 -0
  38. package/dist/schemas/compliance/compliance-schema.d.ts +3388 -0
  39. package/dist/schemas/compliance/compliance-schema.js +472 -0
  40. package/dist/schemas/compliance/kyc-schema.d.ts +2642 -0
  41. package/dist/schemas/compliance/kyc-schema.js +361 -0
  42. package/dist/schemas/customer/customer-schema.d.ts +2727 -0
  43. package/dist/schemas/customer/customer-schema.js +399 -0
  44. package/dist/schemas/index.d.ts +27 -0
  45. package/dist/schemas/index.js +138 -0
  46. package/dist/schemas/inventory/inventory-tables.d.ts +9476 -0
  47. package/dist/schemas/inventory/inventory-tables.js +1470 -0
  48. package/dist/schemas/inventory/lot-tables.d.ts +3281 -0
  49. package/dist/schemas/inventory/lot-tables.js +608 -0
  50. package/dist/schemas/order/order-schema.d.ts +5825 -0
  51. package/dist/schemas/order/order-schema.js +954 -0
  52. package/dist/schemas/product/discount-relations.d.ts +15 -0
  53. package/dist/schemas/product/discount-relations.js +34 -0
  54. package/dist/schemas/product/discount-schema.d.ts +1975 -0
  55. package/dist/schemas/product/discount-schema.js +297 -0
  56. package/dist/schemas/product/product-relations.d.ts +41 -0
  57. package/dist/schemas/product/product-relations.js +133 -0
  58. package/dist/schemas/product/product-schema.d.ts +4544 -0
  59. package/dist/schemas/product/product-schema.js +671 -0
  60. package/dist/schemas/store/store-audit-schema.d.ts +4135 -0
  61. package/dist/schemas/store/store-audit-schema.js +556 -0
  62. package/dist/schemas/store/store-schema.d.ts +3100 -0
  63. package/dist/schemas/store/store-schema.js +381 -0
  64. package/dist/schemas/store/store-settings-schema.d.ts +665 -0
  65. package/dist/schemas/store/store-settings-schema.js +141 -0
  66. package/dist/schemas/types.d.ts +50 -0
  67. package/dist/schemas/types.js +3 -0
  68. package/dist/types/events.d.ts +2396 -0
  69. package/dist/types/events.js +505 -0
  70. package/dist/utils/errorHandler.d.ts +12 -0
  71. package/dist/utils/errorHandler.js +36 -0
  72. package/dist/utils/subdomain.d.ts +6 -0
  73. package/dist/utils/subdomain.js +20 -0
  74. package/nul +8 -0
  75. package/package.json +43 -0
  76. package/src/configs/index.ts +654 -0
  77. package/src/events/kafka.ts +429 -0
  78. package/src/index.ts +26 -0
  79. package/src/interfaces/customer-events.ts +106 -0
  80. package/src/interfaces/inventory-events.ts +545 -0
  81. package/src/interfaces/inventory-types.ts +1004 -0
  82. package/src/interfaces/order-events.ts +381 -0
  83. package/src/lib/auditLogger.ts +1117 -0
  84. package/src/lib/authOrganization.ts +153 -0
  85. package/src/lib/db.ts +64 -0
  86. package/src/middleware/serviceAuth.ts +328 -0
  87. package/src/middleware/storeOwnership.ts +199 -0
  88. package/src/middleware/storeValidationMiddleware.ts +247 -0
  89. package/src/middleware/userAuth.ts +248 -0
  90. package/src/schemas/admin/admin-schema.ts +208 -0
  91. package/src/schemas/ai-moderation/ai-moderation-schema.ts +180 -0
  92. package/src/schemas/common/common-schemas.ts +108 -0
  93. package/src/schemas/compliance/compliance-schema.ts +927 -0
  94. package/src/schemas/compliance/kyc-schema.ts +649 -0
  95. package/src/schemas/customer/customer-schema.ts +576 -0
  96. package/src/schemas/index.ts +189 -0
  97. package/src/schemas/inventory/inventory-tables.ts +1927 -0
  98. package/src/schemas/inventory/lot-tables.ts +799 -0
  99. package/src/schemas/order/order-schema.ts +1400 -0
  100. package/src/schemas/product/discount-relations.ts +44 -0
  101. package/src/schemas/product/discount-schema.ts +464 -0
  102. package/src/schemas/product/product-relations.ts +187 -0
  103. package/src/schemas/product/product-schema.ts +955 -0
  104. package/src/schemas/store/ethiopian_business_api.md.resolved +212 -0
  105. package/src/schemas/store/store-audit-schema.ts +1257 -0
  106. package/src/schemas/store/store-schema.ts +661 -0
  107. package/src/schemas/store/store-settings-schema.ts +231 -0
  108. package/src/schemas/types.ts +67 -0
  109. package/src/types/events.ts +646 -0
  110. package/src/utils/errorHandler.ts +44 -0
  111. package/src/utils/subdomain.ts +19 -0
  112. package/tsconfig.json +21 -0
@@ -0,0 +1,429 @@
1
+ import { createId } from "@paralleldrive/cuid2";
2
+ import {
3
+ type Consumer,
4
+ type ConsumerConfig,
5
+ Kafka,
6
+ type KafkaConfig,
7
+ type KafkaMessage,
8
+ type Producer,
9
+ type ProducerConfig,
10
+ } from "kafkajs";
11
+ import {
12
+ type AxovaEvent,
13
+ KAFKA_TOPICS,
14
+ type KafkaTopic,
15
+ } from "../types/events";
16
+
17
+ // Kafka configuration interface
18
+ export interface AxovaKafkaConfig {
19
+ brokers: string[];
20
+ clientId: string;
21
+ ssl?: boolean;
22
+ sasl?:
23
+ | {
24
+ mechanism: "plain";
25
+ username: string;
26
+ password: string;
27
+ }
28
+ | {
29
+ mechanism: "scram-sha-256";
30
+ username: string;
31
+ password: string;
32
+ }
33
+ | {
34
+ mechanism: "scram-sha-512";
35
+ username: string;
36
+ password: string;
37
+ };
38
+ }
39
+
40
+ // Event handler type
41
+ export type EventHandler<T = unknown> = (event: T) => Promise<void>;
42
+
43
+ // Kafka client wrapper
44
+ export class AxovaKafka {
45
+ private kafka: Kafka;
46
+ private producer: Producer | null = null;
47
+ private consumers: Map<string, Consumer> = new Map();
48
+ private eventHandlers: Map<string, EventHandler[]> = new Map();
49
+
50
+ constructor(config: AxovaKafkaConfig) {
51
+ const kafkaConfig: KafkaConfig = {
52
+ clientId: config.clientId,
53
+ brokers: config.brokers,
54
+ };
55
+
56
+ if (config.ssl) {
57
+ kafkaConfig.ssl = config.ssl;
58
+ }
59
+
60
+ if (config.sasl) {
61
+ kafkaConfig.sasl = config.sasl;
62
+ }
63
+
64
+ this.kafka = new Kafka(kafkaConfig);
65
+ }
66
+
67
+ // Initialize producer with optimized settings for high throughput and reliability
68
+ async initializeProducer(config?: ProducerConfig): Promise<void> {
69
+ if (this.producer) return;
70
+
71
+ const optimizedConfig: ProducerConfig = {
72
+ // Idempotency ensures exactly-once semantics
73
+ idempotent: true,
74
+
75
+ // Allow up to 5 requests in flight for better throughput
76
+ // (safe with idempotent=true)
77
+ maxInFlightRequests: 5,
78
+
79
+ // Batching configuration for better throughput
80
+ allowAutoTopicCreation: false,
81
+ transactionTimeout: 60000,
82
+
83
+ // Retry configuration
84
+ retry: {
85
+ initialRetryTime: 100,
86
+ retries: 10,
87
+ maxRetryTime: 30000,
88
+ multiplier: 2,
89
+ factor: 0.2,
90
+ },
91
+
92
+ // Override with user config if provided
93
+ ...config,
94
+ };
95
+
96
+ this.producer = this.kafka.producer(optimizedConfig);
97
+ await this.producer.connect();
98
+ console.log("✅ Kafka producer connected with optimized settings");
99
+ console.log(" • Idempotent: enabled");
100
+ console.log(" • Max in-flight: 5");
101
+ }
102
+
103
+ // Initialize consumer with optimized settings for throughput and reliability
104
+ async initializeConsumer(
105
+ groupId: string,
106
+ topics: KafkaTopic[],
107
+ config?: ConsumerConfig,
108
+ ): Promise<void> {
109
+ if (this.consumers.has(groupId)) return;
110
+
111
+ const optimizedConfig: ConsumerConfig = {
112
+ groupId,
113
+
114
+ // Session timeout - how long broker waits before considering consumer dead
115
+ sessionTimeout: 30000,
116
+
117
+ // Rebalance timeout - max time for rebalancing
118
+ rebalanceTimeout: 60000,
119
+
120
+ // Heartbeat interval - how often to send heartbeats
121
+ heartbeatInterval: 3000,
122
+
123
+ // Retry configuration
124
+ retry: {
125
+ initialRetryTime: 100,
126
+ retries: 10,
127
+ maxRetryTime: 30000,
128
+ multiplier: 2,
129
+ factor: 0.2,
130
+ },
131
+
132
+ // Auto-commit configuration
133
+ // Note: For critical applications, consider manual commits
134
+ // autoCommit: false,
135
+ // autoCommitInterval: 5000,
136
+
137
+ // Performance tuning
138
+ maxBytesPerPartition: 1048576, // 1MB
139
+ maxWaitTimeInMs: 500, // Wait up to 500ms for batch
140
+
141
+ // Override with user config if provided
142
+ ...config,
143
+ };
144
+
145
+ const consumer = this.kafka.consumer(optimizedConfig);
146
+
147
+ await consumer.connect();
148
+ await consumer.subscribe({
149
+ topics: topics,
150
+ fromBeginning: false,
151
+ });
152
+
153
+ this.consumers.set(groupId, consumer);
154
+ console.log(`✅ Kafka consumer connected for group: ${groupId}`);
155
+ console.log(` • Topics: ${topics.join(", ")}`);
156
+ console.log(" • Session timeout: 30s");
157
+ console.log(" • Max bytes per partition: 1MB");
158
+ }
159
+
160
+ // Publish event with optimized batching and error handling
161
+ async publishEvent(topic: KafkaTopic, event: AxovaEvent): Promise<void> {
162
+ if (!this.producer) {
163
+ throw new Error(
164
+ "Producer not initialized. Call initializeProducer() first.",
165
+ );
166
+ }
167
+
168
+ // Ensure all header values are strings (KafkaJS requirement)
169
+ const headers: Record<string, string> = {};
170
+ if (event.type) headers.eventType = String(event.type);
171
+ if (event.source) headers.source = String(event.source);
172
+ if (event.version) headers.version = String(event.version);
173
+
174
+ const message = {
175
+ key: event.id || createId(), // Use event ID or generate a new one as string key
176
+ value: JSON.stringify(event),
177
+ timestamp: Date.now().toString(),
178
+ headers,
179
+ };
180
+
181
+ try {
182
+ const result = await this.producer.send({
183
+ topic,
184
+ messages: [message],
185
+ // Require acknowledgment from all in-sync replicas for durability
186
+ acks: -1, // Wait for all in-sync replicas
187
+ timeout: 30000,
188
+ compression: 1, // LZ4 compression
189
+ });
190
+
191
+ console.log(`✅ Event published successfully to ${topic}:`, event.type);
192
+ console.log(
193
+ ` • Partition: ${result[0].partition}, Offset: ${result[0].baseOffset}`,
194
+ );
195
+ } catch (error) {
196
+ console.error(`❌ Failed to publish event to ${topic}:`, error);
197
+ if (error instanceof Error) {
198
+ if (error.message.includes("UNKNOWN_TOPIC_OR_PARTITION")) {
199
+ console.error(
200
+ ` • Topic '${topic}' does not exist. Create it manually or enable auto-creation.`,
201
+ );
202
+ } else if (error.message.includes("NOT_LEADER_FOR_PARTITION")) {
203
+ console.error(
204
+ " • Kafka cluster leadership issue. Check broker status.",
205
+ );
206
+ } else if (error.message.includes("NOT_ENOUGH_REPLICAS")) {
207
+ console.error(
208
+ " • Not enough in-sync replicas available. Check broker health.",
209
+ );
210
+ } else if (error.message.includes("REQUEST_TIMED_OUT")) {
211
+ console.error(
212
+ " • Request timed out. Check network and broker performance.",
213
+ );
214
+ }
215
+ }
216
+ throw error;
217
+ }
218
+ }
219
+
220
+ // Batch publish events for higher throughput
221
+ async publishEventsBatch(topic: KafkaTopic, events: AxovaEvent[]): Promise<void> {
222
+ if (!this.producer) {
223
+ throw new Error(
224
+ "Producer not initialized. Call initializeProducer() first.",
225
+ );
226
+ }
227
+
228
+ if (events.length === 0) return;
229
+
230
+ const messages = events.map((event) => {
231
+ const headers: Record<string, string> = {};
232
+ if (event.type) headers.eventType = String(event.type);
233
+ if (event.source) headers.source = String(event.source);
234
+ if (event.version) headers.version = String(event.version);
235
+
236
+ return {
237
+ key: event.id || createId(),
238
+ value: JSON.stringify(event),
239
+ timestamp: Date.now().toString(),
240
+ headers,
241
+ };
242
+ });
243
+
244
+ try {
245
+ const result = await this.producer.send({
246
+ topic,
247
+ messages,
248
+ acks: -1,
249
+ timeout: 30000,
250
+ compression: 1,
251
+ });
252
+
253
+ console.log(`✅ Batch published ${events.length} events to ${topic}`);
254
+ console.log(` • Partitions used: ${result.length}`);
255
+ } catch (error) {
256
+ console.error(`❌ Failed to batch publish events to ${topic}:`, error);
257
+ throw error;
258
+ }
259
+ }
260
+
261
+ // Subscribe to events
262
+ onEvent<T extends AxovaEvent>(
263
+ eventType: T["type"],
264
+ handler: EventHandler<T>,
265
+ ): void {
266
+ const handlers = this.eventHandlers.get(eventType) || [];
267
+ handlers.push(handler as EventHandler);
268
+ this.eventHandlers.set(eventType, handlers);
269
+ }
270
+
271
+ // Start consuming events with optimized batch processing
272
+ async startConsuming(groupId: string): Promise<void> {
273
+ const consumer = this.consumers.get(groupId);
274
+ if (!consumer) {
275
+ throw new Error(`Consumer for group ${groupId} not initialized`);
276
+ }
277
+
278
+ await consumer.run({
279
+ // Process messages in batches for better throughput
280
+ // Can be configured per service based on requirements
281
+ partitionsConsumedConcurrently: 3, // Process 3 partitions concurrently
282
+
283
+ eachMessage: async ({
284
+ topic,
285
+ partition,
286
+ message,
287
+ }: {
288
+ topic: string;
289
+ partition: number;
290
+ message: KafkaMessage;
291
+ }) => {
292
+ const startTime = Date.now();
293
+ try {
294
+ if (!message.value) return;
295
+
296
+ const event = JSON.parse(message.value.toString()) as AxovaEvent;
297
+ const handlers = this.eventHandlers.get(event.type) || [];
298
+
299
+ if (handlers.length === 0) {
300
+ console.warn(`⚠️ No handlers registered for event type: ${event.type}`);
301
+ return;
302
+ }
303
+
304
+ // Execute all handlers for this event type
305
+ const handlerPromises = handlers.map(async (handler) => {
306
+ try {
307
+ await handler(event);
308
+ } catch (handlerError) {
309
+ console.error(
310
+ `❌ Handler error for event ${event.type}:`,
311
+ handlerError,
312
+ );
313
+ // Log error but don't throw - allows other handlers to execute
314
+ // TODO: Consider implementing dead letter queue for failed handlers
315
+ }
316
+ });
317
+
318
+ // Wait for all handlers to complete
319
+ await Promise.allSettled(handlerPromises);
320
+
321
+ const processingTime = Date.now() - startTime;
322
+ console.log(
323
+ `✅ Processed ${handlers.length} handler(s) for event: ${event.type} (${processingTime}ms)`,
324
+ );
325
+ } catch (error) {
326
+ const processingTime = Date.now() - startTime;
327
+ console.error(
328
+ `❌ Failed to process message from ${topic} [partition ${partition}] (${processingTime}ms):`,
329
+ error,
330
+ );
331
+ // TODO: Implement dead letter queue for failed messages
332
+ // For now, the message will be retried based on consumer config
333
+ }
334
+ },
335
+ });
336
+
337
+ console.log(`✅ Started consuming events for group: ${groupId}`);
338
+ console.log(" • Processing 3 partitions concurrently");
339
+ console.log(" • Handlers execute in parallel per message");
340
+ }
341
+
342
+ // Disconnect all connections
343
+ async disconnect(): Promise<void> {
344
+ if (this.producer) {
345
+ await this.producer.disconnect();
346
+ this.producer = null;
347
+ }
348
+
349
+ for (const [_groupId, consumer] of this.consumers.entries()) {
350
+ await consumer.disconnect();
351
+ }
352
+ this.consumers.clear();
353
+
354
+ console.log("All Kafka connections disconnected");
355
+ }
356
+
357
+ // Utility method to create event with base properties
358
+ createEvent<T extends AxovaEvent>(
359
+ type: T["type"],
360
+ data: T["data"],
361
+ source: string,
362
+ ): T {
363
+ // Validate required fields
364
+ if (!type) {
365
+ throw new Error("Event type is required for Kafka event creation");
366
+ }
367
+ if (!source) {
368
+ throw new Error("Event source is required for Kafka event creation");
369
+ }
370
+
371
+ return {
372
+ id: createId(),
373
+ timestamp: new Date().toISOString(),
374
+ source: String(source),
375
+ version: "1.0",
376
+ type: String(type),
377
+ data: data || {},
378
+ } as T;
379
+ }
380
+ }
381
+
382
+ // Singleton factory for Kafka instance
383
+ let kafkaInstance: AxovaKafka | null = null;
384
+
385
+ export function createKafkaInstance(config: AxovaKafkaConfig): AxovaKafka {
386
+ if (!kafkaInstance) {
387
+ kafkaInstance = new AxovaKafka(config);
388
+ }
389
+ return kafkaInstance;
390
+ }
391
+
392
+ export function getKafkaInstance(): AxovaKafka {
393
+ if (!kafkaInstance) {
394
+ throw new Error(
395
+ "Kafka instance not created. Call createKafkaInstance() first.",
396
+ );
397
+ }
398
+ return kafkaInstance;
399
+ }
400
+
401
+ // Utility function to get Kafka config from environment
402
+ export function getKafkaConfigFromEnv(): AxovaKafkaConfig {
403
+ const brokers = process.env.KAFKA_BROKERS?.split(",") || ["localhost:9092"];
404
+ const clientId = process.env.KAFKA_CLIENT_ID || "axova-service";
405
+
406
+ const config: AxovaKafkaConfig = {
407
+ brokers,
408
+ clientId,
409
+ };
410
+
411
+ // Add SSL if configured
412
+ if (process.env.KAFKA_SSL === "true") {
413
+ config.ssl = true;
414
+ }
415
+
416
+ // Add SASL if configured
417
+ if (process.env.KAFKA_USERNAME && process.env.KAFKA_PASSWORD) {
418
+ config.sasl = {
419
+ mechanism: "plain",
420
+ username: process.env.KAFKA_USERNAME,
421
+ password: process.env.KAFKA_PASSWORD,
422
+ };
423
+ }
424
+
425
+ return config;
426
+ }
427
+
428
+ // Export common topics for easy access
429
+ export { KAFKA_TOPICS };
package/src/index.ts ADDED
@@ -0,0 +1,26 @@
1
+ // Database
2
+
3
+ // Service Configuration Profiles
4
+ export * from "./configs";
5
+ // Kafka Events and Utilities
6
+ export * from "./events/kafka";
7
+ // Audit Logging
8
+ export * from "./lib/auditLogger";
9
+ // Auth Organization
10
+ export * from "./lib/authOrganization";
11
+ export { db, pool } from "./lib/db";
12
+ // Service Authentication
13
+ export * from "./middleware/serviceAuth";
14
+ export * from "./middleware/storeOwnership";
15
+ export * from "./middleware/storeValidationMiddleware";
16
+ export * from "./middleware/userAuth";
17
+
18
+ // Schemas - Organized by Service
19
+ export * from "./schemas";
20
+
21
+ // Types and Interfaces
22
+ export * from "./types/events";
23
+
24
+ // Utilities
25
+ export * from "./utils/errorHandler";
26
+ export * from "./utils/subdomain";
@@ -0,0 +1,106 @@
1
+ // Customer Service Events Configuration
2
+ export interface CustomerServiceConfig {
3
+ serviceName: string;
4
+ version: string;
5
+ events: CustomerServiceEvents;
6
+ }
7
+
8
+ export interface CustomerServiceEvents {
9
+ CUSTOMER_CREATED: "customer.created";
10
+ CUSTOMER_UPDATED: "customer.updated";
11
+ CUSTOMER_DELETED: "customer.deleted";
12
+ CUSTOMER_VISIT_CREATED: "customer.visit.created";
13
+ CUSTOMER_INTERACTION_CREATED: "customer.interaction.created";
14
+ CUSTOMER_NOTE_CREATED: "customer.note.created";
15
+ CUSTOMER_SUPPORT_TICKET_CREATED: "customer.support.ticket.created";
16
+ CUSTOMER_REVIEW_SUBMITTED: "customer.review.submitted";
17
+ }
18
+
19
+ // Customer Service Event Types
20
+ export type CustomerEventType =
21
+ | "customer.created"
22
+ | "customer.updated"
23
+ | "customer.deleted"
24
+ | "customer.visit.created"
25
+ | "customer.interaction.created"
26
+ | "customer.note.created"
27
+ | "customer.support.ticket.created"
28
+ | "customer.review.submitted";
29
+
30
+ // Customer Event Payloads
31
+ export interface CustomerCreatedEvent {
32
+ customerId: string;
33
+ userId: string;
34
+ email: string;
35
+ firstName: string;
36
+ lastName: string;
37
+ customerType: string;
38
+ registrationSource: string;
39
+ storeLocationId?: string;
40
+ }
41
+
42
+ export interface CustomerUpdatedEvent {
43
+ customerId: string;
44
+ userId: string;
45
+ changes: Record<string, unknown>;
46
+ previousValues: Record<string, unknown>;
47
+ registrationSourceChanged: boolean;
48
+ }
49
+
50
+ export interface CustomerDeletedEvent {
51
+ customerId: string;
52
+ userId: string;
53
+ email: string;
54
+ customerType: string;
55
+ registrationSource: string;
56
+ totalSpent: string;
57
+ totalOrders: number;
58
+ totalVisits: number;
59
+ totalInteractions: number;
60
+ }
61
+
62
+ export interface CustomerVisitCreatedEvent {
63
+ visitId: string;
64
+ customerId: string;
65
+ visitType: string;
66
+ storeLocationId?: string;
67
+ outcome?: string;
68
+ duration?: number;
69
+ }
70
+
71
+ export interface CustomerInteractionCreatedEvent {
72
+ interactionId: string;
73
+ customerId: string;
74
+ interactionType: string;
75
+ targetType?: string;
76
+ targetId?: string;
77
+ outcome?: string;
78
+ staffId?: string;
79
+ }
80
+
81
+ export interface CustomerNoteCreatedEvent {
82
+ noteId: string;
83
+ customerId: string;
84
+ noteType: string;
85
+ priority: string;
86
+ isAlert: boolean;
87
+ createdBy: string;
88
+ }
89
+
90
+ export interface CustomerSupportTicketCreatedEvent {
91
+ ticketId: string;
92
+ ticketNumber: string;
93
+ customerId: string;
94
+ subject: string;
95
+ category: string;
96
+ priority: string;
97
+ }
98
+
99
+ export interface CustomerReviewSubmittedEvent {
100
+ reviewId: string;
101
+ customerId: string;
102
+ targetType: string;
103
+ targetId: string;
104
+ rating: number;
105
+ isVerifiedPurchase: boolean;
106
+ }