@geenius/db 0.2.0
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 +1 -0
- package/README.md +164 -0
- package/package.json +65 -0
- package/packages/react/package.json +47 -0
- package/packages/react/src/index.tsx +82 -0
- package/packages/react/tsconfig.json +10 -0
- package/packages/react/tsup.config.ts +17 -0
- package/packages/shared/package.json +55 -0
- package/packages/shared/src/index.ts +42 -0
- package/packages/shared/src/providers/cloudflareKV.ts +110 -0
- package/packages/shared/src/providers/convex.ts +151 -0
- package/packages/shared/src/providers/memory.ts +92 -0
- package/packages/shared/src/providers/mongodb.ts +137 -0
- package/packages/shared/src/providers/neon.ts +187 -0
- package/packages/shared/src/providers/supabase.ts +160 -0
- package/packages/shared/tsconfig.json +19 -0
- package/packages/shared/tsup.config.ts +18 -0
- package/packages/solidjs/package.json +46 -0
- package/packages/solidjs/src/index.tsx +83 -0
- package/packages/solidjs/tsconfig.json +11 -0
- package/packages/solidjs/tsup.config.ts +18 -0
- package/pnpm-workspace.yaml +2 -0
- package/tsconfig.json +23 -0
package/LICENSE
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
MIT License — © Antigravity HQ
|
package/README.md
ADDED
|
@@ -0,0 +1,164 @@
|
|
|
1
|
+
# `@geenius/db`
|
|
2
|
+
|
|
3
|
+
> Multi-provider database adapter implementations for the Geenius ecosystem.
|
|
4
|
+
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
## Overview
|
|
8
|
+
|
|
9
|
+
`@geenius/db` provides real database provider implementations that conform to the `DbAdapter` interface from `@geenius/adapters-shared`. This is the package you use when you need to connect to an actual database.
|
|
10
|
+
|
|
11
|
+
### Providers
|
|
12
|
+
|
|
13
|
+
| Provider | Factory | SDK |
|
|
14
|
+
|----------|---------|-----|
|
|
15
|
+
| **Convex** | `createConvexDbAdapter()` | `convex` |
|
|
16
|
+
| **Supabase** | `createSupabaseDbAdapter()` | `@supabase/supabase-js` |
|
|
17
|
+
| **Neon (Drizzle)** | `createNeonDbAdapter()` | `@neondatabase/serverless` + `drizzle-orm` |
|
|
18
|
+
| **MongoDB** | `createMongoDbAdapter()` | `mongodb` |
|
|
19
|
+
| **Cloudflare KV** | `createCloudflareKVDbAdapter()` | `@cloudflare/workers-types` |
|
|
20
|
+
| **In-Memory** | `createMemoryDbAdapter()` | *(none)* |
|
|
21
|
+
|
|
22
|
+
> For localStorage-based prototyping (Pronto tier), use `createLocalStorageDbAdapter()` from `@geenius/adapters`.
|
|
23
|
+
|
|
24
|
+
---
|
|
25
|
+
|
|
26
|
+
## Installation
|
|
27
|
+
|
|
28
|
+
```bash
|
|
29
|
+
pnpm add @geenius/db
|
|
30
|
+
```
|
|
31
|
+
|
|
32
|
+
## Sub-package Exports
|
|
33
|
+
|
|
34
|
+
```typescript
|
|
35
|
+
import { createConvexDbAdapter, createSupabaseDbAdapter } from '@geenius/db' // shared
|
|
36
|
+
import { DbProvider, useDb } from '@geenius/db/react' // React hooks
|
|
37
|
+
import { DbProvider, createDb } from '@geenius/db/solidjs' // SolidJS primitives
|
|
38
|
+
```
|
|
39
|
+
|
|
40
|
+
---
|
|
41
|
+
|
|
42
|
+
## Quick Start
|
|
43
|
+
|
|
44
|
+
### Convex
|
|
45
|
+
|
|
46
|
+
```typescript
|
|
47
|
+
import { createConvexDbAdapter } from '@geenius/db'
|
|
48
|
+
|
|
49
|
+
const db = createConvexDbAdapter({
|
|
50
|
+
client: convexClient,
|
|
51
|
+
functions: {
|
|
52
|
+
create: api.adapters.create,
|
|
53
|
+
get: api.adapters.get,
|
|
54
|
+
update: api.adapters.update,
|
|
55
|
+
delete: api.adapters.delete,
|
|
56
|
+
list: api.adapters.list,
|
|
57
|
+
query: api.adapters.query,
|
|
58
|
+
count: api.adapters.count,
|
|
59
|
+
},
|
|
60
|
+
})
|
|
61
|
+
```
|
|
62
|
+
|
|
63
|
+
### Supabase
|
|
64
|
+
|
|
65
|
+
```typescript
|
|
66
|
+
import { createSupabaseDbAdapter } from '@geenius/db'
|
|
67
|
+
|
|
68
|
+
const db = createSupabaseDbAdapter({
|
|
69
|
+
supabaseUrl: process.env.SUPABASE_URL!,
|
|
70
|
+
supabaseKey: process.env.SUPABASE_ANON_KEY!,
|
|
71
|
+
})
|
|
72
|
+
```
|
|
73
|
+
|
|
74
|
+
### Neon (Drizzle)
|
|
75
|
+
|
|
76
|
+
```typescript
|
|
77
|
+
import { createNeonDbAdapter } from '@geenius/db'
|
|
78
|
+
import { eq, and, gt, like } from 'drizzle-orm'
|
|
79
|
+
|
|
80
|
+
const db = createNeonDbAdapter({
|
|
81
|
+
connectionString: process.env.DATABASE_URL!,
|
|
82
|
+
schema: myDrizzleSchema,
|
|
83
|
+
operators: { eq, and, gt, like, or, ne, lt, lte, gte, sql },
|
|
84
|
+
})
|
|
85
|
+
```
|
|
86
|
+
|
|
87
|
+
### MongoDB
|
|
88
|
+
|
|
89
|
+
```typescript
|
|
90
|
+
import { createMongoDbAdapter } from '@geenius/db'
|
|
91
|
+
|
|
92
|
+
const db = createMongoDbAdapter({
|
|
93
|
+
uri: process.env.MONGODB_URI!,
|
|
94
|
+
dbName: 'my-app',
|
|
95
|
+
})
|
|
96
|
+
```
|
|
97
|
+
|
|
98
|
+
### React Provider
|
|
99
|
+
|
|
100
|
+
```tsx
|
|
101
|
+
import { DbProvider, useDb } from '@geenius/db/react'
|
|
102
|
+
import { createConvexDbAdapter } from '@geenius/db'
|
|
103
|
+
|
|
104
|
+
function App() {
|
|
105
|
+
const db = createConvexDbAdapter({ ... })
|
|
106
|
+
return (
|
|
107
|
+
<DbProvider adapter={db}>
|
|
108
|
+
<MyComponent />
|
|
109
|
+
</DbProvider>
|
|
110
|
+
)
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
function MyComponent() {
|
|
114
|
+
const db = useDb()
|
|
115
|
+
// db.list('users'), db.create('users', { name: '...' }), etc.
|
|
116
|
+
}
|
|
117
|
+
```
|
|
118
|
+
|
|
119
|
+
### SolidJS Provider
|
|
120
|
+
|
|
121
|
+
```tsx
|
|
122
|
+
import { DbProvider, createDb } from '@geenius/db/solidjs'
|
|
123
|
+
import { createConvexDbAdapter } from '@geenius/db'
|
|
124
|
+
|
|
125
|
+
function App() {
|
|
126
|
+
const db = createConvexDbAdapter({ ... })
|
|
127
|
+
return (
|
|
128
|
+
<DbProvider adapter={db}>
|
|
129
|
+
<MyComponent />
|
|
130
|
+
</DbProvider>
|
|
131
|
+
)
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
function MyComponent() {
|
|
135
|
+
const db = createDb()
|
|
136
|
+
// db.list('users'), db.create('users', { name: '...' }), etc.
|
|
137
|
+
}
|
|
138
|
+
```
|
|
139
|
+
|
|
140
|
+
---
|
|
141
|
+
|
|
142
|
+
## Architecture
|
|
143
|
+
|
|
144
|
+
This package implements the `DbAdapter` interface defined in `@geenius/adapters-shared`:
|
|
145
|
+
|
|
146
|
+
```typescript
|
|
147
|
+
interface DbAdapter {
|
|
148
|
+
create<T>(collection: string, data: Omit<T, 'id'>): Promise<T & { id: string }>
|
|
149
|
+
get<T>(collection: string, id: string): Promise<T | null>
|
|
150
|
+
update<T>(collection: string, id: string, data: Partial<T>): Promise<T | null>
|
|
151
|
+
delete(collection: string, id: string): Promise<boolean>
|
|
152
|
+
list<T>(collection: string, options?: ListOptions): Promise<T[]>
|
|
153
|
+
query<T>(collection: string, filter: QueryFilter): Promise<T[]>
|
|
154
|
+
count(collection: string, filter?: QueryFilter): Promise<number>
|
|
155
|
+
}
|
|
156
|
+
```
|
|
157
|
+
|
|
158
|
+
All providers throw `DbError` from `@geenius/adapters-shared` on failure.
|
|
159
|
+
|
|
160
|
+
---
|
|
161
|
+
|
|
162
|
+
## License
|
|
163
|
+
|
|
164
|
+
MIT — © Antigravity HQ
|
package/package.json
ADDED
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@geenius/db",
|
|
3
|
+
"type": "module",
|
|
4
|
+
"version": "0.2.0",
|
|
5
|
+
"description": "Geenius DB — Multi-provider database adapter implementations (Convex, Supabase, Neon, MongoDB, CloudflareKV)",
|
|
6
|
+
"author": "Antigravity HQ",
|
|
7
|
+
"license": "MIT",
|
|
8
|
+
"repository": {
|
|
9
|
+
"type": "git",
|
|
10
|
+
"url": "https://github.com/geenius-dev/geenius-db.git"
|
|
11
|
+
},
|
|
12
|
+
"keywords": [
|
|
13
|
+
"database",
|
|
14
|
+
"adapter",
|
|
15
|
+
"convex",
|
|
16
|
+
"supabase",
|
|
17
|
+
"neon",
|
|
18
|
+
"mongodb",
|
|
19
|
+
"cloudflare-kv",
|
|
20
|
+
"react",
|
|
21
|
+
"solidjs",
|
|
22
|
+
"geenius"
|
|
23
|
+
],
|
|
24
|
+
"scripts": {
|
|
25
|
+
"dev": "pnpm -r --parallel type-check",
|
|
26
|
+
"build": "pnpm -r build",
|
|
27
|
+
"clean": "pnpm -r clean",
|
|
28
|
+
"lint": "pnpm -r --parallel type-check",
|
|
29
|
+
"test": "echo \"No tests configured yet\" && exit 0",
|
|
30
|
+
"type-check": "pnpm -r type-check",
|
|
31
|
+
"format": "prettier --write \"packages/*/src/**/*.{ts,tsx}\"",
|
|
32
|
+
"version:patch": "pnpm -r exec -- npm version patch --no-git-tag-version && npm version patch -m 'v%s'",
|
|
33
|
+
"version:minor": "pnpm -r exec -- npm version minor --no-git-tag-version && npm version minor -m 'v%s'",
|
|
34
|
+
"version:major": "pnpm -r exec -- npm version major --no-git-tag-version && npm version major -m 'v%s'",
|
|
35
|
+
"release": "git push && git push --tags",
|
|
36
|
+
"publish:all": "pnpm -r publish --access public"
|
|
37
|
+
},
|
|
38
|
+
"devDependencies": {
|
|
39
|
+
"prettier": "^3.8.1",
|
|
40
|
+
"typescript": "~6.0.2"
|
|
41
|
+
},
|
|
42
|
+
"engines": {
|
|
43
|
+
"node": ">=20.0.0"
|
|
44
|
+
},
|
|
45
|
+
"exports": {
|
|
46
|
+
".": {
|
|
47
|
+
"types": "./packages/shared/dist/index.d.ts",
|
|
48
|
+
"import": "./packages/shared/dist/index.js"
|
|
49
|
+
},
|
|
50
|
+
"./shared": {
|
|
51
|
+
"types": "./packages/shared/dist/index.d.ts",
|
|
52
|
+
"import": "./packages/shared/dist/index.js"
|
|
53
|
+
},
|
|
54
|
+
"./react": {
|
|
55
|
+
"types": "./packages/react/dist/index.d.ts",
|
|
56
|
+
"import": "./packages/react/dist/index.js"
|
|
57
|
+
},
|
|
58
|
+
"./solidjs": {
|
|
59
|
+
"types": "./packages/solidjs/dist/index.d.ts",
|
|
60
|
+
"import": "./packages/solidjs/dist/index.js"
|
|
61
|
+
}
|
|
62
|
+
},
|
|
63
|
+
"main": "./packages/shared/dist/index.js",
|
|
64
|
+
"types": "./packages/shared/dist/index.d.ts"
|
|
65
|
+
}
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@geenius/db-react",
|
|
3
|
+
"version": "0.2.0",
|
|
4
|
+
"private": false,
|
|
5
|
+
"type": "module",
|
|
6
|
+
"description": "Geenius DB — React hooks and providers for database adapters",
|
|
7
|
+
"author": "Antigravity HQ",
|
|
8
|
+
"license": "MIT",
|
|
9
|
+
"publishConfig": {
|
|
10
|
+
"access": "restricted"
|
|
11
|
+
},
|
|
12
|
+
"main": "./dist/index.js",
|
|
13
|
+
"module": "./dist/index.js",
|
|
14
|
+
"types": "./dist/index.d.ts",
|
|
15
|
+
"exports": {
|
|
16
|
+
".": {
|
|
17
|
+
"types": "./dist/index.d.ts",
|
|
18
|
+
"import": "./dist/index.js"
|
|
19
|
+
}
|
|
20
|
+
},
|
|
21
|
+
"files": [
|
|
22
|
+
"dist",
|
|
23
|
+
"src"
|
|
24
|
+
],
|
|
25
|
+
"scripts": {
|
|
26
|
+
"build": "tsup",
|
|
27
|
+
"clean": "rm -rf dist",
|
|
28
|
+
"type-check": "tsc --noEmit",
|
|
29
|
+
"prepublishOnly": "pnpm clean && pnpm build"
|
|
30
|
+
},
|
|
31
|
+
"peerDependencies": {
|
|
32
|
+
"@geenius/adapters-shared": ">=0.2.0",
|
|
33
|
+
"@geenius/db-shared": ">=0.2.0",
|
|
34
|
+
"react": ">=19.0.0"
|
|
35
|
+
},
|
|
36
|
+
"devDependencies": {
|
|
37
|
+
"@geenius/adapters-shared": "link:../../../geenius-adapters/packages/shared",
|
|
38
|
+
"@geenius/db-shared": "workspace:*",
|
|
39
|
+
"@types/react": "^19.1.4",
|
|
40
|
+
"react": "^19.1.0",
|
|
41
|
+
"tsup": "^8.5.1",
|
|
42
|
+
"typescript": "~6.0.2"
|
|
43
|
+
},
|
|
44
|
+
"engines": {
|
|
45
|
+
"node": ">=20.0.0"
|
|
46
|
+
}
|
|
47
|
+
}
|
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
// @geenius/db-react — React hooks and providers for database adapters
|
|
2
|
+
|
|
3
|
+
'use client'
|
|
4
|
+
|
|
5
|
+
import { createContext, useContext, useMemo, type ReactNode } from 'react'
|
|
6
|
+
import type { DbAdapter } from '@geenius/adapters-shared'
|
|
7
|
+
|
|
8
|
+
// ─── Context ─────────────────────────────────────────────────────────────────
|
|
9
|
+
|
|
10
|
+
const DbContext = createContext<DbAdapter | null>(null)
|
|
11
|
+
|
|
12
|
+
// ─── Provider ────────────────────────────────────────────────────────────────
|
|
13
|
+
|
|
14
|
+
export interface DbProviderProps {
|
|
15
|
+
adapter: DbAdapter
|
|
16
|
+
children: ReactNode
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
/**
|
|
20
|
+
* Provides a DbAdapter instance to all child components via React context.
|
|
21
|
+
*
|
|
22
|
+
* @example
|
|
23
|
+
* ```tsx
|
|
24
|
+
* import { DbProvider } from '@geenius/db/react'
|
|
25
|
+
* import { createConvexDbAdapter } from '@geenius/db'
|
|
26
|
+
*
|
|
27
|
+
* function App() {
|
|
28
|
+
* const db = createConvexDbAdapter({ client: convex, functions: myFns })
|
|
29
|
+
* return (
|
|
30
|
+
* <DbProvider adapter={db}>
|
|
31
|
+
* <Router />
|
|
32
|
+
* </DbProvider>
|
|
33
|
+
* )
|
|
34
|
+
* }
|
|
35
|
+
* ```
|
|
36
|
+
*/
|
|
37
|
+
export function DbProvider({ adapter, children }: DbProviderProps) {
|
|
38
|
+
const value = useMemo(() => adapter, [adapter])
|
|
39
|
+
return <DbContext.Provider value={value}>{children}</DbContext.Provider>
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
// ─── Hooks ───────────────────────────────────────────────────────────────────
|
|
43
|
+
|
|
44
|
+
/**
|
|
45
|
+
* Access the DbAdapter from context.
|
|
46
|
+
*
|
|
47
|
+
* @throws Error if used outside of a `<DbProvider>`
|
|
48
|
+
*
|
|
49
|
+
* @example
|
|
50
|
+
* ```tsx
|
|
51
|
+
* function UserList() {
|
|
52
|
+
* const db = useDb()
|
|
53
|
+
* const [users, setUsers] = useState([])
|
|
54
|
+
*
|
|
55
|
+
* useEffect(() => {
|
|
56
|
+
* db.list('users').then(setUsers)
|
|
57
|
+
* }, [db])
|
|
58
|
+
* }
|
|
59
|
+
* ```
|
|
60
|
+
*/
|
|
61
|
+
export function useDb(): DbAdapter {
|
|
62
|
+
const ctx = useContext(DbContext)
|
|
63
|
+
if (!ctx) {
|
|
64
|
+
throw new Error(
|
|
65
|
+
'[@geenius/db] useDb() must be used inside a <DbProvider>. '
|
|
66
|
+
+ 'Wrap your app with <DbProvider adapter={...}>.',
|
|
67
|
+
)
|
|
68
|
+
}
|
|
69
|
+
return ctx
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
/**
|
|
73
|
+
* Access the DbAdapter from context, returning null if not available.
|
|
74
|
+
* Useful for optional DB access in shared components.
|
|
75
|
+
*/
|
|
76
|
+
export function useDbOptional(): DbAdapter | null {
|
|
77
|
+
return useContext(DbContext)
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
// ─── Re-exports ──────────────────────────────────────────────────────────────
|
|
81
|
+
|
|
82
|
+
export type { DbAdapter } from '@geenius/adapters-shared'
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import { defineConfig } from 'tsup'
|
|
2
|
+
|
|
3
|
+
export default defineConfig({
|
|
4
|
+
entry: { index: 'src/index.tsx' },
|
|
5
|
+
outDir: 'dist',
|
|
6
|
+
format: ['esm'],
|
|
7
|
+
dts: true,
|
|
8
|
+
sourcemap: true,
|
|
9
|
+
clean: true,
|
|
10
|
+
treeshake: true,
|
|
11
|
+
external: [
|
|
12
|
+
'@geenius/adapters-shared',
|
|
13
|
+
'@geenius/db-shared',
|
|
14
|
+
'react',
|
|
15
|
+
'react/jsx-runtime',
|
|
16
|
+
],
|
|
17
|
+
})
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@geenius/db-shared",
|
|
3
|
+
"version": "0.2.0",
|
|
4
|
+
"private": false,
|
|
5
|
+
"type": "module",
|
|
6
|
+
"description": "Geenius DB — Database provider implementations (Convex, Supabase, Neon, MongoDB, CloudflareKV)",
|
|
7
|
+
"author": "Antigravity HQ",
|
|
8
|
+
"license": "MIT",
|
|
9
|
+
"publishConfig": {
|
|
10
|
+
"access": "restricted"
|
|
11
|
+
},
|
|
12
|
+
"main": "./dist/index.js",
|
|
13
|
+
"module": "./dist/index.js",
|
|
14
|
+
"types": "./dist/index.d.ts",
|
|
15
|
+
"exports": {
|
|
16
|
+
".": {
|
|
17
|
+
"types": "./dist/index.d.ts",
|
|
18
|
+
"import": "./dist/index.js"
|
|
19
|
+
}
|
|
20
|
+
},
|
|
21
|
+
"files": [
|
|
22
|
+
"dist",
|
|
23
|
+
"src"
|
|
24
|
+
],
|
|
25
|
+
"scripts": {
|
|
26
|
+
"build": "tsup",
|
|
27
|
+
"clean": "rm -rf dist",
|
|
28
|
+
"type-check": "tsc --noEmit",
|
|
29
|
+
"prepublishOnly": "pnpm clean && pnpm build",
|
|
30
|
+
"test": "vitest run",
|
|
31
|
+
"test:watch": "vitest",
|
|
32
|
+
"test:coverage": "vitest run --coverage"
|
|
33
|
+
},
|
|
34
|
+
"peerDependencies": {
|
|
35
|
+
"@geenius/adapters-shared": ">=0.2.0"
|
|
36
|
+
},
|
|
37
|
+
"peerDependenciesMeta": {
|
|
38
|
+
"@geenius/adapters-shared": {
|
|
39
|
+
"optional": false
|
|
40
|
+
}
|
|
41
|
+
},
|
|
42
|
+
"devDependencies": {
|
|
43
|
+
"@geenius/adapters-shared": "link:../../../geenius-adapters/packages/shared",
|
|
44
|
+
"@neondatabase/serverless": "^1.0.2",
|
|
45
|
+
"@supabase/supabase-js": "^2.101.1",
|
|
46
|
+
"convex": "^1.34.1",
|
|
47
|
+
"mongodb": "^7.1.1",
|
|
48
|
+
"tsup": "^8.5.1",
|
|
49
|
+
"typescript": "~6.0.2",
|
|
50
|
+
"vitest": "^4.0.0"
|
|
51
|
+
},
|
|
52
|
+
"engines": {
|
|
53
|
+
"node": ">=20.0.0"
|
|
54
|
+
}
|
|
55
|
+
}
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
// @geenius/db-shared — src/index.ts
|
|
2
|
+
//
|
|
3
|
+
// Database provider implementations for the Geenius ecosystem.
|
|
4
|
+
// All providers implement the DbAdapter interface from @geenius/adapters-shared.
|
|
5
|
+
//
|
|
6
|
+
// Usage:
|
|
7
|
+
// import { createConvexDbAdapter } from '@geenius/db'
|
|
8
|
+
// import { createSupabaseDbAdapter } from '@geenius/db'
|
|
9
|
+
// import type { DbAdapter } from '@geenius/adapters-shared'
|
|
10
|
+
|
|
11
|
+
// ─── Re-export DbAdapter interface from @geenius/adapters-shared ─────────────
|
|
12
|
+
// Consumers can import the interface directly from this package for convenience.
|
|
13
|
+
|
|
14
|
+
export type {
|
|
15
|
+
DbAdapter,
|
|
16
|
+
ListOptions,
|
|
17
|
+
QueryOperator,
|
|
18
|
+
QueryCondition,
|
|
19
|
+
QueryFilter,
|
|
20
|
+
} from '@geenius/adapters-shared'
|
|
21
|
+
|
|
22
|
+
export { DbError } from '@geenius/adapters-shared'
|
|
23
|
+
export type { DbErrorCode } from '@geenius/adapters-shared'
|
|
24
|
+
|
|
25
|
+
// ─── Provider Implementations ────────────────────────────────────────────────
|
|
26
|
+
|
|
27
|
+
export { createConvexDbAdapter } from './providers/convex'
|
|
28
|
+
export type { ConvexDbAdapterOptions } from './providers/convex'
|
|
29
|
+
|
|
30
|
+
export { createSupabaseDbAdapter } from './providers/supabase'
|
|
31
|
+
export type { SupabaseDbAdapterOptions } from './providers/supabase'
|
|
32
|
+
|
|
33
|
+
export { createNeonDbAdapter } from './providers/neon'
|
|
34
|
+
export type { NeonDbAdapterOptions, DrizzleOperators } from './providers/neon'
|
|
35
|
+
|
|
36
|
+
export { createMongoDbAdapter } from './providers/mongodb'
|
|
37
|
+
export type { MongoDbAdapterOptions } from './providers/mongodb'
|
|
38
|
+
|
|
39
|
+
export { createCloudflareKVDbAdapter } from './providers/cloudflareKV'
|
|
40
|
+
export type { KVNamespaceLike } from './providers/cloudflareKV'
|
|
41
|
+
|
|
42
|
+
export { createMemoryDbAdapter } from './providers/memory'
|
|
@@ -0,0 +1,110 @@
|
|
|
1
|
+
// @geenius/adapters — Cloudflare KV DB implementation
|
|
2
|
+
|
|
3
|
+
import type { ListOptions, QueryFilter, QueryCondition } from '@geenius/adapters-shared'
|
|
4
|
+
import type { DbAdapter } from '@geenius/adapters-shared'
|
|
5
|
+
import { DbError } from '@geenius/adapters-shared'
|
|
6
|
+
|
|
7
|
+
// Generic typing for the global KVNamespace if @cloudflare/workers-types isn't explicitly imported
|
|
8
|
+
export interface KVNamespaceLike {
|
|
9
|
+
get(key: string, type: 'json'): Promise<unknown>
|
|
10
|
+
put(key: string, value: string): Promise<void>
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
const DB_PREFIX = 'geenius_db_'
|
|
14
|
+
|
|
15
|
+
function matchesCondition(item: Record<string, unknown>, cond: QueryCondition): boolean {
|
|
16
|
+
const val = item[cond.field]
|
|
17
|
+
switch (cond.operator) {
|
|
18
|
+
case 'eq': return val === cond.value
|
|
19
|
+
case 'neq': return val !== cond.value
|
|
20
|
+
case 'gt': return (val as number) > (cond.value as number)
|
|
21
|
+
case 'gte': return (val as number) >= (cond.value as number)
|
|
22
|
+
case 'lt': return (val as number) < (cond.value as number)
|
|
23
|
+
case 'lte': return (val as number) <= (cond.value as number)
|
|
24
|
+
case 'in': return Array.isArray(cond.value) && cond.value.includes(val)
|
|
25
|
+
case 'contains': return typeof val === 'string' && val.includes(cond.value as string)
|
|
26
|
+
default: return false
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
function matchesFilter(item: Record<string, unknown>, filter: QueryFilter): boolean {
|
|
31
|
+
return filter.every(cond => matchesCondition(item, cond))
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
export function createCloudflareKVDbAdapter(kv: KVNamespaceLike): DbAdapter {
|
|
35
|
+
async function getCollection(collection: string): Promise<Record<string, unknown>[]> {
|
|
36
|
+
try {
|
|
37
|
+
const data = await kv.get(DB_PREFIX + collection, 'json')
|
|
38
|
+
return (data as Record<string, unknown>[]) || []
|
|
39
|
+
} catch (err) {
|
|
40
|
+
throw new DbError(`Failed to read collection "${collection}" from KV`, 'CONNECTION_ERROR', err)
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
async function saveCollection(collection: string, data: Record<string, unknown>[]): Promise<void> {
|
|
45
|
+
try {
|
|
46
|
+
await kv.put(DB_PREFIX + collection, JSON.stringify(data))
|
|
47
|
+
} catch (err) {
|
|
48
|
+
throw new DbError(`Failed to write collection "${collection}" to KV`, 'CONNECTION_ERROR', err)
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
return {
|
|
53
|
+
async create<T extends Record<string, unknown>>(collection: string, data: Omit<T, 'id'>) {
|
|
54
|
+
const items = await getCollection(collection)
|
|
55
|
+
const item = { ...data, id: crypto.randomUUID() }
|
|
56
|
+
items.push(item)
|
|
57
|
+
await saveCollection(collection, items)
|
|
58
|
+
return item as T & { id: string }
|
|
59
|
+
},
|
|
60
|
+
|
|
61
|
+
async get<T>(collection: string, id: string) {
|
|
62
|
+
const items = await getCollection(collection)
|
|
63
|
+
const found = items.find(item => item.id === id)
|
|
64
|
+
return (found as T | undefined) ?? null
|
|
65
|
+
},
|
|
66
|
+
|
|
67
|
+
async update<T extends Record<string, unknown>>(collection: string, id: string, data: Partial<T>) {
|
|
68
|
+
const items = await getCollection(collection)
|
|
69
|
+
const idx = items.findIndex(item => item.id === id)
|
|
70
|
+
if (idx === -1) return null
|
|
71
|
+
Object.assign(items[idx], data)
|
|
72
|
+
await saveCollection(collection, items)
|
|
73
|
+
return items[idx] as T | null
|
|
74
|
+
},
|
|
75
|
+
|
|
76
|
+
async delete(collection: string, id: string) {
|
|
77
|
+
const items = await getCollection(collection)
|
|
78
|
+
const idx = items.findIndex(item => item.id === id)
|
|
79
|
+
if (idx === -1) return false
|
|
80
|
+
items.splice(idx, 1)
|
|
81
|
+
await saveCollection(collection, items)
|
|
82
|
+
return true
|
|
83
|
+
},
|
|
84
|
+
|
|
85
|
+
async list<T>(collection: string, options?: ListOptions) {
|
|
86
|
+
let items = await getCollection(collection)
|
|
87
|
+
if (options?.orderBy) {
|
|
88
|
+
const dir = options.order === 'desc' ? -1 : 1
|
|
89
|
+
items.sort((a, b) => {
|
|
90
|
+
const av = a[options.orderBy!] as string | number, bv = b[options.orderBy!] as string | number
|
|
91
|
+
return av < bv ? -dir : av > bv ? dir : 0
|
|
92
|
+
})
|
|
93
|
+
}
|
|
94
|
+
if (options?.offset) items = items.slice(options.offset)
|
|
95
|
+
if (options?.limit) items = items.slice(0, options.limit)
|
|
96
|
+
return items as T[]
|
|
97
|
+
},
|
|
98
|
+
|
|
99
|
+
async query<T>(collection: string, filter: QueryFilter) {
|
|
100
|
+
const items = await getCollection(collection)
|
|
101
|
+
return items.filter(item => matchesFilter(item, filter)) as T[]
|
|
102
|
+
},
|
|
103
|
+
|
|
104
|
+
async count(collection: string, filter?: QueryFilter) {
|
|
105
|
+
const items = await getCollection(collection)
|
|
106
|
+
if (!filter || filter.length === 0) return items.length
|
|
107
|
+
return items.filter(item => matchesFilter(item, filter)).length
|
|
108
|
+
},
|
|
109
|
+
}
|
|
110
|
+
}
|