@forgebase/database 0.0.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 (219) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +470 -0
  3. package/dist/cjs/adapters/base.d.ts +20 -0
  4. package/dist/cjs/adapters/base.d.ts.map +1 -0
  5. package/dist/cjs/adapters/base.js +13 -0
  6. package/dist/cjs/adapters/base.js.map +1 -0
  7. package/dist/cjs/adapters/index.d.ts +6 -0
  8. package/dist/cjs/adapters/index.d.ts.map +1 -0
  9. package/dist/cjs/adapters/index.js +24 -0
  10. package/dist/cjs/adapters/index.js.map +1 -0
  11. package/dist/cjs/adapters/postgres.d.ts +13 -0
  12. package/dist/cjs/adapters/postgres.d.ts.map +1 -0
  13. package/dist/cjs/adapters/postgres.js +51 -0
  14. package/dist/cjs/adapters/postgres.js.map +1 -0
  15. package/dist/cjs/adapters/sqlite.d.ts +13 -0
  16. package/dist/cjs/adapters/sqlite.d.ts.map +1 -0
  17. package/dist/cjs/adapters/sqlite.js +47 -0
  18. package/dist/cjs/adapters/sqlite.js.map +1 -0
  19. package/dist/cjs/adapters/types.d.ts +8 -0
  20. package/dist/cjs/adapters/types.d.ts.map +1 -0
  21. package/dist/cjs/adapters/types.js +3 -0
  22. package/dist/cjs/adapters/types.js.map +1 -0
  23. package/dist/cjs/database.d.ts +73 -0
  24. package/dist/cjs/database.d.ts.map +1 -0
  25. package/dist/cjs/database.js +673 -0
  26. package/dist/cjs/database.js.map +1 -0
  27. package/dist/cjs/errors.d.ts +37 -0
  28. package/dist/cjs/errors.d.ts.map +1 -0
  29. package/dist/cjs/errors.js +64 -0
  30. package/dist/cjs/errors.js.map +1 -0
  31. package/dist/cjs/index.d.ts +16 -0
  32. package/dist/cjs/index.d.ts.map +1 -0
  33. package/dist/cjs/index.js +31 -0
  34. package/dist/cjs/index.js.map +1 -0
  35. package/dist/cjs/kysely-hooks.d.ts +45 -0
  36. package/dist/cjs/kysely-hooks.d.ts.map +1 -0
  37. package/dist/cjs/kysely-hooks.js +93 -0
  38. package/dist/cjs/kysely-hooks.js.map +1 -0
  39. package/dist/cjs/libsql/example.d.ts +2 -0
  40. package/dist/cjs/libsql/example.d.ts.map +1 -0
  41. package/dist/cjs/libsql/example.js +44 -0
  42. package/dist/cjs/libsql/example.js.map +1 -0
  43. package/dist/cjs/libsql/index.d.ts +36 -0
  44. package/dist/cjs/libsql/index.d.ts.map +1 -0
  45. package/dist/cjs/libsql/index.js +155 -0
  46. package/dist/cjs/libsql/index.js.map +1 -0
  47. package/dist/cjs/permissionService.d.ts +20 -0
  48. package/dist/cjs/permissionService.d.ts.map +1 -0
  49. package/dist/cjs/permissionService.js +107 -0
  50. package/dist/cjs/permissionService.js.map +1 -0
  51. package/dist/cjs/rlsFunctionRegistry.d.ts +43 -0
  52. package/dist/cjs/rlsFunctionRegistry.d.ts.map +1 -0
  53. package/dist/cjs/rlsFunctionRegistry.js +63 -0
  54. package/dist/cjs/rlsFunctionRegistry.js.map +1 -0
  55. package/dist/cjs/rlsManager.d.ts +23 -0
  56. package/dist/cjs/rlsManager.d.ts.map +1 -0
  57. package/dist/cjs/rlsManager.js +371 -0
  58. package/dist/cjs/rlsManager.js.map +1 -0
  59. package/dist/cjs/schema.d.ts +15 -0
  60. package/dist/cjs/schema.d.ts.map +1 -0
  61. package/dist/cjs/schema.js +119 -0
  62. package/dist/cjs/schema.js.map +1 -0
  63. package/dist/cjs/sdk/client.d.ts +324 -0
  64. package/dist/cjs/sdk/client.d.ts.map +1 -0
  65. package/dist/cjs/sdk/client.js +554 -0
  66. package/dist/cjs/sdk/client.js.map +1 -0
  67. package/dist/cjs/sdk/examples.d.ts +68 -0
  68. package/dist/cjs/sdk/examples.d.ts.map +1 -0
  69. package/dist/cjs/sdk/examples.js +232 -0
  70. package/dist/cjs/sdk/examples.js.map +1 -0
  71. package/dist/cjs/sdk/server.d.ts +115 -0
  72. package/dist/cjs/sdk/server.d.ts.map +1 -0
  73. package/dist/cjs/sdk/server.js +140 -0
  74. package/dist/cjs/sdk/server.js.map +1 -0
  75. package/dist/cjs/types.d.ts +217 -0
  76. package/dist/cjs/types.d.ts.map +1 -0
  77. package/dist/cjs/types.js +5 -0
  78. package/dist/cjs/types.js.map +1 -0
  79. package/dist/cjs/utils/column-utils.d.ts +8 -0
  80. package/dist/cjs/utils/column-utils.d.ts.map +1 -0
  81. package/dist/cjs/utils/column-utils.js +131 -0
  82. package/dist/cjs/utils/column-utils.js.map +1 -0
  83. package/dist/cjs/utils/db.d.ts +2 -0
  84. package/dist/cjs/utils/db.d.ts.map +1 -0
  85. package/dist/cjs/utils/db.js +6 -0
  86. package/dist/cjs/utils/db.js.map +1 -0
  87. package/dist/cjs/utils/inspector.d.ts +39 -0
  88. package/dist/cjs/utils/inspector.d.ts.map +1 -0
  89. package/dist/cjs/utils/inspector.js +164 -0
  90. package/dist/cjs/utils/inspector.js.map +1 -0
  91. package/dist/cjs/utils/permission-initializer.d.ts +15 -0
  92. package/dist/cjs/utils/permission-initializer.d.ts.map +1 -0
  93. package/dist/cjs/utils/permission-initializer.js +173 -0
  94. package/dist/cjs/utils/permission-initializer.js.map +1 -0
  95. package/dist/cjs/websocket/RealtimeAdapter.d.ts +22 -0
  96. package/dist/cjs/websocket/RealtimeAdapter.d.ts.map +1 -0
  97. package/dist/cjs/websocket/RealtimeAdapter.js +3 -0
  98. package/dist/cjs/websocket/RealtimeAdapter.js.map +1 -0
  99. package/dist/cjs/websocket/SSEManager.d.ts +40 -0
  100. package/dist/cjs/websocket/SSEManager.d.ts.map +1 -0
  101. package/dist/cjs/websocket/SSEManager.js +268 -0
  102. package/dist/cjs/websocket/SSEManager.js.map +1 -0
  103. package/dist/cjs/websocket/WebSocketManager.d.ts +28 -0
  104. package/dist/cjs/websocket/WebSocketManager.d.ts.map +1 -0
  105. package/dist/cjs/websocket/WebSocketManager.js +156 -0
  106. package/dist/cjs/websocket/WebSocketManager.js.map +1 -0
  107. package/dist/cjs/websocket/index.d.ts +4 -0
  108. package/dist/cjs/websocket/index.d.ts.map +1 -0
  109. package/dist/cjs/websocket/index.js +20 -0
  110. package/dist/cjs/websocket/index.js.map +1 -0
  111. package/dist/esm/adapters/base.d.ts +20 -0
  112. package/dist/esm/adapters/base.d.ts.map +1 -0
  113. package/dist/esm/adapters/base.js +10 -0
  114. package/dist/esm/adapters/base.js.map +1 -0
  115. package/dist/esm/adapters/index.d.ts +6 -0
  116. package/dist/esm/adapters/index.d.ts.map +1 -0
  117. package/dist/esm/adapters/index.js +19 -0
  118. package/dist/esm/adapters/index.js.map +1 -0
  119. package/dist/esm/adapters/postgres.d.ts +13 -0
  120. package/dist/esm/adapters/postgres.d.ts.map +1 -0
  121. package/dist/esm/adapters/postgres.js +47 -0
  122. package/dist/esm/adapters/postgres.js.map +1 -0
  123. package/dist/esm/adapters/sqlite.d.ts +13 -0
  124. package/dist/esm/adapters/sqlite.d.ts.map +1 -0
  125. package/dist/esm/adapters/sqlite.js +43 -0
  126. package/dist/esm/adapters/sqlite.js.map +1 -0
  127. package/dist/esm/adapters/types.d.ts +8 -0
  128. package/dist/esm/adapters/types.d.ts.map +1 -0
  129. package/dist/esm/adapters/types.js +2 -0
  130. package/dist/esm/adapters/types.js.map +1 -0
  131. package/dist/esm/database.d.ts +73 -0
  132. package/dist/esm/database.d.ts.map +1 -0
  133. package/dist/esm/database.js +668 -0
  134. package/dist/esm/database.js.map +1 -0
  135. package/dist/esm/errors.d.ts +37 -0
  136. package/dist/esm/errors.d.ts.map +1 -0
  137. package/dist/esm/errors.js +55 -0
  138. package/dist/esm/errors.js.map +1 -0
  139. package/dist/esm/index.d.ts +16 -0
  140. package/dist/esm/index.d.ts.map +1 -0
  141. package/dist/esm/index.js +15 -0
  142. package/dist/esm/index.js.map +1 -0
  143. package/dist/esm/kysely-hooks.d.ts +45 -0
  144. package/dist/esm/kysely-hooks.d.ts.map +1 -0
  145. package/dist/esm/kysely-hooks.js +86 -0
  146. package/dist/esm/kysely-hooks.js.map +1 -0
  147. package/dist/esm/libsql/example.d.ts +2 -0
  148. package/dist/esm/libsql/example.d.ts.map +1 -0
  149. package/dist/esm/libsql/example.js +42 -0
  150. package/dist/esm/libsql/example.js.map +1 -0
  151. package/dist/esm/libsql/index.d.ts +36 -0
  152. package/dist/esm/libsql/index.d.ts.map +1 -0
  153. package/dist/esm/libsql/index.js +116 -0
  154. package/dist/esm/libsql/index.js.map +1 -0
  155. package/dist/esm/permissionService.d.ts +20 -0
  156. package/dist/esm/permissionService.d.ts.map +1 -0
  157. package/dist/esm/permissionService.js +103 -0
  158. package/dist/esm/permissionService.js.map +1 -0
  159. package/dist/esm/rlsFunctionRegistry.d.ts +43 -0
  160. package/dist/esm/rlsFunctionRegistry.d.ts.map +1 -0
  161. package/dist/esm/rlsFunctionRegistry.js +60 -0
  162. package/dist/esm/rlsFunctionRegistry.js.map +1 -0
  163. package/dist/esm/rlsManager.d.ts +23 -0
  164. package/dist/esm/rlsManager.d.ts.map +1 -0
  165. package/dist/esm/rlsManager.js +366 -0
  166. package/dist/esm/rlsManager.js.map +1 -0
  167. package/dist/esm/schema.d.ts +15 -0
  168. package/dist/esm/schema.d.ts.map +1 -0
  169. package/dist/esm/schema.js +113 -0
  170. package/dist/esm/schema.js.map +1 -0
  171. package/dist/esm/sdk/client.d.ts +324 -0
  172. package/dist/esm/sdk/client.d.ts.map +1 -0
  173. package/dist/esm/sdk/client.js +550 -0
  174. package/dist/esm/sdk/client.js.map +1 -0
  175. package/dist/esm/sdk/examples.d.ts +68 -0
  176. package/dist/esm/sdk/examples.d.ts.map +1 -0
  177. package/dist/esm/sdk/examples.js +229 -0
  178. package/dist/esm/sdk/examples.js.map +1 -0
  179. package/dist/esm/sdk/server.d.ts +115 -0
  180. package/dist/esm/sdk/server.d.ts.map +1 -0
  181. package/dist/esm/sdk/server.js +136 -0
  182. package/dist/esm/sdk/server.js.map +1 -0
  183. package/dist/esm/types.d.ts +217 -0
  184. package/dist/esm/types.d.ts.map +1 -0
  185. package/dist/esm/types.js +2 -0
  186. package/dist/esm/types.js.map +1 -0
  187. package/dist/esm/utils/column-utils.d.ts +8 -0
  188. package/dist/esm/utils/column-utils.d.ts.map +1 -0
  189. package/dist/esm/utils/column-utils.js +127 -0
  190. package/dist/esm/utils/column-utils.js.map +1 -0
  191. package/dist/esm/utils/db.d.ts +2 -0
  192. package/dist/esm/utils/db.d.ts.map +1 -0
  193. package/dist/esm/utils/db.js +3 -0
  194. package/dist/esm/utils/db.js.map +1 -0
  195. package/dist/esm/utils/inspector.d.ts +39 -0
  196. package/dist/esm/utils/inspector.d.ts.map +1 -0
  197. package/dist/esm/utils/inspector.js +160 -0
  198. package/dist/esm/utils/inspector.js.map +1 -0
  199. package/dist/esm/utils/permission-initializer.d.ts +15 -0
  200. package/dist/esm/utils/permission-initializer.d.ts.map +1 -0
  201. package/dist/esm/utils/permission-initializer.js +137 -0
  202. package/dist/esm/utils/permission-initializer.js.map +1 -0
  203. package/dist/esm/websocket/RealtimeAdapter.d.ts +22 -0
  204. package/dist/esm/websocket/RealtimeAdapter.d.ts.map +1 -0
  205. package/dist/esm/websocket/RealtimeAdapter.js +2 -0
  206. package/dist/esm/websocket/RealtimeAdapter.js.map +1 -0
  207. package/dist/esm/websocket/SSEManager.d.ts +40 -0
  208. package/dist/esm/websocket/SSEManager.d.ts.map +1 -0
  209. package/dist/esm/websocket/SSEManager.js +231 -0
  210. package/dist/esm/websocket/SSEManager.js.map +1 -0
  211. package/dist/esm/websocket/WebSocketManager.d.ts +28 -0
  212. package/dist/esm/websocket/WebSocketManager.d.ts.map +1 -0
  213. package/dist/esm/websocket/WebSocketManager.js +152 -0
  214. package/dist/esm/websocket/WebSocketManager.js.map +1 -0
  215. package/dist/esm/websocket/index.d.ts +4 -0
  216. package/dist/esm/websocket/index.d.ts.map +1 -0
  217. package/dist/esm/websocket/index.js +4 -0
  218. package/dist/esm/websocket/index.js.map +1 -0
  219. package/package.json +67 -0
