@almadar/server 2.1.1 → 2.1.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/dist/contracts.d.ts +174 -0
- package/dist/contracts.d.ts.map +1 -0
- package/dist/deepagent/__tests__/memory.test.d.ts +6 -0
- package/dist/deepagent/__tests__/memory.test.d.ts.map +1 -0
- package/dist/deepagent/__tests__/session.test.d.ts +6 -0
- package/dist/deepagent/__tests__/session.test.d.ts.map +1 -0
- package/dist/deepagent/__tests__/skill-agent.test.d.ts +6 -0
- package/dist/deepagent/__tests__/skill-agent.test.d.ts.map +1 -0
- package/dist/deepagent/memory.d.ts +16 -0
- package/dist/deepagent/memory.d.ts.map +1 -0
- package/dist/deepagent/session.d.ts +16 -0
- package/dist/deepagent/session.d.ts.map +1 -0
- package/dist/deepagent/skill-agent.d.ts +22 -0
- package/dist/deepagent/skill-agent.d.ts.map +1 -0
- package/dist/index.js +2616 -0
- package/dist/index.js.map +1 -0
- package/dist/lib/db.d.ts +36 -0
- package/dist/lib/db.d.ts.map +1 -0
- package/dist/lib/debugRouter.d.ts +21 -0
- package/dist/lib/debugRouter.d.ts.map +1 -0
- package/dist/lib/env.d.ts +16 -0
- package/dist/lib/env.d.ts.map +1 -0
- package/dist/lib/eventBus.d.ts +44 -0
- package/dist/lib/eventBus.d.ts.map +1 -0
- package/dist/lib/eventBusTransport.d.ts +143 -0
- package/dist/lib/eventBusTransport.d.ts.map +1 -0
- package/dist/lib/eventPersistence.d.ts +151 -0
- package/dist/lib/eventPersistence.d.ts.map +1 -0
- package/dist/lib/index.d.ts +6 -0
- package/dist/lib/index.d.ts.map +1 -0
- package/dist/lib/index.js +288 -0
- package/dist/lib/index.js.map +1 -0
- package/dist/lib/logger.d.ts +7 -0
- package/dist/lib/logger.d.ts.map +1 -0
- package/dist/lib/serviceDiscovery.d.ts +168 -0
- package/dist/lib/serviceDiscovery.d.ts.map +1 -0
- package/dist/lib/websocket.d.ts +41 -0
- package/dist/lib/websocket.d.ts.map +1 -0
- package/dist/middleware/__tests__/multi-user.test.d.ts +6 -0
- package/dist/middleware/__tests__/multi-user.test.d.ts.map +1 -0
- package/dist/middleware/authenticateFirebase.d.ts +4 -0
- package/dist/middleware/authenticateFirebase.d.ts.map +1 -0
- package/dist/middleware/errorHandler.d.ts +53 -0
- package/dist/middleware/errorHandler.d.ts.map +1 -0
- package/dist/middleware/index.d.ts +4 -0
- package/dist/middleware/index.d.ts.map +1 -0
- package/dist/middleware/index.js +284 -0
- package/dist/middleware/index.js.map +1 -0
- package/dist/middleware/multi-user.d.ts +34 -0
- package/dist/middleware/multi-user.d.ts.map +1 -0
- package/dist/middleware/validation.d.ts +15 -0
- package/dist/middleware/validation.d.ts.map +1 -0
- package/dist/routes/__tests__/observability.test.d.ts +6 -0
- package/dist/routes/__tests__/observability.test.d.ts.map +1 -0
- package/dist/routes/observability.d.ts +11 -0
- package/dist/routes/observability.d.ts.map +1 -0
- package/dist/services/DataService.d.ts +70 -0
- package/dist/services/DataService.d.ts.map +1 -0
- package/dist/services/MockDataService.d.ts +110 -0
- package/dist/services/MockDataService.d.ts.map +1 -0
- package/dist/services/index.d.ts +8 -0
- package/dist/services/index.d.ts.map +1 -0
- package/dist/services/index.js +735 -0
- package/dist/services/index.js.map +1 -0
- package/dist/stores/ChangeSetStore.d.ts +24 -0
- package/dist/stores/ChangeSetStore.d.ts.map +1 -0
- package/dist/stores/SchemaProtectionService.d.ts +23 -0
- package/dist/stores/SchemaProtectionService.d.ts.map +1 -0
- package/dist/stores/SchemaStore.d.ts +52 -0
- package/dist/stores/SchemaStore.d.ts.map +1 -0
- package/dist/stores/SnapshotStore.d.ts +26 -0
- package/dist/stores/SnapshotStore.d.ts.map +1 -0
- package/dist/stores/ValidationStore.d.ts +19 -0
- package/dist/stores/ValidationStore.d.ts.map +1 -0
- package/dist/stores/firestoreFormat.d.ts +21 -0
- package/dist/stores/firestoreFormat.d.ts.map +1 -0
- package/dist/stores/index.d.ts +14 -0
- package/dist/stores/index.d.ts.map +1 -0
- package/dist/stores/index.js +519 -0
- package/dist/stores/index.js.map +1 -0
- package/dist/utils/index.d.ts +9 -0
- package/dist/utils/index.d.ts.map +1 -0
- package/dist/utils/index.js +106 -0
- package/dist/utils/index.js.map +1 -0
- package/dist/utils/queryFilters.d.ts +87 -0
- package/dist/utils/queryFilters.d.ts.map +1 -0
- package/dist/websocket/__tests__/state-sync.test.d.ts +6 -0
- package/dist/websocket/__tests__/state-sync.test.d.ts.map +1 -0
- package/dist/websocket/state-sync.d.ts +39 -0
- package/dist/websocket/state-sync.d.ts.map +1 -0
- package/package.json +1 -1
|
@@ -0,0 +1,288 @@
|
|
|
1
|
+
import { z } from 'zod';
|
|
2
|
+
import dotenv from 'dotenv';
|
|
3
|
+
import admin from 'firebase-admin';
|
|
4
|
+
export { default as admin } from 'firebase-admin';
|
|
5
|
+
import { WebSocketServer, WebSocket } from 'ws';
|
|
6
|
+
|
|
7
|
+
// src/lib/env.ts
|
|
8
|
+
dotenv.config();
|
|
9
|
+
var envSchema = z.object({
|
|
10
|
+
NODE_ENV: z.enum(["development", "production", "test"]).default("development"),
|
|
11
|
+
PORT: z.string().default("3030").transform((val) => parseInt(val, 10)),
|
|
12
|
+
CORS_ORIGIN: z.string().default("http://localhost:5173").transform((val) => val.includes(",") ? val.split(",").map((s) => s.trim()) : val),
|
|
13
|
+
// Database (Prisma/SQL) - optional
|
|
14
|
+
DATABASE_URL: z.string().optional(),
|
|
15
|
+
// Firebase/Firestore configuration
|
|
16
|
+
FIREBASE_PROJECT_ID: z.string().optional(),
|
|
17
|
+
FIREBASE_CLIENT_EMAIL: z.string().optional(),
|
|
18
|
+
FIREBASE_PRIVATE_KEY: z.string().optional(),
|
|
19
|
+
FIREBASE_SERVICE_ACCOUNT_PATH: z.string().optional(),
|
|
20
|
+
FIRESTORE_EMULATOR_HOST: z.string().optional(),
|
|
21
|
+
FIREBASE_AUTH_EMULATOR_HOST: z.string().optional(),
|
|
22
|
+
// API configuration
|
|
23
|
+
API_PREFIX: z.string().default("/api"),
|
|
24
|
+
// Mock data configuration
|
|
25
|
+
USE_MOCK_DATA: z.string().default("true").transform((v) => v === "true"),
|
|
26
|
+
MOCK_SEED: z.string().optional().transform((v) => v ? parseInt(v, 10) : void 0)
|
|
27
|
+
});
|
|
28
|
+
var parsed = envSchema.safeParse(process.env);
|
|
29
|
+
if (!parsed.success) {
|
|
30
|
+
console.error("\u274C Invalid environment variables:", parsed.error.flatten().fieldErrors);
|
|
31
|
+
throw new Error("Invalid environment variables");
|
|
32
|
+
}
|
|
33
|
+
var env = parsed.data;
|
|
34
|
+
function getApp() {
|
|
35
|
+
if (admin.apps.length === 0) {
|
|
36
|
+
throw new Error(
|
|
37
|
+
"@almadar/server: Firebase Admin SDK is not initialized. Call initializeFirebase() or admin.initializeApp() before using @almadar/server."
|
|
38
|
+
);
|
|
39
|
+
}
|
|
40
|
+
return admin.app();
|
|
41
|
+
}
|
|
42
|
+
function getFirestore() {
|
|
43
|
+
return getApp().firestore();
|
|
44
|
+
}
|
|
45
|
+
function getAuth() {
|
|
46
|
+
return getApp().auth();
|
|
47
|
+
}
|
|
48
|
+
new Proxy({}, {
|
|
49
|
+
get(_target, prop, receiver) {
|
|
50
|
+
const firestore = getFirestore();
|
|
51
|
+
const value = Reflect.get(firestore, prop, receiver);
|
|
52
|
+
return typeof value === "function" ? value.bind(firestore) : value;
|
|
53
|
+
}
|
|
54
|
+
});
|
|
55
|
+
|
|
56
|
+
// src/lib/logger.ts
|
|
57
|
+
var colors = {
|
|
58
|
+
debug: "\x1B[36m",
|
|
59
|
+
// Cyan
|
|
60
|
+
info: "\x1B[32m",
|
|
61
|
+
// Green
|
|
62
|
+
warn: "\x1B[33m",
|
|
63
|
+
// Yellow
|
|
64
|
+
error: "\x1B[31m",
|
|
65
|
+
// Red
|
|
66
|
+
reset: "\x1B[0m"
|
|
67
|
+
};
|
|
68
|
+
var shouldLog = (level) => {
|
|
69
|
+
const levels = ["debug", "info", "warn", "error"];
|
|
70
|
+
const minLevel = env.NODE_ENV === "production" ? "info" : "debug";
|
|
71
|
+
return levels.indexOf(level) >= levels.indexOf(minLevel);
|
|
72
|
+
};
|
|
73
|
+
var formatMessage = (level, message, meta) => {
|
|
74
|
+
const timestamp = (/* @__PURE__ */ new Date()).toISOString();
|
|
75
|
+
const color = colors[level];
|
|
76
|
+
const prefix = `${color}[${level.toUpperCase()}]${colors.reset}`;
|
|
77
|
+
const metaStr = meta ? ` ${JSON.stringify(meta)}` : "";
|
|
78
|
+
return `${timestamp} ${prefix} ${message}${metaStr}`;
|
|
79
|
+
};
|
|
80
|
+
var logger = {
|
|
81
|
+
debug: (message, meta) => {
|
|
82
|
+
if (shouldLog("debug")) {
|
|
83
|
+
console.log(formatMessage("debug", message, meta));
|
|
84
|
+
}
|
|
85
|
+
},
|
|
86
|
+
info: (message, meta) => {
|
|
87
|
+
if (shouldLog("info")) {
|
|
88
|
+
console.log(formatMessage("info", message, meta));
|
|
89
|
+
}
|
|
90
|
+
},
|
|
91
|
+
warn: (message, meta) => {
|
|
92
|
+
if (shouldLog("warn")) {
|
|
93
|
+
console.warn(formatMessage("warn", message, meta));
|
|
94
|
+
}
|
|
95
|
+
},
|
|
96
|
+
error: (message, meta) => {
|
|
97
|
+
if (shouldLog("error")) {
|
|
98
|
+
console.error(formatMessage("error", message, meta));
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
};
|
|
102
|
+
|
|
103
|
+
// src/lib/eventBus.ts
|
|
104
|
+
var MAX_EVENT_LOG = 200;
|
|
105
|
+
var EventBus = class {
|
|
106
|
+
handlers = /* @__PURE__ */ new Map();
|
|
107
|
+
debug;
|
|
108
|
+
eventLog = [];
|
|
109
|
+
constructor(options) {
|
|
110
|
+
this.debug = options?.debug ?? false;
|
|
111
|
+
}
|
|
112
|
+
on(event, handler) {
|
|
113
|
+
if (!this.handlers.has(event)) {
|
|
114
|
+
this.handlers.set(event, /* @__PURE__ */ new Set());
|
|
115
|
+
}
|
|
116
|
+
this.handlers.get(event).add(handler);
|
|
117
|
+
return () => {
|
|
118
|
+
this.handlers.get(event)?.delete(handler);
|
|
119
|
+
};
|
|
120
|
+
}
|
|
121
|
+
off(event, handler) {
|
|
122
|
+
this.handlers.get(event)?.delete(handler);
|
|
123
|
+
}
|
|
124
|
+
emit(event, payload, meta) {
|
|
125
|
+
if (this.debug) {
|
|
126
|
+
console.log(`[EventBus] Emitting ${event}:`, payload);
|
|
127
|
+
}
|
|
128
|
+
const handlers = this.handlers.get(event);
|
|
129
|
+
const listenerCount = handlers?.size ?? 0;
|
|
130
|
+
if (handlers) {
|
|
131
|
+
handlers.forEach((handler) => {
|
|
132
|
+
try {
|
|
133
|
+
handler(payload, meta);
|
|
134
|
+
} catch (err) {
|
|
135
|
+
console.error(`[EventBus] Error in handler for ${event}:`, err);
|
|
136
|
+
}
|
|
137
|
+
});
|
|
138
|
+
}
|
|
139
|
+
let wildcardListenerCount = 0;
|
|
140
|
+
if (event !== "*") {
|
|
141
|
+
const wildcardHandlers = this.handlers.get("*");
|
|
142
|
+
wildcardListenerCount = wildcardHandlers?.size ?? 0;
|
|
143
|
+
if (wildcardHandlers) {
|
|
144
|
+
wildcardHandlers.forEach((handler) => {
|
|
145
|
+
try {
|
|
146
|
+
handler({ type: event, payload, timestamp: Date.now() }, meta);
|
|
147
|
+
} catch (err) {
|
|
148
|
+
console.error(`[EventBus] Error in wildcard handler for ${event}:`, err);
|
|
149
|
+
}
|
|
150
|
+
});
|
|
151
|
+
}
|
|
152
|
+
}
|
|
153
|
+
if (this.debug) {
|
|
154
|
+
this.eventLog.push({
|
|
155
|
+
event,
|
|
156
|
+
payload,
|
|
157
|
+
timestamp: Date.now(),
|
|
158
|
+
listenerCount,
|
|
159
|
+
wildcardListenerCount
|
|
160
|
+
});
|
|
161
|
+
if (this.eventLog.length > MAX_EVENT_LOG) {
|
|
162
|
+
this.eventLog.splice(0, this.eventLog.length - MAX_EVENT_LOG);
|
|
163
|
+
}
|
|
164
|
+
}
|
|
165
|
+
}
|
|
166
|
+
getRecentEvents(limit = 50) {
|
|
167
|
+
return this.eventLog.slice(-limit);
|
|
168
|
+
}
|
|
169
|
+
clearEventLog() {
|
|
170
|
+
this.eventLog.length = 0;
|
|
171
|
+
}
|
|
172
|
+
getListenerCounts() {
|
|
173
|
+
const counts = {};
|
|
174
|
+
this.handlers.forEach((handlers, event) => {
|
|
175
|
+
counts[event] = handlers.size;
|
|
176
|
+
});
|
|
177
|
+
return counts;
|
|
178
|
+
}
|
|
179
|
+
clear() {
|
|
180
|
+
this.handlers.clear();
|
|
181
|
+
this.eventLog.length = 0;
|
|
182
|
+
}
|
|
183
|
+
};
|
|
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
|
+
}
|
|
197
|
+
function emitEntityEvent(entityType, action, payload) {
|
|
198
|
+
const eventType = `${entityType.toUpperCase()}_${action}`;
|
|
199
|
+
getServerEventBus().emit(eventType, payload, { orbital: entityType });
|
|
200
|
+
}
|
|
201
|
+
var wss = null;
|
|
202
|
+
function setupEventBroadcast(server, path = "/ws/events") {
|
|
203
|
+
if (wss) {
|
|
204
|
+
logger.warn("[WebSocket] Server already initialized");
|
|
205
|
+
return wss;
|
|
206
|
+
}
|
|
207
|
+
wss = new WebSocketServer({ server, path });
|
|
208
|
+
logger.info(`[WebSocket] Server listening at ${path}`);
|
|
209
|
+
wss.on("connection", (ws, req) => {
|
|
210
|
+
const clientId = req.headers["sec-websocket-key"] || "unknown";
|
|
211
|
+
logger.debug(`[WebSocket] Client connected: ${clientId}`);
|
|
212
|
+
ws.send(
|
|
213
|
+
JSON.stringify({
|
|
214
|
+
type: "CONNECTED",
|
|
215
|
+
timestamp: Date.now(),
|
|
216
|
+
message: "Connected to event stream"
|
|
217
|
+
})
|
|
218
|
+
);
|
|
219
|
+
ws.on("message", (data) => {
|
|
220
|
+
try {
|
|
221
|
+
const message = JSON.parse(data.toString());
|
|
222
|
+
logger.debug(`[WebSocket] Received from ${clientId}:`, message);
|
|
223
|
+
if (message.type && message.payload) {
|
|
224
|
+
getServerEventBus().emit(message.type, message.payload, {
|
|
225
|
+
orbital: "client",
|
|
226
|
+
entity: clientId
|
|
227
|
+
});
|
|
228
|
+
}
|
|
229
|
+
} catch (error) {
|
|
230
|
+
logger.error(`[WebSocket] Failed to parse message:`, error);
|
|
231
|
+
}
|
|
232
|
+
});
|
|
233
|
+
ws.on("close", () => {
|
|
234
|
+
logger.debug(`[WebSocket] Client disconnected: ${clientId}`);
|
|
235
|
+
});
|
|
236
|
+
ws.on("error", (error) => {
|
|
237
|
+
logger.error(`[WebSocket] Client error:`, error);
|
|
238
|
+
});
|
|
239
|
+
});
|
|
240
|
+
getServerEventBus().on("*", (event) => {
|
|
241
|
+
if (!wss) return;
|
|
242
|
+
const typedEvent = event;
|
|
243
|
+
const message = JSON.stringify({
|
|
244
|
+
type: typedEvent.type,
|
|
245
|
+
payload: typedEvent.payload,
|
|
246
|
+
timestamp: typedEvent.timestamp,
|
|
247
|
+
source: typedEvent.source
|
|
248
|
+
});
|
|
249
|
+
let broadcastCount = 0;
|
|
250
|
+
wss.clients.forEach((client) => {
|
|
251
|
+
if (client.readyState === WebSocket.OPEN) {
|
|
252
|
+
client.send(message);
|
|
253
|
+
broadcastCount++;
|
|
254
|
+
}
|
|
255
|
+
});
|
|
256
|
+
if (broadcastCount > 0) {
|
|
257
|
+
logger.debug(`[WebSocket] Broadcast ${typedEvent.type} to ${broadcastCount} client(s)`);
|
|
258
|
+
}
|
|
259
|
+
});
|
|
260
|
+
return wss;
|
|
261
|
+
}
|
|
262
|
+
function getWebSocketServer() {
|
|
263
|
+
return wss;
|
|
264
|
+
}
|
|
265
|
+
function closeWebSocketServer() {
|
|
266
|
+
return new Promise((resolve, reject) => {
|
|
267
|
+
if (!wss) {
|
|
268
|
+
resolve();
|
|
269
|
+
return;
|
|
270
|
+
}
|
|
271
|
+
wss.close((err) => {
|
|
272
|
+
if (err) {
|
|
273
|
+
reject(err);
|
|
274
|
+
} else {
|
|
275
|
+
wss = null;
|
|
276
|
+
resolve();
|
|
277
|
+
}
|
|
278
|
+
});
|
|
279
|
+
});
|
|
280
|
+
}
|
|
281
|
+
function getConnectedClientCount() {
|
|
282
|
+
if (!wss) return 0;
|
|
283
|
+
return wss.clients.size;
|
|
284
|
+
}
|
|
285
|
+
|
|
286
|
+
export { closeWebSocketServer, emitEntityEvent, env, getAuth, getConnectedClientCount, getFirestore, getServerEventBus, getWebSocketServer, logger, resetServerEventBus, setupEventBroadcast };
|
|
287
|
+
//# sourceMappingURL=index.js.map
|
|
288
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +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 // Dynamic require for JSON service account file at runtime\n const serviceAccount = require(serviceAccountPath) as Record<string, unknown>;\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"}
|
|
@@ -0,0 +1,168 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Service Discovery — Registry for service orbital instances
|
|
3
|
+
*
|
|
4
|
+
* Enables distributed event routing by tracking which services are running,
|
|
5
|
+
* what events they emit/listen for, and where they can be reached.
|
|
6
|
+
*
|
|
7
|
+
* Supports:
|
|
8
|
+
* - In-memory registry (default, single-process)
|
|
9
|
+
* - Registration/deregistration with heartbeat
|
|
10
|
+
* - Event routing: find services that listen for a given event
|
|
11
|
+
* - Health tracking via TTL-based expiry
|
|
12
|
+
*
|
|
13
|
+
* @packageDocumentation
|
|
14
|
+
*/
|
|
15
|
+
/** Service registration record */
|
|
16
|
+
export interface ServiceRegistration {
|
|
17
|
+
/** Service name (from .orb schema name) */
|
|
18
|
+
name: string;
|
|
19
|
+
/** Unique instance ID */
|
|
20
|
+
instanceId: string;
|
|
21
|
+
/** Host where the service is running */
|
|
22
|
+
host: string;
|
|
23
|
+
/** Port number */
|
|
24
|
+
port: number;
|
|
25
|
+
/** Hash of the .orb schema for version tracking */
|
|
26
|
+
orbHash: string;
|
|
27
|
+
/** Events this service emits */
|
|
28
|
+
emits: string[];
|
|
29
|
+
/** Events this service listens for */
|
|
30
|
+
listens: string[];
|
|
31
|
+
/** Registration timestamp */
|
|
32
|
+
registeredAt: number;
|
|
33
|
+
/** Last heartbeat timestamp */
|
|
34
|
+
lastHeartbeat: number;
|
|
35
|
+
/** Service status */
|
|
36
|
+
status: 'starting' | 'ready' | 'degraded' | 'stopping';
|
|
37
|
+
/** Optional metadata */
|
|
38
|
+
metadata?: Record<string, unknown>;
|
|
39
|
+
}
|
|
40
|
+
/** Options for the service registry */
|
|
41
|
+
export interface ServiceRegistryOptions {
|
|
42
|
+
/** TTL for service registrations in ms (default: 60000) */
|
|
43
|
+
heartbeatTtlMs?: number;
|
|
44
|
+
/** Cleanup interval for expired registrations (default: 30000) */
|
|
45
|
+
cleanupIntervalMs?: number;
|
|
46
|
+
}
|
|
47
|
+
/** Service registry interface for pluggable backends */
|
|
48
|
+
export interface IServiceRegistry {
|
|
49
|
+
/** Register a service */
|
|
50
|
+
register(service: ServiceRegistration): Promise<void>;
|
|
51
|
+
/** Deregister a service */
|
|
52
|
+
deregister(instanceId: string): Promise<void>;
|
|
53
|
+
/** Update heartbeat for a service */
|
|
54
|
+
heartbeat(instanceId: string): Promise<void>;
|
|
55
|
+
/** Update service status */
|
|
56
|
+
updateStatus(instanceId: string, status: ServiceRegistration['status']): Promise<void>;
|
|
57
|
+
/** Get all registered services */
|
|
58
|
+
getAll(): Promise<ServiceRegistration[]>;
|
|
59
|
+
/** Get services by name */
|
|
60
|
+
getByName(name: string): Promise<ServiceRegistration[]>;
|
|
61
|
+
/** Find services that listen for a given event */
|
|
62
|
+
findListeners(event: string): Promise<ServiceRegistration[]>;
|
|
63
|
+
/** Find services that emit a given event */
|
|
64
|
+
findEmitters(event: string): Promise<ServiceRegistration[]>;
|
|
65
|
+
/** Remove expired registrations */
|
|
66
|
+
cleanup(ttlMs: number): Promise<number>;
|
|
67
|
+
}
|
|
68
|
+
/**
|
|
69
|
+
* In-memory service registry for single-process and development mode.
|
|
70
|
+
*/
|
|
71
|
+
export declare class InMemoryServiceRegistry implements IServiceRegistry {
|
|
72
|
+
private services;
|
|
73
|
+
register(service: ServiceRegistration): Promise<void>;
|
|
74
|
+
deregister(instanceId: string): Promise<void>;
|
|
75
|
+
heartbeat(instanceId: string): Promise<void>;
|
|
76
|
+
updateStatus(instanceId: string, status: ServiceRegistration['status']): Promise<void>;
|
|
77
|
+
getAll(): Promise<ServiceRegistration[]>;
|
|
78
|
+
getByName(name: string): Promise<ServiceRegistration[]>;
|
|
79
|
+
findListeners(event: string): Promise<ServiceRegistration[]>;
|
|
80
|
+
findEmitters(event: string): Promise<ServiceRegistration[]>;
|
|
81
|
+
cleanup(ttlMs: number): Promise<number>;
|
|
82
|
+
}
|
|
83
|
+
/**
|
|
84
|
+
* Service discovery manager that wraps a registry backend.
|
|
85
|
+
*
|
|
86
|
+
* Usage:
|
|
87
|
+
* ```typescript
|
|
88
|
+
* const discovery = new ServiceDiscovery();
|
|
89
|
+
* discovery.startCleanup();
|
|
90
|
+
*
|
|
91
|
+
* // Register a service
|
|
92
|
+
* await discovery.register({
|
|
93
|
+
* name: 'LLMService',
|
|
94
|
+
* instanceId: 'llm-001',
|
|
95
|
+
* host: 'localhost',
|
|
96
|
+
* port: 3001,
|
|
97
|
+
* orbHash: 'abc123',
|
|
98
|
+
* emits: ['LLM_RESPONSE', 'LLM_READY'],
|
|
99
|
+
* listens: ['AGENT_LLM_REQUEST'],
|
|
100
|
+
* });
|
|
101
|
+
*
|
|
102
|
+
* // Find who can handle an event
|
|
103
|
+
* const listeners = await discovery.findListeners('AGENT_LLM_REQUEST');
|
|
104
|
+
* ```
|
|
105
|
+
*/
|
|
106
|
+
export declare class ServiceDiscovery {
|
|
107
|
+
private registry;
|
|
108
|
+
private options;
|
|
109
|
+
private cleanupTimer;
|
|
110
|
+
constructor(options?: ServiceRegistryOptions, registry?: IServiceRegistry);
|
|
111
|
+
/**
|
|
112
|
+
* Register a service with the registry.
|
|
113
|
+
*/
|
|
114
|
+
register(service: Omit<ServiceRegistration, 'registeredAt' | 'lastHeartbeat' | 'status'> & {
|
|
115
|
+
status?: ServiceRegistration['status'];
|
|
116
|
+
}): Promise<void>;
|
|
117
|
+
/**
|
|
118
|
+
* Deregister a service.
|
|
119
|
+
*/
|
|
120
|
+
deregister(instanceId: string): Promise<void>;
|
|
121
|
+
/**
|
|
122
|
+
* Send heartbeat for a service.
|
|
123
|
+
*/
|
|
124
|
+
heartbeat(instanceId: string): Promise<void>;
|
|
125
|
+
/**
|
|
126
|
+
* Mark a service as ready.
|
|
127
|
+
*/
|
|
128
|
+
markReady(instanceId: string): Promise<void>;
|
|
129
|
+
/**
|
|
130
|
+
* Mark a service as degraded.
|
|
131
|
+
*/
|
|
132
|
+
markDegraded(instanceId: string): Promise<void>;
|
|
133
|
+
/**
|
|
134
|
+
* Find all services that listen for a given event.
|
|
135
|
+
*/
|
|
136
|
+
findListeners(event: string): Promise<ServiceRegistration[]>;
|
|
137
|
+
/**
|
|
138
|
+
* Find all services that emit a given event.
|
|
139
|
+
*/
|
|
140
|
+
findEmitters(event: string): Promise<ServiceRegistration[]>;
|
|
141
|
+
/**
|
|
142
|
+
* Get all registered services.
|
|
143
|
+
*/
|
|
144
|
+
getAll(): Promise<ServiceRegistration[]>;
|
|
145
|
+
/**
|
|
146
|
+
* Get the full event topology (who emits what, who listens for what).
|
|
147
|
+
*/
|
|
148
|
+
getEventTopology(): Promise<{
|
|
149
|
+
events: Array<{
|
|
150
|
+
event: string;
|
|
151
|
+
emitters: string[];
|
|
152
|
+
listeners: string[];
|
|
153
|
+
}>;
|
|
154
|
+
}>;
|
|
155
|
+
/**
|
|
156
|
+
* Start periodic cleanup of expired services.
|
|
157
|
+
*/
|
|
158
|
+
startCleanup(): void;
|
|
159
|
+
/**
|
|
160
|
+
* Stop periodic cleanup.
|
|
161
|
+
*/
|
|
162
|
+
stopCleanup(): void;
|
|
163
|
+
/**
|
|
164
|
+
* Get the underlying registry (for testing).
|
|
165
|
+
*/
|
|
166
|
+
getRegistry(): IServiceRegistry;
|
|
167
|
+
}
|
|
168
|
+
//# sourceMappingURL=serviceDiscovery.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"serviceDiscovery.d.ts","sourceRoot":"","sources":["../../src/lib/serviceDiscovery.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* WebSocket Event Broadcast - Cross-client event synchronization
|
|
3
|
+
*
|
|
4
|
+
* Broadcasts server-side events to all connected clients via WebSocket.
|
|
5
|
+
* This enables real-time updates across multiple browser clients.
|
|
6
|
+
*
|
|
7
|
+
* @packageDocumentation
|
|
8
|
+
*/
|
|
9
|
+
import { WebSocketServer } from 'ws';
|
|
10
|
+
import type { Server } from 'http';
|
|
11
|
+
/**
|
|
12
|
+
* Setup WebSocket server for event broadcasting.
|
|
13
|
+
*
|
|
14
|
+
* Listens to all server events via wildcard and broadcasts to connected clients.
|
|
15
|
+
*
|
|
16
|
+
* @param server - HTTP server to attach WebSocket to
|
|
17
|
+
* @param path - WebSocket endpoint path (default: '/ws/events')
|
|
18
|
+
*
|
|
19
|
+
* @example
|
|
20
|
+
* ```typescript
|
|
21
|
+
* import { createServer } from 'http';
|
|
22
|
+
* import { setupEventBroadcast } from '@/lib/websocket';
|
|
23
|
+
*
|
|
24
|
+
* const server = createServer(app);
|
|
25
|
+
* setupEventBroadcast(server);
|
|
26
|
+
* ```
|
|
27
|
+
*/
|
|
28
|
+
export declare function setupEventBroadcast(server: Server, path?: string): WebSocketServer;
|
|
29
|
+
/**
|
|
30
|
+
* Get the WebSocket server instance (for testing or advanced usage)
|
|
31
|
+
*/
|
|
32
|
+
export declare function getWebSocketServer(): WebSocketServer | null;
|
|
33
|
+
/**
|
|
34
|
+
* Close the WebSocket server
|
|
35
|
+
*/
|
|
36
|
+
export declare function closeWebSocketServer(): Promise<void>;
|
|
37
|
+
/**
|
|
38
|
+
* Get connected client count
|
|
39
|
+
*/
|
|
40
|
+
export declare function getConnectedClientCount(): number;
|
|
41
|
+
//# sourceMappingURL=websocket.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"websocket.d.ts","sourceRoot":"","sources":["../../src/lib/websocket.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAEH,OAAO,EAAE,eAAe,EAA2B,MAAM,IAAI,CAAC;AAC9D,OAAO,KAAK,EAAE,MAAM,EAAmB,MAAM,MAAM,CAAC;AAmBpD;;;;;;;;;;;;;;;;GAgBG;AACH,wBAAgB,mBAAmB,CAAC,MAAM,EAAE,MAAM,EAAE,IAAI,GAAE,MAAqB,GAAG,eAAe,CA8EhG;AAED;;GAEG;AACH,wBAAgB,kBAAkB,IAAI,eAAe,GAAG,IAAI,CAE3D;AAED;;GAEG;AACH,wBAAgB,oBAAoB,IAAI,OAAO,CAAC,IAAI,CAAC,CAgBpD;AAED;;GAEG;AACH,wBAAgB,uBAAuB,IAAI,MAAM,CAGhD"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"multi-user.test.d.ts","sourceRoot":"","sources":["../../../src/middleware/__tests__/multi-user.test.ts"],"names":[],"mappings":"AAAA;;;GAGG"}
|
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
import { NextFunction, Request, Response } from 'express';
|
|
2
|
+
export declare function authenticateFirebase(req: Request, res: Response, next: NextFunction): Promise<void | Response<any, Record<string, any>>>;
|
|
3
|
+
export default authenticateFirebase;
|
|
4
|
+
//# sourceMappingURL=authenticateFirebase.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"authenticateFirebase.d.ts","sourceRoot":"","sources":["../../src/middleware/authenticateFirebase.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAE,OAAO,EAAE,QAAQ,EAAE,MAAM,SAAS,CAAC;AAwB1D,wBAAsB,oBAAoB,CAAC,GAAG,EAAE,OAAO,EAAE,GAAG,EAAE,QAAQ,EAAE,IAAI,EAAE,YAAY,sDA0BzF;AAED,eAAe,oBAAoB,CAAC"}
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
import { Request, Response, NextFunction } from 'express';
|
|
2
|
+
/**
|
|
3
|
+
* Base application error class
|
|
4
|
+
*/
|
|
5
|
+
export declare class AppError extends Error {
|
|
6
|
+
statusCode: number;
|
|
7
|
+
message: string;
|
|
8
|
+
code?: string | undefined;
|
|
9
|
+
constructor(statusCode: number, message: string, code?: string | undefined);
|
|
10
|
+
}
|
|
11
|
+
/**
|
|
12
|
+
* 404 Not Found error
|
|
13
|
+
*/
|
|
14
|
+
export declare class NotFoundError extends AppError {
|
|
15
|
+
constructor(message?: string);
|
|
16
|
+
}
|
|
17
|
+
/**
|
|
18
|
+
* 400 Bad Request / Validation error
|
|
19
|
+
*/
|
|
20
|
+
export declare class ValidationError extends AppError {
|
|
21
|
+
constructor(message?: string);
|
|
22
|
+
}
|
|
23
|
+
/**
|
|
24
|
+
* 401 Unauthorized error
|
|
25
|
+
*/
|
|
26
|
+
export declare class UnauthorizedError extends AppError {
|
|
27
|
+
constructor(message?: string);
|
|
28
|
+
}
|
|
29
|
+
/**
|
|
30
|
+
* 403 Forbidden error
|
|
31
|
+
*/
|
|
32
|
+
export declare class ForbiddenError extends AppError {
|
|
33
|
+
constructor(message?: string);
|
|
34
|
+
}
|
|
35
|
+
/**
|
|
36
|
+
* 409 Conflict error
|
|
37
|
+
*/
|
|
38
|
+
export declare class ConflictError extends AppError {
|
|
39
|
+
constructor(message?: string);
|
|
40
|
+
}
|
|
41
|
+
/**
|
|
42
|
+
* Global error handler middleware
|
|
43
|
+
*/
|
|
44
|
+
export declare const errorHandler: (err: Error, _req: Request, res: Response, _next: NextFunction) => void;
|
|
45
|
+
/**
|
|
46
|
+
* Async handler wrapper to catch errors in async route handlers
|
|
47
|
+
*/
|
|
48
|
+
export declare const asyncHandler: (fn: (req: Request, res: Response, next: NextFunction) => Promise<unknown>) => (req: Request, res: Response, next: NextFunction) => void;
|
|
49
|
+
/**
|
|
50
|
+
* 404 handler for unmatched routes
|
|
51
|
+
*/
|
|
52
|
+
export declare const notFoundHandler: (req: Request, res: Response) => void;
|
|
53
|
+
//# sourceMappingURL=errorHandler.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"errorHandler.d.ts","sourceRoot":"","sources":["../../src/middleware/errorHandler.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,QAAQ,EAAE,YAAY,EAAE,MAAM,SAAS,CAAC;AAI1D;;GAEG;AACH,qBAAa,QAAS,SAAQ,KAAK;IAExB,UAAU,EAAE,MAAM;IAClB,OAAO,EAAE,MAAM;IACf,IAAI,CAAC,EAAE,MAAM;gBAFb,UAAU,EAAE,MAAM,EAClB,OAAO,EAAE,MAAM,EACf,IAAI,CAAC,EAAE,MAAM,YAAA;CAKvB;AAED;;GAEG;AACH,qBAAa,aAAc,SAAQ,QAAQ;gBAC7B,OAAO,GAAE,MAA6B;CAGnD;AAED;;GAEG;AACH,qBAAa,eAAgB,SAAQ,QAAQ;gBAC/B,OAAO,GAAE,MAA4B;CAGlD;AAED;;GAEG;AACH,qBAAa,iBAAkB,SAAQ,QAAQ;gBACjC,OAAO,GAAE,MAAuB;CAG7C;AAED;;GAEG;AACH,qBAAa,cAAe,SAAQ,QAAQ;gBAC9B,OAAO,GAAE,MAAoB;CAG1C;AAED;;GAEG;AACH,qBAAa,aAAc,SAAQ,QAAQ;gBAC7B,OAAO,GAAE,MAA4B;CAGlD;AAED;;GAEG;AACH,eAAO,MAAM,YAAY,GACvB,KAAK,KAAK,EACV,MAAM,OAAO,EACb,KAAK,QAAQ,EACb,OAAO,YAAY,KAClB,IA2CF,CAAC;AAEF;;GAEG;AACH,eAAO,MAAM,YAAY,GACtB,IAAI,CAAC,GAAG,EAAE,OAAO,EAAE,GAAG,EAAE,QAAQ,EAAE,IAAI,EAAE,YAAY,KAAK,OAAO,CAAC,OAAO,CAAC,MACzE,KAAK,OAAO,EAAE,KAAK,QAAQ,EAAE,MAAM,YAAY,SAE/C,CAAC;AAEJ;;GAEG;AACH,eAAO,MAAM,eAAe,GAAI,KAAK,OAAO,EAAE,KAAK,QAAQ,KAAG,IAM7D,CAAC"}
|
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
export { errorHandler, notFoundHandler, asyncHandler, AppError, NotFoundError, ValidationError, UnauthorizedError, ForbiddenError, ConflictError, } from './errorHandler';
|
|
2
|
+
export { validateBody, validateQuery, validateParams } from './validation';
|
|
3
|
+
export { authenticateFirebase } from './authenticateFirebase.js';
|
|
4
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/middleware/index.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,YAAY,EACZ,eAAe,EACf,YAAY,EACZ,QAAQ,EACR,aAAa,EACb,eAAe,EACf,iBAAiB,EACjB,cAAc,EACd,aAAa,GACd,MAAM,gBAAgB,CAAC;AAExB,OAAO,EAAE,YAAY,EAAE,aAAa,EAAE,cAAc,EAAE,MAAM,cAAc,CAAC;AAE3E,OAAO,EAAE,oBAAoB,EAAE,MAAM,2BAA2B,CAAC"}
|