@almadar/server 2.0.0 → 2.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.
Files changed (85) hide show
  1. package/dist/deepagent/__tests__/memory.test.d.ts +6 -0
  2. package/dist/deepagent/__tests__/memory.test.d.ts.map +1 -0
  3. package/dist/deepagent/__tests__/session.test.d.ts +6 -0
  4. package/dist/deepagent/__tests__/session.test.d.ts.map +1 -0
  5. package/dist/deepagent/__tests__/skill-agent.test.d.ts +6 -0
  6. package/dist/deepagent/__tests__/skill-agent.test.d.ts.map +1 -0
  7. package/dist/deepagent/memory.d.ts +17 -0
  8. package/dist/deepagent/memory.d.ts.map +1 -0
  9. package/dist/deepagent/session.d.ts +17 -0
  10. package/dist/deepagent/session.d.ts.map +1 -0
  11. package/dist/deepagent/skill-agent.d.ts +22 -0
  12. package/dist/deepagent/skill-agent.d.ts.map +1 -0
  13. package/dist/index.d.ts +35 -0
  14. package/dist/index.d.ts.map +1 -0
  15. package/dist/index.js +48 -19
  16. package/dist/index.js.map +1 -1
  17. package/dist/lib/db.d.ts +36 -0
  18. package/dist/lib/db.d.ts.map +1 -0
  19. package/dist/lib/debugRouter.d.ts +20 -0
  20. package/dist/lib/debugRouter.d.ts.map +1 -0
  21. package/dist/lib/env.d.ts +16 -0
  22. package/dist/lib/env.d.ts.map +1 -0
  23. package/dist/lib/eventBus.d.ts +44 -0
  24. package/dist/lib/eventBus.d.ts.map +1 -0
  25. package/dist/lib/eventBusTransport.d.ts +143 -0
  26. package/dist/lib/eventBusTransport.d.ts.map +1 -0
  27. package/dist/lib/eventPersistence.d.ts +151 -0
  28. package/dist/lib/eventPersistence.d.ts.map +1 -0
  29. package/dist/lib/index.d.ts +6 -0
  30. package/dist/lib/index.d.ts.map +1 -0
  31. package/dist/lib/index.js +17 -7
  32. package/dist/lib/index.js.map +1 -1
  33. package/dist/lib/logger.d.ts +7 -0
  34. package/dist/lib/logger.d.ts.map +1 -0
  35. package/dist/lib/serviceDiscovery.d.ts +168 -0
  36. package/dist/lib/serviceDiscovery.d.ts.map +1 -0
  37. package/dist/lib/websocket.d.ts +41 -0
  38. package/dist/lib/websocket.d.ts.map +1 -0
  39. package/dist/middleware/__tests__/multi-user.test.d.ts +6 -0
  40. package/dist/middleware/__tests__/multi-user.test.d.ts.map +1 -0
  41. package/dist/middleware/authenticateFirebase.d.ts +4 -0
  42. package/dist/middleware/authenticateFirebase.d.ts.map +1 -0
  43. package/dist/middleware/errorHandler.d.ts +53 -0
  44. package/dist/middleware/errorHandler.d.ts.map +1 -0
  45. package/dist/middleware/index.d.ts +4 -0
  46. package/dist/middleware/index.d.ts.map +1 -0
  47. package/dist/middleware/multi-user.d.ts +34 -0
  48. package/dist/middleware/multi-user.d.ts.map +1 -0
  49. package/dist/middleware/validation.d.ts +15 -0
  50. package/dist/middleware/validation.d.ts.map +1 -0
  51. package/dist/routes/__tests__/observability.test.d.ts +6 -0
  52. package/dist/routes/__tests__/observability.test.d.ts.map +1 -0
  53. package/dist/routes/observability.d.ts +10 -0
  54. package/dist/routes/observability.d.ts.map +1 -0
  55. package/dist/services/DataService.d.ts +66 -0
  56. package/dist/services/DataService.d.ts.map +1 -0
  57. package/dist/services/MockDataService.d.ts +110 -0
  58. package/dist/services/MockDataService.d.ts.map +1 -0
  59. package/dist/services/index.d.ts +8 -0
  60. package/dist/services/index.d.ts.map +1 -0
  61. package/dist/services/index.js +29 -10
  62. package/dist/services/index.js.map +1 -1
  63. package/dist/stores/ChangeSetStore.d.ts +24 -0
  64. package/dist/stores/ChangeSetStore.d.ts.map +1 -0
  65. package/dist/stores/SchemaProtectionService.d.ts +23 -0
  66. package/dist/stores/SchemaProtectionService.d.ts.map +1 -0
  67. package/dist/stores/SchemaStore.d.ts +52 -0
  68. package/dist/stores/SchemaStore.d.ts.map +1 -0
  69. package/dist/stores/SnapshotStore.d.ts +26 -0
  70. package/dist/stores/SnapshotStore.d.ts.map +1 -0
  71. package/dist/stores/ValidationStore.d.ts +19 -0
  72. package/dist/stores/ValidationStore.d.ts.map +1 -0
  73. package/dist/stores/firestoreFormat.d.ts +21 -0
  74. package/dist/stores/firestoreFormat.d.ts.map +1 -0
  75. package/dist/stores/index.d.ts +14 -0
  76. package/dist/stores/index.d.ts.map +1 -0
  77. package/dist/utils/index.d.ts +9 -0
  78. package/dist/utils/index.d.ts.map +1 -0
  79. package/dist/utils/queryFilters.d.ts +77 -0
  80. package/dist/utils/queryFilters.d.ts.map +1 -0
  81. package/dist/websocket/__tests__/state-sync.test.d.ts +6 -0
  82. package/dist/websocket/__tests__/state-sync.test.d.ts.map +1 -0
  83. package/dist/websocket/state-sync.d.ts +39 -0
  84. package/dist/websocket/state-sync.d.ts.map +1 -0
  85. package/package.json +4 -4