@@ -0,0 +1,137 @@
1
+ import * as fs from 'fs';
2
+ import * as path from 'path';
3
+ export async function initializePermissions(db, permissionService, dbInspector, excludedTables, defaultPermissions, reportPath, onComplete) {
4
+ // Start the initialization process in the background
5
+ setTimeout(async () => {
6
+ const report = {
7
+ startTime: new Date(),
8
+ endTime: new Date(),
9
+ totalTables: 0,
10
+ tablesWithPermissions: 0,
11
+ tablesInitialized: 0,
12
+ tablesExcluded: 0,
13
+ initializedTables: [],
14
+ existingPermissionTables: [],
15
+ excludedTables: [],
16
+ errors: [],
17
+ };
18
+ try {
19
+ // Get all tables
20
+ const allTables = await dbInspector.getTables();
21
+ // Filter out excluded tables
22
+ const tablesToProcess = allTables.filter((table) => !excludedTables.includes(table));
23
+ report.totalTables = allTables.length;
24
+ report.tablesExcluded = allTables.length - tablesToProcess.length;
25
+ report.excludedTables = excludedTables;
26
+ // Process each table
27
+ for (const table of tablesToProcess) {
28
+ try {
29
+ // Check if the table already has permissions
30
+ const existingPermissions = await permissionService.getPermissionsForTable(table);
31
+ if (existingPermissions &&
32
+ Object.keys(existingPermissions).length > 0) {
33
+ // Table already has permissions
34
+ report.tablesWithPermissions++;
35
+ report.existingPermissionTables.push(table);
36
+ }
37
+ else {
38
+ // Table doesn't have permissions, set default permissions
39
+ await permissionService.setPermissionsForTable(table, defaultPermissions);
40
+ report.tablesInitialized++;
41
+ report.initializedTables.push(table);
42
+ }
43
+ }
44
+ catch (error) {
45
+ // Log the error and continue with the next table
46
+ const errorMessage = error instanceof Error ? error.message : String(error);
47
+ report.errors.push({ table, error: errorMessage });
48
+ console.error(`Error initializing permissions for table ${table}:`, error);
49
+ }
50
+ }
51
+ }
52
+ catch (error) {
53
+ const errorMessage = error instanceof Error ? error.message : String(error);
54
+ report.errors.push({ table: 'global', error: errorMessage });
55
+ console.error('Error during permission initialization:', error);
56
+ }
57
+ finally {
58
+ // Update the end time
59
+ report.endTime = new Date();
60
+ // Generate the report file if a path is provided
61
+ if (reportPath) {
62
+ generateReportFile(report, reportPath);
63
+ }
64
+ // Call the callback if provided
65
+ if (onComplete) {
66
+ onComplete(report);
67
+ }
68
+ console.log('Permission initialization completed');
69
+ }
70
+ }, 0);
71
+ }
72
+ /**
73
+ * Generates a report file with the initialization results
74
+ * @param report The initialization report
75
+ * @param reportPath Path where to save the report
76
+ */
77
+ function generateReportFile(report, reportPath) {
78
+ try {
79
+ // Ensure the directory exists
80
+ const dir = path.dirname(reportPath);
81
+ if (!fs.existsSync(dir)) {
82
+ fs.mkdirSync(dir, { recursive: true });
83
+ }
84
+ // Format the report as a readable string
85
+ const formattedReport = formatReportAsMarkdown(report);
86
+ // Write the report to the file
87
+ fs.writeFileSync(reportPath, formattedReport);
88
+ console.log(`Permission initialization report saved to ${reportPath}`);
89
+ }
90
+ catch (error) {
91
+ console.error('Error generating report file:', error);
92
+ }
93
+ }
94
+ /**
95
+ * Formats the report as a markdown string
96
+ * @param report The initialization report
97
+ * @returns Formatted report as markdown
98
+ */
99
+ function formatReportAsMarkdown(report) {
100
+ const duration = (report.endTime.getTime() - report.startTime.getTime()) / 1000;
101
+ return `# ForgeBase Permission Initialization Report
102
+
103
+ ## Summary
104
+ - **Start Time**: ${report.startTime.toISOString()}
105
+ - **End Time**: ${report.endTime.toISOString()}
106
+ - **Duration**: ${duration.toFixed(2)} seconds
107
+ - **Total Tables**: ${report.totalTables}
108
+ - **Tables with Existing Permissions**: ${report.tablesWithPermissions}
109
+ - **Tables Initialized**: ${report.tablesInitialized}
110
+ - **Tables Excluded**: ${report.tablesExcluded}
111
+
112
+ ## Details
113
+
114
+ ### Tables Initialized (${report.initializedTables.length})
115
+ ${report.initializedTables.length > 0
116
+ ? report.initializedTables.map((table) => `- \`${table}\``).join('\n')
117
+ : '- None'}
118
+
119
+ ### Tables with Existing Permissions (${report.existingPermissionTables.length})
120
+ ${report.existingPermissionTables.length > 0
121
+ ? report.existingPermissionTables
122
+ .map((table) => `- \`${table}\``)
123
+ .join('\n')
124
+ : '- None'}
125
+
126
+ ### Excluded Tables (${report.excludedTables.length})
127
+ ${report.excludedTables.length > 0
128
+ ? report.excludedTables.map((table) => `- \`${table}\``).join('\n')
129
+ : '- None'}
130
+
131
+ ### Errors (${report.errors.length})
132
+ ${report.errors.length > 0
133
+ ? report.errors.map((err) => `- \`${err.table}\`: ${err.error}`).join('\n')
134
+ : '- None'}
135
+ `;
136
+ }
137
+ //# sourceMappingURL=permission-initializer.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"permission-initializer.js","sourceRoot":"","sources":["../../../src/utils/permission-initializer.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,MAAM,IAAI,CAAC;AACzB,OAAO,KAAK,IAAI,MAAM,MAAM,CAAC;AAmB7B,MAAM,CAAC,KAAK,UAAU,qBAAqB,CACzC,EAAe,EACf,iBAAoC,EACpC,WAAwB,EACxB,cAAwB,EACxB,kBAAoC,EACpC,UAAmB,EACnB,UAA6D;IAE7D,qDAAqD;IACrD,UAAU,CAAC,KAAK,IAAI,EAAE;QACpB,MAAM,MAAM,GAAmC;YAC7C,SAAS,EAAE,IAAI,IAAI,EAAE;YACrB,OAAO,EAAE,IAAI,IAAI,EAAE;YACnB,WAAW,EAAE,CAAC;YACd,qBAAqB,EAAE,CAAC;YACxB,iBAAiB,EAAE,CAAC;YACpB,cAAc,EAAE,CAAC;YACjB,iBAAiB,EAAE,EAAE;YACrB,wBAAwB,EAAE,EAAE;YAC5B,cAAc,EAAE,EAAE;YAClB,MAAM,EAAE,EAAE;SACX,CAAC;QAEF,IAAI,CAAC;YACH,iBAAiB;YACjB,MAAM,SAAS,GAAG,MAAM,WAAW,CAAC,SAAS,EAAE,CAAC;YAEhD,6BAA6B;YAC7B,MAAM,eAAe,GAAG,SAAS,CAAC,MAAM,CACtC,CAAC,KAAK,EAAE,EAAE,CAAC,CAAC,cAAc,CAAC,QAAQ,CAAC,KAAK,CAAC,CAC3C,CAAC;YAEF,MAAM,CAAC,WAAW,GAAG,SAAS,CAAC,MAAM,CAAC;YACtC,MAAM,CAAC,cAAc,GAAG,SAAS,CAAC,MAAM,GAAG,eAAe,CAAC,MAAM,CAAC;YAClE,MAAM,CAAC,cAAc,GAAG,cAAc,CAAC;YAEvC,qBAAqB;YACrB,KAAK,MAAM,KAAK,IAAI,eAAe,EAAE,CAAC;gBACpC,IAAI,CAAC;oBACH,6CAA6C;oBAC7C,MAAM,mBAAmB,GACvB,MAAM,iBAAiB,CAAC,sBAAsB,CAAC,KAAK,CAAC,CAAC;oBAExD,IACE,mBAAmB;wBACnB,MAAM,CAAC,IAAI,CAAC,mBAAmB,CAAC,CAAC,MAAM,GAAG,CAAC,EAC3C,CAAC;wBACD,gCAAgC;wBAChC,MAAM,CAAC,qBAAqB,EAAE,CAAC;wBAC/B,MAAM,CAAC,wBAAwB,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;oBAC9C,CAAC;yBAAM,CAAC;wBACN,0DAA0D;wBAC1D,MAAM,iBAAiB,CAAC,sBAAsB,CAC5C,KAAK,EACL,kBAAkB,CACnB,CAAC;wBACF,MAAM,CAAC,iBAAiB,EAAE,CAAC;wBAC3B,MAAM,CAAC,iBAAiB,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;oBACvC,CAAC;gBACH,CAAC;gBAAC,OAAO,KAAK,EAAE,CAAC;oBACf,iDAAiD;oBACjD,MAAM,YAAY,GAChB,KAAK,YAAY,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC;oBACzD,MAAM,CAAC,MAAM,CAAC,IAAI,CAAC,EAAE,KAAK,EAAE,KAAK,EAAE,YAAY,EAAE,CAAC,CAAC;oBACnD,OAAO,CAAC,KAAK,CACX,4CAA4C,KAAK,GAAG,EACpD,KAAK,CACN,CAAC;gBACJ,CAAC;YACH,CAAC;QACH,CAAC;QAAC,OAAO,KAAK,EAAE,CAAC;YACf,MAAM,YAAY,GAChB,KAAK,YAAY,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC;YACzD,MAAM,CAAC,MAAM,CAAC,IAAI,CAAC,EAAE,KAAK,EAAE,QAAQ,EAAE,KAAK,EAAE,YAAY,EAAE,CAAC,CAAC;YAC7D,OAAO,CAAC,KAAK,CAAC,yCAAyC,EAAE,KAAK,CAAC,CAAC;QAClE,CAAC;gBAAS,CAAC;YACT,sBAAsB;YACtB,MAAM,CAAC,OAAO,GAAG,IAAI,IAAI,EAAE,CAAC;YAE5B,iDAAiD;YACjD,IAAI,UAAU,EAAE,CAAC;gBACf,kBAAkB,CAAC,MAAM,EAAE,UAAU,CAAC,CAAC;YACzC,CAAC;YAED,gCAAgC;YAChC,IAAI,UAAU,EAAE,CAAC;gBACf,UAAU,CAAC,MAAM,CAAC,CAAC;YACrB,CAAC;YAED,OAAO,CAAC,GAAG,CAAC,qCAAqC,CAAC,CAAC;QACrD,CAAC;IACH,CAAC,EAAE,CAAC,CAAC,CAAC;AACR,CAAC;AAED;;;;GAIG;AACH,SAAS,kBAAkB,CACzB,MAAsC,EACtC,UAAkB;IAElB,IAAI,CAAC;QACH,8BAA8B;QAC9B,MAAM,GAAG,GAAG,IAAI,CAAC,OAAO,CAAC,UAAU,CAAC,CAAC;QACrC,IAAI,CAAC,EAAE,CAAC,UAAU,CAAC,GAAG,CAAC,EAAE,CAAC;YACxB,EAAE,CAAC,SAAS,CAAC,GAAG,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC;QACzC,CAAC;QAED,yCAAyC;QACzC,MAAM,eAAe,GAAG,sBAAsB,CAAC,MAAM,CAAC,CAAC;QAEvD,+BAA+B;QAC/B,EAAE,CAAC,aAAa,CAAC,UAAU,EAAE,eAAe,CAAC,CAAC;QAE9C,OAAO,CAAC,GAAG,CAAC,6CAA6C,UAAU,EAAE,CAAC,CAAC;IACzE,CAAC;IAAC,OAAO,KAAK,EAAE,CAAC;QACf,OAAO,CAAC,KAAK,CAAC,+BAA+B,EAAE,KAAK,CAAC,CAAC;IACxD,CAAC;AACH,CAAC;AAED;;;;GAIG;AACH,SAAS,sBAAsB,CAC7B,MAAsC;IAEtC,MAAM,QAAQ,GACZ,CAAC,MAAM,CAAC,OAAO,CAAC,OAAO,EAAE,GAAG,MAAM,CAAC,SAAS,CAAC,OAAO,EAAE,CAAC,GAAG,IAAI,CAAC;IAEjE,OAAO;;;oBAGW,MAAM,CAAC,SAAS,CAAC,WAAW,EAAE;kBAChC,MAAM,CAAC,OAAO,CAAC,WAAW,EAAE;kBAC5B,QAAQ,CAAC,OAAO,CAAC,CAAC,CAAC;sBACf,MAAM,CAAC,WAAW;0CACE,MAAM,CAAC,qBAAqB;4BAC1C,MAAM,CAAC,iBAAiB;yBAC3B,MAAM,CAAC,cAAc;;;;0BAIpB,MAAM,CAAC,iBAAiB,CAAC,MAAM;EAEvD,MAAM,CAAC,iBAAiB,CAAC,MAAM,GAAG,CAAC;QACjC,CAAC,CAAC,MAAM,CAAC,iBAAiB,CAAC,GAAG,CAAC,CAAC,KAAK,EAAE,EAAE,CAAC,OAAO,KAAK,IAAI,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC;QACtE,CAAC,CAAC,QACN;;wCAEwC,MAAM,CAAC,wBAAwB,CAAC,MAAM;EAE5E,MAAM,CAAC,wBAAwB,CAAC,MAAM,GAAG,CAAC;QACxC,CAAC,CAAC,MAAM,CAAC,wBAAwB;aAC5B,GAAG,CAAC,CAAC,KAAK,EAAE,EAAE,CAAC,OAAO,KAAK,IAAI,CAAC;aAChC,IAAI,CAAC,IAAI,CAAC;QACf,CAAC,CAAC,QACN;;uBAEuB,MAAM,CAAC,cAAc,CAAC,MAAM;EAEjD,MAAM,CAAC,cAAc,CAAC,MAAM,GAAG,CAAC;QAC9B,CAAC,CAAC,MAAM,CAAC,cAAc,CAAC,GAAG,CAAC,CAAC,KAAK,EAAE,EAAE,CAAC,OAAO,KAAK,IAAI,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC;QACnE,CAAC,CAAC,QACN;;cAEc,MAAM,CAAC,MAAM,CAAC,MAAM;EAEhC,MAAM,CAAC,MAAM,CAAC,MAAM,GAAG,CAAC;QACtB,CAAC,CAAC,MAAM,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,GAAG,EAAE,EAAE,CAAC,OAAO,GAAG,CAAC,KAAK,OAAO,GAAG,CAAC,KAAK,EAAE,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC;QAC3E,CAAC,CAAC,QACN;CACC,CAAC;AACF,CAAC"}
@@ -0,0 +1,22 @@
1
+ /**
2
+ * Interface for realtime adapters that can be used for database table broadcasts
3
+ */
4
+ export interface RealtimeAdapter {
5
+ /**
6
+ * Initialize the adapter
7
+ */
8
+ initialize(): void;
9
+ /**
10
+ * Broadcast a message to all clients subscribed to a table
11
+ * @param tableName The name of the table
12
+ * @param event The event type (create, update, delete)
13
+ * @param data The data to broadcast
14
+ */
15
+ broadcast(tableName: string, event: string, data: any): Promise<void>;
16
+ /**
17
+ * Get the port the adapter is listening on
18
+ */
19
+ getPort(): number;
20
+ handleRequest?(request: Request): Promise<Response>;
21
+ }
22
+ //# sourceMappingURL=RealtimeAdapter.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"RealtimeAdapter.d.ts","sourceRoot":"","sources":["../../../src/websocket/RealtimeAdapter.ts"],"names":[],"mappings":"AAEA;;GAEG;AACH,MAAM,WAAW,eAAe;IAC9B;;OAEG;IACH,UAAU,IAAI,IAAI,CAAC;IAEnB;;;;;OAKG;IACH,SAAS,CAAC,SAAS,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,EAAE,IAAI,EAAE,GAAG,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IAEtE;;OAEG;IACH,OAAO,IAAI,MAAM,CAAC;IAElB,aAAa,CAAC,CAAC,OAAO,EAAE,OAAO,GAAG,OAAO,CAAC,QAAQ,CAAC,CAAC;CACrD"}
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=RealtimeAdapter.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"RealtimeAdapter.js","sourceRoot":"","sources":["../../../src/websocket/RealtimeAdapter.ts"],"names":[],"mappings":""}
@@ -0,0 +1,40 @@
1
+ import { PermissionService } from '../permissionService';
2
+ import { RealtimeAdapter } from './RealtimeAdapter';
3
+ export declare class SSEManager implements RealtimeAdapter {
4
+ private port;
5
+ private permissionService;
6
+ private sseAdapter;
7
+ constructor(port: number, permissionService: PermissionService);
8
+ /**
9
+ * Initialize the SSE server
10
+ */
11
+ initialize(): Promise<void>;
12
+ private setupSSEServer;
13
+ private canSubscribe;
14
+ /**
15
+ * Broadcast a message to all clients subscribed to a table
16
+ * @param tableName The name of the table
17
+ * @param event The event type (create, update, delete)
18
+ * @param data The data to broadcast
19
+ */
20
+ broadcast(tableName: string, event: string, data: Record<string, unknown> | Record<string, unknown>[]): Promise<void>;
21
+ /**
22
+ * Get the port the SSE server is listening on
23
+ * @returns The port number
24
+ */
25
+ getPort(): number;
26
+ /**
27
+ * Get the SSE adapter instance
28
+ * This can be used to integrate with an HTTP server
29
+ * @returns The SSE adapter instance
30
+ */
31
+ getSSEAdapter(): Promise<any>;
32
+ /**
33
+ * Handle an incoming HTTP request
34
+ * This method can be used to integrate with any HTTP server framework
35
+ * @param request The HTTP request object (must be compatible with the Fetch API Request)
36
+ * @returns A Response object
37
+ */
38
+ handleRequest(request: Request): Promise<Response>;
39
+ }
40
+ //# sourceMappingURL=SSEManager.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"SSEManager.d.ts","sourceRoot":"","sources":["../../../src/websocket/SSEManager.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,iBAAiB,EAAE,MAAM,sBAAsB,CAAC;AACzD,OAAO,EAAE,eAAe,EAAE,MAAM,mBAAmB,CAAC;AA0BpD,qBAAa,UAAW,YAAW,eAAe;IAI9C,OAAO,CAAC,IAAI;IACZ,OAAO,CAAC,iBAAiB;IAJ3B,OAAO,CAAC,UAAU,CAAkB;gBAG1B,IAAI,EAAE,MAAM,EACZ,iBAAiB,EAAE,iBAAiB;IAS9C;;OAEG;IACU,UAAU,IAAI,OAAO,CAAC,IAAI,CAAC;YAI1B,cAAc;YA6Jd,YAAY;IAmB1B;;;;;OAKG;IACU,SAAS,CACpB,SAAS,EAAE,MAAM,EACjB,KAAK,EAAE,MAAM,EACb,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAAE,GACxD,OAAO,CAAC,IAAI,CAAC;IAiChB;;;OAGG;IACI,OAAO,IAAI,MAAM;IAIxB;;;;OAIG;IACU,aAAa;IAW1B;;;;;OAKG;IACU,aAAa,CAAC,OAAO,EAAE,OAAO,GAAG,OAAO,CAAC,QAAQ,CAAC;CAoBhE"}
@@ -0,0 +1,231 @@
1
+ import { evaluatePermission } from '../rlsManager';
2
+ export class SSEManager {
3
+ constructor(port, permissionService) {
4
+ this.port = port;
5
+ this.permissionService = permissionService;
6
+ // Call initialize but don't await it here
7
+ // This allows the constructor to return synchronously
8
+ this.initialize().catch((error) => {
9
+ console.error('Failed to initialize SSE adapter:', error);
10
+ });
11
+ }
12
+ /**
13
+ * Initialize the SSE server
14
+ */
15
+ async initialize() {
16
+ await this.setupSSEServer();
17
+ }
18
+ async setupSSEServer() {
19
+ // Dynamically import the SSE adapter
20
+ const { default: sseAdapter } = await import('crossws/adapters/sse');
21
+ this.sseAdapter = sseAdapter({
22
+ bidir: true, // Enable bidirectional messaging support
23
+ hooks: {
24
+ upgrade: (request) => {
25
+ // Extract user context from request headers
26
+ let userContext;
27
+ try {
28
+ const userContextHeader = request.headers.get('userContext');
29
+ // console.log('sse request context', request.context);
30
+ // console.log('sse request header', request.headers);
31
+ if (userContextHeader) {
32
+ console.log('userContextHeader', userContextHeader);
33
+ userContext = JSON.parse(userContextHeader);
34
+ }
35
+ if (!userContext) {
36
+ // return new Response('Authentication required', { status: 401 });
37
+ userContext = {
38
+ userId: '1',
39
+ role: 'user',
40
+ labels: [],
41
+ teams: [],
42
+ permissions: [],
43
+ };
44
+ }
45
+ }
46
+ catch (error) {
47
+ console.error('Error parsing user context:', error);
48
+ return new Response('Invalid user context', { status: 400 });
49
+ }
50
+ request.context.userContext = userContext;
51
+ // In case of bidirectional mode, extra auth is recommended based on request
52
+ // return {
53
+ // headers: {},
54
+ // };
55
+ },
56
+ open: (peer) => {
57
+ // Send welcome message
58
+ peer.send(JSON.stringify({
59
+ type: 'welcome',
60
+ message: `Welcome ${peer.id}`,
61
+ }));
62
+ },
63
+ message: async (peer, message) => {
64
+ try {
65
+ // Handle both string and ReadableStream message types
66
+ let messageData;
67
+ console.log('message', typeof message);
68
+ console.log('message event', message.event);
69
+ console.log('message peer', message.peer);
70
+ console.log('message data', message.data);
71
+ console.log('message text', message.text());
72
+ // console.log('message json', message.json());
73
+ console.log('message blob', message.blob);
74
+ console.log('message array', message.uint8Array().toString());
75
+ console.log('message array buffer', message.arrayBuffer());
76
+ if (message instanceof ReadableStream) {
77
+ const reader = message.getReader();
78
+ const { value } = await reader.read();
79
+ messageData = new TextDecoder().decode(value);
80
+ }
81
+ else {
82
+ messageData = message.toString();
83
+ }
84
+ console.log('Received message:', messageData);
85
+ const msg = JSON.parse(messageData);
86
+ if (msg.type === 'subscribe' && msg.tableName) {
87
+ // Get user context from peer data
88
+ const userContext = peer.context.userContext;
89
+ if (!userContext) {
90
+ peer.send(JSON.stringify({
91
+ type: 'error',
92
+ message: 'Authentication required',
93
+ }));
94
+ return;
95
+ }
96
+ // Check table permissions
97
+ const permissions = await this.permissionService.getPermissionsForTable(msg.tableName);
98
+ if (!permissions) {
99
+ peer.send(JSON.stringify({
100
+ type: 'error',
101
+ message: 'Table not found',
102
+ }));
103
+ return;
104
+ }
105
+ // Check if user can subscribe
106
+ const canSubscribe = await this.canSubscribe(userContext, permissions);
107
+ if (!canSubscribe) {
108
+ peer.send(JSON.stringify({
109
+ type: 'error',
110
+ message: 'Permission denied',
111
+ }));
112
+ return;
113
+ }
114
+ // Subscribe the peer to the table channel
115
+ peer.subscribe(`table:${msg.tableName}`);
116
+ peer.send(JSON.stringify({
117
+ type: 'subscribed',
118
+ tableName: msg.tableName,
119
+ }));
120
+ }
121
+ else if (msg.type === 'unsubscribe' && msg.tableName) {
122
+ // Unsubscribe from the table channel
123
+ peer.unsubscribe(`table:${msg.tableName}`);
124
+ peer.send(JSON.stringify({
125
+ type: 'unsubscribed',
126
+ tableName: msg.tableName,
127
+ }));
128
+ }
129
+ }
130
+ catch (error) {
131
+ console.error('Error handling message:', error);
132
+ peer.send(JSON.stringify({
133
+ type: 'error',
134
+ message: 'Invalid message format',
135
+ }));
136
+ }
137
+ },
138
+ close: (peer) => {
139
+ // No need to manually clean up subscriptions
140
+ // crossws handles this automatically
141
+ console.log(`Client ${peer.id} disconnected`);
142
+ },
143
+ },
144
+ });
145
+ }
146
+ async canSubscribe(userContext, permissions) {
147
+ if (!permissions.operations.SELECT)
148
+ return false;
149
+ // Create a copy of the rules without fieldCheck
150
+ const selectRules = permissions.operations.SELECT;
151
+ const rulesWithoutFieldCheck = selectRules.map((rule) => {
152
+ // Using destructuring to remove fieldCheck from the rule
153
+ // eslint-disable-next-line @typescript-eslint/no-unused-vars
154
+ const { fieldCheck, ...ruleWithoutFieldCheck } = rule;
155
+ return ruleWithoutFieldCheck;
156
+ });
157
+ // Check if the user has the required permissions to subscribe
158
+ return evaluatePermission(rulesWithoutFieldCheck, userContext);
159
+ }
160
+ /**
161
+ * Broadcast a message to all clients subscribed to a table
162
+ * @param tableName The name of the table
163
+ * @param event The event type (create, update, delete)
164
+ * @param data The data to broadcast
165
+ */
166
+ async broadcast(tableName, event, data) {
167
+ // Check if the SSE adapter is initialized
168
+ if (!this.sseAdapter) {
169
+ console.warn('SSE adapter not initialized yet. Waiting for initialization...');
170
+ await this.initialize();
171
+ }
172
+ // Get permissions for the table
173
+ const permissions = await this.permissionService.getPermissionsForTable(tableName);
174
+ if (!permissions || !permissions.operations.SELECT)
175
+ return;
176
+ // Prepare the data to broadcast
177
+ const dataArray = Array.isArray(data) ? data : [data];
178
+ // Publish to the table channel
179
+ // Note: In a real implementation, we would filter data based on user permissions
180
+ // But since we're using pub/sub, all subscribers will receive the same message
181
+ // FIXME: Assess the permissions for each row
182
+ const message = JSON.stringify({
183
+ event,
184
+ tableName,
185
+ data: dataArray,
186
+ });
187
+ // Use the SSE adapter to publish to all subscribers of this table
188
+ this.sseAdapter.publish(`table:${tableName}`, message);
189
+ }
190
+ /**
191
+ * Get the port the SSE server is listening on
192
+ * @returns The port number
193
+ */
194
+ getPort() {
195
+ return this.port;
196
+ }
197
+ /**
198
+ * Get the SSE adapter instance
199
+ * This can be used to integrate with an HTTP server
200
+ * @returns The SSE adapter instance
201
+ */
202
+ async getSSEAdapter() {
203
+ // Check if the SSE adapter is initialized
204
+ if (!this.sseAdapter) {
205
+ console.warn('SSE adapter not initialized yet. Waiting for initialization...');
206
+ await this.initialize();
207
+ }
208
+ return this.sseAdapter;
209
+ }
210
+ /**
211
+ * Handle an incoming HTTP request
212
+ * This method can be used to integrate with any HTTP server framework
213
+ * @param request The HTTP request object (must be compatible with the Fetch API Request)
214
+ * @returns A Response object
215
+ */
216
+ async handleRequest(request) {
217
+ // Check if the SSE adapter is initialized
218
+ if (!this.sseAdapter) {
219
+ console.warn('SSE adapter not initialized yet. Waiting for initialization...');
220
+ await this.initialize();
221
+ }
222
+ // Check if this is an SSE request
223
+ if (request.headers.get('accept') === 'text/event-stream' ||
224
+ request.headers.has('x-crossws-id')) {
225
+ return this.sseAdapter.fetch(request);
226
+ }
227
+ // Return 404 for non-SSE requests
228
+ return new Response('Not found', { status: 404 });
229
+ }
230
+ }
231
+ //# sourceMappingURL=SSEManager.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"SSEManager.js","sourceRoot":"","sources":["../../../src/websocket/SSEManager.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,28 @@
1
+ import { PermissionService } from '../permissionService';
2
+ import { RealtimeAdapter } from './RealtimeAdapter';
3
+ export declare class WebSocketManager implements RealtimeAdapter {
4
+ private port;
5
+ private permissionService;
6
+ private app;
7
+ private clients;
8
+ private tableSubscriptions;
9
+ constructor(port: number, permissionService: PermissionService);
10
+ /**
11
+ * Initialize the WebSocket server
12
+ */
13
+ initialize(): void;
14
+ private setupWebSocketServer;
15
+ private handleSubscribe;
16
+ private handleUnsubscribe;
17
+ private removeClient;
18
+ private canSubscribe;
19
+ private processBatch;
20
+ broadcast(tableName: string, event: string, data: any): Promise<void>;
21
+ private sendError;
22
+ /**
23
+ * Get the port the WebSocket server is listening on
24
+ * @returns The port number
25
+ */
26
+ getPort(): number;
27
+ }
28
+ //# sourceMappingURL=WebSocketManager.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"WebSocketManager.d.ts","sourceRoot":"","sources":["../../../src/websocket/WebSocketManager.ts"],"names":[],"mappings":"AAQA,OAAO,EAAE,iBAAiB,EAAE,MAAM,sBAAsB,CAAC;AACzD,OAAO,EAAE,eAAe,EAAE,MAAM,mBAAmB,CAAC;AAQpD,qBAAa,gBAAiB,YAAW,eAAe;IAMpD,OAAO,CAAC,IAAI;IACZ,OAAO,CAAC,iBAAiB;IAN3B,OAAO,CAAC,GAAG,CAAe;IAC1B,OAAO,CAAC,OAAO,CAAwC;IACvD,OAAO,CAAC,kBAAkB,CAAuC;gBAGvD,IAAI,EAAE,MAAM,EACZ,iBAAiB,EAAE,iBAAiB;IAM9C;;OAEG;IACI,UAAU,IAAI,IAAI;IAIzB,OAAO,CAAC,oBAAoB;YAyDd,eAAe;IAuB7B,OAAO,CAAC,iBAAiB;IAKzB,OAAO,CAAC,YAAY;YAON,YAAY;YAoBZ,YAAY;IAsCb,SAAS,CAAC,SAAS,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,EAAE,IAAI,EAAE,GAAG;IA2BlE,OAAO,CAAC,SAAS;IAIjB;;;OAGG;IACI,OAAO,IAAI,MAAM;CAGzB"}
@@ -0,0 +1,152 @@
1
+ import { App, SHARED_COMPRESSOR, } from 'uWebSockets.js';
2
+ import { evaluatePermission } from '../rlsManager';
3
+ export class WebSocketManager {
4
+ constructor(port, permissionService) {
5
+ this.port = port;
6
+ this.permissionService = permissionService;
7
+ this.clients = new Map();
8
+ this.tableSubscriptions = new Map();
9
+ this.app = App();
10
+ this.initialize();
11
+ }
12
+ /**
13
+ * Initialize the WebSocket server
14
+ */
15
+ initialize() {
16
+ this.setupWebSocketServer();
17
+ }
18
+ setupWebSocketServer() {
19
+ this.app.ws('/*', {
20
+ compression: SHARED_COMPRESSOR,
21
+ maxPayloadLength: 16 * 1024 * 1024,
22
+ idleTimeout: 10,
23
+ upgrade: (res, req, context) => {
24
+ res.upgrade({
25
+ userContext: JSON.parse(req.getHeader('user-context')),
26
+ },
27
+ /* Spell these correctly */
28
+ req.getHeader('sec-websocket-key'), req.getHeader('sec-websocket-protocol'), req.getHeader('sec-websocket-extensions'), context);
29
+ },
30
+ open: (ws) => {
31
+ const socketClient = ws;
32
+ socketClient.id = Math.random().toString(36).substr(2, 9);
33
+ socketClient.subscribedTables = new Set();
34
+ socketClient.userContext = ws.userContext;
35
+ this.clients.set(socketClient.id, socketClient);
36
+ },
37
+ message: async (ws, message) => {
38
+ const socketClient = ws;
39
+ const msg = JSON.parse(Buffer.from(message).toString());
40
+ switch (msg.type) {
41
+ case 'subscribe':
42
+ await this.handleSubscribe(socketClient, msg.tableName);
43
+ break;
44
+ case 'unsubscribe':
45
+ this.handleUnsubscribe(socketClient, msg.tableName);
46
+ break;
47
+ default:
48
+ this.sendError(socketClient, 'Invalid message type');
49
+ break;
50
+ }
51
+ },
52
+ close: (ws) => {
53
+ const socketClient = ws;
54
+ this.removeClient(socketClient);
55
+ },
56
+ drain: (ws) => {
57
+ const socketClient = ws;
58
+ this.removeClient(socketClient);
59
+ },
60
+ });
61
+ this.app.listen(this.port, () => {
62
+ console.log(`WebSocket server listening on port ${this.port}`);
63
+ });
64
+ }
65
+ async handleSubscribe(client, tableName) {
66
+ if (!client.userContext) {
67
+ this.sendError(client, 'Authentication required');
68
+ return;
69
+ }
70
+ // Check table permissions
71
+ const permissions = await this.permissionService.getPermissionsForTable(tableName);
72
+ if (!this.canSubscribe(client.userContext, permissions)) {
73
+ this.sendError(client, 'Permission denied');
74
+ return;
75
+ }
76
+ client.subscribedTables.add(tableName);
77
+ if (!this.tableSubscriptions.has(tableName)) {
78
+ this.tableSubscriptions.set(tableName, new Set());
79
+ }
80
+ this.tableSubscriptions.get(tableName).add(client.id);
81
+ }
82
+ handleUnsubscribe(client, tableName) {
83
+ client.subscribedTables.delete(tableName);
84
+ this.tableSubscriptions.get(tableName)?.delete(client.id);
85
+ }
86
+ removeClient(client) {
87
+ client.subscribedTables.forEach((tableName) => {
88
+ this.tableSubscriptions.get(tableName)?.delete(client.id);
89
+ });
90
+ this.clients.delete(client.id);
91
+ }
92
+ async canSubscribe(userContext, permissions) {
93
+ if (!permissions)
94
+ return false;
95
+ const selectRules = permissions.operations.SELECT;
96
+ if (!selectRules)
97
+ return false;
98
+ // Create a copy of the rules without fieldCheck
99
+ const rulesWithoutFieldCheck = selectRules.map((rule) => {
100
+ const { fieldCheck, ...ruleWithoutFieldCheck } = rule;
101
+ return ruleWithoutFieldCheck;
102
+ });
103
+ // Check if the user has the required permissions to subscribe
104
+ return evaluatePermission(rulesWithoutFieldCheck, userContext);
105
+ }
106
+ async processBatch(clients, tableName, event, data, permissions) {
107
+ const BATCH_SIZE = 100; // Adjust based on your needs
108
+ for (let i = 0; i < clients.length; i += BATCH_SIZE) {
109
+ const batchClients = clients.slice(i, i + BATCH_SIZE);
110
+ await Promise.all(batchClients.map(async (client) => {
111
+ if (!client.userContext || !permissions.operations.SELECT)
112
+ return;
113
+ // Filter data based on user permissions
114
+ const filteredData = data.filter((item) => evaluatePermission(permissions.operations.SELECT, client.userContext, item));
115
+ if (filteredData.length > 0) {
116
+ client.send(JSON.stringify({
117
+ event,
118
+ tableName,
119
+ data: filteredData,
120
+ }));
121
+ }
122
+ }));
123
+ }
124
+ }
125
+ async broadcast(tableName, event, data) {
126
+ const subscribers = this.tableSubscriptions.get(tableName);
127
+ if (!subscribers)
128
+ return;
129
+ const permissions = await this.permissionService.getPermissionsForTable(tableName);
130
+ if (!permissions || !permissions.operations.SELECT)
131
+ return;
132
+ // Get all active clients for this table
133
+ const activeClients = Array.from(subscribers)
134
+ .map((clientId) => this.clients.get(clientId))
135
+ .filter((client) => client !== undefined);
136
+ // Handle single record vs array of records
137
+ const dataArray = Array.isArray(data) ? data : [data];
138
+ // Process in batches
139
+ await this.processBatch(activeClients, tableName, event, dataArray, permissions);
140
+ }
141
+ sendError(client, message) {
142
+ client.send(JSON.stringify({ type: 'error', message }));
143
+ }
144
+ /**
145
+ * Get the port the WebSocket server is listening on
146
+ * @returns The port number
147
+ */
148
+ getPort() {
149
+ return this.port;
150
+ }
151
+ }
152
+ //# sourceMappingURL=WebSocketManager.js.map