@almadar/server 2.1.0 → 2.1.1

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 (103) hide show
  1. package/package.json +1 -1
  2. package/dist/contracts.d.ts +0 -174
  3. package/dist/contracts.d.ts.map +0 -1
  4. package/dist/deepagent/__tests__/memory.test.d.ts +0 -6
  5. package/dist/deepagent/__tests__/memory.test.d.ts.map +0 -1
  6. package/dist/deepagent/__tests__/session.test.d.ts +0 -6
  7. package/dist/deepagent/__tests__/session.test.d.ts.map +0 -1
  8. package/dist/deepagent/__tests__/skill-agent.test.d.ts +0 -6
  9. package/dist/deepagent/__tests__/skill-agent.test.d.ts.map +0 -1
  10. package/dist/deepagent/memory.d.ts +0 -17
  11. package/dist/deepagent/memory.d.ts.map +0 -1
  12. package/dist/deepagent/memory.js +0 -48
  13. package/dist/deepagent/memory.js.map +0 -1
  14. package/dist/deepagent/session.d.ts +0 -17
  15. package/dist/deepagent/session.d.ts.map +0 -1
  16. package/dist/deepagent/session.js +0 -68
  17. package/dist/deepagent/session.js.map +0 -1
  18. package/dist/deepagent/skill-agent.d.ts +0 -22
  19. package/dist/deepagent/skill-agent.d.ts.map +0 -1
  20. package/dist/deepagent/skill-agent.js +0 -114
  21. package/dist/deepagent/skill-agent.js.map +0 -1
  22. package/dist/index.js +0 -2598
  23. package/dist/index.js.map +0 -1
  24. package/dist/lib/db.d.ts +0 -36
  25. package/dist/lib/db.d.ts.map +0 -1
  26. package/dist/lib/debugRouter.d.ts +0 -21
  27. package/dist/lib/debugRouter.d.ts.map +0 -1
  28. package/dist/lib/env.d.ts +0 -16
  29. package/dist/lib/env.d.ts.map +0 -1
  30. package/dist/lib/eventBus.d.ts +0 -44
  31. package/dist/lib/eventBus.d.ts.map +0 -1
  32. package/dist/lib/eventBusTransport.d.ts +0 -143
  33. package/dist/lib/eventBusTransport.d.ts.map +0 -1
  34. package/dist/lib/eventPersistence.d.ts +0 -151
  35. package/dist/lib/eventPersistence.d.ts.map +0 -1
  36. package/dist/lib/index.d.ts +0 -6
  37. package/dist/lib/index.d.ts.map +0 -1
  38. package/dist/lib/index.js +0 -288
  39. package/dist/lib/index.js.map +0 -1
  40. package/dist/lib/logger.d.ts +0 -7
  41. package/dist/lib/logger.d.ts.map +0 -1
  42. package/dist/lib/serviceDiscovery.d.ts +0 -168
  43. package/dist/lib/serviceDiscovery.d.ts.map +0 -1
  44. package/dist/lib/websocket.d.ts +0 -41
  45. package/dist/lib/websocket.d.ts.map +0 -1
  46. package/dist/middleware/__tests__/multi-user.test.d.ts +0 -6
  47. package/dist/middleware/__tests__/multi-user.test.d.ts.map +0 -1
  48. package/dist/middleware/authenticateFirebase.d.ts +0 -4
  49. package/dist/middleware/authenticateFirebase.d.ts.map +0 -1
  50. package/dist/middleware/errorHandler.d.ts +0 -53
  51. package/dist/middleware/errorHandler.d.ts.map +0 -1
  52. package/dist/middleware/index.d.ts +0 -4
  53. package/dist/middleware/index.d.ts.map +0 -1
  54. package/dist/middleware/index.js +0 -284
  55. package/dist/middleware/index.js.map +0 -1
  56. package/dist/middleware/multi-user.d.ts +0 -34
  57. package/dist/middleware/multi-user.d.ts.map +0 -1
  58. package/dist/middleware/multi-user.js +0 -76
  59. package/dist/middleware/multi-user.js.map +0 -1
  60. package/dist/middleware/validation.d.ts +0 -15
  61. package/dist/middleware/validation.d.ts.map +0 -1
  62. package/dist/routes/__tests__/observability.test.d.ts +0 -6
  63. package/dist/routes/__tests__/observability.test.d.ts.map +0 -1
  64. package/dist/routes/observability.d.ts +0 -11
  65. package/dist/routes/observability.d.ts.map +0 -1
  66. package/dist/routes/observability.js +0 -62
  67. package/dist/routes/observability.js.map +0 -1
  68. package/dist/services/DataService.d.ts +0 -70
  69. package/dist/services/DataService.d.ts.map +0 -1
  70. package/dist/services/MockDataService.d.ts +0 -110
  71. package/dist/services/MockDataService.d.ts.map +0 -1
  72. package/dist/services/index.d.ts +0 -8
  73. package/dist/services/index.d.ts.map +0 -1
  74. package/dist/services/index.js +0 -735
  75. package/dist/services/index.js.map +0 -1
  76. package/dist/stores/ChangeSetStore.d.ts +0 -24
  77. package/dist/stores/ChangeSetStore.d.ts.map +0 -1
  78. package/dist/stores/SchemaProtectionService.d.ts +0 -23
  79. package/dist/stores/SchemaProtectionService.d.ts.map +0 -1
  80. package/dist/stores/SchemaStore.d.ts +0 -52
  81. package/dist/stores/SchemaStore.d.ts.map +0 -1
  82. package/dist/stores/SnapshotStore.d.ts +0 -26
  83. package/dist/stores/SnapshotStore.d.ts.map +0 -1
  84. package/dist/stores/ValidationStore.d.ts +0 -19
  85. package/dist/stores/ValidationStore.d.ts.map +0 -1
  86. package/dist/stores/firestoreFormat.d.ts +0 -21
  87. package/dist/stores/firestoreFormat.d.ts.map +0 -1
  88. package/dist/stores/index.d.ts +0 -14
  89. package/dist/stores/index.d.ts.map +0 -1
  90. package/dist/stores/index.js +0 -519
  91. package/dist/stores/index.js.map +0 -1
  92. package/dist/utils/index.d.ts +0 -9
  93. package/dist/utils/index.d.ts.map +0 -1
  94. package/dist/utils/index.js +0 -106
  95. package/dist/utils/index.js.map +0 -1
  96. package/dist/utils/queryFilters.d.ts +0 -87
  97. package/dist/utils/queryFilters.d.ts.map +0 -1
  98. package/dist/websocket/__tests__/state-sync.test.d.ts +0 -6
  99. package/dist/websocket/__tests__/state-sync.test.d.ts.map +0 -1
  100. package/dist/websocket/state-sync.d.ts +0 -39
  101. package/dist/websocket/state-sync.d.ts.map +0 -1
  102. package/dist/websocket/state-sync.js +0 -77
  103. package/dist/websocket/state-sync.js.map +0 -1
