@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.
- package/LICENSE +21 -0
- package/README.md +470 -0
- package/dist/cjs/adapters/base.d.ts +20 -0
- package/dist/cjs/adapters/base.d.ts.map +1 -0
- package/dist/cjs/adapters/base.js +13 -0
- package/dist/cjs/adapters/base.js.map +1 -0
- package/dist/cjs/adapters/index.d.ts +6 -0
- package/dist/cjs/adapters/index.d.ts.map +1 -0
- package/dist/cjs/adapters/index.js +24 -0
- package/dist/cjs/adapters/index.js.map +1 -0
- package/dist/cjs/adapters/postgres.d.ts +13 -0
- package/dist/cjs/adapters/postgres.d.ts.map +1 -0
- package/dist/cjs/adapters/postgres.js +51 -0
- package/dist/cjs/adapters/postgres.js.map +1 -0
- package/dist/cjs/adapters/sqlite.d.ts +13 -0
- package/dist/cjs/adapters/sqlite.d.ts.map +1 -0
- package/dist/cjs/adapters/sqlite.js +47 -0
- package/dist/cjs/adapters/sqlite.js.map +1 -0
- package/dist/cjs/adapters/types.d.ts +8 -0
- package/dist/cjs/adapters/types.d.ts.map +1 -0
- package/dist/cjs/adapters/types.js +3 -0
- package/dist/cjs/adapters/types.js.map +1 -0
- package/dist/cjs/database.d.ts +73 -0
- package/dist/cjs/database.d.ts.map +1 -0
- package/dist/cjs/database.js +673 -0
- package/dist/cjs/database.js.map +1 -0
- package/dist/cjs/errors.d.ts +37 -0
- package/dist/cjs/errors.d.ts.map +1 -0
- package/dist/cjs/errors.js +64 -0
- package/dist/cjs/errors.js.map +1 -0
- package/dist/cjs/index.d.ts +16 -0
- package/dist/cjs/index.d.ts.map +1 -0
- package/dist/cjs/index.js +31 -0
- package/dist/cjs/index.js.map +1 -0
- package/dist/cjs/kysely-hooks.d.ts +45 -0
- package/dist/cjs/kysely-hooks.d.ts.map +1 -0
- package/dist/cjs/kysely-hooks.js +93 -0
- package/dist/cjs/kysely-hooks.js.map +1 -0
- package/dist/cjs/libsql/example.d.ts +2 -0
- package/dist/cjs/libsql/example.d.ts.map +1 -0
- package/dist/cjs/libsql/example.js +44 -0
- package/dist/cjs/libsql/example.js.map +1 -0
- package/dist/cjs/libsql/index.d.ts +36 -0
- package/dist/cjs/libsql/index.d.ts.map +1 -0
- package/dist/cjs/libsql/index.js +155 -0
- package/dist/cjs/libsql/index.js.map +1 -0
- package/dist/cjs/permissionService.d.ts +20 -0
- package/dist/cjs/permissionService.d.ts.map +1 -0
- package/dist/cjs/permissionService.js +107 -0
- package/dist/cjs/permissionService.js.map +1 -0
- package/dist/cjs/rlsFunctionRegistry.d.ts +43 -0
- package/dist/cjs/rlsFunctionRegistry.d.ts.map +1 -0
- package/dist/cjs/rlsFunctionRegistry.js +63 -0
- package/dist/cjs/rlsFunctionRegistry.js.map +1 -0
- package/dist/cjs/rlsManager.d.ts +23 -0
- package/dist/cjs/rlsManager.d.ts.map +1 -0
- package/dist/cjs/rlsManager.js +371 -0
- package/dist/cjs/rlsManager.js.map +1 -0
- package/dist/cjs/schema.d.ts +15 -0
- package/dist/cjs/schema.d.ts.map +1 -0
- package/dist/cjs/schema.js +119 -0
- package/dist/cjs/schema.js.map +1 -0
- package/dist/cjs/sdk/client.d.ts +324 -0
- package/dist/cjs/sdk/client.d.ts.map +1 -0
- package/dist/cjs/sdk/client.js +554 -0
- package/dist/cjs/sdk/client.js.map +1 -0
- package/dist/cjs/sdk/examples.d.ts +68 -0
- package/dist/cjs/sdk/examples.d.ts.map +1 -0
- package/dist/cjs/sdk/examples.js +232 -0
- package/dist/cjs/sdk/examples.js.map +1 -0
- package/dist/cjs/sdk/server.d.ts +115 -0
- package/dist/cjs/sdk/server.d.ts.map +1 -0
- package/dist/cjs/sdk/server.js +140 -0
- package/dist/cjs/sdk/server.js.map +1 -0
- package/dist/cjs/types.d.ts +217 -0
- package/dist/cjs/types.d.ts.map +1 -0
- package/dist/cjs/types.js +5 -0
- package/dist/cjs/types.js.map +1 -0
- package/dist/cjs/utils/column-utils.d.ts +8 -0
- package/dist/cjs/utils/column-utils.d.ts.map +1 -0
- package/dist/cjs/utils/column-utils.js +131 -0
- package/dist/cjs/utils/column-utils.js.map +1 -0
- package/dist/cjs/utils/db.d.ts +2 -0
- package/dist/cjs/utils/db.d.ts.map +1 -0
- package/dist/cjs/utils/db.js +6 -0
- package/dist/cjs/utils/db.js.map +1 -0
- package/dist/cjs/utils/inspector.d.ts +39 -0
- package/dist/cjs/utils/inspector.d.ts.map +1 -0
- package/dist/cjs/utils/inspector.js +164 -0
- package/dist/cjs/utils/inspector.js.map +1 -0
- package/dist/cjs/utils/permission-initializer.d.ts +15 -0
- package/dist/cjs/utils/permission-initializer.d.ts.map +1 -0
- package/dist/cjs/utils/permission-initializer.js +173 -0
- package/dist/cjs/utils/permission-initializer.js.map +1 -0
- package/dist/cjs/websocket/RealtimeAdapter.d.ts +22 -0
- package/dist/cjs/websocket/RealtimeAdapter.d.ts.map +1 -0
- package/dist/cjs/websocket/RealtimeAdapter.js +3 -0
- package/dist/cjs/websocket/RealtimeAdapter.js.map +1 -0
- package/dist/cjs/websocket/SSEManager.d.ts +40 -0
- package/dist/cjs/websocket/SSEManager.d.ts.map +1 -0
- package/dist/cjs/websocket/SSEManager.js +268 -0
- package/dist/cjs/websocket/SSEManager.js.map +1 -0
- package/dist/cjs/websocket/WebSocketManager.d.ts +28 -0
- package/dist/cjs/websocket/WebSocketManager.d.ts.map +1 -0
- package/dist/cjs/websocket/WebSocketManager.js +156 -0
- package/dist/cjs/websocket/WebSocketManager.js.map +1 -0
- package/dist/cjs/websocket/index.d.ts +4 -0
- package/dist/cjs/websocket/index.d.ts.map +1 -0
- package/dist/cjs/websocket/index.js +20 -0
- package/dist/cjs/websocket/index.js.map +1 -0
- package/dist/esm/adapters/base.d.ts +20 -0
- package/dist/esm/adapters/base.d.ts.map +1 -0
- package/dist/esm/adapters/base.js +10 -0
- package/dist/esm/adapters/base.js.map +1 -0
- package/dist/esm/adapters/index.d.ts +6 -0
- package/dist/esm/adapters/index.d.ts.map +1 -0
- package/dist/esm/adapters/index.js +19 -0
- package/dist/esm/adapters/index.js.map +1 -0
- package/dist/esm/adapters/postgres.d.ts +13 -0
- package/dist/esm/adapters/postgres.d.ts.map +1 -0
- package/dist/esm/adapters/postgres.js +47 -0
- package/dist/esm/adapters/postgres.js.map +1 -0
- package/dist/esm/adapters/sqlite.d.ts +13 -0
- package/dist/esm/adapters/sqlite.d.ts.map +1 -0
- package/dist/esm/adapters/sqlite.js +43 -0
- package/dist/esm/adapters/sqlite.js.map +1 -0
- package/dist/esm/adapters/types.d.ts +8 -0
- package/dist/esm/adapters/types.d.ts.map +1 -0
- package/dist/esm/adapters/types.js +2 -0
- package/dist/esm/adapters/types.js.map +1 -0
- package/dist/esm/database.d.ts +73 -0
- package/dist/esm/database.d.ts.map +1 -0
- package/dist/esm/database.js +668 -0
- package/dist/esm/database.js.map +1 -0
- package/dist/esm/errors.d.ts +37 -0
- package/dist/esm/errors.d.ts.map +1 -0
- package/dist/esm/errors.js +55 -0
- package/dist/esm/errors.js.map +1 -0
- package/dist/esm/index.d.ts +16 -0
- package/dist/esm/index.d.ts.map +1 -0
- package/dist/esm/index.js +15 -0
- package/dist/esm/index.js.map +1 -0
- package/dist/esm/kysely-hooks.d.ts +45 -0
- package/dist/esm/kysely-hooks.d.ts.map +1 -0
- package/dist/esm/kysely-hooks.js +86 -0
- package/dist/esm/kysely-hooks.js.map +1 -0
- package/dist/esm/libsql/example.d.ts +2 -0
- package/dist/esm/libsql/example.d.ts.map +1 -0
- package/dist/esm/libsql/example.js +42 -0
- package/dist/esm/libsql/example.js.map +1 -0
- package/dist/esm/libsql/index.d.ts +36 -0
- package/dist/esm/libsql/index.d.ts.map +1 -0
- package/dist/esm/libsql/index.js +116 -0
- package/dist/esm/libsql/index.js.map +1 -0
- package/dist/esm/permissionService.d.ts +20 -0
- package/dist/esm/permissionService.d.ts.map +1 -0
- package/dist/esm/permissionService.js +103 -0
- package/dist/esm/permissionService.js.map +1 -0
- package/dist/esm/rlsFunctionRegistry.d.ts +43 -0
- package/dist/esm/rlsFunctionRegistry.d.ts.map +1 -0
- package/dist/esm/rlsFunctionRegistry.js +60 -0
- package/dist/esm/rlsFunctionRegistry.js.map +1 -0
- package/dist/esm/rlsManager.d.ts +23 -0
- package/dist/esm/rlsManager.d.ts.map +1 -0
- package/dist/esm/rlsManager.js +366 -0
- package/dist/esm/rlsManager.js.map +1 -0
- package/dist/esm/schema.d.ts +15 -0
- package/dist/esm/schema.d.ts.map +1 -0
- package/dist/esm/schema.js +113 -0
- package/dist/esm/schema.js.map +1 -0
- package/dist/esm/sdk/client.d.ts +324 -0
- package/dist/esm/sdk/client.d.ts.map +1 -0
- package/dist/esm/sdk/client.js +550 -0
- package/dist/esm/sdk/client.js.map +1 -0
- package/dist/esm/sdk/examples.d.ts +68 -0
- package/dist/esm/sdk/examples.d.ts.map +1 -0
- package/dist/esm/sdk/examples.js +229 -0
- package/dist/esm/sdk/examples.js.map +1 -0
- package/dist/esm/sdk/server.d.ts +115 -0
- package/dist/esm/sdk/server.d.ts.map +1 -0
- package/dist/esm/sdk/server.js +136 -0
- package/dist/esm/sdk/server.js.map +1 -0
- package/dist/esm/types.d.ts +217 -0
- package/dist/esm/types.d.ts.map +1 -0
- package/dist/esm/types.js +2 -0
- package/dist/esm/types.js.map +1 -0
- package/dist/esm/utils/column-utils.d.ts +8 -0
- package/dist/esm/utils/column-utils.d.ts.map +1 -0
- package/dist/esm/utils/column-utils.js +127 -0
- package/dist/esm/utils/column-utils.js.map +1 -0
- package/dist/esm/utils/db.d.ts +2 -0
- package/dist/esm/utils/db.d.ts.map +1 -0
- package/dist/esm/utils/db.js +3 -0
- package/dist/esm/utils/db.js.map +1 -0
- package/dist/esm/utils/inspector.d.ts +39 -0
- package/dist/esm/utils/inspector.d.ts.map +1 -0
- package/dist/esm/utils/inspector.js +160 -0
- package/dist/esm/utils/inspector.js.map +1 -0
- package/dist/esm/utils/permission-initializer.d.ts +15 -0
- package/dist/esm/utils/permission-initializer.d.ts.map +1 -0
- package/dist/esm/utils/permission-initializer.js +137 -0
- package/dist/esm/utils/permission-initializer.js.map +1 -0
- package/dist/esm/websocket/RealtimeAdapter.d.ts +22 -0
- package/dist/esm/websocket/RealtimeAdapter.d.ts.map +1 -0
- package/dist/esm/websocket/RealtimeAdapter.js +2 -0
- package/dist/esm/websocket/RealtimeAdapter.js.map +1 -0
- package/dist/esm/websocket/SSEManager.d.ts +40 -0
- package/dist/esm/websocket/SSEManager.d.ts.map +1 -0
- package/dist/esm/websocket/SSEManager.js +231 -0
- package/dist/esm/websocket/SSEManager.js.map +1 -0
- package/dist/esm/websocket/WebSocketManager.d.ts +28 -0
- package/dist/esm/websocket/WebSocketManager.d.ts.map +1 -0
- package/dist/esm/websocket/WebSocketManager.js +152 -0
- package/dist/esm/websocket/WebSocketManager.js.map +1 -0
- package/dist/esm/websocket/index.d.ts +4 -0
- package/dist/esm/websocket/index.d.ts.map +1 -0
- package/dist/esm/websocket/index.js +4 -0
- package/dist/esm/websocket/index.js.map +1 -0
- package/package.json +67 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2025 ForgeBase
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,470 @@
|
|
|
1
|
+
# ForgeBase Database
|
|
2
|
+
|
|
3
|
+
A flexible, powerful database abstraction layer for ForgeBase, providing database operations, schema management, row-level security (RLS), and real-time capabilities.
|
|
4
|
+
|
|
5
|
+
## Features
|
|
6
|
+
|
|
7
|
+
- 🔄 **Multiple Database Support**: Works with SQLite, PostgreSQL, MySQL, MSSQL, and any Knex-compatible database
|
|
8
|
+
- 🔒 **Row-Level Security (RLS)**: Fine-grained access control at the row level
|
|
9
|
+
- 🔐 **Permission Management**: Role-based access control for tables and operations
|
|
10
|
+
- 📊 **Schema Management**: Create, modify, and delete tables and columns dynamically
|
|
11
|
+
- 🔍 **Query Builder**: Powerful query building with filtering, sorting, and pagination
|
|
12
|
+
- ⚡ **Real-time Updates**: Optional real-time database changes via WebSockets
|
|
13
|
+
- 🧩 **Type Safety**: Full TypeScript support with type definitions
|
|
14
|
+
- 🔌 **Adapter System**: Extensible adapter system for different database engines
|
|
15
|
+
- 🪝 **Event Hooks**: Before/After hooks for queries and mutations
|
|
16
|
+
- 🔎 **Database Inspection**: Retrieve complete database schema and structure
|
|
17
|
+
- 🔄 **Integration Options**: Use with API package, frontend SDK, REST API, or custom frameworks
|
|
18
|
+
|
|
19
|
+
## Installation
|
|
20
|
+
|
|
21
|
+
```bash
|
|
22
|
+
npm install @forgebase/database
|
|
23
|
+
# or
|
|
24
|
+
yarn add @forgebase/database
|
|
25
|
+
# or
|
|
26
|
+
pnpm add @forgebase/database
|
|
27
|
+
```
|
|
28
|
+
|
|
29
|
+
## Basic Usage
|
|
30
|
+
|
|
31
|
+
### Initialize the Database
|
|
32
|
+
|
|
33
|
+
```typescript
|
|
34
|
+
import { ForgeDatabase } from '@forgebase/database';
|
|
35
|
+
import knex from 'knex';
|
|
36
|
+
|
|
37
|
+
// Create a Knex instance
|
|
38
|
+
const knexInstance = knex({
|
|
39
|
+
client: 'sqlite3',
|
|
40
|
+
connection: {
|
|
41
|
+
filename: './mydb.sqlite',
|
|
42
|
+
},
|
|
43
|
+
useNullAsDefault: true,
|
|
44
|
+
});
|
|
45
|
+
|
|
46
|
+
// Initialize ForgeDatabase
|
|
47
|
+
const db = new ForgeDatabase({
|
|
48
|
+
db: knexInstance,
|
|
49
|
+
enforceRls: true, // Enable row-level security
|
|
50
|
+
realtime: true, // Enable real-time updates
|
|
51
|
+
});
|
|
52
|
+
```
|
|
53
|
+
|
|
54
|
+
### Schema Operations
|
|
55
|
+
|
|
56
|
+
```typescript
|
|
57
|
+
// Create a new table
|
|
58
|
+
await db.endpoints.schema.create({
|
|
59
|
+
tableName: 'users',
|
|
60
|
+
columns: [
|
|
61
|
+
{ name: 'id', type: 'increments', primary: true },
|
|
62
|
+
{ name: 'username', type: 'string', unique: true, nullable: false },
|
|
63
|
+
{ name: 'email', type: 'string', unique: true, nullable: false },
|
|
64
|
+
{ name: 'password', type: 'string', nullable: false },
|
|
65
|
+
{ name: 'role', type: 'string', defaultValue: 'user' },
|
|
66
|
+
{ name: 'created_at', type: 'timestamp', defaultToNow: true },
|
|
67
|
+
],
|
|
68
|
+
});
|
|
69
|
+
|
|
70
|
+
// Get database schema
|
|
71
|
+
const schema = await db.endpoints.schema.get();
|
|
72
|
+
console.log(schema);
|
|
73
|
+
|
|
74
|
+
// Modify a table
|
|
75
|
+
await db.endpoints.schema.modify({
|
|
76
|
+
tableName: 'users',
|
|
77
|
+
addColumns: [{ name: 'last_login', type: 'timestamp', nullable: true }],
|
|
78
|
+
dropColumns: ['unused_column'],
|
|
79
|
+
modifyColumns: [{ name: 'role', type: 'string', defaultValue: 'member' }],
|
|
80
|
+
});
|
|
81
|
+
```
|
|
82
|
+
|
|
83
|
+
### Data Operations
|
|
84
|
+
|
|
85
|
+
```typescript
|
|
86
|
+
// Query data
|
|
87
|
+
const users = await db.endpoints.data.query(
|
|
88
|
+
'users',
|
|
89
|
+
{
|
|
90
|
+
select: ['id', 'username', 'email', 'role'],
|
|
91
|
+
where: { role: 'admin' },
|
|
92
|
+
orderBy: [{ column: 'created_at', direction: 'desc' }],
|
|
93
|
+
limit: 10,
|
|
94
|
+
offset: 0,
|
|
95
|
+
},
|
|
96
|
+
{ id: 1, role: 'admin' }, // User context for RLS
|
|
97
|
+
);
|
|
98
|
+
|
|
99
|
+
// Create data
|
|
100
|
+
const newUser = await db.endpoints.data.create(
|
|
101
|
+
{
|
|
102
|
+
tableName: 'users',
|
|
103
|
+
data: {
|
|
104
|
+
username: 'johndoe',
|
|
105
|
+
email: 'john@example.com',
|
|
106
|
+
password: 'hashedpassword',
|
|
107
|
+
role: 'user',
|
|
108
|
+
},
|
|
109
|
+
},
|
|
110
|
+
{ id: 1, role: 'admin' }, // User context for RLS
|
|
111
|
+
);
|
|
112
|
+
|
|
113
|
+
// Update data
|
|
114
|
+
await db.endpoints.data.update(
|
|
115
|
+
{
|
|
116
|
+
tableName: 'users',
|
|
117
|
+
id: 1,
|
|
118
|
+
data: {
|
|
119
|
+
role: 'moderator',
|
|
120
|
+
},
|
|
121
|
+
},
|
|
122
|
+
{ id: 1, role: 'admin' }, // User context for RLS
|
|
123
|
+
);
|
|
124
|
+
|
|
125
|
+
// Delete data
|
|
126
|
+
await db.endpoints.data.delete(
|
|
127
|
+
{
|
|
128
|
+
tableName: 'users',
|
|
129
|
+
id: 1,
|
|
130
|
+
},
|
|
131
|
+
{ id: 1, role: 'admin' }, // User context for RLS
|
|
132
|
+
);
|
|
133
|
+
```
|
|
134
|
+
|
|
135
|
+
### Permissions Management
|
|
136
|
+
|
|
137
|
+
```typescript
|
|
138
|
+
// Get permissions for a table
|
|
139
|
+
const permissions = await db.getPermissionService().getPermissionsForTable('users');
|
|
140
|
+
|
|
141
|
+
// Set permissions for a table
|
|
142
|
+
await db.setPermissions('users', {
|
|
143
|
+
operations: {
|
|
144
|
+
SELECT: [
|
|
145
|
+
// Allow authenticated users to see their own data
|
|
146
|
+
{
|
|
147
|
+
allow: 'auth',
|
|
148
|
+
fieldCheck: {
|
|
149
|
+
field: 'id',
|
|
150
|
+
operator: '===',
|
|
151
|
+
valueType: 'userContext',
|
|
152
|
+
value: 'userId',
|
|
153
|
+
},
|
|
154
|
+
},
|
|
155
|
+
// Allow admins and moderators to see all data
|
|
156
|
+
{
|
|
157
|
+
allow: 'role',
|
|
158
|
+
roles: ['admin', 'moderator'],
|
|
159
|
+
},
|
|
160
|
+
// Allow users with specific labels
|
|
161
|
+
{
|
|
162
|
+
allow: 'labels',
|
|
163
|
+
labels: ['user_manager'],
|
|
164
|
+
},
|
|
165
|
+
// Allow users in specific teams
|
|
166
|
+
{
|
|
167
|
+
allow: 'teams',
|
|
168
|
+
teams: ['support_team'],
|
|
169
|
+
},
|
|
170
|
+
],
|
|
171
|
+
INSERT: [
|
|
172
|
+
// Only admins can create users
|
|
173
|
+
{
|
|
174
|
+
allow: 'role',
|
|
175
|
+
roles: ['admin'],
|
|
176
|
+
},
|
|
177
|
+
],
|
|
178
|
+
UPDATE: [
|
|
179
|
+
// Users can update their own data
|
|
180
|
+
{
|
|
181
|
+
allow: 'auth',
|
|
182
|
+
fieldCheck: {
|
|
183
|
+
field: 'id',
|
|
184
|
+
operator: '===',
|
|
185
|
+
valueType: 'userContext',
|
|
186
|
+
value: 'userId',
|
|
187
|
+
},
|
|
188
|
+
},
|
|
189
|
+
// Admins and moderators can update any user
|
|
190
|
+
{
|
|
191
|
+
allow: 'role',
|
|
192
|
+
roles: ['admin', 'moderator'],
|
|
193
|
+
},
|
|
194
|
+
],
|
|
195
|
+
DELETE: [
|
|
196
|
+
// Only admins can delete users
|
|
197
|
+
{
|
|
198
|
+
allow: 'role',
|
|
199
|
+
roles: ['admin'],
|
|
200
|
+
},
|
|
201
|
+
// Custom SQL condition for complex rules
|
|
202
|
+
{
|
|
203
|
+
allow: 'customSql',
|
|
204
|
+
customSql: `
|
|
205
|
+
SELECT 1 WHERE
|
|
206
|
+
EXISTS (SELECT 1 FROM user_managers WHERE manager_id = :userId AND user_id = users.id)
|
|
207
|
+
`,
|
|
208
|
+
},
|
|
209
|
+
],
|
|
210
|
+
},
|
|
211
|
+
});
|
|
212
|
+
```
|
|
213
|
+
|
|
214
|
+
### Transactions
|
|
215
|
+
|
|
216
|
+
ForgeBase Database supports transactions to ensure data consistency and atomicity. All database operations support transactions, allowing you to perform multiple operations as a single unit of work.
|
|
217
|
+
|
|
218
|
+
There are two ways to use transactions:
|
|
219
|
+
|
|
220
|
+
#### Explicit Transactions
|
|
221
|
+
|
|
222
|
+
Pass a transaction object to each method:
|
|
223
|
+
|
|
224
|
+
```typescript
|
|
225
|
+
// Execute operations in a transaction
|
|
226
|
+
await db.transaction(async (trx) => {
|
|
227
|
+
// Create a user
|
|
228
|
+
const user = await db.endpoints.data.create(
|
|
229
|
+
{
|
|
230
|
+
tableName: 'users',
|
|
231
|
+
data: { username: 'jane', email: 'jane@example.com', password: 'hashedpw' },
|
|
232
|
+
},
|
|
233
|
+
{ id: 1, role: 'admin' },
|
|
234
|
+
false, // Not a system operation
|
|
235
|
+
trx, // Pass the transaction
|
|
236
|
+
);
|
|
237
|
+
|
|
238
|
+
// Create a profile for the user
|
|
239
|
+
await db.endpoints.data.create(
|
|
240
|
+
{
|
|
241
|
+
tableName: 'profiles',
|
|
242
|
+
data: { user_id: user.id, bio: 'New user' },
|
|
243
|
+
},
|
|
244
|
+
{ id: 1, role: 'admin' },
|
|
245
|
+
false,
|
|
246
|
+
trx,
|
|
247
|
+
);
|
|
248
|
+
});
|
|
249
|
+
```
|
|
250
|
+
|
|
251
|
+
#### Implicit Transactions
|
|
252
|
+
|
|
253
|
+
Many methods automatically create a transaction if one isn't provided:
|
|
254
|
+
|
|
255
|
+
```typescript
|
|
256
|
+
// This will automatically create a transaction internally
|
|
257
|
+
const user = await db.endpoints.data.create(
|
|
258
|
+
{
|
|
259
|
+
tableName: 'users',
|
|
260
|
+
data: { username: 'john', email: 'john@example.com', password: 'hashedpw' },
|
|
261
|
+
},
|
|
262
|
+
{ id: 1, role: 'admin' },
|
|
263
|
+
);
|
|
264
|
+
```
|
|
265
|
+
|
|
266
|
+
## Security Best Practices
|
|
267
|
+
|
|
268
|
+
### Row-Level Security (RLS)
|
|
269
|
+
|
|
270
|
+
ForgeBase Database provides powerful row-level security capabilities that allow you to define fine-grained access control rules at the row level. There are several ways to implement RLS:
|
|
271
|
+
|
|
272
|
+
#### Basic RLS with Field Checks
|
|
273
|
+
|
|
274
|
+
```typescript
|
|
275
|
+
// Enable RLS
|
|
276
|
+
const db = new ForgeDatabase({
|
|
277
|
+
enforceRls: true,
|
|
278
|
+
// ...
|
|
279
|
+
});
|
|
280
|
+
|
|
281
|
+
// Set row-level policies
|
|
282
|
+
await db.setPermissions('documents', {
|
|
283
|
+
operations: {
|
|
284
|
+
SELECT: [
|
|
285
|
+
{
|
|
286
|
+
allow: 'auth',
|
|
287
|
+
fieldCheck: {
|
|
288
|
+
field: 'owner_id',
|
|
289
|
+
operator: '===',
|
|
290
|
+
valueType: 'userContext',
|
|
291
|
+
value: 'userId',
|
|
292
|
+
},
|
|
293
|
+
},
|
|
294
|
+
],
|
|
295
|
+
},
|
|
296
|
+
});
|
|
297
|
+
```
|
|
298
|
+
|
|
299
|
+
#### Advanced RLS with Custom SQL
|
|
300
|
+
|
|
301
|
+
For complex permission rules that require database queries:
|
|
302
|
+
|
|
303
|
+
```typescript
|
|
304
|
+
// Example: Limit free users to 5 CVs, but allow pro users unlimited CVs
|
|
305
|
+
await db.setPermissions('cvs', {
|
|
306
|
+
operations: {
|
|
307
|
+
INSERT: [
|
|
308
|
+
{
|
|
309
|
+
allow: 'customSql',
|
|
310
|
+
customSql: `
|
|
311
|
+
SELECT 1 WHERE
|
|
312
|
+
-- Check if user is on pro plan
|
|
313
|
+
EXISTS (SELECT 1 FROM subscriptions WHERE user_id = :userId AND plan_type = 'pro')
|
|
314
|
+
-- OR check if user is on free plan but has fewer than 5 CVs
|
|
315
|
+
OR (
|
|
316
|
+
NOT EXISTS (SELECT 1 FROM subscriptions WHERE user_id = :userId AND plan_type = 'pro')
|
|
317
|
+
AND (SELECT COUNT(*) FROM cvs WHERE user_id = :userId) < 5
|
|
318
|
+
)
|
|
319
|
+
`,
|
|
320
|
+
},
|
|
321
|
+
],
|
|
322
|
+
},
|
|
323
|
+
});
|
|
324
|
+
```
|
|
325
|
+
|
|
326
|
+
#### Advanced RLS with Custom Functions
|
|
327
|
+
|
|
328
|
+
For the most flexible permission rules, you can register custom JavaScript functions:
|
|
329
|
+
|
|
330
|
+
```typescript
|
|
331
|
+
// Register a custom RLS function
|
|
332
|
+
import { rlsFunctionRegistry } from '@forgebase/database';
|
|
333
|
+
|
|
334
|
+
// Register a function that checks subscription limits
|
|
335
|
+
rlsFunctionRegistry.register('checkSubscriptionLimits', async (userContext, row, knex) => {
|
|
336
|
+
if (!knex) return false;
|
|
337
|
+
|
|
338
|
+
// Check if user is on pro plan
|
|
339
|
+
const proSub = await knex('subscriptions').where({ user_id: userContext.userId, plan_type: 'pro' }).first();
|
|
340
|
+
|
|
341
|
+
if (proSub) return true; // Pro users can create unlimited resources
|
|
342
|
+
|
|
343
|
+
// For free users, check resource count
|
|
344
|
+
const count = await knex('cvs').where({ user_id: userContext.userId }).count('id as count').first();
|
|
345
|
+
|
|
346
|
+
return count && count.count < 5; // Allow if less than 5 resources
|
|
347
|
+
});
|
|
348
|
+
|
|
349
|
+
// Use the registered function in permissions
|
|
350
|
+
await db.setPermissions('cvs', {
|
|
351
|
+
operations: {
|
|
352
|
+
INSERT: [
|
|
353
|
+
{
|
|
354
|
+
allow: 'customFunction',
|
|
355
|
+
customFunction: 'checkSubscriptionLimits',
|
|
356
|
+
},
|
|
357
|
+
],
|
|
358
|
+
},
|
|
359
|
+
});
|
|
360
|
+
```
|
|
361
|
+
|
|
362
|
+
### Automatic Permission Initialization
|
|
363
|
+
|
|
364
|
+
ForgeBase Database can automatically initialize permissions for all tables in your database. This feature is useful when you want to ensure that all tables have at least basic permissions set.
|
|
365
|
+
|
|
366
|
+
#### Configuration
|
|
367
|
+
|
|
368
|
+
You can enable automatic permission initialization when creating the ForgeDatabase instance:
|
|
369
|
+
|
|
370
|
+
```typescript
|
|
371
|
+
const db = new ForgeDatabase({
|
|
372
|
+
db: knexInstance,
|
|
373
|
+
// Enable automatic permission initialization
|
|
374
|
+
initializePermissions: true,
|
|
375
|
+
// Optional: Specify where to save the initialization report
|
|
376
|
+
permissionReportPath: './permission-report.md',
|
|
377
|
+
// Optional: Callback function when initialization completes
|
|
378
|
+
onPermissionInitComplete: (report) => {
|
|
379
|
+
console.log(`Initialized permissions for ${report.tablesInitialized} tables`);
|
|
380
|
+
},
|
|
381
|
+
});
|
|
382
|
+
```
|
|
383
|
+
|
|
384
|
+
#### Manual Initialization
|
|
385
|
+
|
|
386
|
+
You can also manually trigger permission initialization at any time:
|
|
387
|
+
|
|
388
|
+
```typescript
|
|
389
|
+
// Initialize permissions with default options from config
|
|
390
|
+
db.initializePermissions();
|
|
391
|
+
|
|
392
|
+
// Or specify custom options
|
|
393
|
+
db.initializePermissions('./custom-report-path.md', (report) => {
|
|
394
|
+
console.log('Permission initialization completed!');
|
|
395
|
+
console.log(`Tables initialized: ${report.initializedTables.join(', ')}`);
|
|
396
|
+
});
|
|
397
|
+
```
|
|
398
|
+
|
|
399
|
+
### Real-time Updates
|
|
400
|
+
|
|
401
|
+
When enabled, ForgeBase Database can provide real-time updates via WebSockets:
|
|
402
|
+
|
|
403
|
+
```typescript
|
|
404
|
+
// Enable real-time updates when initializing
|
|
405
|
+
const db = new ForgeDatabase({
|
|
406
|
+
db: knexInstance,
|
|
407
|
+
realtime: true,
|
|
408
|
+
websocketPort: 8080, // Optional, defaults to 8080
|
|
409
|
+
});
|
|
410
|
+
|
|
411
|
+
// The WebSocket server will automatically broadcast changes to connected clients
|
|
412
|
+
```
|
|
413
|
+
|
|
414
|
+
## Frontend Integration
|
|
415
|
+
|
|
416
|
+
ForgeBase Database can be easily integrated with frontend applications using the `@forgebase/sdk` package:
|
|
417
|
+
|
|
418
|
+
```typescript
|
|
419
|
+
// Initialize the SDK with your API URL
|
|
420
|
+
import { DatabaseSDK } from '@forgebase/sdk/client';
|
|
421
|
+
|
|
422
|
+
const db = new DatabaseSDK({
|
|
423
|
+
baseUrl: 'http://localhost:3000/api',
|
|
424
|
+
axiosConfig: {
|
|
425
|
+
withCredentials: true, // Important for auth cookies
|
|
426
|
+
},
|
|
427
|
+
});
|
|
428
|
+
|
|
429
|
+
// Query data with a fluent API
|
|
430
|
+
const users = await db.table('users').select('id', 'name', 'email').where('status', 'active').orderBy('name', 'asc').query();
|
|
431
|
+
|
|
432
|
+
// Create a new record
|
|
433
|
+
const newUser = await db.table('users').create({
|
|
434
|
+
name: 'John Doe',
|
|
435
|
+
email: 'john@example.com',
|
|
436
|
+
role: 'user',
|
|
437
|
+
});
|
|
438
|
+
|
|
439
|
+
// Update a record
|
|
440
|
+
await db.table('users').update(123, {
|
|
441
|
+
name: 'John Smith',
|
|
442
|
+
});
|
|
443
|
+
|
|
444
|
+
// Delete a record
|
|
445
|
+
await db.table('users').delete(123);
|
|
446
|
+
|
|
447
|
+
// Real-time updates
|
|
448
|
+
const unsubscribe = db.table('users').subscribe((event) => {
|
|
449
|
+
if (event.type === 'create') {
|
|
450
|
+
console.log('New user created:', event.record);
|
|
451
|
+
} else if (event.type === 'update') {
|
|
452
|
+
console.log('User updated:', event.record);
|
|
453
|
+
} else if (event.type === 'delete') {
|
|
454
|
+
console.log('User deleted:', event.id);
|
|
455
|
+
}
|
|
456
|
+
});
|
|
457
|
+
|
|
458
|
+
// Later, unsubscribe when no longer needed
|
|
459
|
+
unsubscribe();
|
|
460
|
+
```
|
|
461
|
+
|
|
462
|
+
For more details on integration options, see the [Complete Integration](/database/complete-integration) guide.
|
|
463
|
+
|
|
464
|
+
## API Reference
|
|
465
|
+
|
|
466
|
+
For detailed API documentation, please refer to the [ForgeBase Documentation](https://docs.forgebase.dev/database).
|
|
467
|
+
|
|
468
|
+
## License
|
|
469
|
+
|
|
470
|
+
[MIT](https://github.com/The-ForgeBase/forgebase-ts/blob/main/LICENSE)
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import { WindowFunction, OrderByClause } from '../sdk/server';
|
|
2
|
+
export interface DatabaseAdapter {
|
|
3
|
+
buildWindowFunction(wf: WindowFunction): string;
|
|
4
|
+
buildOrderByClause(clauses: OrderByClause[]): {
|
|
5
|
+
column: string;
|
|
6
|
+
order: 'asc' | 'desc';
|
|
7
|
+
null?: 'first' | 'last';
|
|
8
|
+
}[];
|
|
9
|
+
supportsFeature(feature: DatabaseFeature): boolean;
|
|
10
|
+
sanitizeIdentifier(identifier: string): string;
|
|
11
|
+
}
|
|
12
|
+
export declare enum DatabaseFeature {
|
|
13
|
+
WindowFunctions = "windowFunctions",
|
|
14
|
+
CTEs = "ctes",
|
|
15
|
+
RecursiveCTEs = "recursiveCTEs",
|
|
16
|
+
NullsOrdering = "nullsOrdering",
|
|
17
|
+
JsonOperations = "jsonOperations",
|
|
18
|
+
ArrayOperations = "arrayOperations"
|
|
19
|
+
}
|
|
20
|
+
//# sourceMappingURL=base.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"base.d.ts","sourceRoot":"","sources":["../../../src/adapters/base.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,cAAc,EAAE,aAAa,EAAE,MAAM,eAAe,CAAC;AAE9D,MAAM,WAAW,eAAe;IAC9B,mBAAmB,CAAC,EAAE,EAAE,cAAc,GAAG,MAAM,CAAC;IAChD,kBAAkB,CAChB,OAAO,EAAE,aAAa,EAAE,GACvB;QAAE,MAAM,EAAE,MAAM,CAAC;QAAC,KAAK,EAAE,KAAK,GAAG,MAAM,CAAC;QAAC,IAAI,CAAC,EAAE,OAAO,GAAG,MAAM,CAAA;KAAE,EAAE,CAAC;IACxE,eAAe,CAAC,OAAO,EAAE,eAAe,GAAG,OAAO,CAAC;IACnD,kBAAkB,CAAC,UAAU,EAAE,MAAM,GAAG,MAAM,CAAC;CAChD;AAED,oBAAY,eAAe;IACzB,eAAe,oBAAoB;IACnC,IAAI,SAAS;IACb,aAAa,kBAAkB;IAC/B,aAAa,kBAAkB;IAC/B,cAAc,mBAAmB;IACjC,eAAe,oBAAoB;CACpC"}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.DatabaseFeature = void 0;
|
|
4
|
+
var DatabaseFeature;
|
|
5
|
+
(function (DatabaseFeature) {
|
|
6
|
+
DatabaseFeature["WindowFunctions"] = "windowFunctions";
|
|
7
|
+
DatabaseFeature["CTEs"] = "ctes";
|
|
8
|
+
DatabaseFeature["RecursiveCTEs"] = "recursiveCTEs";
|
|
9
|
+
DatabaseFeature["NullsOrdering"] = "nullsOrdering";
|
|
10
|
+
DatabaseFeature["JsonOperations"] = "jsonOperations";
|
|
11
|
+
DatabaseFeature["ArrayOperations"] = "arrayOperations";
|
|
12
|
+
})(DatabaseFeature || (exports.DatabaseFeature = DatabaseFeature = {}));
|
|
13
|
+
//# sourceMappingURL=base.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"base.js","sourceRoot":"","sources":["../../../src/adapters/base.ts"],"names":[],"mappings":";;;AAWA,IAAY,eAOX;AAPD,WAAY,eAAe;IACzB,sDAAmC,CAAA;IACnC,gCAAa,CAAA;IACb,kDAA+B,CAAA;IAC/B,kDAA+B,CAAA;IAC/B,oDAAiC,CAAA;IACjC,sDAAmC,CAAA;AACrC,CAAC,EAPW,eAAe,+BAAf,eAAe,QAO1B"}
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
import type { Kysely } from 'kysely';
|
|
2
|
+
import type { DatabaseAdapter } from './base';
|
|
3
|
+
export declare function getAdapter(db: Kysely<any>): DatabaseAdapter;
|
|
4
|
+
export { DatabaseFeature } from './base';
|
|
5
|
+
export type { DatabaseAdapter } from './base';
|
|
6
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/adapters/index.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,QAAQ,CAAC;AAGrC,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,QAAQ,CAAC;AAE9C,wBAAgB,UAAU,CAAC,EAAE,EAAE,MAAM,CAAC,GAAG,CAAC,GAAG,eAAe,CAgB3D;AAED,OAAO,EAAE,eAAe,EAAE,MAAM,QAAQ,CAAC;AACzC,YAAY,EAAE,eAAe,EAAE,MAAM,QAAQ,CAAC"}
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.DatabaseFeature = void 0;
|
|
4
|
+
exports.getAdapter = getAdapter;
|
|
5
|
+
const sqlite_1 = require("./sqlite");
|
|
6
|
+
const postgres_1 = require("./postgres");
|
|
7
|
+
function getAdapter(db) {
|
|
8
|
+
// Try to determine the adapter from the executor or provided config
|
|
9
|
+
// Kysely structure: db.getExecutor().adapter
|
|
10
|
+
// This is a heuristic based on Kysely internal class names or we can rely on passed config if we had it.
|
|
11
|
+
// Assuming standard Kysely adapters.
|
|
12
|
+
const adapterName = db.getExecutor().adapter.constructor.name;
|
|
13
|
+
if (adapterName.includes('Postgres')) {
|
|
14
|
+
return new postgres_1.PostgresAdapter();
|
|
15
|
+
}
|
|
16
|
+
else if (adapterName.includes('Sqlite') || adapterName.includes('Libsql')) {
|
|
17
|
+
return new sqlite_1.SQLiteAdapter();
|
|
18
|
+
}
|
|
19
|
+
// Default fallback or more checks
|
|
20
|
+
return new sqlite_1.SQLiteAdapter(); // Safest default for now? Or better throw?
|
|
21
|
+
}
|
|
22
|
+
var base_1 = require("./base");
|
|
23
|
+
Object.defineProperty(exports, "DatabaseFeature", { enumerable: true, get: function () { return base_1.DatabaseFeature; } });
|
|
24
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../../src/adapters/index.ts"],"names":[],"mappings":";;;AAKA,gCAgBC;AApBD,qCAAyC;AACzC,yCAA6C;AAG7C,SAAgB,UAAU,CAAC,EAAe;IACxC,oEAAoE;IACpE,6CAA6C;IAE7C,yGAAyG;IACzG,qCAAqC;IACrC,MAAM,WAAW,GAAG,EAAE,CAAC,WAAW,EAAE,CAAC,OAAO,CAAC,WAAW,CAAC,IAAI,CAAC;IAE9D,IAAI,WAAW,CAAC,QAAQ,CAAC,UAAU,CAAC,EAAE,CAAC;QACrC,OAAO,IAAI,0BAAe,EAAE,CAAC;IAC/B,CAAC;SAAM,IAAI,WAAW,CAAC,QAAQ,CAAC,QAAQ,CAAC,IAAI,WAAW,CAAC,QAAQ,CAAC,QAAQ,CAAC,EAAE,CAAC;QAC5E,OAAO,IAAI,sBAAa,EAAE,CAAC;IAC7B,CAAC;IAED,kCAAkC;IAClC,OAAO,IAAI,sBAAa,EAAE,CAAC,CAAC,2CAA2C;AACzE,CAAC;AAED,+BAAyC;AAAhC,uGAAA,eAAe,OAAA"}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import { type DatabaseAdapter, DatabaseFeature } from './base';
|
|
2
|
+
import type { WindowFunction, OrderByClause } from '../sdk/server';
|
|
3
|
+
export declare class PostgresAdapter implements DatabaseAdapter {
|
|
4
|
+
buildWindowFunction(wf: WindowFunction): string;
|
|
5
|
+
buildOrderByClause(clauses: OrderByClause[]): {
|
|
6
|
+
column: string;
|
|
7
|
+
order: 'asc' | 'desc';
|
|
8
|
+
null?: 'first' | 'last';
|
|
9
|
+
}[];
|
|
10
|
+
supportsFeature(feature: DatabaseFeature): boolean;
|
|
11
|
+
sanitizeIdentifier(identifier: string): string;
|
|
12
|
+
}
|
|
13
|
+
//# sourceMappingURL=postgres.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"postgres.d.ts","sourceRoot":"","sources":["../../../src/adapters/postgres.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,eAAe,EAAE,eAAe,EAAE,MAAM,QAAQ,CAAC;AAC/D,OAAO,KAAK,EAAE,cAAc,EAAE,aAAa,EAAE,MAAM,eAAe,CAAC;AAEnE,qBAAa,eAAgB,YAAW,eAAe;IACrD,mBAAmB,CAAC,EAAE,EAAE,cAAc,GAAG,MAAM;IAwB/C,kBAAkB,CAChB,OAAO,EAAE,aAAa,EAAE,GACvB;QAAE,MAAM,EAAE,MAAM,CAAC;QAAC,KAAK,EAAE,KAAK,GAAG,MAAM,CAAC;QAAC,IAAI,CAAC,EAAE,OAAO,GAAG,MAAM,CAAA;KAAE,EAAE;IASvE,eAAe,CAAC,OAAO,EAAE,eAAe,GAAG,OAAO;IAYlD,kBAAkB,CAAC,UAAU,EAAE,MAAM,GAAG,MAAM;CAI/C"}
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.PostgresAdapter = void 0;
|
|
4
|
+
const base_1 = require("./base");
|
|
5
|
+
class PostgresAdapter {
|
|
6
|
+
buildWindowFunction(wf) {
|
|
7
|
+
// PostgreSQL full window function support
|
|
8
|
+
const fnCall = wf.type === 'row_number'
|
|
9
|
+
? 'ROW_NUMBER()'
|
|
10
|
+
: `${wf.type}(${wf.field || '*'})`;
|
|
11
|
+
let overClause = 'OVER (';
|
|
12
|
+
if (wf.partitionBy?.length) {
|
|
13
|
+
overClause += `PARTITION BY ${wf.partitionBy.join(',')}`;
|
|
14
|
+
}
|
|
15
|
+
if (wf.orderBy?.length) {
|
|
16
|
+
overClause += ` ORDER BY ${wf.orderBy
|
|
17
|
+
.map((ob) => `${ob.field} ${ob.direction || 'ASC'}`)
|
|
18
|
+
.join(',')}`;
|
|
19
|
+
}
|
|
20
|
+
if (wf.frameClause) {
|
|
21
|
+
overClause += ` ${wf.frameClause}`;
|
|
22
|
+
}
|
|
23
|
+
overClause += ')';
|
|
24
|
+
return `${fnCall} ${overClause} AS ${wf.alias}`;
|
|
25
|
+
}
|
|
26
|
+
buildOrderByClause(clauses) {
|
|
27
|
+
// PostgreSQL supports NULLS FIRST/LAST natively
|
|
28
|
+
return clauses.map(({ field, direction, nulls }) => ({
|
|
29
|
+
column: field,
|
|
30
|
+
order: direction || 'asc',
|
|
31
|
+
nulls: nulls,
|
|
32
|
+
}));
|
|
33
|
+
}
|
|
34
|
+
supportsFeature(feature) {
|
|
35
|
+
const supported = {
|
|
36
|
+
[base_1.DatabaseFeature.WindowFunctions]: true,
|
|
37
|
+
[base_1.DatabaseFeature.CTEs]: true,
|
|
38
|
+
[base_1.DatabaseFeature.RecursiveCTEs]: true,
|
|
39
|
+
[base_1.DatabaseFeature.NullsOrdering]: true,
|
|
40
|
+
[base_1.DatabaseFeature.JsonOperations]: true,
|
|
41
|
+
[base_1.DatabaseFeature.ArrayOperations]: true,
|
|
42
|
+
};
|
|
43
|
+
return supported[feature] || false;
|
|
44
|
+
}
|
|
45
|
+
sanitizeIdentifier(identifier) {
|
|
46
|
+
// PostgreSQL identifier sanitization
|
|
47
|
+
return `"${identifier.replace(/"/g, '""')}"`;
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
exports.PostgresAdapter = PostgresAdapter;
|
|
51
|
+
//# sourceMappingURL=postgres.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"postgres.js","sourceRoot":"","sources":["../../../src/adapters/postgres.ts"],"names":[],"mappings":";;;AAAA,iCAA+D;AAG/D,MAAa,eAAe;IAC1B,mBAAmB,CAAC,EAAkB;QACpC,0CAA0C;QAC1C,MAAM,MAAM,GACV,EAAE,CAAC,IAAI,KAAK,YAAY;YACtB,CAAC,CAAC,cAAc;YAChB,CAAC,CAAC,GAAG,EAAE,CAAC,IAAI,IAAI,EAAE,CAAC,KAAK,IAAI,GAAG,GAAG,CAAC;QAEvC,IAAI,UAAU,GAAG,QAAQ,CAAC;QAC1B,IAAI,EAAE,CAAC,WAAW,EAAE,MAAM,EAAE,CAAC;YAC3B,UAAU,IAAI,gBAAgB,EAAE,CAAC,WAAW,CAAC,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC;QAC3D,CAAC;QACD,IAAI,EAAE,CAAC,OAAO,EAAE,MAAM,EAAE,CAAC;YACvB,UAAU,IAAI,aAAa,EAAE,CAAC,OAAO;iBAClC,GAAG,CAAC,CAAC,EAAE,EAAE,EAAE,CAAC,GAAG,EAAE,CAAC,KAAK,IAAI,EAAE,CAAC,SAAS,IAAI,KAAK,EAAE,CAAC;iBACnD,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC;QACjB,CAAC;QACD,IAAI,EAAE,CAAC,WAAW,EAAE,CAAC;YACnB,UAAU,IAAI,IAAI,EAAE,CAAC,WAAW,EAAE,CAAC;QACrC,CAAC;QACD,UAAU,IAAI,GAAG,CAAC;QAElB,OAAO,GAAG,MAAM,IAAI,UAAU,OAAO,EAAE,CAAC,KAAK,EAAE,CAAC;IAClD,CAAC;IAED,kBAAkB,CAChB,OAAwB;QAExB,gDAAgD;QAChD,OAAO,OAAO,CAAC,GAAG,CAAC,CAAC,EAAE,KAAK,EAAE,SAAS,EAAE,KAAK,EAAE,EAAE,EAAE,CAAC,CAAC;YACnD,MAAM,EAAE,KAAK;YACb,KAAK,EAAE,SAAS,IAAI,KAAK;YACzB,KAAK,EAAE,KAAK;SACb,CAAC,CAAC,CAAC;IACN,CAAC;IAED,eAAe,CAAC,OAAwB;QACtC,MAAM,SAAS,GAAG;YAChB,CAAC,sBAAe,CAAC,eAAe,CAAC,EAAE,IAAI;YACvC,CAAC,sBAAe,CAAC,IAAI,CAAC,EAAE,IAAI;YAC5B,CAAC,sBAAe,CAAC,aAAa,CAAC,EAAE,IAAI;YACrC,CAAC,sBAAe,CAAC,aAAa,CAAC,EAAE,IAAI;YACrC,CAAC,sBAAe,CAAC,cAAc,CAAC,EAAE,IAAI;YACtC,CAAC,sBAAe,CAAC,eAAe,CAAC,EAAE,IAAI;SACxC,CAAC;QACF,OAAO,SAAS,CAAC,OAAO,CAAC,IAAI,KAAK,CAAC;IACrC,CAAC;IAED,kBAAkB,CAAC,UAAkB;QACnC,qCAAqC;QACrC,OAAO,IAAI,UAAU,CAAC,OAAO,CAAC,IAAI,EAAE,IAAI,CAAC,GAAG,CAAC;IAC/C,CAAC;CACF;AApDD,0CAoDC"}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import { type DatabaseAdapter, DatabaseFeature } from './base';
|
|
2
|
+
import { WindowFunction, OrderByClause } from '../sdk/server';
|
|
3
|
+
export declare class SQLiteAdapter implements DatabaseAdapter {
|
|
4
|
+
buildWindowFunction(wf: WindowFunction): string;
|
|
5
|
+
buildOrderByClause(clauses: OrderByClause[]): {
|
|
6
|
+
column: string;
|
|
7
|
+
order: 'asc' | 'desc';
|
|
8
|
+
null?: 'first' | 'last';
|
|
9
|
+
}[];
|
|
10
|
+
supportsFeature(feature: DatabaseFeature): boolean;
|
|
11
|
+
sanitizeIdentifier(identifier: string): string;
|
|
12
|
+
}
|
|
13
|
+
//# sourceMappingURL=sqlite.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"sqlite.d.ts","sourceRoot":"","sources":["../../../src/adapters/sqlite.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,eAAe,EAAE,eAAe,EAAE,MAAM,QAAQ,CAAC;AAC/D,OAAO,EAAE,cAAc,EAAE,aAAa,EAAE,MAAM,eAAe,CAAC;AAE9D,qBAAa,aAAc,YAAW,eAAe;IACnD,mBAAmB,CAAC,EAAE,EAAE,cAAc,GAAG,MAAM;IAqB/C,kBAAkB,CAChB,OAAO,EAAE,aAAa,EAAE,GACvB;QAAE,MAAM,EAAE,MAAM,CAAC;QAAC,KAAK,EAAE,KAAK,GAAG,MAAM,CAAC;QAAC,IAAI,CAAC,EAAE,OAAO,GAAG,MAAM,CAAA;KAAE,EAAE;IAQvE,eAAe,CAAC,OAAO,EAAE,eAAe,GAAG,OAAO;IAYlD,kBAAkB,CAAC,UAAU,EAAE,MAAM,GAAG,MAAM;CAI/C"}
|