@@ -0,0 +1,36 @@
1
+ /**
2
+ * Database Accessors & Initialization
3
+ *
4
+ * This module provides:
5
+ * - `initializeFirebase()` — convenience function to init Firebase from env vars
6
+ * - `getFirestore()`, `getAuth()` — accessors for Firebase services
7
+ * - `db` — lazy Firestore proxy (no eager initialization)
8
+ *
9
+ * The consuming application MUST call `initializeFirebase()` or
10
+ * `admin.initializeApp()` before using any Firebase-dependent features.
11
+ */
12
+ import admin from 'firebase-admin';
13
+ /**
14
+ * Initialize Firebase Admin SDK from environment variables.
15
+ *
16
+ * Reads: FIREBASE_PROJECT_ID, FIREBASE_CLIENT_EMAIL, FIREBASE_PRIVATE_KEY,
17
+ * FIREBASE_SERVICE_ACCOUNT_PATH, FIRESTORE_EMULATOR_HOST
18
+ *
19
+ * Safe to call multiple times — returns existing app if already initialized.
20
+ */
21
+ export declare function initializeFirebase(): admin.app.App;
22
+ /**
23
+ * Get Firestore instance from the pre-initialized Firebase app.
24
+ */
25
+ export declare function getFirestore(): admin.firestore.Firestore;
26
+ /**
27
+ * Get Firebase Auth instance from the pre-initialized Firebase app.
28
+ */
29
+ export declare function getAuth(): admin.auth.Auth;
30
+ export { admin };
31
+ /**
32
+ * Lazy Firestore proxy — resolves on first property access, not at import time.
33
+ * This prevents the "Firebase not initialized" error during module loading.
34
+ */
35
+ export declare const db: admin.firestore.Firestore;
36
+ //# sourceMappingURL=db.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"db.d.ts","sourceRoot":"","sources":["../../src/lib/db.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AAEH,OAAO,KAAK,MAAM,gBAAgB,CAAC;AAEnC;;;;;;;GAOG;AACH,wBAAgB,kBAAkB,IAAI,KAAK,CAAC,GAAG,CAAC,GAAG,CAwDlD;AAgBD;;GAEG;AACH,wBAAgB,YAAY,IAAI,KAAK,CAAC,SAAS,CAAC,SAAS,CAExD;AAED;;GAEG;AACH,wBAAgB,OAAO,IAAI,KAAK,CAAC,IAAI,CAAC,IAAI,CAEzC;AAGD,OAAO,EAAE,KAAK,EAAE,CAAC;AAEjB;;;GAGG;AACH,eAAO,MAAM,EAAE,2BAMb,CAAC"}
@@ -0,0 +1,20 @@
1
+ /**
2
+ * Debug Events Router
3
+ *
4
+ * Provides diagnostic endpoints for inspecting the server EventBus.
5
+ * Only active when NODE_ENV=development.
6
+ *
7
+ * Endpoints:
8
+ * GET /event-log - Recent emitted events with listener counts
9
+ * DELETE /event-log - Clear the event log
10
+ * GET /listeners - Registered listener counts per event
11
+ *
12
+ * @packageDocumentation
13
+ */
14
+ import { Router } from 'express';
15
+ /**
16
+ * Creates an Express router with debug endpoints for the server EventBus.
17
+ * Returns a no-op router in production (no routes registered).
18
+ */
19
+ export declare function debugEventsRouter(): Router;
20
+ //# sourceMappingURL=debugRouter.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"debugRouter.d.ts","sourceRoot":"","sources":["../../src/lib/debugRouter.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;GAYG;AAEH,OAAO,EAAE,MAAM,EAAE,MAAM,SAAS,CAAC;AAGjC;;;GAGG;AACH,wBAAgB,iBAAiB,IAAI,MAAM,CAyB1C"}
@@ -0,0 +1,16 @@
1
+ export declare const env: {
2
+ NODE_ENV: "development" | "production" | "test";
3
+ PORT: number;
4
+ CORS_ORIGIN: string | string[];
5
+ API_PREFIX: string;
6
+ USE_MOCK_DATA: boolean;
7
+ DATABASE_URL?: string | undefined;
8
+ FIREBASE_PROJECT_ID?: string | undefined;
9
+ FIREBASE_CLIENT_EMAIL?: string | undefined;
10
+ FIREBASE_PRIVATE_KEY?: string | undefined;
11
+ FIREBASE_SERVICE_ACCOUNT_PATH?: string | undefined;
12
+ FIRESTORE_EMULATOR_HOST?: string | undefined;
13
+ FIREBASE_AUTH_EMULATOR_HOST?: string | undefined;
14
+ MOCK_SEED?: number | undefined;
15
+ };
16
+ //# sourceMappingURL=env.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"env.d.ts","sourceRoot":"","sources":["../../src/lib/env.ts"],"names":[],"mappings":"AA8CA,eAAO,MAAM,GAAG;;;;;;;;;;;;;;CAAc,CAAC"}
@@ -0,0 +1,44 @@
1
+ /**
2
+ * Server EventBus - Singleton for server-side cross-trait communication
3
+ *
4
+ * This EventBus enables:
5
+ * - Server-side trait event emission after CRUD operations
6
+ * - Server-side trait listeners responding to events
7
+ * - Cross-client event broadcast via WebSocket
8
+ *
9
+ * @packageDocumentation
10
+ */
11
+ type EventHandler = (payload: unknown, meta?: Record<string, unknown>) => void;
12
+ export interface EventLogEntry {
13
+ event: string;
14
+ payload: unknown;
15
+ timestamp: number;
16
+ listenerCount: number;
17
+ wildcardListenerCount: number;
18
+ }
19
+ /**
20
+ * Simple EventBus implementation for server-side events
21
+ */
22
+ export declare class EventBus {
23
+ private handlers;
24
+ private debug;
25
+ private eventLog;
26
+ constructor(options?: {
27
+ debug?: boolean;
28
+ });
29
+ on(event: string, handler: EventHandler): () => void;
30
+ off(event: string, handler: EventHandler): void;
31
+ emit(event: string, payload?: unknown, meta?: Record<string, unknown>): void;
32
+ getRecentEvents(limit?: number): EventLogEntry[];
33
+ clearEventLog(): void;
34
+ getListenerCounts(): Record<string, number>;
35
+ clear(): void;
36
+ }
37
+ export declare function getServerEventBus(): EventBus;
38
+ export declare function resetServerEventBus(): void;
39
+ /**
40
+ * Type-safe event emission helper
41
+ */
42
+ export declare function emitEntityEvent(entityType: string, action: 'CREATED' | 'UPDATED' | 'DELETED', payload: Record<string, unknown>): void;
43
+ export {};
44
+ //# sourceMappingURL=eventBus.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"eventBus.d.ts","sourceRoot":"","sources":["../../src/lib/eventBus.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAEH,KAAK,YAAY,GAAG,CAAC,OAAO,EAAE,OAAO,EAAE,IAAI,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,KAAK,IAAI,CAAC;AAE/E,MAAM,WAAW,aAAa;IAC5B,KAAK,EAAE,MAAM,CAAC;IACd,OAAO,EAAE,OAAO,CAAC;IACjB,SAAS,EAAE,MAAM,CAAC;IAClB,aAAa,EAAE,MAAM,CAAC;IACtB,qBAAqB,EAAE,MAAM,CAAC;CAC/B;AAID;;GAEG;AACH,qBAAa,QAAQ;IACnB,OAAO,CAAC,QAAQ,CAA6C;IAC7D,OAAO,CAAC,KAAK,CAAU;IACvB,OAAO,CAAC,QAAQ,CAAuB;gBAE3B,OAAO,CAAC,EAAE;QAAE,KAAK,CAAC,EAAE,OAAO,CAAA;KAAE;IAIzC,EAAE,CAAC,KAAK,EAAE,MAAM,EAAE,OAAO,EAAE,YAAY,GAAG,MAAM,IAAI;IAYpD,GAAG,CAAC,KAAK,EAAE,MAAM,EAAE,OAAO,EAAE,YAAY,GAAG,IAAI;IAI/C,IAAI,CAAC,KAAK,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,OAAO,EAAE,IAAI,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,IAAI;IAgD5E,eAAe,CAAC,KAAK,SAAK,GAAG,aAAa,EAAE;IAI5C,aAAa,IAAI,IAAI;IAIrB,iBAAiB,IAAI,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC;IAQ3C,KAAK,IAAI,IAAI;CAId;AAOD,wBAAgB,iBAAiB,IAAI,QAAQ,CAO5C;AAED,wBAAgB,mBAAmB,IAAI,IAAI,CAG1C;AAED;;GAEG;AACH,wBAAgB,eAAe,CAC7B,UAAU,EAAE,MAAM,EAClB,MAAM,EAAE,SAAS,GAAG,SAAS,GAAG,SAAS,EACzC,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAC/B,IAAI,CAGN"}
@@ -0,0 +1,143 @@
1
+ /**
2
+ * EventBus Transport Layer — Pluggable backends for distributed event communication
3
+ *
4
+ * Transports enable emit/listen to work across processes:
5
+ * - InMemoryTransport: Default, same-process (no-op relay)
6
+ * - RedisTransport: Redis pub/sub for cross-process communication
7
+ *
8
+ * The DistributedEventBus wraps the existing EventBus and relays events
9
+ * through a transport. Local listeners still fire immediately; the transport
10
+ * handles cross-process delivery.
11
+ *
12
+ * @packageDocumentation
13
+ */
14
+ import { type EventLogEntry } from './eventBus.js';
15
+ /**
16
+ * Message shape sent over the transport wire.
17
+ */
18
+ export interface TransportMessage {
19
+ event: string;
20
+ payload: unknown;
21
+ meta?: Record<string, unknown>;
22
+ /** Source instance ID to prevent echo loops */
23
+ sourceId: string;
24
+ timestamp: number;
25
+ }
26
+ /**
27
+ * Callback invoked when a message arrives from another process.
28
+ */
29
+ export type TransportReceiver = (message: TransportMessage) => void;
30
+ /**
31
+ * Pluggable transport interface for cross-process event delivery.
32
+ */
33
+ export interface IEventBusTransport {
34
+ /** Publish a message to the transport */
35
+ publish(message: TransportMessage): Promise<void>;
36
+ /** Subscribe to incoming messages from other processes */
37
+ subscribe(receiver: TransportReceiver): Promise<void>;
38
+ /** Unsubscribe and clean up resources */
39
+ close(): Promise<void>;
40
+ }
41
+ /**
42
+ * No-op transport for single-process mode.
43
+ * Events stay in the local EventBus only.
44
+ */
45
+ export declare class InMemoryTransport implements IEventBusTransport {
46
+ publish(): Promise<void>;
47
+ subscribe(): Promise<void>;
48
+ close(): Promise<void>;
49
+ }
50
+ /**
51
+ * Options for RedisTransport.
52
+ */
53
+ export interface RedisTransportOptions {
54
+ /** Redis channel name for event relay (default: 'almadar:events') */
55
+ channel?: string;
56
+ /**
57
+ * Publish function — sends serialized message to Redis channel.
58
+ * This is injected so we don't depend on a specific Redis client library.
59
+ */
60
+ publishFn: (channel: string, message: string) => Promise<void>;
61
+ /**
62
+ * Subscribe function — registers a callback for messages on a Redis channel.
63
+ * This is injected so we don't depend on a specific Redis client library.
64
+ */
65
+ subscribeFn: (channel: string, callback: (message: string) => void) => Promise<void>;
66
+ /**
67
+ * Unsubscribe/cleanup function.
68
+ */
69
+ closeFn?: () => Promise<void>;
70
+ }
71
+ /**
72
+ * Redis pub/sub transport for cross-process event delivery.
73
+ *
74
+ * Uses dependency-injected publish/subscribe functions so the transport
75
+ * is not coupled to any specific Redis client library (ioredis, redis, etc.).
76
+ *
77
+ * Usage with ioredis:
78
+ * ```typescript
79
+ * import Redis from 'ioredis';
80
+ * const pub = new Redis(process.env.REDIS_URL);
81
+ * const sub = new Redis(process.env.REDIS_URL);
82
+ *
83
+ * const transport = new RedisTransport({
84
+ * publishFn: (ch, msg) => pub.publish(ch, msg).then(() => {}),
85
+ * subscribeFn: (ch, cb) => { sub.subscribe(ch); sub.on('message', (_, msg) => cb(msg)); return Promise.resolve(); },
86
+ * closeFn: async () => { await pub.quit(); await sub.quit(); },
87
+ * });
88
+ * ```
89
+ */
90
+ export declare class RedisTransport implements IEventBusTransport {
91
+ private channel;
92
+ private publishFn;
93
+ private subscribeFn;
94
+ private closeFn;
95
+ constructor(options: RedisTransportOptions);
96
+ publish(message: TransportMessage): Promise<void>;
97
+ subscribe(receiver: TransportReceiver): Promise<void>;
98
+ close(): Promise<void>;
99
+ }
100
+ /**
101
+ * DistributedEventBus wraps the existing EventBus and relays events
102
+ * through a pluggable transport for cross-process communication.
103
+ *
104
+ * - Local emit → fires local listeners + publishes to transport
105
+ * - Transport message received → fires local listeners (skips re-publish)
106
+ *
107
+ * Drop-in replacement for EventBus — same API, same singleton pattern.
108
+ */
109
+ export declare class DistributedEventBus {
110
+ private localBus;
111
+ private transport;
112
+ private instanceId;
113
+ private isRelaying;
114
+ constructor(options?: {
115
+ debug?: boolean;
116
+ transport?: IEventBusTransport;
117
+ });
118
+ /**
119
+ * Initialize the transport subscription. Call once at startup.
120
+ */
121
+ connect(): Promise<void>;
122
+ /**
123
+ * Emit an event locally and publish to transport for other processes.
124
+ */
125
+ emit(event: string, payload?: unknown, meta?: Record<string, unknown>): void;
126
+ /** Subscribe to an event */
127
+ on(event: string, handler: (payload: unknown, meta?: Record<string, unknown>) => void): () => void;
128
+ /** Unsubscribe from an event */
129
+ off(event: string, handler: (payload: unknown, meta?: Record<string, unknown>) => void): void;
130
+ /** Get recent events (dev diagnostics) */
131
+ getRecentEvents(limit?: number): EventLogEntry[];
132
+ /** Clear event log */
133
+ clearEventLog(): void;
134
+ /** Get listener counts */
135
+ getListenerCounts(): Record<string, number>;
136
+ /** Clear all listeners and log */
137
+ clear(): void;
138
+ /** Disconnect transport */
139
+ disconnect(): Promise<void>;
140
+ /** Get the instance ID (for debugging) */
141
+ getInstanceId(): string;
142
+ }
143
+ //# sourceMappingURL=eventBusTransport.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"eventBusTransport.d.ts","sourceRoot":"","sources":["../../src/lib/eventBusTransport.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;GAYG;AAEH,OAAO,EAAY,KAAK,aAAa,EAAE,MAAM,eAAe,CAAC;AAM7D;;GAEG;AACH,MAAM,WAAW,gBAAgB;IAC/B,KAAK,EAAE,MAAM,CAAC;IACd,OAAO,EAAE,OAAO,CAAC;IACjB,IAAI,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IAC/B,+CAA+C;IAC/C,QAAQ,EAAE,MAAM,CAAC;IACjB,SAAS,EAAE,MAAM,CAAC;CACnB;AAED;;GAEG;AACH,MAAM,MAAM,iBAAiB,GAAG,CAAC,OAAO,EAAE,gBAAgB,KAAK,IAAI,CAAC;AAEpE;;GAEG;AACH,MAAM,WAAW,kBAAkB;IACjC,yCAAyC;IACzC,OAAO,CAAC,OAAO,EAAE,gBAAgB,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IAClD,0DAA0D;IAC1D,SAAS,CAAC,QAAQ,EAAE,iBAAiB,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IACtD,yCAAyC;IACzC,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC,CAAC;CACxB;AAMD;;;GAGG;AACH,qBAAa,iBAAkB,YAAW,kBAAkB;IACpD,OAAO,IAAI,OAAO,CAAC,IAAI,CAAC;IAIxB,SAAS,IAAI,OAAO,CAAC,IAAI,CAAC;IAI1B,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC;CAG7B;AAMD;;GAEG;AACH,MAAM,WAAW,qBAAqB;IACpC,qEAAqE;IACrE,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB;;;OAGG;IACH,SAAS,EAAE,CAAC,OAAO,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;IAC/D;;;OAGG;IACH,WAAW,EAAE,CAAC,OAAO,EAAE,MAAM,EAAE,QAAQ,EAAE,CAAC,OAAO,EAAE,MAAM,KAAK,IAAI,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;IACrF;;OAEG;IACH,OAAO,CAAC,EAAE,MAAM,OAAO,CAAC,IAAI,CAAC,CAAC;CAC/B;AAED;;;;;;;;;;;;;;;;;;GAkBG;AACH,qBAAa,cAAe,YAAW,kBAAkB;IACvD,OAAO,CAAC,OAAO,CAAS;IACxB,OAAO,CAAC,SAAS,CAAqC;IACtD,OAAO,CAAC,WAAW,CAAuC;IAC1D,OAAO,CAAC,OAAO,CAAmC;gBAEtC,OAAO,EAAE,qBAAqB;IAOpC,OAAO,CAAC,OAAO,EAAE,gBAAgB,GAAG,OAAO,CAAC,IAAI,CAAC;IAKjD,SAAS,CAAC,QAAQ,EAAE,iBAAiB,GAAG,OAAO,CAAC,IAAI,CAAC;IAWrD,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC;CAK7B;AAQD;;;;;;;;GAQG;AACH,qBAAa,mBAAmB;IAC9B,OAAO,CAAC,QAAQ,CAAW;IAC3B,OAAO,CAAC,SAAS,CAAqB;IACtC,OAAO,CAAC,UAAU,CAAS;IAC3B,OAAO,CAAC,UAAU,CAAS;gBAEf,OAAO,CAAC,EAAE;QACpB,KAAK,CAAC,EAAE,OAAO,CAAC;QAChB,SAAS,CAAC,EAAE,kBAAkB,CAAC;KAChC;IAMD;;OAEG;IACG,OAAO,IAAI,OAAO,CAAC,IAAI,CAAC;IAiB9B;;OAEG;IACH,IAAI,CAAC,KAAK,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,OAAO,EAAE,IAAI,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,IAAI;IAoB5E,4BAA4B;IAC5B,EAAE,CAAC,KAAK,EAAE,MAAM,EAAE,OAAO,EAAE,CAAC,OAAO,EAAE,OAAO,EAAE,IAAI,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,KAAK,IAAI,GAAG,MAAM,IAAI;IAIlG,gCAAgC;IAChC,GAAG,CAAC,KAAK,EAAE,MAAM,EAAE,OAAO,EAAE,CAAC,OAAO,EAAE,OAAO,EAAE,IAAI,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,KAAK,IAAI,GAAG,IAAI;IAI7F,0CAA0C;IAC1C,eAAe,CAAC,KAAK,CAAC,EAAE,MAAM,GAAG,aAAa,EAAE;IAIhD,sBAAsB;IACtB,aAAa,IAAI,IAAI;IAIrB,0BAA0B;IAC1B,iBAAiB,IAAI,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC;IAI3C,kCAAkC;IAClC,KAAK,IAAI,IAAI;IAIb,2BAA2B;IACrB,UAAU,IAAI,OAAO,CAAC,IAAI,CAAC;IAIjC,0CAA0C;IAC1C,aAAa,IAAI,MAAM;CAGxB"}
@@ -0,0 +1,151 @@
1
+ /**
2
+ * Event Persistence Layer — Optional durable event storage for replay and debugging
3
+ *
4
+ * Wraps the EventBus to persist emitted events. Supports:
5
+ * - In-memory storage (default, for development)
6
+ * - Configurable retention (TTL-based cleanup)
7
+ * - Replay API: query events by name, time range, or source
8
+ * - Required for saga compensation and distributed debugging
9
+ *
10
+ * @packageDocumentation
11
+ */
12
+ /** Persisted event record */
13
+ export interface PersistedEvent {
14
+ /** Unique event ID */
15
+ id: string;
16
+ /** Event name (e.g., 'LLM_RESPONSE') */
17
+ eventName: string;
18
+ /** Event payload */
19
+ payload: unknown;
20
+ /** Source orbital/service that emitted this event */
21
+ source: string;
22
+ /** Timestamp when event was emitted */
23
+ timestamp: number;
24
+ /** Trace ID for distributed tracing correlation */
25
+ traceId: string;
26
+ /** Optional metadata */
27
+ meta?: Record<string, unknown>;
28
+ }
29
+ /** Query filters for replaying events */
30
+ export interface EventQuery {
31
+ /** Filter by event name (exact match) */
32
+ eventName?: string;
33
+ /** Filter by source orbital */
34
+ source?: string;
35
+ /** Filter by trace ID */
36
+ traceId?: string;
37
+ /** Events after this timestamp */
38
+ after?: number;
39
+ /** Events before this timestamp */
40
+ before?: number;
41
+ /** Maximum number of events to return */
42
+ limit?: number;
43
+ /** Sort order */
44
+ order?: 'asc' | 'desc';
45
+ }
46
+ /** Options for the event persistence layer */
47
+ export interface EventPersistenceOptions {
48
+ /** Whether persistence is enabled (default: true) */
49
+ enabled?: boolean;
50
+ /** TTL in milliseconds for event retention (default: 24 hours) */
51
+ retentionMs?: number;
52
+ /** Maximum events to store (default: 10000) */
53
+ maxEvents?: number;
54
+ /** Cleanup interval in milliseconds (default: 5 minutes) */
55
+ cleanupIntervalMs?: number;
56
+ /** Default source identifier */
57
+ defaultSource?: string;
58
+ }
59
+ /** Storage backend interface */
60
+ export interface IEventStore {
61
+ /** Persist an event */
62
+ store(event: PersistedEvent): Promise<void>;
63
+ /** Query events */
64
+ query(filters: EventQuery): Promise<PersistedEvent[]>;
65
+ /** Get a single event by ID */
66
+ get(id: string): Promise<PersistedEvent | null>;
67
+ /** Delete events older than timestamp */
68
+ deleteOlderThan(timestamp: number): Promise<number>;
69
+ /** Get total event count */
70
+ count(): Promise<number>;
71
+ /** Clear all stored events */
72
+ clear(): Promise<void>;
73
+ }
74
+ /**
75
+ * In-memory event store for development and testing.
76
+ */
77
+ export declare class InMemoryEventStore implements IEventStore {
78
+ private events;
79
+ private index;
80
+ store(event: PersistedEvent): Promise<void>;
81
+ query(filters: EventQuery): Promise<PersistedEvent[]>;
82
+ get(id: string): Promise<PersistedEvent | null>;
83
+ deleteOlderThan(timestamp: number): Promise<number>;
84
+ count(): Promise<number>;
85
+ clear(): Promise<void>;
86
+ }
87
+ /**
88
+ * Event persistence layer that can be attached to an EventBus.
89
+ *
90
+ * Usage:
91
+ * ```typescript
92
+ * const persistence = new EventPersistence({ retentionMs: 3600000 });
93
+ * persistence.startCleanup();
94
+ *
95
+ * // Persist an event
96
+ * await persistence.persist('LLM_RESPONSE', { content: '...' }, { source: 'llm-service' });
97
+ *
98
+ * // Replay events
99
+ * const events = await persistence.replay({ eventName: 'LLM_RESPONSE', limit: 10 });
100
+ *
101
+ * // Stop cleanup on shutdown
102
+ * persistence.stopCleanup();
103
+ * ```
104
+ */
105
+ export declare class EventPersistence {
106
+ private store;
107
+ private options;
108
+ private cleanupTimer;
109
+ constructor(options?: EventPersistenceOptions, store?: IEventStore);
110
+ /**
111
+ * Persist an event.
112
+ */
113
+ persist(eventName: string, payload: unknown, meta?: {
114
+ source?: string;
115
+ traceId?: string;
116
+ [key: string]: unknown;
117
+ }): Promise<PersistedEvent>;
118
+ /**
119
+ * Replay events matching the query filters.
120
+ */
121
+ replay(query: EventQuery): Promise<PersistedEvent[]>;
122
+ /**
123
+ * Get a single event by ID.
124
+ */
125
+ getEvent(id: string): Promise<PersistedEvent | null>;
126
+ /**
127
+ * Get event count.
128
+ */
129
+ getEventCount(): Promise<number>;
130
+ /**
131
+ * Run cleanup — delete events older than retention period.
132
+ */
133
+ cleanup(): Promise<number>;
134
+ /**
135
+ * Start periodic cleanup timer.
136
+ */
137
+ startCleanup(): void;
138
+ /**
139
+ * Stop periodic cleanup timer.
140
+ */
141
+ stopCleanup(): void;
142
+ /**
143
+ * Clear all persisted events.
144
+ */
145
+ clear(): Promise<void>;
146
+ /**
147
+ * Get the underlying store (for testing or custom queries).
148
+ */
149
+ getStore(): IEventStore;
150
+ }
151
+ //# sourceMappingURL=eventPersistence.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"eventPersistence.d.ts","sourceRoot":"","sources":["../../src/lib/eventPersistence.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,6 @@
1
+ export { env } from './env.js';
2
+ export { getFirestore, getAuth, admin } from './db.js';
3
+ export { logger } from './logger.js';
4
+ export { getServerEventBus, resetServerEventBus, emitEntityEvent } from './eventBus.js';
5
+ export { setupEventBroadcast, getWebSocketServer, closeWebSocketServer, getConnectedClientCount, } from './websocket.js';
6
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/lib/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,GAAG,EAAE,MAAM,UAAU,CAAC;AAC/B,OAAO,EAAE,YAAY,EAAE,OAAO,EAAE,KAAK,EAAE,MAAM,SAAS,CAAC;AACvD,OAAO,EAAE,MAAM,EAAE,MAAM,aAAa,CAAC;AACrC,OAAO,EAAE,iBAAiB,EAAE,mBAAmB,EAAE,eAAe,EAAE,MAAM,eAAe,CAAC;AACxF,OAAO,EACL,mBAAmB,EACnB,kBAAkB,EAClB,oBAAoB,EACpB,uBAAuB,GACxB,MAAM,gBAAgB,CAAC"}
package/dist/lib/index.js CHANGED
@@ -181,12 +181,22 @@ var EventBus = class {
181
181
  this.eventLog.length = 0;
182
182
  }
