@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,673 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.createForgeDatabase = exports.ForgeDatabase = void 0;
4
+ const kysely_1 = require("kysely");
5
+ const permissionService_1 = require("./permissionService");
6
+ const rlsManager_1 = require("./rlsManager");
7
+ const inspector_1 = require("./utils/inspector");
8
+ const kysely_hooks_1 = require("./kysely-hooks");
9
+ const errors_1 = require("./errors");
10
+ const types_1 = require("./types");
11
+ const column_utils_1 = require("./utils/column-utils");
12
+ const schema_1 = require("./schema");
13
+ const server_1 = require("./sdk/server");
14
+ const WebSocketManager_1 = require("./websocket/WebSocketManager");
15
+ const SSEManager_1 = require("./websocket/SSEManager");
16
+ const permission_initializer_1 = require("./utils/permission-initializer");
17
+ const libsql_1 = require("./libsql");
18
+ class ForgeDatabase {
19
+ constructor(config = {}) {
20
+ this.config = config;
21
+ this.defaultPermissions = {
22
+ operations: {
23
+ SELECT: [
24
+ {
25
+ allow: 'private',
26
+ },
27
+ ],
28
+ INSERT: [
29
+ {
30
+ allow: 'private',
31
+ },
32
+ ],
33
+ UPDATE: [
34
+ {
35
+ allow: 'private',
36
+ },
37
+ ],
38
+ DELETE: [
39
+ {
40
+ allow: 'private',
41
+ },
42
+ ],
43
+ },
44
+ };
45
+ this.excludedTables = [types_1.FG_PERMISSION_TABLE];
46
+ /**
47
+ * Get the endpoints for the database
48
+ * @returns Endpoints for the database
49
+ */
50
+ this.endpoints = {
51
+ /**
52
+ * Work with the database schema
53
+ * @returns Schema endpoints
54
+ */
55
+ schema: {
56
+ get: async (trx) => {
57
+ return await this.dbInspector.getDatabaseSchema(this.excludedTables);
58
+ },
59
+ create: async (payload, trx) => {
60
+ const { tableName, columns } = payload;
61
+ if (!tableName) {
62
+ throw new Error('Invalid request body');
63
+ }
64
+ // Efficiently check if table exists
65
+ const hasTable = await this.dbInspector.hasTable(tableName);
66
+ if (hasTable) {
67
+ console.log('Table already exists');
68
+ throw new Error('Table already exists');
69
+ }
70
+ // Use transaction if provided, otherwise use the db instance
71
+ const builder = trx || this.hooks.getDbInstance();
72
+ // Start creating the table
73
+ // Start creating the table
74
+ let tableBuilder = builder.schema.createTable(tableName);
75
+ // Add columns using strict createColumn util
76
+ columns.forEach((col) => {
77
+ tableBuilder = (0, column_utils_1.createColumn)(tableBuilder, col, this.hooks.getDbInstance());
78
+ });
79
+ await tableBuilder.execute();
80
+ await this.permissionService.setPermissionsForTable(tableName, this.defaultPermissions, builder);
81
+ return {
82
+ message: 'Table created successfully',
83
+ tablename: tableName,
84
+ action: 'create',
85
+ };
86
+ },
87
+ delete: async (tableName, trx) => {
88
+ if (this.excludedTables.includes(tableName)) {
89
+ throw new errors_1.ExcludedTableError(tableName);
90
+ }
91
+ // Use transaction if provided, otherwise use the db instance
92
+ const builder = trx || this.hooks.getDbInstance();
93
+ await builder.schema.dropTable(tableName).ifExists().execute();
94
+ await this.permissionService.deletePermissionsForTable(tableName, builder);
95
+ return {
96
+ message: 'Table deleted successfully',
97
+ tablename: tableName,
98
+ action: 'delete',
99
+ };
100
+ },
101
+ modify: async (payload, trx) => {
102
+ if (this.excludedTables.includes(payload.tableName)) {
103
+ throw new errors_1.ExcludedTableError(payload.tableName);
104
+ }
105
+ const builder = this.hooks.getDbInstance();
106
+ return await (0, schema_1.modifySchema)(builder, payload, trx);
107
+ },
108
+ addForeingKey: async (payload, trx) => {
109
+ if (this.excludedTables.includes(payload.tableName)) {
110
+ throw new errors_1.ExcludedTableError(payload.tableName);
111
+ }
112
+ if (this.excludedTables.includes(payload.foreignTableName)) {
113
+ throw new errors_1.ExcludedTableError(payload.foreignTableName);
114
+ }
115
+ return await (0, schema_1.addForeignKey)(payload, this.hooks.getDbInstance(), trx);
116
+ },
117
+ dropForeignKey: async (payload, trx) => {
118
+ if (this.excludedTables.includes(payload.tableName)) {
119
+ throw new errors_1.ExcludedTableError(payload.tableName);
120
+ }
121
+ return await (0, schema_1.dropForeignKey)(payload, this.hooks.getDbInstance(), trx);
122
+ },
123
+ truncateTable: async (tableName, trx) => {
124
+ if (this.excludedTables.includes(tableName)) {
125
+ throw new errors_1.ExcludedTableError(tableName);
126
+ }
127
+ return await (0, schema_1.truncateTable)(tableName, this.hooks.getDbInstance(), trx);
128
+ },
129
+ getTableSchema: async (tableName, trx) => {
130
+ if (this.excludedTables.includes(tableName)) {
131
+ throw new errors_1.ExcludedTableError(tableName);
132
+ }
133
+ const tableInfo = await this.dbInspector.getTableInfo(tableName);
134
+ return {
135
+ name: tableName,
136
+ info: tableInfo,
137
+ };
138
+ },
139
+ getTables: async (trx) => {
140
+ let tables = await this.dbInspector.getTables();
141
+ tables = tables.filter((t) => !this.excludedTables.includes(t));
142
+ return tables;
143
+ },
144
+ getTablePermissions: async (tableName, trx) => {
145
+ if (this.excludedTables.includes(tableName)) {
146
+ throw new errors_1.ExcludedTableError(tableName);
147
+ }
148
+ return await this.permissionService.getPermissionsForTable(tableName, trx);
149
+ },
150
+ getTableSchemaWithPermissions: async (tableName, trx) => {
151
+ if (this.excludedTables.includes(tableName)) {
152
+ throw new errors_1.ExcludedTableError(tableName);
153
+ }
154
+ const tableInfo = await this.dbInspector.getTableInfo(tableName);
155
+ const permissions = await this.permissionService.getPermissionsForTable(tableName, trx);
156
+ return {
157
+ name: tableName,
158
+ info: tableInfo,
159
+ permissions,
160
+ };
161
+ },
162
+ },
163
+ /**
164
+ * Work with the database data
165
+ * @returns Data endpoints
166
+ */
167
+ data: {
168
+ query: async (tableName, params, user, isSystem = false, trx) => {
169
+ if (this.excludedTables.includes(tableName)) {
170
+ throw new errors_1.ExcludedTableError(tableName);
171
+ }
172
+ // const queryParams = this.parseQueryParams(params);
173
+ const queryParams = params; // KyselyQueryHandler handles params object directly
174
+ if (!this.config.enforceRls || isSystem) {
175
+ return this.hooks.query(tableName, (db) => this.queryHandler
176
+ .buildQuery(queryParams, db.selectFrom(tableName))
177
+ .execute(), queryParams, trx);
178
+ }
179
+ if (!user && !isSystem && this.config.enforceRls) {
180
+ throw new errors_1.AuthenticationRequiredError('Authentication required to query records');
181
+ }
182
+ const { status: initialStatus, hasFieldCheck: initialHasFieldCheck, hasCustomFunction: initialHasCustomFunction, fieldCheckRules: extractedFieldCheckRules, } = await (0, rlsManager_1.enforcePermissions)(tableName, 'SELECT', user, this.permissionService, undefined, this.hooks.getDbInstance());
183
+ if (!initialStatus &&
184
+ !initialHasFieldCheck &&
185
+ !initialHasCustomFunction) {
186
+ throw new errors_1.PermissionDeniedError(`User does not have permission to query table "${tableName}"`);
187
+ }
188
+ const records = (await this.hooks.query(tableName, (db) => this.queryHandler
189
+ .buildQuery(queryParams, db.selectFrom(tableName))
190
+ .execute(), queryParams, trx));
191
+ if (initialStatus) {
192
+ // Simple rule matched — return all records
193
+ return records;
194
+ }
195
+ // Fast path: filter rows using pre-extracted fieldCheck rules
196
+ if (extractedFieldCheckRules?.length) {
197
+ const filtered = [];
198
+ for (const record of records) {
199
+ const allowed = await (0, rlsManager_1.evaluateFieldCheckForRow)(extractedFieldCheckRules, user, record, this.hooks.getDbInstance());
200
+ if (allowed) {
201
+ filtered.push(record);
202
+ }
203
+ }
204
+ if (filtered.length === 0) {
205
+ throw new errors_1.PermissionDeniedError(`User does not have permission to query table "${tableName}"`);
206
+ }
207
+ return filtered;
208
+ }
209
+ // Fallback for customFunction rules
210
+ const { status, row } = await (0, rlsManager_1.enforcePermissions)(tableName, 'SELECT', user, this.permissionService, records, this.hooks.getDbInstance());
211
+ if (!status) {
212
+ throw new errors_1.PermissionDeniedError(`User does not have permission to query table "${tableName}"`);
213
+ }
214
+ return row;
215
+ },
216
+ create: async (params, user, isSystem = false, trx) => {
217
+ if (this.excludedTables.includes(params.tableName)) {
218
+ throw new errors_1.ExcludedTableError(params.tableName);
219
+ }
220
+ const { data, tableName } = params;
221
+ // console.log('data-db', data, tableName);
222
+ // Handle both single record and array of records
223
+ const isArray = Array.isArray(data);
224
+ const records = isArray ? data : [data];
225
+ // Validate records
226
+ if (!records.length ||
227
+ !records.every((record) => typeof record === 'object' && Object.keys(record).length > 0)) {
228
+ console.log('Invalid request body', records);
229
+ throw new Error('Invalid request body');
230
+ }
231
+ if (!this.config.enforceRls || isSystem) {
232
+ return this.hooks.mutate(tableName, 'create', async (db) => db.insertInto(tableName).values(records).returningAll().execute(), records, undefined, trx);
233
+ }
234
+ if (!user && !isSystem && this.config.enforceRls) {
235
+ throw new errors_1.AuthenticationRequiredError('Authentication required to create records');
236
+ }
237
+ const { status: initialStatus, hasFieldCheck: initialHasFieldCheck, hasCustomFunction: initialHasCustomFunction, } = await (0, rlsManager_1.enforcePermissions)(tableName, 'INSERT', user, this.permissionService, undefined, this.hooks.getDbInstance());
238
+ if (!initialStatus &&
239
+ !initialHasFieldCheck &&
240
+ !initialHasCustomFunction) {
241
+ throw new errors_1.PermissionDeniedError(`User does not have permission to create record in table "${tableName}"`);
242
+ }
243
+ if (initialStatus) {
244
+ // If the user has permission to create, proceed with the creation
245
+ return this.hooks.mutate(tableName, 'create', async (db) => db.insertInto(tableName).values(records).returningAll().execute(), records, undefined, trx);
246
+ }
247
+ const { status, row } = await (0, rlsManager_1.enforcePermissions)(tableName, 'INSERT', user, this.permissionService, records, this.hooks.getDbInstance());
248
+ if (!status) {
249
+ throw new Error(`User does not have permission to create record in table "${tableName}"`);
250
+ }
251
+ if (!row || (Array.isArray(row) && row.length === 0)) {
252
+ throw new errors_1.PermissionDeniedError(`User does not have permission to create this record in table "${tableName}"`);
253
+ }
254
+ const result = this.hooks.mutate(tableName, 'create', async (db) => db.insertInto(tableName).values(row).returningAll().execute(), row, undefined, trx);
255
+ return result;
256
+ },
257
+ update: async (params, user, isSystem = false, trx) => {
258
+ if (this.excludedTables.includes(params.tableName)) {
259
+ throw new errors_1.ExcludedTableError(params.tableName);
260
+ }
261
+ const { id, tableName, data } = params;
262
+ if (!this.config.enforceRls || isSystem) {
263
+ const result = this.hooks.mutate(tableName, 'update', async (db) => db
264
+ .updateTable(tableName)
265
+ .set(data)
266
+ .where('id', '=', id)
267
+ .returningAll()
268
+ .execute(), { id, ...data }, undefined, trx);
269
+ return result;
270
+ }
271
+ if (!user && !isSystem && this.config.enforceRls) {
272
+ throw new errors_1.AuthenticationRequiredError('Authentication required to update records');
273
+ }
274
+ const { status: initialStatus, hasFieldCheck: initialHasFieldCheck, hasCustomFunction: initialHasCustomFunction, fieldCheckRules: extractedFieldCheckRules, } = await (0, rlsManager_1.enforcePermissions)(tableName, 'UPDATE', user, this.permissionService, undefined, this.hooks.getDbInstance());
275
+ if (!initialStatus &&
276
+ !initialHasFieldCheck &&
277
+ !initialHasCustomFunction) {
278
+ throw new Error(`User does not have permission to update record with id ${id}`);
279
+ }
280
+ if (initialStatus) {
281
+ // Simple rule matched — proceed directly
282
+ const result = this.hooks.mutate(tableName, 'update', async (db) => db
283
+ .updateTable(tableName)
284
+ .set(data)
285
+ .where('id', '=', id)
286
+ .returningAll()
287
+ .execute(), { id, ...data }, undefined, trx);
288
+ return result;
289
+ }
290
+ // fieldCheck/customFunction path — fetch the row then evaluate
291
+ const record = await this.hooks.query(tableName, (db) => {
292
+ return db
293
+ .selectFrom(tableName)
294
+ .where('id', '=', id)
295
+ .selectAll()
296
+ .execute();
297
+ }, { id }, trx);
298
+ // Fast path: use pre-extracted rules instead of full enforcePermissions
299
+ if (extractedFieldCheckRules?.length) {
300
+ const allowed = await (0, rlsManager_1.evaluateFieldCheckForRow)(extractedFieldCheckRules, user, record[0], this.hooks.getDbInstance());
301
+ if (!allowed) {
302
+ throw new errors_1.PermissionDeniedError(`User does not have permission to update record with id ${id}`);
303
+ }
304
+ }
305
+ else {
306
+ // Fallback for customFunction rules
307
+ const { status } = await (0, rlsManager_1.enforcePermissions)(tableName, 'UPDATE', user, this.permissionService, record[0], this.hooks.getDbInstance());
308
+ if (!status) {
309
+ throw new errors_1.PermissionDeniedError(`User does not have permission to update record with id ${id}`);
310
+ }
311
+ }
312
+ const result = this.hooks.mutate(tableName, 'update', async (db) => db
313
+ .updateTable(tableName)
314
+ .set(data)
315
+ .where('id', '=', id)
316
+ .returningAll()
317
+ .execute(), { id, ...data }, undefined, trx);
318
+ return result;
319
+ },
320
+ advanceUpdate: async (params, user, isSystem = false, trx) => {
321
+ if (this.excludedTables.includes(params.tableName)) {
322
+ throw new errors_1.ExcludedTableError(params.tableName);
323
+ }
324
+ const { query, tableName, data } = params;
325
+ // const queryParams = this.parseQueryParams(query);
326
+ const queryParams = query;
327
+ if (!this.config.enforceRls || isSystem) {
328
+ const result = this.hooks.mutate(tableName, 'update', async (db) => this.queryHandler
329
+ .buildQuery(queryParams, db.updateTable(tableName))
330
+ .set(data)
331
+ .returningAll()
332
+ .execute(), { ...data }, undefined, trx);
333
+ return result;
334
+ }
335
+ if (!user && !isSystem && this.config.enforceRls) {
336
+ throw new errors_1.AuthenticationRequiredError('Authentication required to update records');
337
+ }
338
+ const { status: initialStatus, hasFieldCheck: initialHasFieldCheck, hasCustomFunction: initialHasCustomFunction, fieldCheckRules: extractedFieldCheckRules, } = await (0, rlsManager_1.enforcePermissions)(tableName, 'UPDATE', user, this.permissionService, undefined, this.hooks.getDbInstance());
339
+ if (!initialStatus &&
340
+ !initialHasFieldCheck &&
341
+ !initialHasCustomFunction) {
342
+ throw new Error(`User does not have permission to update this records`);
343
+ }
344
+ if (initialStatus) {
345
+ // Simple rule matched — proceed directly
346
+ const result = this.hooks.mutate(tableName, 'update', async (db) => this.queryHandler
347
+ .buildQuery(queryParams, db.updateTable(tableName))
348
+ .set(data)
349
+ .returningAll()
350
+ .execute(), { ...data }, queryParams, trx);
351
+ return result;
352
+ }
353
+ // fieldCheck/customFunction path — fetch matching rows then evaluate
354
+ const records = (await this.hooks.query(tableName, (db) => this.queryHandler
355
+ .buildQuery(queryParams, db.selectFrom(tableName))
356
+ .selectAll()
357
+ .execute(), queryParams, trx));
358
+ // Fast path: use pre-extracted rules instead of full enforcePermissions
359
+ if (extractedFieldCheckRules?.length) {
360
+ for (const record of records) {
361
+ const allowed = await (0, rlsManager_1.evaluateFieldCheckForRow)(extractedFieldCheckRules, user, record, this.hooks.getDbInstance());
362
+ if (!allowed) {
363
+ throw new errors_1.PermissionDeniedError(`User does not have permission to update this records`);
364
+ }
365
+ }
366
+ }
367
+ else {
368
+ // Fallback for customFunction rules
369
+ const { status } = await (0, rlsManager_1.enforcePermissions)(tableName, 'UPDATE', user, this.permissionService, records, this.hooks.getDbInstance());
370
+ if (!status) {
371
+ throw new errors_1.PermissionDeniedError(`User does not have permission to update this records`);
372
+ }
373
+ }
374
+ const result = this.hooks.mutate(tableName, 'update', async (db) => this.queryHandler
375
+ .buildQuery(queryParams, db.updateTable(tableName))
376
+ .set(data)
377
+ .returningAll()
378
+ .execute(), { ...data }, queryParams, trx);
379
+ return result;
380
+ },
381
+ delete: async (params, user, isSystem = false, trx) => {
382
+ if (this.excludedTables.includes(params.tableName)) {
383
+ throw new errors_1.ExcludedTableError(params.tableName);
384
+ }
385
+ const { id, tableName } = params;
386
+ if (!this.config.enforceRls || isSystem) {
387
+ return this.hooks.mutate(tableName, 'delete', async (db) => db
388
+ .deleteFrom(tableName)
389
+ .where('id', '=', id)
390
+ .returning('id')
391
+ .execute(), { id }, undefined, trx);
392
+ }
393
+ if (!user && !isSystem && this.config.enforceRls) {
394
+ throw new errors_1.AuthenticationRequiredError('Authentication required to delete records');
395
+ }
396
+ const { status: initialStatus, hasFieldCheck: initialHasFieldCheck, hasCustomFunction: initialHasCustomFunction, fieldCheckRules: extractedFieldCheckRules, } = await (0, rlsManager_1.enforcePermissions)(tableName, 'DELETE', user, this.permissionService, undefined, this.hooks.getDbInstance());
397
+ if (!initialStatus &&
398
+ !initialHasFieldCheck &&
399
+ !initialHasCustomFunction) {
400
+ throw new errors_1.PermissionDeniedError(`User does not have permission to delete record with id ${id}`);
401
+ }
402
+ if (initialStatus) {
403
+ // If the user has permission to delete, proceed with the deletion
404
+ return this.hooks.mutate(tableName, 'delete', async (db) => db.deleteFrom(tableName).where('id', '=', id).execute(), { id }, undefined, trx);
405
+ }
406
+ // fieldCheck/customFunction path — fetch the record then evaluate
407
+ const record = (await this.hooks.query(tableName, (db) => {
408
+ return db
409
+ .selectFrom(tableName)
410
+ .where('id', '=', id)
411
+ .selectAll()
412
+ .execute();
413
+ }, { id }, trx));
414
+ // Fast path: use pre-extracted rules instead of full enforcePermissions
415
+ if (extractedFieldCheckRules?.length) {
416
+ const allowed = await (0, rlsManager_1.evaluateFieldCheckForRow)(extractedFieldCheckRules, user, record[0], this.hooks.getDbInstance());
417
+ if (!allowed) {
418
+ throw new errors_1.PermissionDeniedError(`User does not have permission to delete record with id ${id}`);
419
+ }
420
+ }
421
+ else {
422
+ // Fallback for customFunction rules
423
+ const { status } = await (0, rlsManager_1.enforcePermissions)(tableName, 'DELETE', user, this.permissionService, record, this.hooks.getDbInstance());
424
+ if (!status) {
425
+ throw new errors_1.PermissionDeniedError(`User does not have permission to delete record with id ${id}`);
426
+ }
427
+ }
428
+ return this.hooks.mutate(tableName, 'delete', async (db) => db.deleteFrom(tableName).where('id', '=', id).execute(), { id }, undefined, trx);
429
+ },
430
+ advanceDelete: async (params, user, isSystem = false, trx) => {
431
+ if (this.excludedTables.includes(params.tableName)) {
432
+ throw new errors_1.ExcludedTableError(params.tableName);
433
+ }
434
+ const { query, tableName } = params;
435
+ // const queryParams = this.parseQueryParams(query);
436
+ const queryParams = query;
437
+ if (!this.config.enforceRls || isSystem) {
438
+ return this.hooks.mutate(tableName, 'delete', async (db) => this.queryHandler
439
+ .buildQuery(queryParams, db.deleteFrom(tableName))
440
+ .returningAll()
441
+ .execute(), undefined, queryParams, trx);
442
+ }
443
+ if (!user && !isSystem && this.config.enforceRls) {
444
+ throw new errors_1.AuthenticationRequiredError('Authentication required to delete records');
445
+ }
446
+ const { status: initialStatus, hasFieldCheck: initialHasFieldCheck, hasCustomFunction: initialHasCustomFunction, fieldCheckRules: extractedFieldCheckRules, } = await (0, rlsManager_1.enforcePermissions)(tableName, 'DELETE', user, this.permissionService, undefined, this.hooks.getDbInstance());
447
+ if (!initialStatus &&
448
+ !initialHasFieldCheck &&
449
+ !initialHasCustomFunction) {
450
+ throw new errors_1.PermissionDeniedError(`User does not have permission to delete this records`);
451
+ }
452
+ if (initialStatus) {
453
+ // Simple rule matched — proceed directly
454
+ return this.hooks.mutate(tableName, 'delete', async (db) => this.queryHandler
455
+ .buildQuery(queryParams, db.deleteFrom(tableName))
456
+ .returningAll()
457
+ .execute(), undefined, queryParams, trx);
458
+ }
459
+ // fieldCheck/customFunction path — fetch matching rows then evaluate
460
+ const records = (await this.hooks.query(tableName, async (db) => this.queryHandler
461
+ .buildQuery(queryParams, db.selectFrom(tableName))
462
+ .execute(), queryParams, trx));
463
+ // Fast path: use pre-extracted rules instead of full enforcePermissions
464
+ if (extractedFieldCheckRules?.length) {
465
+ for (const record of records) {
466
+ const allowed = await (0, rlsManager_1.evaluateFieldCheckForRow)(extractedFieldCheckRules, user, record, this.hooks.getDbInstance());
467
+ if (!allowed) {
468
+ throw new errors_1.PermissionDeniedError(`User does not have permission to delete this records`);
469
+ }
470
+ }
471
+ }
472
+ else {
473
+ // Fallback for customFunction rules
474
+ const { status } = await (0, rlsManager_1.enforcePermissions)(tableName, 'DELETE', user, this.permissionService, records, this.hooks.getDbInstance());
475
+ if (!status) {
476
+ throw new errors_1.PermissionDeniedError(`User does not have permission to delete this records`);
477
+ }
478
+ }
479
+ return this.hooks.mutate(tableName, 'delete', async (db) => this.queryHandler
480
+ .buildQuery(queryParams, db.deleteFrom(tableName))
481
+ .returningAll()
482
+ .execute(), undefined, queryParams, trx);
483
+ },
484
+ },
485
+ /**
486
+ * Work with the database permissions
487
+ * @returns Permissions endpoints
488
+ */
489
+ permissions: {
490
+ get: async (params, trx) => {
491
+ if (this.excludedTables.includes(params.tableName)) {
492
+ throw new errors_1.ExcludedTableError(params.tableName);
493
+ }
494
+ // If no transaction is provided, create one internally
495
+ if (!trx) {
496
+ return this.transaction(async (newTrx) => {
497
+ return await this.endpoints.permissions.get(params, newTrx);
498
+ });
499
+ }
500
+ const { tableName } = params;
501
+ return this.permissionService.getPermissionsForTable(tableName, trx);
502
+ },
503
+ set: async (params, trx) => {
504
+ if (this.excludedTables.includes(params.tableName)) {
505
+ throw new errors_1.ExcludedTableError(params.tableName);
506
+ }
507
+ // If no transaction is provided, create one internally and manage it
508
+ if (!trx) {
509
+ return this.transaction(async (newTrx) => {
510
+ return await this.endpoints.permissions.set(params, newTrx);
511
+ });
512
+ }
513
+ const { tableName, permissions } = params;
514
+ if (!permissions) {
515
+ throw new Error('Permissions object is required');
516
+ }
517
+ return this.permissionService.setPermissionsForTable(tableName, permissions, trx);
518
+ },
519
+ },
520
+ };
521
+ if (!config.db && !config.libsql) {
522
+ throw new Error('Either database instance (db) or libsql config is required');
523
+ }
524
+ if (!config.db && config.libsql) {
525
+ this.config.db = new kysely_1.Kysely({
526
+ dialect: new libsql_1.LibsqlDialect(config.libsql),
527
+ });
528
+ }
529
+ if (config.excludedTables) {
530
+ this.excludedTables = [...this.excludedTables, ...config.excludedTables];
531
+ }
532
+ this.permissionService =
533
+ config.permissionsService || new permissionService_1.PermissionService(config.db);
534
+ this.dbInspector = new inspector_1.DBInspector(config.db);
535
+ // Initialize realtime adapter if realtime is enabled
536
+ if (config.realtime) {
537
+ const adapterType = config.realtimeAdapter || 'sse';
538
+ const port = config.websocketPort || 9001;
539
+ if (adapterType === 'websocket') {
540
+ this.realtimeAdapter = new WebSocketManager_1.WebSocketManager(port, this.permissionService);
541
+ }
542
+ else if (adapterType === 'sse') {
543
+ this.realtimeAdapter = new SSEManager_1.SSEManager(port, this.permissionService);
544
+ }
545
+ }
546
+ this.hooks =
547
+ config.hooks || new kysely_hooks_1.KyselyHooks(config.db, this.realtimeAdapter);
548
+ this.queryHandler = new server_1.KyselyQueryHandler(this.hooks.getDbInstance());
549
+ // Set default permissions for all tables
550
+ if (config.defaultPermissions) {
551
+ this.defaultPermissions = config.defaultPermissions;
552
+ }
553
+ // Initialize permissions for all tables if enabled
554
+ if (config.initializePermissions) {
555
+ this.initializeTablePermissions();
556
+ }
557
+ }
558
+ getDbInspector() {
559
+ return this.dbInspector;
560
+ }
561
+ async ready() {
562
+ await this.permissionService.ready();
563
+ }
564
+ getExcludedTables() {
565
+ return this.excludedTables;
566
+ }
567
+ getDefaultPermissions() {
568
+ return this.defaultPermissions;
569
+ }
570
+ /**
571
+ * Initialize permissions for all tables in the database
572
+ * This is a non-blocking operation that runs in the background
573
+ */
574
+ initializeTablePermissions() {
575
+ const { permissionReportPath, onPermissionInitComplete } = this.config;
576
+ // Use the public method to avoid code duplication
577
+ this.initializePermissions(permissionReportPath, onPermissionInitComplete);
578
+ }
579
+ /**
580
+ * Add a table to the excluded tables list
581
+ * @param tables List of tables to exclude
582
+ */
583
+ addExcludedTables(tables) {
584
+ this.excludedTables = [...this.excludedTables, ...tables];
585
+ }
586
+ /**
587
+ * Remove a table from the excluded tables list
588
+ * @param tables List of tables to include
589
+ */
590
+ removeExcludedTables(tables) {
591
+ this.excludedTables = this.excludedTables.filter((table) => !tables.includes(table));
592
+ }
593
+ /**
594
+ * Manually trigger permission initialization for all tables
595
+ * @param reportPath Optional path to save the report file
596
+ * @param onComplete Optional callback function to call when initialization is complete
597
+ */
598
+ initializePermissions(reportPath, onComplete) {
599
+ // Use provided parameters or fall back to config values
600
+ const finalReportPath = reportPath || this.config.permissionReportPath;
601
+ const finalCallback = onComplete || this.config.onPermissionInitComplete;
602
+ // Start the initialization process
603
+ (0, permission_initializer_1.initializePermissions)(this.hooks.getDbInstance(), this.permissionService, this.dbInspector, this.excludedTables, this.defaultPermissions, finalReportPath, finalCallback);
604
+ console.log('Permission initialization started in the background');
605
+ }
606
+ /**
607
+ * Get the database schema
608
+ * @returns Database schema
609
+ */
610
+ getEndpoints() {
611
+ return this.endpoints;
612
+ }
613
+ /**
614
+ * Get the knex instance
615
+ * @returns Knex instance
616
+ */
617
+ getDbInstance() {
618
+ return this.hooks.getDbInstance();
619
+ }
620
+ getHooksDb() {
621
+ return this.hooks;
622
+ }
623
+ getPermissionService() {
624
+ return this.permissionService;
625
+ }
626
+ /**
627
+ * Execute a function within a transaction
628
+ * @param callback Function to execute within the transaction
629
+ * @returns Result of the callback function
630
+ */
631
+ async transaction(callback) {
632
+ try {
633
+ return await this.hooks.getDbInstance().transaction().execute(callback);
634
+ }
635
+ catch (error) {
636
+ console.error('Transaction error:', error);
637
+ throw error;
638
+ }
639
+ }
640
+ parseQueryParams(params) {
641
+ const queryParams = {};
642
+ Object.entries(params).forEach(([key, value]) => {
643
+ if (typeof value === 'string') {
644
+ try {
645
+ queryParams[key] = JSON.parse(value);
646
+ }
647
+ catch {
648
+ if (key === 'limit' || key === 'offset') {
649
+ queryParams[key] = parseInt(value, 10) || 10;
650
+ }
651
+ else {
652
+ queryParams[key] = value;
653
+ }
654
+ }
655
+ }
656
+ else {
657
+ queryParams[key] = value;
658
+ }
659
+ });
660
+ return queryParams;
661
+ }
662
+ }
663
+ exports.ForgeDatabase = ForgeDatabase;
664
+ /**
665
+ * Create a new instance of the ForgeDatabase class
666
+ * @param config Configuration object for the ForgeDatabase instance
667
+ * @returns A new instance of the ForgeDatabase class
668
+ */
669
+ const createForgeDatabase = (config) => {
670
+ return new ForgeDatabase(config);
671
+ };
672
+ exports.createForgeDatabase = createForgeDatabase;
673
+ //# sourceMappingURL=database.js.map