@@ -1,106 +0,0 @@
1
- // src/utils/queryFilters.ts
2
- var OPERATOR_MAP = {
3
- "eq": "==",
4
- "neq": "!=",
5
- "gt": ">",
6
- "gte": ">=",
7
- "lt": "<",
8
- "lte": "<=",
9
- "contains": "array-contains",
10
- "contains_any": "array-contains-any",
11
- "in": "in",
12
- "not_in": "not-in",
13
- // Date operators map to same comparison operators
14
- "date_eq": "==",
15
- "date_gte": ">=",
16
- "date_lte": "<="
17
- };
18
- var RESERVED_PARAMS = /* @__PURE__ */ new Set([
19
- "page",
20
- "pageSize",
21
- "limit",
22
- "offset",
23
- "search",
24
- "q",
25
- "sortBy",
26
- "sortOrder",
27
- "orderBy",
28
- "orderDirection"
29
- ]);
30
- function parseQueryFilters(query) {
31
- const filters = [];
32
- for (const [key, value] of Object.entries(query)) {
33
- if (RESERVED_PARAMS.has(key)) continue;
34
- if (value === void 0 || value === null || value === "") continue;
35
- const match = key.match(/^(.+)__(\w+)$/);
36
- if (match) {
37
- const [, field, op] = match;
38
- const firestoreOp = OPERATOR_MAP[op];
39
- if (firestoreOp) {
40
- filters.push({
41
- field,
42
- operator: firestoreOp,
43
- value: parseValue(value, op)
44
- });
45
- } else {
46
- filters.push({
47
- field: key,
48
- operator: "==",
49
- value: parseValue(value, "eq")
50
- });
51
- }
52
- } else {
53
- filters.push({
54
- field: key,
55
- operator: "==",
56
- value: parseValue(value, "eq")
57
- });
58
- }
59
- }
60
- return filters;
61
- }
62
- function parseValue(value, operator) {
63
- if (operator === "in" || operator === "not_in" || operator === "contains_any") {
64
- if (typeof value === "string") {
65
- return value.split(",").map((v) => v.trim());
66
- }
67
- if (Array.isArray(value)) {
68
- return value;
69
- }
70
- return [value];
71
- }
72
- if (typeof value === "string") {
73
- if (/^-?\d+(\.\d+)?$/.test(value)) {
74
- const num = parseFloat(value);
75
- if (!isNaN(num)) {
76
- return num;
77
- }
78
- }
79
- if (value === "true") return true;
80
- if (value === "false") return false;
81
- }
82
- return value;
83
- }
84
- function applyFiltersToQuery(collection, filters) {
85
- let query = collection;
86
- for (const filter of filters) {
87
- query = query.where(
88
- filter.field,
89
- filter.operator,
90
- filter.value
91
- );
92
- }
93
- return query;
94
- }
95
- function extractPaginationParams(query, defaults = {}) {
96
- return {
97
- page: parseInt(query.page, 10) || defaults.page || 1,
98
- pageSize: parseInt(query.pageSize, 10) || parseInt(query.limit, 10) || defaults.pageSize || 20,
99
- sortBy: query.sortBy || query.orderBy,
100
- sortOrder: query.sortOrder || query.orderDirection || defaults.sortOrder || "asc"
101
- };
102
- }
103
-
104
- export { applyFiltersToQuery, extractPaginationParams, parseQueryFilters };
105
- //# sourceMappingURL=index.js.map
106
- //# sourceMappingURL=index.js.map
@@ -1 +0,0 @@
1
- {"version":3,"sources":["../../src/utils/queryFilters.ts"],"names":[],"mappings":";AA8CA,IAAM,YAAA,GAAuD;AAAA,EAC3D,IAAA,EAAM,IAAA;AAAA,EACN,KAAA,EAAO,IAAA;AAAA,EACP,IAAA,EAAM,GAAA;AAAA,EACN,KAAA,EAAO,IAAA;AAAA,EACP,IAAA,EAAM,GAAA;AAAA,EACN,KAAA,EAAO,IAAA;AAAA,EACP,UAAA,EAAY,gBAAA;AAAA,EACZ,cAAA,EAAgB,oBAAA;AAAA,EAChB,IAAA,EAAM,IAAA;AAAA,EACN,QAAA,EAAU,QAAA;AAAA;AAAA,EAEV,SAAA,EAAW,IAAA;AAAA,EACX,UAAA,EAAY,IAAA;AAAA,EACZ,UAAA,EAAY;AACd,CAAA;AAKA,IAAM,eAAA,uBAAsB,GAAA,CAAI;AAAA,EAC9B,MAAA;AAAA,EACA,UAAA;AAAA,EACA,OAAA;AAAA,EACA,QAAA;AAAA,EACA,QAAA;AAAA,EACA,GAAA;AAAA,EACA,QAAA;AAAA,EACA,WAAA;AAAA,EACA,SAAA;AAAA,EACA;AACF,CAAC,CAAA;AAqBM,SAAS,kBACd,KAAA,EACgB;AAChB,EAAA,MAAM,UAA0B,EAAC;AAEjC,EAAA,KAAA,MAAW,CAAC,GAAA,EAAK,KAAK,KAAK,MAAA,CAAO,OAAA,CAAQ,KAAK,CAAA,EAAG;AAEhD,IAAA,IAAI,eAAA,CAAgB,GAAA,CAAI,GAAG,CAAA,EAAG;AAG9B,IAAA,IAAI,KAAA,KAAU,MAAA,IAAa,KAAA,KAAU,IAAA,IAAQ,UAAU,EAAA,EAAI;AAG3D,IAAA,MAAM,KAAA,GAAQ,GAAA,CAAI,KAAA,CAAM,eAAe,CAAA;AAEvC,IAAA,IAAI,KAAA,EAAO;AACT,MAAA,MAAM,GAAG,KAAA,EAAO,EAAE,CAAA,GAAI,KAAA;AACtB,MAAA,MAAM,WAAA,GAAc,aAAa,EAAE,CAAA;AAEnC,MAAA,IAAI,WAAA,EAAa;AACf,QAAA,OAAA,CAAQ,IAAA,CAAK;AAAA,UACX,KAAA;AAAA,UACA,QAAA,EAAU,WAAA;AAAA,UACV,KAAA,EAAO,UAAA,CAAW,KAAA,EAAO,EAAE;AAAA,SAC5B,CAAA;AAAA,MACH,CAAA,MAAO;AAEL,QAAA,OAAA,CAAQ,IAAA,CAAK;AAAA,UACX,KAAA,EAAO,GAAA;AAAA,UACP,QAAA,EAAU,IAAA;AAAA,UACV,KAAA,EAAO,UAAA,CAAW,KAAA,EAAO,IAAI;AAAA,SAC9B,CAAA;AAAA,MACH;AAAA,IACF,CAAA,MAAO;AAEL,MAAA,OAAA,CAAQ,IAAA,CAAK;AAAA,QACX,KAAA,EAAO,GAAA;AAAA,QACP,QAAA,EAAU,IAAA;AAAA,QACV,KAAA,EAAO,UAAA,CAAW,KAAA,EAAO,IAAI;AAAA,OAC9B,CAAA;AAAA,IACH;AAAA,EACF;AAEA,EAAA,OAAO,OAAA;AACT;AAKA,SAAS,UAAA,CAAW,OAAgB,QAAA,EAA2B;AAE7D,EAAA,IAAI,QAAA,KAAa,IAAA,IAAQ,QAAA,KAAa,QAAA,IAAY,aAAa,cAAA,EAAgB;AAC7E,IAAA,IAAI,OAAO,UAAU,QAAA,EAAU;AAE7B,MAAA,OAAO,KAAA,CAAM,MAAM,GAAG,CAAA,CAAE,IAAI,CAAA,CAAA,KAAK,CAAA,CAAE,MAAM,CAAA;AAAA,IAC3C;AACA,IAAA,IAAI,KAAA,CAAM,OAAA,CAAQ,KAAK,CAAA,EAAG;AACxB,MAAA,OAAO,KAAA;AAAA,IACT;AACA,IAAA,OAAO,CAAC,KAAK,CAAA;AAAA,EACf;AAGA,EAAA,IAAI,OAAO,UAAU,QAAA,EAAU;AAE7B,IAAA,IAAI,iBAAA,CAAkB,IAAA,CAAK,KAAK,CAAA,EAAG;AACjC,MAAA,MAAM,GAAA,GAAM,WAAW,KAAK,CAAA;AAC5B,MAAA,IAAI,CAAC,KAAA,CAAM,GAAG,CAAA,EAAG;AACf,QAAA,OAAO,GAAA;AAAA,MACT;AAAA,IACF;AAGA,IAAA,IAAI,KAAA,KAAU,QAAQ,OAAO,IAAA;AAC7B,IAAA,IAAI,KAAA,KAAU,SAAS,OAAO,KAAA;AAAA,EAChC;AAEA,EAAA,OAAO,KAAA;AACT;AAiBO,SAAS,mBAAA,CACd,YACA,OAAA,EAC4B;AAC5B,EAAA,IAAI,KAAA,GAAQ,UAAA;AAEZ,EAAA,KAAA,MAAW,UAAU,OAAA,EAAS;AAC5B,IAAA,KAAA,GAAQ,KAAA,CAAM,KAAA;AAAA,MACZ,MAAA,CAAO,KAAA;AAAA,MACP,MAAA,CAAO,QAAA;AAAA,MACP,MAAA,CAAO;AAAA,KACT;AAAA,EACF;AAEA,EAAA,OAAO,KAAA;AACT;AAsBO,SAAS,uBAAA,CACd,KAAA,EACA,QAAA,GAAsC,EAAC,EACrB;AAClB,EAAA,OAAO;AAAA,IACL,MAAM,QAAA,CAAS,KAAA,CAAM,MAAgB,EAAE,CAAA,IAAK,SAAS,IAAA,IAAQ,CAAA;AAAA,IAC7D,QAAA,EAAU,QAAA,CAAS,KAAA,CAAM,QAAA,EAAoB,EAAE,CAAA,IAAK,QAAA,CAAS,KAAA,CAAM,KAAA,EAAiB,EAAE,CAAA,IAAK,QAAA,CAAS,QAAA,IAAY,EAAA;AAAA,IAChH,MAAA,EAAS,KAAA,CAAM,MAAA,IAAU,KAAA,CAAM,OAAA;AAAA,IAC/B,WAAa,KAAA,CAAM,SAAA,IAAa,KAAA,CAAM,cAAA,IAAsC,SAAS,SAAA,IAAa;AAAA,GACpG;AACF","file":"index.js","sourcesContent":["/**\n * Query Filter Utilities\n *\n * Parses URL query parameters into Firestore-compatible filter objects.\n * Supports operator encoding in parameter names: field__operator=value\n *\n * @example\n * // URL: /api/tasks?status=active&date__gte=2025-01-01&tags__contains=urgent\n * const filters = parseQueryFilters(req.query);\n * // Returns:\n * // [\n * // { field: 'status', operator: '==', value: 'active' },\n * // { field: 'date', operator: '>=', value: '2025-01-01' },\n * // { field: 'tags', operator: 'array-contains', value: 'urgent' }\n * // ]\n *\n * @packageDocumentation\n */\n\n/**\n * Parsed filter ready for Firestore query\n */\nexport interface ParsedFilter {\n field: string;\n operator: FirestoreWhereFilterOp;\n value: unknown;\n}\n\n/**\n * Firestore where filter operators\n */\nexport type FirestoreWhereFilterOp =\n | '=='\n | '!='\n | '>'\n | '>='\n | '<'\n | '<='\n | 'array-contains'\n | 'array-contains-any'\n | 'in'\n | 'not-in';\n\n/**\n * Map of URL operator suffixes to Firestore operators\n */\nconst OPERATOR_MAP: Record<string, FirestoreWhereFilterOp> = {\n 'eq': '==',\n 'neq': '!=',\n 'gt': '>',\n 'gte': '>=',\n 'lt': '<',\n 'lte': '<=',\n 'contains': 'array-contains',\n 'contains_any': 'array-contains-any',\n 'in': 'in',\n 'not_in': 'not-in',\n // Date operators map to same comparison operators\n 'date_eq': '==',\n 'date_gte': '>=',\n 'date_lte': '<=',\n};\n\n/**\n * Reserved query parameters that should not be treated as filters\n */\nconst RESERVED_PARAMS = new Set([\n 'page',\n 'pageSize',\n 'limit',\n 'offset',\n 'search',\n 'q',\n 'sortBy',\n 'sortOrder',\n 'orderBy',\n 'orderDirection',\n]);\n\n/**\n * Parse query parameters into Firestore-compatible filters.\n *\n * Supports operator encoding: field__operator=value\n *\n * @param query - Express req.query object\n * @returns Array of parsed filters ready for Firestore .where() calls\n *\n * @example\n * ```typescript\n * // In Express route handler:\n * const filters = parseQueryFilters(req.query);\n *\n * let query = db.collection('tasks');\n * for (const filter of filters) {\n * query = query.where(filter.field, filter.operator, filter.value);\n * }\n * ```\n */\nexport function parseQueryFilters(\n query: Record<string, unknown>\n): ParsedFilter[] {\n const filters: ParsedFilter[] = [];\n\n for (const [key, value] of Object.entries(query)) {\n // Skip reserved pagination/sort params\n if (RESERVED_PARAMS.has(key)) continue;\n\n // Skip empty values\n if (value === undefined || value === null || value === '') continue;\n\n // Parse operator from key: field__operator → { field, operator }\n const match = key.match(/^(.+)__(\\w+)$/);\n\n if (match) {\n const [, field, op] = match;\n const firestoreOp = OPERATOR_MAP[op];\n\n if (firestoreOp) {\n filters.push({\n field,\n operator: firestoreOp,\n value: parseValue(value, op),\n });\n } else {\n // Unknown operator - treat as field name with double underscore\n filters.push({\n field: key,\n operator: '==',\n value: parseValue(value, 'eq'),\n });\n }\n } else {\n // No operator suffix → equals\n filters.push({\n field: key,\n operator: '==',\n value: parseValue(value, 'eq'),\n });\n }\n }\n\n return filters;\n}\n\n/**\n * Parse and coerce value based on operator type\n */\nfunction parseValue(value: unknown, operator: string): unknown {\n // Handle array values for 'in' operator\n if (operator === 'in' || operator === 'not_in' || operator === 'contains_any') {\n if (typeof value === 'string') {\n // Parse comma-separated values: \"a,b,c\" → ['a', 'b', 'c']\n return value.split(',').map(v => v.trim());\n }\n if (Array.isArray(value)) {\n return value;\n }\n return [value];\n }\n\n // Handle numeric values\n if (typeof value === 'string') {\n // Try to parse as number if it looks like one\n if (/^-?\\d+(\\.\\d+)?$/.test(value)) {\n const num = parseFloat(value);\n if (!isNaN(num)) {\n return num;\n }\n }\n\n // Handle boolean strings\n if (value === 'true') return true;\n if (value === 'false') return false;\n }\n\n return value;\n}\n\n/**\n * Build a Firestore query with filters applied.\n *\n * @param collection - Base Firestore collection reference or query\n * @param filters - Parsed filters from parseQueryFilters\n * @returns Query with all filters applied\n *\n * @example\n * ```typescript\n * const filters = parseQueryFilters(req.query);\n * const baseQuery = db.collection('tasks');\n * const filteredQuery = applyFiltersToQuery(baseQuery, filters);\n * const snapshot = await filteredQuery.get();\n * ```\n */\nexport function applyFiltersToQuery<T>(\n collection: FirebaseFirestore.Query<T>,\n filters: ParsedFilter[]\n): FirebaseFirestore.Query<T> {\n let query = collection;\n\n for (const filter of filters) {\n query = query.where(\n filter.field,\n filter.operator as FirebaseFirestore.WhereFilterOp,\n filter.value\n );\n }\n\n return query;\n}\n\n/**\n * Extract pagination parameters from query\n */\nexport interface PaginationParams {\n page: number;\n pageSize: number;\n sortBy?: string;\n sortOrder: 'asc' | 'desc';\n}\n\n/**\n * Extract pagination parameters from a query object.\n *\n * Parses page, pageSize/limit, sortBy/orderBy, and sortOrder/orderDirection\n * from query parameters with sensible defaults.\n *\n * @param {Record<string, unknown>} query - Query object containing pagination params\n * @param {Partial<PaginationParams>} [defaults] - Default values for pagination\n * @returns {PaginationParams} Extracted pagination parameters\n */\nexport function extractPaginationParams(\n query: Record<string, unknown>,\n defaults: Partial<PaginationParams> = {}\n): PaginationParams {\n return {\n page: parseInt(query.page as string, 10) || defaults.page || 1,\n pageSize: parseInt(query.pageSize as string, 10) || parseInt(query.limit as string, 10) || defaults.pageSize || 20,\n sortBy: (query.sortBy || query.orderBy) as string | undefined,\n sortOrder: ((query.sortOrder || query.orderDirection) as 'asc' | 'desc') || defaults.sortOrder || 'asc',\n };\n}\n"]}
@@ -1,87 +0,0 @@
1
- /**
2
- * Query Filter Utilities
3
- *
4
- * Parses URL query parameters into Firestore-compatible filter objects.
5
- * Supports operator encoding in parameter names: field__operator=value
6
- *
7
- * @example
8
- * // URL: /api/tasks?status=active&date__gte=2025-01-01&tags__contains=urgent
9
- * const filters = parseQueryFilters(req.query);
10
- * // Returns:
11
- * // [
12
- * // { field: 'status', operator: '==', value: 'active' },
13
- * // { field: 'date', operator: '>=', value: '2025-01-01' },
14
- * // { field: 'tags', operator: 'array-contains', value: 'urgent' }
15
- * // ]
16
- *
17
- * @packageDocumentation
18
- */
19
- /**
20
- * Parsed filter ready for Firestore query
21
- */
22
- export interface ParsedFilter {
23
- field: string;
24
- operator: FirestoreWhereFilterOp;
25
- value: unknown;
26
- }
27
- /**
28
- * Firestore where filter operators
29
- */
30
- export type FirestoreWhereFilterOp = '==' | '!=' | '>' | '>=' | '<' | '<=' | 'array-contains' | 'array-contains-any' | 'in' | 'not-in';
31
- /**
32
- * Parse query parameters into Firestore-compatible filters.
33
- *
34
- * Supports operator encoding: field__operator=value
35
- *
36
- * @param query - Express req.query object
37
- * @returns Array of parsed filters ready for Firestore .where() calls
38
- *
39
- * @example
40
- * ```typescript
41
- * // In Express route handler:
42
- * const filters = parseQueryFilters(req.query);
43
- *
44
- * let query = db.collection('tasks');
45
- * for (const filter of filters) {
46
- * query = query.where(filter.field, filter.operator, filter.value);
47
- * }
48
- * ```
49
- */
50
- export declare function parseQueryFilters(query: Record<string, unknown>): ParsedFilter[];
51
- /**
52
- * Build a Firestore query with filters applied.
53
- *
54
- * @param collection - Base Firestore collection reference or query
55
- * @param filters - Parsed filters from parseQueryFilters
56
- * @returns Query with all filters applied
57
- *
58
- * @example
59
- * ```typescript
60
- * const filters = parseQueryFilters(req.query);
61
- * const baseQuery = db.collection('tasks');
62
- * const filteredQuery = applyFiltersToQuery(baseQuery, filters);
63
- * const snapshot = await filteredQuery.get();
64
- * ```
65
- */
66
- export declare function applyFiltersToQuery<T>(collection: FirebaseFirestore.Query<T>, filters: ParsedFilter[]): FirebaseFirestore.Query<T>;
67
- /**
68
- * Extract pagination parameters from query
69
- */
70
- export interface PaginationParams {
71
- page: number;
72
- pageSize: number;
73
- sortBy?: string;
74
- sortOrder: 'asc' | 'desc';
75
- }
76
- /**
77
- * Extract pagination parameters from a query object.
78
- *
79
- * Parses page, pageSize/limit, sortBy/orderBy, and sortOrder/orderDirection
80
- * from query parameters with sensible defaults.
81
- *
82
- * @param {Record<string, unknown>} query - Query object containing pagination params
83
- * @param {Partial<PaginationParams>} [defaults] - Default values for pagination
84
- * @returns {PaginationParams} Extracted pagination parameters
85
- */
86
- export declare function extractPaginationParams(query: Record<string, unknown>, defaults?: Partial<PaginationParams>): PaginationParams;
87
- //# sourceMappingURL=queryFilters.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"queryFilters.d.ts","sourceRoot":"","sources":["../../src/utils/queryFilters.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;GAiBG;AAEH;;GAEG;AACH,MAAM,WAAW,YAAY;IAC3B,KAAK,EAAE,MAAM,CAAC;IACd,QAAQ,EAAE,sBAAsB,CAAC;IACjC,KAAK,EAAE,OAAO,CAAC;CAChB;AAED;;GAEG;AACH,MAAM,MAAM,sBAAsB,GAC9B,IAAI,GACJ,IAAI,GACJ,GAAG,GACH,IAAI,GACJ,GAAG,GACH,IAAI,GACJ,gBAAgB,GAChB,oBAAoB,GACpB,IAAI,GACJ,QAAQ,CAAC;AAsCb;;;;;;;;;;;;;;;;;;GAkBG;AACH,wBAAgB,iBAAiB,CAC/B,KAAK,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAC7B,YAAY,EAAE,CA0ChB;AAoCD;;;;;;;;;;;;;;GAcG;AACH,wBAAgB,mBAAmB,CAAC,CAAC,EACnC,UAAU,EAAE,iBAAiB,CAAC,KAAK,CAAC,CAAC,CAAC,EACtC,OAAO,EAAE,YAAY,EAAE,GACtB,iBAAiB,CAAC,KAAK,CAAC,CAAC,CAAC,CAY5B;AAED;;GAEG;AACH,MAAM,WAAW,gBAAgB;IAC/B,IAAI,EAAE,MAAM,CAAC;IACb,QAAQ,EAAE,MAAM,CAAC;IACjB,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,SAAS,EAAE,KAAK,GAAG,MAAM,CAAC;CAC3B;AAED;;;;;;;;;GASG;AACH,wBAAgB,uBAAuB,CACrC,KAAK,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAC9B,QAAQ,GAAE,OAAO,CAAC,gBAAgB,CAAM,GACvC,gBAAgB,CAOlB"}
@@ -1,6 +0,0 @@
1
- /**
2
- * @fileoverview Unit tests for state sync WebSocket handler
3
- * @module @almadar/server/websocket/state-sync.test
4
- */
5
- export {};
6
- //# sourceMappingURL=state-sync.test.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"state-sync.test.d.ts","sourceRoot":"","sources":["../../../src/websocket/__tests__/state-sync.test.ts"],"names":[],"mappings":"AAAA;;;GAGG"}
@@ -1,39 +0,0 @@
1
- /**
2
- * State Sync WebSocket Handler
3
- *
4
- * Provides real-time state synchronization using Socket.IO.
5
- *
6
- * @packageDocumentation
7
- */
8
- interface Socket {
9
- data: {
10
- user: {
11
- uid: string;
12
- roles: string[];
13
- orgId?: string;
14
- };
15
- };
16
- handshake: {
17
- auth: {
18
- token: string;
19
- clientId: string;
20
- };
21
- };
22
- join: (room: string) => void;
23
- leave: (room: string) => void;
24
- on: (event: string, handler: (...args: unknown[]) => void) => void;
25
- emit: (event: string, ...args: unknown[]) => void;
26
- to: (room: string) => {
27
- emit: (event: string, ...args: unknown[]) => void;
28
- };
29
- }
30
- interface SocketServer {
31
- use: (middleware: (socket: Socket, next: (err?: Error) => void) => void) => void;
32
- on: (event: string, handler: (socket: Socket) => void) => void;
33
- }
34
- /**
35
- * Set up state sync WebSocket with Firebase Auth
36
- */
37
- export declare function setupStateSyncWebSocket(io: SocketServer): void;
38
- export {};
39
- //# sourceMappingURL=state-sync.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"state-sync.d.ts","sourceRoot":"","sources":["../../src/websocket/state-sync.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAOH,UAAU,MAAM;IACd,IAAI,EAAE;QAAE,IAAI,EAAE;YAAE,GAAG,EAAE,MAAM,CAAC;YAAC,KAAK,EAAE,MAAM,EAAE,CAAC;YAAC,KAAK,CAAC,EAAE,MAAM,CAAA;SAAE,CAAA;KAAE,CAAC;IACjE,SAAS,EAAE;QAAE,IAAI,EAAE;YAAE,KAAK,EAAE,MAAM,CAAC;YAAC,QAAQ,EAAE,MAAM,CAAA;SAAE,CAAA;KAAE,CAAC;IACzD,IAAI,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,IAAI,CAAC;IAC7B,KAAK,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,IAAI,CAAC;IAC9B,EAAE,EAAE,CAAC,KAAK,EAAE,MAAM,EAAE,OAAO,EAAE,CAAC,GAAG,IAAI,EAAE,OAAO,EAAE,KAAK,IAAI,KAAK,IAAI,CAAC;IACnE,IAAI,EAAE,CAAC,KAAK,EAAE,MAAM,EAAE,GAAG,IAAI,EAAE,OAAO,EAAE,KAAK,IAAI,CAAC;IAClD,EAAE,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK;QAAE,IAAI,EAAE,CAAC,KAAK,EAAE,MAAM,EAAE,GAAG,IAAI,EAAE,OAAO,EAAE,KAAK,IAAI,CAAA;KAAE,CAAC;CAC7E;AAED,UAAU,YAAY;IACpB,GAAG,EAAE,CAAC,UAAU,EAAE,CAAC,MAAM,EAAE,MAAM,EAAE,IAAI,EAAE,CAAC,GAAG,CAAC,EAAE,KAAK,KAAK,IAAI,KAAK,IAAI,KAAK,IAAI,CAAC;IACjF,EAAE,EAAE,CAAC,KAAK,EAAE,MAAM,EAAE,OAAO,EAAE,CAAC,MAAM,EAAE,MAAM,KAAK,IAAI,KAAK,IAAI,CAAC;CAChE;AAED;;GAEG;AACH,wBAAgB,uBAAuB,CAAC,EAAE,EAAE,YAAY,GAAG,IAAI,CAmE9D"}
@@ -1,77 +0,0 @@
1
- import { getStateSyncManager, getMultiUserManager } from '@almadar-io/agent';
2
- import admin from 'firebase-admin';
3
-
4
- // src/websocket/state-sync.ts
5
- function getApp() {
6
- if (admin.apps.length === 0) {
7
- throw new Error(
8
- "@almadar/server: Firebase Admin SDK is not initialized. Call initializeFirebase() or admin.initializeApp() before using @almadar/server."
9
- );
10
- }
11
- return admin.app();
12
- }
13
- function getFirestore() {
14
- return getApp().firestore();
15
- }
16
- function getAuth() {
17
- return getApp().auth();
18
- }
19
- new Proxy({}, {
20
- get(_target, prop, receiver) {
21
- const firestore = getFirestore();
22
- const value = Reflect.get(firestore, prop, receiver);
23
- return typeof value === "function" ? value.bind(firestore) : value;
24
- }
25
- });
26
-
27
- // src/websocket/state-sync.ts
28
- function setupStateSyncWebSocket(io) {
29
- const stateSync = getStateSyncManager();
30
- io.use(async (socket, next) => {
31
- try {
32
- const token = socket.handshake.auth.token;
33
- if (!token) {
34
- return next(new Error("Authentication required"));
35
- }
36
- const decodedToken = await getAuth().verifyIdToken(token);
37
- const user = await getAuth().getUser(decodedToken.uid);
38
- socket.data.user = {
39
- uid: decodedToken.uid,
40
- roles: user.customClaims?.roles ?? ["user"],
41
- orgId: user.customClaims?.orgId
42
- };
43
- next();
44
- } catch (error) {
45
- console.error("Socket auth failed:", error);
46
- next(new Error("Invalid token"));
47
- }
48
- });
49
- io.on("connection", (socket) => {
50
- const userId = socket.data.user.uid;
51
- const clientId = socket.handshake.auth.clientId;
52
- console.log(`[StateSync] Client ${clientId} connected for user ${userId}`);
53
- stateSync.updateConfig({ clientId });
54
- socket.join(`user:${userId}`);
55
- socket.on("stateChange", (...args) => {
56
- const event = args[0];
57
- const multiUser = getMultiUserManager();
58
- if (!multiUser.isSessionOwner(event.threadId, userId)) {
59
- socket.emit("error", { message: "Not session owner" });
60
- return;
61
- }
62
- stateSync.receiveRemoteChange(event);
63
- socket.to(`user:${userId}`).emit("remoteChange", event);
64
- });
65
- stateSync.on("syncRequired", (changes) => {
66
- socket.emit("syncBatch", changes);
67
- });
68
- socket.on("disconnect", () => {
69
- console.log(`[StateSync] Client ${clientId} disconnected`);
70
- socket.leave(`user:${userId}`);
71
- });
72
- });
73
- }
74
-
75
- export { setupStateSyncWebSocket };
76
- //# sourceMappingURL=state-sync.js.map
77
- //# sourceMappingURL=state-sync.js.map
@@ -1 +0,0 @@
1
- {"version":3,"sources":["../../src/lib/db.ts","../../src/websocket/state-sync.ts"],"names":[],"mappings":";;;;AAoFA,SAAS,MAAA,GAAwB;AAC/B,EAAA,IAAI,KAAA,CAAM,IAAA,CAAK,MAAA,KAAW,CAAA,EAAG;AAC3B,IAAA,MAAM,IAAI,KAAA;AAAA,MACR;AAAA,KAEF;AAAA,EACF;AACA,EAAA,OAAO,MAAM,GAAA,EAAI;AACnB;AAKO,SAAS,YAAA,GAA0C;AACxD,EAAA,OAAO,MAAA,GAAS,SAAA,EAAU;AAC5B;AAKO,SAAS,OAAA,GAA2B;AACzC,EAAA,OAAO,MAAA,GAAS,IAAA,EAAK;AACvB;AASkB,IAAI,KAAA,CAAM,EAAC,EAAgC;AAAA,EAC3D,GAAA,CAAI,OAAA,EAAS,IAAA,EAAM,QAAA,EAAU;AAC3B,IAAA,MAAM,YAAY,YAAA,EAAa;AAC/B,IAAA,MAAM,KAAA,GAAQ,OAAA,CAAQ,GAAA,CAAI,SAAA,EAAW,MAAM,QAAQ,CAAA;AACnD,IAAA,OAAO,OAAO,KAAA,KAAU,UAAA,GAAa,KAAA,CAAM,IAAA,CAAK,SAAS,CAAA,GAAI,KAAA;AAAA,EAC/D;AACF,CAAC;;;AC1FM,SAAS,wBAAwB,EAAA,EAAwB;AAC9D,EAAA,MAAM,YAAY,mBAAA,EAAoB;AAGtC,EAAA,EAAA,CAAG,GAAA,CAAI,OAAO,MAAA,EAAQ,IAAA,KAAS;AAC7B,IAAA,IAAI;AACF,MAAA,MAAM,KAAA,GAAQ,MAAA,CAAO,SAAA,CAAU,IAAA,CAAK,KAAA;AACpC,MAAA,IAAI,CAAC,KAAA,EAAO;AACV,QAAA,OAAO,IAAA,CAAK,IAAI,KAAA,CAAM,yBAAyB,CAAC,CAAA;AAAA,MAClD;AAEA,MAAA,MAAM,YAAA,GAAe,MAAM,OAAA,EAAQ,CAAE,cAAc,KAAK,CAAA;AACxD,MAAA,MAAM,OAAO,MAAM,OAAA,EAAQ,CAAE,OAAA,CAAQ,aAAa,GAAG,CAAA;AAErD,MAAA,MAAA,CAAO,KAAK,IAAA,GAAO;AAAA,QACjB,KAAK,YAAA,CAAa,GAAA;AAAA,QAClB,KAAA,EAAQ,IAAA,CAAK,YAAA,EAAc,KAAA,IAAsB,CAAC,MAAM,CAAA;AAAA,QACxD,KAAA,EAAO,KAAK,YAAA,EAAc;AAAA,OAC5B;AAEA,MAAA,IAAA,EAAK;AAAA,IACP,SAAS,KAAA,EAAO;AACd,MAAA,OAAA,CAAQ,KAAA,CAAM,uBAAuB,KAAK,CAAA;AAC1C,MAAA,IAAA,CAAK,IAAI,KAAA,CAAM,eAAe,CAAC,CAAA;AAAA,IACjC;AAAA,EACF,CAAC,CAAA;AAED,EAAA,EAAA,CAAG,EAAA,CAAG,YAAA,EAAc,CAAC,MAAA,KAAmB;AACtC,IAAA,MAAM,MAAA,GAAS,MAAA,CAAO,IAAA,CAAK,IAAA,CAAK,GAAA;AAChC,IAAA,MAAM,QAAA,GAAW,MAAA,CAAO,SAAA,CAAU,IAAA,CAAK,QAAA;AAEvC,IAAA,OAAA,CAAQ,GAAA,CAAI,CAAA,mBAAA,EAAsB,QAAQ,CAAA,oBAAA,EAAuB,MAAM,CAAA,CAAE,CAAA;AAGzE,IAAA,SAAA,CAAU,YAAA,CAAa,EAAE,QAAA,EAAU,CAAA;AAGnC,IAAA,MAAA,CAAO,IAAA,CAAK,CAAA,KAAA,EAAQ,MAAM,CAAA,CAAE,CAAA;AAG5B,IAAA,MAAA,CAAO,EAAA,CAAG,aAAA,EAAe,CAAA,GAAI,IAAA,KAAoB;AAC/C,MAAA,MAAM,KAAA,GAAQ,KAAK,CAAC,CAAA;AAGpB,MAAA,MAAM,YAAY,mBAAA,EAAoB;AACtC,MAAA,IAAI,CAAC,SAAA,CAAU,cAAA,CAAe,KAAA,CAAM,QAAA,EAAU,MAAM,CAAA,EAAG;AACrD,QAAA,MAAA,CAAO,IAAA,CAAK,OAAA,EAAS,EAAE,OAAA,EAAS,qBAAqB,CAAA;AACrD,QAAA;AAAA,MACF;AAGA,MAAA,SAAA,CAAU,oBAAoB,KAAgE,CAAA;AAG9F,MAAA,MAAA,CAAO,GAAG,CAAA,KAAA,EAAQ,MAAM,EAAE,CAAA,CAAE,IAAA,CAAK,gBAAgB,KAAK,CAAA;AAAA,IACxD,CAAC,CAAA;AAGD,IAAA,SAAA,CAAU,EAAA,CAAG,cAAA,EAAgB,CAAC,OAAA,KAAuB;AACnD,MAAA,MAAA,CAAO,IAAA,CAAK,aAAa,OAAO,CAAA;AAAA,IAClC,CAAC,CAAA;AAED,IAAA,MAAA,CAAO,EAAA,CAAG,cAAc,MAAM;AAC5B,MAAA,OAAA,CAAQ,GAAA,CAAI,CAAA,mBAAA,EAAsB,QAAQ,CAAA,aAAA,CAAe,CAAA;AACzD,MAAA,MAAA,CAAO,KAAA,CAAM,CAAA,KAAA,EAAQ,MAAM,CAAA,CAAE,CAAA;AAAA,IAC/B,CAAC,CAAA;AAAA,EACH,CAAC,CAAA;AACH","file":"state-sync.js","sourcesContent":["/**\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","/**\n * State Sync WebSocket Handler\n *\n * Provides real-time state synchronization using Socket.IO.\n *\n * @packageDocumentation\n */\n\nimport { getStateSyncManager } from '@almadar-io/agent';\nimport { getMultiUserManager } from '@almadar-io/agent';\nimport { getAuth } from '../lib/db.js';\n\n// Type definitions for Socket.IO (to avoid dependency)\ninterface Socket {\n data: { user: { uid: string; roles: string[]; orgId?: string } };\n handshake: { auth: { token: string; clientId: string } };\n join: (room: string) => void;\n leave: (room: string) => void;\n on: (event: string, handler: (...args: unknown[]) => void) => void;\n emit: (event: string, ...args: unknown[]) => void;\n to: (room: string) => { emit: (event: string, ...args: unknown[]) => void };\n}\n\ninterface SocketServer {\n use: (middleware: (socket: Socket, next: (err?: Error) => void) => void) => void;\n on: (event: string, handler: (socket: Socket) => void) => void;\n}\n\n/**\n * Set up state sync WebSocket with Firebase Auth\n */\nexport function setupStateSyncWebSocket(io: SocketServer): void {\n const stateSync = getStateSyncManager();\n\n // Firebase Auth middleware for Socket.IO\n io.use(async (socket, next) => {\n try {\n const token = socket.handshake.auth.token as string;\n if (!token) {\n return next(new Error('Authentication required'));\n }\n\n const decodedToken = await getAuth().verifyIdToken(token);\n const user = await getAuth().getUser(decodedToken.uid);\n\n socket.data.user = {\n uid: decodedToken.uid,\n roles: (user.customClaims?.roles as string[]) ?? ['user'],\n orgId: user.customClaims?.orgId as string,\n };\n\n next();\n } catch (error) {\n console.error('Socket auth failed:', error);\n next(new Error('Invalid token'));\n }\n });\n\n io.on('connection', (socket: Socket) => {\n const userId = socket.data.user.uid;\n const clientId = socket.handshake.auth.clientId;\n\n console.log(`[StateSync] Client ${clientId} connected for user ${userId}`);\n\n // Update client ID in sync manager\n stateSync.updateConfig({ clientId });\n\n // Join user's room for targeted updates\n socket.join(`user:${userId}`);\n\n // Listen for state changes from client\n socket.on('stateChange', (...args: unknown[]) => {\n const event = args[0] as { threadId: string };\n \n // Verify ownership\n const multiUser = getMultiUserManager();\n if (!multiUser.isSessionOwner(event.threadId, userId)) {\n socket.emit('error', { message: 'Not session owner' });\n return;\n }\n\n // Process through sync manager\n stateSync.receiveRemoteChange(event as unknown as import('@almadar-io/agent').StateChangeEvent);\n\n // Broadcast to other clients of same user\n socket.to(`user:${userId}`).emit('remoteChange', event);\n });\n\n // Handle sync required events from agent\n stateSync.on('syncRequired', (changes: unknown[]) => {\n socket.emit('syncBatch', changes);\n });\n\n socket.on('disconnect', () => {\n console.log(`[StateSync] Client ${clientId} disconnected`);\n socket.leave(`user:${userId}`);\n });\n });\n}\n"]}