183
183
  };
184
- var serverEventBus = new EventBus({
185
- debug: process.env.NODE_ENV === "development"
186
- });
184
+ var _serverEventBus = null;
185
+ function getServerEventBus() {
186
+ if (!_serverEventBus) {
187
+ _serverEventBus = new EventBus({
188
+ debug: process.env.NODE_ENV === "development"
189
+ });
190
+ }
191
+ return _serverEventBus;
192
+ }
193
+ function resetServerEventBus() {
194
+ _serverEventBus?.clear();
195
+ _serverEventBus = null;
196
+ }
187
197
  function emitEntityEvent(entityType, action, payload) {
188
198
  const eventType = `${entityType.toUpperCase()}_${action}`;
189
- serverEventBus.emit(eventType, payload, { orbital: entityType });
199
+ getServerEventBus().emit(eventType, payload, { orbital: entityType });
190
200
  }
191
201
  var wss = null;
192
202
  function setupEventBroadcast(server, path = "/ws/events") {
@@ -211,7 +221,7 @@ function setupEventBroadcast(server, path = "/ws/events") {
211
221
  const message = JSON.parse(data.toString());
212
222
  logger.debug(`[WebSocket] Received from ${clientId}:`, message);
213
223
  if (message.type && message.payload) {
214
- serverEventBus.emit(message.type, message.payload, {
224
+ getServerEventBus().emit(message.type, message.payload, {
215
225
  orbital: "client",
216
226
  entity: clientId
217
227
  });
@@ -227,7 +237,7 @@ function setupEventBroadcast(server, path = "/ws/events") {
227
237
  logger.error(`[WebSocket] Client error:`, error);
228
238
  });
229
239
  });
230
- serverEventBus.on("*", (event) => {
240
+ getServerEventBus().on("*", (event) => {
231
241
  if (!wss) return;
232
242
  const typedEvent = event;
233
243
  const message = JSON.stringify({
@@ -273,6 +283,6 @@ function getConnectedClientCount() {
273
283
  return wss.clients.size;
274
284
  }
275
285
 
276
- export { closeWebSocketServer, emitEntityEvent, env, getAuth, getConnectedClientCount, getFirestore, getWebSocketServer, logger, serverEventBus, setupEventBroadcast };
286
+ export { closeWebSocketServer, emitEntityEvent, env, getAuth, getConnectedClientCount, getFirestore, getServerEventBus, getWebSocketServer, logger, resetServerEventBus, setupEventBroadcast };
277
287
  //# sourceMappingURL=index.js.map
278
288
  //# sourceMappingURL=index.js.map
@@ -1 +1 @@
1
- {"version":3,"sources":["../../src/lib/env.ts","../../src/lib/db.ts","../../src/lib/logger.ts","../../src/lib/eventBus.ts","../../src/lib/websocket.ts"],"names":[],"mappings":"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","file":"index.js","sourcesContent":["import { z } from 'zod';\nimport dotenv from 'dotenv';\n\n// Load environment variables\ndotenv.config();\n\nconst envSchema = z.object({\n NODE_ENV: z.enum(['development', 'production', 'test']).default('development'),\n PORT: z\n .string()\n .default('3030')\n .transform((val) => parseInt(val, 10)),\n CORS_ORIGIN: z\n .string()\n .default('http://localhost:5173')\n .transform((val) => (val.includes(',') ? val.split(',').map((s) => s.trim()) : val)),\n \n // Database (Prisma/SQL) - optional\n DATABASE_URL: z.string().optional(),\n \n // Firebase/Firestore configuration\n FIREBASE_PROJECT_ID: z.string().optional(),\n FIREBASE_CLIENT_EMAIL: z.string().optional(),\n FIREBASE_PRIVATE_KEY: z.string().optional(),\n FIREBASE_SERVICE_ACCOUNT_PATH: z.string().optional(),\n FIRESTORE_EMULATOR_HOST: z.string().optional(),\n FIREBASE_AUTH_EMULATOR_HOST: z.string().optional(),\n \n // API configuration\n API_PREFIX: z.string().default('/api'),\n\n // Mock data configuration\n USE_MOCK_DATA: z.string().default('true').transform((v) => v === 'true'),\n MOCK_SEED: z\n .string()\n .optional()\n .transform((v) => (v ? parseInt(v, 10) : undefined)),\n});\n\nconst parsed = envSchema.safeParse(process.env);\n\nif (!parsed.success) {\n console.error('❌ Invalid environment variables:', parsed.error.flatten().fieldErrors);\n throw new Error('Invalid environment variables');\n}\n\nexport const env = parsed.data;\n","/**\n * Database Accessors & Initialization\n *\n * This module provides:\n * - `initializeFirebase()` — convenience function to init Firebase from env vars\n * - `getFirestore()`, `getAuth()` — accessors for Firebase services\n * - `db` — lazy Firestore proxy (no eager initialization)\n *\n * The consuming application MUST call `initializeFirebase()` or\n * `admin.initializeApp()` before using any Firebase-dependent features.\n */\n\nimport admin from 'firebase-admin';\n\n/**\n * Initialize Firebase Admin SDK from environment variables.\n *\n * Reads: FIREBASE_PROJECT_ID, FIREBASE_CLIENT_EMAIL, FIREBASE_PRIVATE_KEY,\n * FIREBASE_SERVICE_ACCOUNT_PATH, FIRESTORE_EMULATOR_HOST\n *\n * Safe to call multiple times — returns existing app if already initialized.\n */\nexport function initializeFirebase(): admin.app.App {\n // Already initialized — return existing app\n if (admin.apps.length > 0) {\n return admin.app();\n }\n\n const projectId = process.env.FIREBASE_PROJECT_ID;\n const emulatorHost = process.env.FIRESTORE_EMULATOR_HOST;\n\n // Emulator mode — no credentials needed\n if (emulatorHost) {\n const app = admin.initializeApp({\n projectId: projectId || 'demo-project',\n });\n console.log(`Firebase Admin initialized for emulator: ${emulatorHost}`);\n return app;\n }\n\n // Service account file\n const serviceAccountPath = process.env.FIREBASE_SERVICE_ACCOUNT_PATH;\n if (serviceAccountPath) {\n // eslint-disable-next-line @typescript-eslint/no-var-requires\n const serviceAccount = require(serviceAccountPath);\n return admin.initializeApp({\n credential: admin.credential.cert(serviceAccount),\n projectId,\n });\n }\n\n // Inline credentials\n const clientEmail = process.env.FIREBASE_CLIENT_EMAIL;\n const privateKey = process.env.FIREBASE_PRIVATE_KEY;\n if (projectId && clientEmail && privateKey) {\n return admin.initializeApp({\n credential: admin.credential.cert({\n projectId,\n clientEmail,\n privateKey: privateKey.replace(/\\\\n/g, '\\n'),\n }),\n projectId,\n });\n }\n\n // Application default credentials (Cloud Run, etc.)\n if (projectId) {\n return admin.initializeApp({\n credential: admin.credential.applicationDefault(),\n projectId,\n });\n }\n\n throw new Error(\n '@almadar/server: Cannot initialize Firebase — no credentials found. ' +\n 'Set FIREBASE_PROJECT_ID + FIREBASE_CLIENT_EMAIL + FIREBASE_PRIVATE_KEY, ' +\n 'or FIREBASE_SERVICE_ACCOUNT_PATH, or FIRESTORE_EMULATOR_HOST.'\n );\n}\n\n/**\n * Get the initialized Firebase app.\n * Throws if Firebase Admin SDK has not been initialized.\n */\nfunction getApp(): admin.app.App {\n if (admin.apps.length === 0) {\n throw new Error(\n '@almadar/server: Firebase Admin SDK is not initialized. ' +\n 'Call initializeFirebase() or admin.initializeApp() before using @almadar/server.'\n );\n }\n return admin.app();\n}\n\n/**\n * Get Firestore instance from the pre-initialized Firebase app.\n */\nexport function getFirestore(): admin.firestore.Firestore {\n return getApp().firestore();\n}\n\n/**\n * Get Firebase Auth instance from the pre-initialized Firebase app.\n */\nexport function getAuth(): admin.auth.Auth {\n return getApp().auth();\n}\n\n// Re-export admin for convenience\nexport { admin };\n\n/**\n * Lazy Firestore proxy — resolves on first property access, not at import time.\n * This prevents the \"Firebase not initialized\" error during module loading.\n */\nexport const db = new Proxy({} as admin.firestore.Firestore, {\n get(_target, prop, receiver) {\n const firestore = getFirestore();\n const value = Reflect.get(firestore, prop, receiver);\n return typeof value === 'function' ? value.bind(firestore) : value;\n },\n});\n","import { env } from './env.js';\n\ntype LogLevel = 'debug' | 'info' | 'warn' | 'error';\n\nconst colors = {\n debug: '\\x1b[36m', // Cyan\n info: '\\x1b[32m', // Green\n warn: '\\x1b[33m', // Yellow\n error: '\\x1b[31m', // Red\n reset: '\\x1b[0m',\n};\n\nconst shouldLog = (level: LogLevel): boolean => {\n const levels: LogLevel[] = ['debug', 'info', 'warn', 'error'];\n const minLevel = env.NODE_ENV === 'production' ? 'info' : 'debug';\n return levels.indexOf(level) >= levels.indexOf(minLevel);\n};\n\nconst formatMessage = (level: LogLevel, message: string, meta?: unknown): string => {\n const timestamp = new Date().toISOString();\n const color = colors[level];\n const prefix = `${color}[${level.toUpperCase()}]${colors.reset}`;\n const metaStr = meta ? ` ${JSON.stringify(meta)}` : '';\n return `${timestamp} ${prefix} ${message}${metaStr}`;\n};\n\nexport const logger = {\n debug: (message: string, meta?: unknown) => {\n if (shouldLog('debug')) {\n console.log(formatMessage('debug', message, meta));\n }\n },\n info: (message: string, meta?: unknown) => {\n if (shouldLog('info')) {\n console.log(formatMessage('info', message, meta));\n }\n },\n warn: (message: string, meta?: unknown) => {\n if (shouldLog('warn')) {\n console.warn(formatMessage('warn', message, meta));\n }\n },\n error: (message: string, meta?: unknown) => {\n if (shouldLog('error')) {\n console.error(formatMessage('error', message, meta));\n }\n },\n};\n","/**\n * Server EventBus - Singleton for server-side cross-trait communication\n *\n * This EventBus enables:\n * - Server-side trait event emission after CRUD operations\n * - Server-side trait listeners responding to events\n * - Cross-client event broadcast via WebSocket\n *\n * @packageDocumentation\n */\n\ntype EventHandler = (payload: unknown, meta?: Record<string, unknown>) => void;\n\nexport interface EventLogEntry {\n event: string;\n payload: unknown;\n timestamp: number;\n listenerCount: number;\n wildcardListenerCount: number;\n}\n\nconst MAX_EVENT_LOG = 200;\n\n/**\n * Simple EventBus implementation for server-side events\n */\nexport class EventBus {\n private handlers: Map<string, Set<EventHandler>> = new Map();\n private debug: boolean;\n private eventLog: EventLogEntry[] = [];\n\n constructor(options?: { debug?: boolean }) {\n this.debug = options?.debug ?? false;\n }\n\n on(event: string, handler: EventHandler): () => void {\n if (!this.handlers.has(event)) {\n this.handlers.set(event, new Set());\n }\n this.handlers.get(event)!.add(handler);\n\n // Return unsubscribe function\n return () => {\n this.handlers.get(event)?.delete(handler);\n };\n }\n\n off(event: string, handler: EventHandler): void {\n this.handlers.get(event)?.delete(handler);\n }\n\n emit(event: string, payload?: unknown, meta?: Record<string, unknown>): void {\n if (this.debug) {\n console.log(`[EventBus] Emitting ${event}:`, payload);\n }\n\n const handlers = this.handlers.get(event);\n const listenerCount = handlers?.size ?? 0;\n if (handlers) {\n handlers.forEach(handler => {\n try {\n handler(payload, meta);\n } catch (err) {\n console.error(`[EventBus] Error in handler for ${event}:`, err);\n }\n });\n }\n\n // Wildcard subscribers receive all events (used by WebSocket broadcast)\n let wildcardListenerCount = 0;\n if (event !== '*') {\n const wildcardHandlers = this.handlers.get('*');\n wildcardListenerCount = wildcardHandlers?.size ?? 0;\n if (wildcardHandlers) {\n wildcardHandlers.forEach(handler => {\n try {\n handler({ type: event, payload, timestamp: Date.now() }, meta);\n } catch (err) {\n console.error(`[EventBus] Error in wildcard handler for ${event}:`, err);\n }\n });\n }\n }\n\n // Record event in log (dev diagnostics)\n if (this.debug) {\n this.eventLog.push({\n event,\n payload,\n timestamp: Date.now(),\n listenerCount,\n wildcardListenerCount,\n });\n if (this.eventLog.length > MAX_EVENT_LOG) {\n this.eventLog.splice(0, this.eventLog.length - MAX_EVENT_LOG);\n }\n }\n }\n\n getRecentEvents(limit = 50): EventLogEntry[] {\n return this.eventLog.slice(-limit);\n }\n\n clearEventLog(): void {\n this.eventLog.length = 0;\n }\n\n getListenerCounts(): Record<string, number> {\n const counts: Record<string, number> = {};\n this.handlers.forEach((handlers, event) => {\n counts[event] = handlers.size;\n });\n return counts;\n }\n\n clear(): void {\n this.handlers.clear();\n this.eventLog.length = 0;\n }\n}\n\n/**\n * Singleton EventBus instance for server-side event communication.\n */\nexport const serverEventBus = new EventBus({\n debug: process.env.NODE_ENV === 'development',\n});\n\n/**\n * Type-safe event emission helper\n */\nexport function emitEntityEvent(\n entityType: string,\n action: 'CREATED' | 'UPDATED' | 'DELETED',\n payload: Record<string, unknown>\n): void {\n const eventType = `${entityType.toUpperCase()}_${action}`;\n serverEventBus.emit(eventType, payload, { orbital: entityType });\n}\n","/**\n * WebSocket Event Broadcast - Cross-client event synchronization\n *\n * Broadcasts server-side events to all connected clients via WebSocket.\n * This enables real-time updates across multiple browser clients.\n *\n * @packageDocumentation\n */\n\nimport { WebSocketServer, WebSocket, type RawData } from 'ws';\nimport type { Server, IncomingMessage } from 'http';\nimport { serverEventBus } from './eventBus.js';\nimport { logger } from './logger.js';\n\n/**\n * Event structure for broadcasting\n */\ninterface BroadcastEvent {\n type: string;\n payload?: unknown;\n timestamp?: number;\n source?: Record<string, unknown>;\n}\n\n/**\n * WebSocket server instance (singleton)\n */\nlet wss: WebSocketServer | null = null;\n\n/**\n * Setup WebSocket server for event broadcasting.\n *\n * Listens to all server events via wildcard and broadcasts to connected clients.\n *\n * @param server - HTTP server to attach WebSocket to\n * @param path - WebSocket endpoint path (default: '/ws/events')\n *\n * @example\n * ```typescript\n * import { createServer } from 'http';\n * import { setupEventBroadcast } from '@/lib/websocket';\n *\n * const server = createServer(app);\n * setupEventBroadcast(server);\n * ```\n */\nexport function setupEventBroadcast(server: Server, path: string = '/ws/events'): WebSocketServer {\n if (wss) {\n logger.warn('[WebSocket] Server already initialized');\n return wss;\n }\n\n wss = new WebSocketServer({ server, path });\n\n logger.info(`[WebSocket] Server listening at ${path}`);\n\n // Handle new connections\n wss.on('connection', (ws: WebSocket, req: IncomingMessage) => {\n const clientId = req.headers['sec-websocket-key'] || 'unknown';\n logger.debug(`[WebSocket] Client connected: ${clientId}`);\n\n // Send welcome message\n ws.send(\n JSON.stringify({\n type: 'CONNECTED',\n timestamp: Date.now(),\n message: 'Connected to event stream',\n })\n );\n\n // Handle client messages (for future bidirectional communication)\n ws.on('message', (data: RawData) => {\n try {\n const message = JSON.parse(data.toString());\n logger.debug(`[WebSocket] Received from ${clientId}:`, message);\n\n // Handle client-to-server events if needed\n if (message.type && message.payload) {\n // Emit to server event bus with client source\n serverEventBus.emit(message.type, message.payload, {\n orbital: 'client',\n entity: clientId,\n });\n }\n } catch (error) {\n logger.error(`[WebSocket] Failed to parse message:`, error);\n }\n });\n\n ws.on('close', () => {\n logger.debug(`[WebSocket] Client disconnected: ${clientId}`);\n });\n\n ws.on('error', (error: Error) => {\n logger.error(`[WebSocket] Client error:`, error);\n });\n });\n\n // Subscribe to all server events and broadcast to clients\n serverEventBus.on('*', (event: unknown) => {\n if (!wss) return;\n\n const typedEvent = event as BroadcastEvent;\n const message = JSON.stringify({\n type: typedEvent.type,\n payload: typedEvent.payload,\n timestamp: typedEvent.timestamp,\n source: typedEvent.source,\n });\n\n let broadcastCount = 0;\n wss.clients.forEach((client: WebSocket) => {\n if (client.readyState === WebSocket.OPEN) {\n client.send(message);\n broadcastCount++;\n }\n });\n\n if (broadcastCount > 0) {\n logger.debug(`[WebSocket] Broadcast ${typedEvent.type} to ${broadcastCount} client(s)`);\n }\n });\n\n return wss;\n}\n\n/**\n * Get the WebSocket server instance (for testing or advanced usage)\n */\nexport function getWebSocketServer(): WebSocketServer | null {\n return wss;\n}\n\n/**\n * Close the WebSocket server\n */\nexport function closeWebSocketServer(): Promise<void> {\n return new Promise((resolve, reject) => {\n if (!wss) {\n resolve();\n return;\n }\n\n wss.close((err?: Error) => {\n if (err) {\n reject(err);\n } else {\n wss = null;\n resolve();\n }\n });\n });\n}\n\n/**\n * Get connected client count\n */\nexport function getConnectedClientCount(): number {\n if (!wss) return 0;\n return wss.clients.size;\n}\n"]}
1
+ {"version":3,"sources":["../../src/lib/env.ts","../../src/lib/db.ts","../../src/lib/logger.ts","../../src/lib/eventBus.ts","../../src/lib/websocket.ts"],"names":[],"mappings":"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","file":"index.js","sourcesContent":["import { z } from 'zod';\nimport dotenv from 'dotenv';\n\n// Load environment variables\ndotenv.config();\n\nconst envSchema = z.object({\n NODE_ENV: z.enum(['development', 'production', 'test']).default('development'),\n PORT: z\n .string()\n .default('3030')\n .transform((val) => parseInt(val, 10)),\n CORS_ORIGIN: z\n .string()\n .default('http://localhost:5173')\n .transform((val) => (val.includes(',') ? val.split(',').map((s) => s.trim()) : val)),\n \n // Database (Prisma/SQL) - optional\n DATABASE_URL: z.string().optional(),\n \n // Firebase/Firestore configuration\n FIREBASE_PROJECT_ID: z.string().optional(),\n FIREBASE_CLIENT_EMAIL: z.string().optional(),\n FIREBASE_PRIVATE_KEY: z.string().optional(),\n FIREBASE_SERVICE_ACCOUNT_PATH: z.string().optional(),\n FIRESTORE_EMULATOR_HOST: z.string().optional(),\n FIREBASE_AUTH_EMULATOR_HOST: z.string().optional(),\n \n // API configuration\n API_PREFIX: z.string().default('/api'),\n\n // Mock data configuration\n USE_MOCK_DATA: z.string().default('true').transform((v) => v === 'true'),\n MOCK_SEED: z\n .string()\n .optional()\n .transform((v) => (v ? parseInt(v, 10) : undefined)),\n});\n\nconst parsed = envSchema.safeParse(process.env);\n\nif (!parsed.success) {\n console.error('❌ Invalid environment variables:', parsed.error.flatten().fieldErrors);\n throw new Error('Invalid environment variables');\n}\n\nexport const env = parsed.data;\n","/**\n * Database Accessors & Initialization\n *\n * This module provides:\n * - `initializeFirebase()` — convenience function to init Firebase from env vars\n * - `getFirestore()`, `getAuth()` — accessors for Firebase services\n * - `db` — lazy Firestore proxy (no eager initialization)\n *\n * The consuming application MUST call `initializeFirebase()` or\n * `admin.initializeApp()` before using any Firebase-dependent features.\n */\n\nimport admin from 'firebase-admin';\n\n/**\n * Initialize Firebase Admin SDK from environment variables.\n *\n * Reads: FIREBASE_PROJECT_ID, FIREBASE_CLIENT_EMAIL, FIREBASE_PRIVATE_KEY,\n * FIREBASE_SERVICE_ACCOUNT_PATH, FIRESTORE_EMULATOR_HOST\n *\n * Safe to call multiple times — returns existing app if already initialized.\n */\nexport function initializeFirebase(): admin.app.App {\n // Already initialized — return existing app\n if (admin.apps.length > 0) {\n return admin.app();\n }\n\n const projectId = process.env.FIREBASE_PROJECT_ID;\n const emulatorHost = process.env.FIRESTORE_EMULATOR_HOST;\n\n // Emulator mode — no credentials needed\n if (emulatorHost) {\n const app = admin.initializeApp({\n projectId: projectId || 'demo-project',\n });\n console.log(`Firebase Admin initialized for emulator: ${emulatorHost}`);\n return app;\n }\n\n // Service account file\n const serviceAccountPath = process.env.FIREBASE_SERVICE_ACCOUNT_PATH;\n if (serviceAccountPath) {\n // eslint-disable-next-line @typescript-eslint/no-var-requires\n const serviceAccount = require(serviceAccountPath);\n return admin.initializeApp({\n credential: admin.credential.cert(serviceAccount),\n projectId,\n });\n }\n\n // Inline credentials\n const clientEmail = process.env.FIREBASE_CLIENT_EMAIL;\n const privateKey = process.env.FIREBASE_PRIVATE_KEY;\n if (projectId && clientEmail && privateKey) {\n return admin.initializeApp({\n credential: admin.credential.cert({\n projectId,\n clientEmail,\n privateKey: privateKey.replace(/\\\\n/g, '\\n'),\n }),\n projectId,\n });\n }\n\n // Application default credentials (Cloud Run, etc.)\n if (projectId) {\n return admin.initializeApp({\n credential: admin.credential.applicationDefault(),\n projectId,\n });\n }\n\n throw new Error(\n '@almadar/server: Cannot initialize Firebase — no credentials found. ' +\n 'Set FIREBASE_PROJECT_ID + FIREBASE_CLIENT_EMAIL + FIREBASE_PRIVATE_KEY, ' +\n 'or FIREBASE_SERVICE_ACCOUNT_PATH, or FIRESTORE_EMULATOR_HOST.'\n );\n}\n\n/**\n * Get the initialized Firebase app.\n * Throws if Firebase Admin SDK has not been initialized.\n */\nfunction getApp(): admin.app.App {\n if (admin.apps.length === 0) {\n throw new Error(\n '@almadar/server: Firebase Admin SDK is not initialized. ' +\n 'Call initializeFirebase() or admin.initializeApp() before using @almadar/server.'\n );\n }\n return admin.app();\n}\n\n/**\n * Get Firestore instance from the pre-initialized Firebase app.\n */\nexport function getFirestore(): admin.firestore.Firestore {\n return getApp().firestore();\n}\n\n/**\n * Get Firebase Auth instance from the pre-initialized Firebase app.\n */\nexport function getAuth(): admin.auth.Auth {\n return getApp().auth();\n}\n\n// Re-export admin for convenience\nexport { admin };\n\n/**\n * Lazy Firestore proxy — resolves on first property access, not at import time.\n * This prevents the \"Firebase not initialized\" error during module loading.\n */\nexport const db = new Proxy({} as admin.firestore.Firestore, {\n get(_target, prop, receiver) {\n const firestore = getFirestore();\n const value = Reflect.get(firestore, prop, receiver);\n return typeof value === 'function' ? value.bind(firestore) : value;\n },\n});\n","import { env } from './env.js';\n\ntype LogLevel = 'debug' | 'info' | 'warn' | 'error';\n\nconst colors = {\n debug: '\\x1b[36m', // Cyan\n info: '\\x1b[32m', // Green\n warn: '\\x1b[33m', // Yellow\n error: '\\x1b[31m', // Red\n reset: '\\x1b[0m',\n};\n\nconst shouldLog = (level: LogLevel): boolean => {\n const levels: LogLevel[] = ['debug', 'info', 'warn', 'error'];\n const minLevel = env.NODE_ENV === 'production' ? 'info' : 'debug';\n return levels.indexOf(level) >= levels.indexOf(minLevel);\n};\n\nconst formatMessage = (level: LogLevel, message: string, meta?: unknown): string => {\n const timestamp = new Date().toISOString();\n const color = colors[level];\n const prefix = `${color}[${level.toUpperCase()}]${colors.reset}`;\n const metaStr = meta ? ` ${JSON.stringify(meta)}` : '';\n return `${timestamp} ${prefix} ${message}${metaStr}`;\n};\n\nexport const logger = {\n debug: (message: string, meta?: unknown) => {\n if (shouldLog('debug')) {\n console.log(formatMessage('debug', message, meta));\n }\n },\n info: (message: string, meta?: unknown) => {\n if (shouldLog('info')) {\n console.log(formatMessage('info', message, meta));\n }\n },\n warn: (message: string, meta?: unknown) => {\n if (shouldLog('warn')) {\n console.warn(formatMessage('warn', message, meta));\n }\n },\n error: (message: string, meta?: unknown) => {\n if (shouldLog('error')) {\n console.error(formatMessage('error', message, meta));\n }\n },\n};\n","/**\n * Server EventBus - Singleton for server-side cross-trait communication\n *\n * This EventBus enables:\n * - Server-side trait event emission after CRUD operations\n * - Server-side trait listeners responding to events\n * - Cross-client event broadcast via WebSocket\n *\n * @packageDocumentation\n */\n\ntype EventHandler = (payload: unknown, meta?: Record<string, unknown>) => void;\n\nexport interface EventLogEntry {\n event: string;\n payload: unknown;\n timestamp: number;\n listenerCount: number;\n wildcardListenerCount: number;\n}\n\nconst MAX_EVENT_LOG = 200;\n\n/**\n * Simple EventBus implementation for server-side events\n */\nexport class EventBus {\n private handlers: Map<string, Set<EventHandler>> = new Map();\n private debug: boolean;\n private eventLog: EventLogEntry[] = [];\n\n constructor(options?: { debug?: boolean }) {\n this.debug = options?.debug ?? false;\n }\n\n on(event: string, handler: EventHandler): () => void {\n if (!this.handlers.has(event)) {\n this.handlers.set(event, new Set());\n }\n this.handlers.get(event)!.add(handler);\n\n // Return unsubscribe function\n return () => {\n this.handlers.get(event)?.delete(handler);\n };\n }\n\n off(event: string, handler: EventHandler): void {\n this.handlers.get(event)?.delete(handler);\n }\n\n emit(event: string, payload?: unknown, meta?: Record<string, unknown>): void {\n if (this.debug) {\n console.log(`[EventBus] Emitting ${event}:`, payload);\n }\n\n const handlers = this.handlers.get(event);\n const listenerCount = handlers?.size ?? 0;\n if (handlers) {\n handlers.forEach(handler => {\n try {\n handler(payload, meta);\n } catch (err) {\n console.error(`[EventBus] Error in handler for ${event}:`, err);\n }\n });\n }\n\n // Wildcard subscribers receive all events (used by WebSocket broadcast)\n let wildcardListenerCount = 0;\n if (event !== '*') {\n const wildcardHandlers = this.handlers.get('*');\n wildcardListenerCount = wildcardHandlers?.size ?? 0;\n if (wildcardHandlers) {\n wildcardHandlers.forEach(handler => {\n try {\n handler({ type: event, payload, timestamp: Date.now() }, meta);\n } catch (err) {\n console.error(`[EventBus] Error in wildcard handler for ${event}:`, err);\n }\n });\n }\n }\n\n // Record event in log (dev diagnostics)\n if (this.debug) {\n this.eventLog.push({\n event,\n payload,\n timestamp: Date.now(),\n listenerCount,\n wildcardListenerCount,\n });\n if (this.eventLog.length > MAX_EVENT_LOG) {\n this.eventLog.splice(0, this.eventLog.length - MAX_EVENT_LOG);\n }\n }\n }\n\n getRecentEvents(limit = 50): EventLogEntry[] {\n return this.eventLog.slice(-limit);\n }\n\n clearEventLog(): void {\n this.eventLog.length = 0;\n }\n\n getListenerCounts(): Record<string, number> {\n const counts: Record<string, number> = {};\n this.handlers.forEach((handlers, event) => {\n counts[event] = handlers.size;\n });\n return counts;\n }\n\n clear(): void {\n this.handlers.clear();\n this.eventLog.length = 0;\n }\n}\n\n/**\n * Lazy singleton EventBus instance for server-side event communication.\n */\nlet _serverEventBus: EventBus | null = null;\n\nexport function getServerEventBus(): EventBus {\n if (!_serverEventBus) {\n _serverEventBus = new EventBus({\n debug: process.env.NODE_ENV === 'development',\n });\n }\n return _serverEventBus;\n}\n\nexport function resetServerEventBus(): void {\n _serverEventBus?.clear();\n _serverEventBus = null;\n}\n\n/**\n * Type-safe event emission helper\n */\nexport function emitEntityEvent(\n entityType: string,\n action: 'CREATED' | 'UPDATED' | 'DELETED',\n payload: Record<string, unknown>\n): void {\n const eventType = `${entityType.toUpperCase()}_${action}`;\n getServerEventBus().emit(eventType, payload, { orbital: entityType });\n}\n","/**\n * WebSocket Event Broadcast - Cross-client event synchronization\n *\n * Broadcasts server-side events to all connected clients via WebSocket.\n * This enables real-time updates across multiple browser clients.\n *\n * @packageDocumentation\n */\n\nimport { WebSocketServer, WebSocket, type RawData } from 'ws';\nimport type { Server, IncomingMessage } from 'http';\nimport { getServerEventBus } from './eventBus.js';\nimport { logger } from './logger.js';\n\n/**\n * Event structure for broadcasting\n */\ninterface BroadcastEvent {\n type: string;\n payload?: unknown;\n timestamp?: number;\n source?: Record<string, unknown>;\n}\n\n/**\n * WebSocket server instance (singleton)\n */\nlet wss: WebSocketServer | null = null;\n\n/**\n * Setup WebSocket server for event broadcasting.\n *\n * Listens to all server events via wildcard and broadcasts to connected clients.\n *\n * @param server - HTTP server to attach WebSocket to\n * @param path - WebSocket endpoint path (default: '/ws/events')\n *\n * @example\n * ```typescript\n * import { createServer } from 'http';\n * import { setupEventBroadcast } from '@/lib/websocket';\n *\n * const server = createServer(app);\n * setupEventBroadcast(server);\n * ```\n */\nexport function setupEventBroadcast(server: Server, path: string = '/ws/events'): WebSocketServer {\n if (wss) {\n logger.warn('[WebSocket] Server already initialized');\n return wss;\n }\n\n wss = new WebSocketServer({ server, path });\n\n logger.info(`[WebSocket] Server listening at ${path}`);\n\n // Handle new connections\n wss.on('connection', (ws: WebSocket, req: IncomingMessage) => {\n const clientId = req.headers['sec-websocket-key'] || 'unknown';\n logger.debug(`[WebSocket] Client connected: ${clientId}`);\n\n // Send welcome message\n ws.send(\n JSON.stringify({\n type: 'CONNECTED',\n timestamp: Date.now(),\n message: 'Connected to event stream',\n })\n );\n\n // Handle client messages (for future bidirectional communication)\n ws.on('message', (data: RawData) => {\n try {\n const message = JSON.parse(data.toString());\n logger.debug(`[WebSocket] Received from ${clientId}:`, message);\n\n // Handle client-to-server events if needed\n if (message.type && message.payload) {\n // Emit to server event bus with client source\n getServerEventBus().emit(message.type, message.payload, {\n orbital: 'client',\n entity: clientId,\n });\n }\n } catch (error) {\n logger.error(`[WebSocket] Failed to parse message:`, error);\n }\n });\n\n ws.on('close', () => {\n logger.debug(`[WebSocket] Client disconnected: ${clientId}`);\n });\n\n ws.on('error', (error: Error) => {\n logger.error(`[WebSocket] Client error:`, error);\n });\n });\n\n // Subscribe to all server events and broadcast to clients\n getServerEventBus().on('*', (event: unknown) => {\n if (!wss) return;\n\n const typedEvent = event as BroadcastEvent;\n const message = JSON.stringify({\n type: typedEvent.type,\n payload: typedEvent.payload,\n timestamp: typedEvent.timestamp,\n source: typedEvent.source,\n });\n\n let broadcastCount = 0;\n wss.clients.forEach((client: WebSocket) => {\n if (client.readyState === WebSocket.OPEN) {\n client.send(message);\n broadcastCount++;\n }\n });\n\n if (broadcastCount > 0) {\n logger.debug(`[WebSocket] Broadcast ${typedEvent.type} to ${broadcastCount} client(s)`);\n }\n });\n\n return wss;\n}\n\n/**\n * Get the WebSocket server instance (for testing or advanced usage)\n */\nexport function getWebSocketServer(): WebSocketServer | null {\n return wss;\n}\n\n/**\n * Close the WebSocket server\n */\nexport function closeWebSocketServer(): Promise<void> {\n return new Promise((resolve, reject) => {\n if (!wss) {\n resolve();\n return;\n }\n\n wss.close((err?: Error) => {\n if (err) {\n reject(err);\n } else {\n wss = null;\n resolve();\n }\n });\n });\n}\n\n/**\n * Get connected client count\n */\nexport function getConnectedClientCount(): number {\n if (!wss) return 0;\n return wss.clients.size;\n}\n"]}
@@ -0,0 +1,7 @@
1
+ export declare const logger: {
2
+ debug: (message: string, meta?: unknown) => void;
3
+ info: (message: string, meta?: unknown) => void;
4
+ warn: (message: string, meta?: unknown) => void;
5
+ error: (message: string, meta?: unknown) => void;
6
+ };
7
+ //# sourceMappingURL=logger.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"logger.d.ts","sourceRoot":"","sources":["../../src/lib/logger.ts"],"names":[],"mappings":"AA0BA,eAAO,MAAM,MAAM;qBACA,MAAM,SAAS,OAAO;oBAKvB,MAAM,SAAS,OAAO;oBAKtB,MAAM,SAAS,OAAO;qBAKrB,MAAM,SAAS,OAAO;CAKxC,CAAC"}