@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
|
@@ -0,0 +1,160 @@
|
|
|
1
|
+
// @geenius/adapters — Supabase DB implementation (MVP tier)
|
|
2
|
+
// Wraps the Supabase client to conform to DbAdapter interface.
|
|
3
|
+
// Requires: @supabase/supabase-js
|
|
4
|
+
|
|
5
|
+
import type { ListOptions, QueryFilter } from '@geenius/adapters-shared'
|
|
6
|
+
import type { DbAdapter } from '@geenius/adapters-shared'
|
|
7
|
+
import { DbError } from '@geenius/adapters-shared'
|
|
8
|
+
|
|
9
|
+
interface SupabaseClient {
|
|
10
|
+
from: (table: string) => SupabaseQueryBuilder
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
interface SupabaseQueryBuilder {
|
|
14
|
+
select: (columns?: string) => SupabaseQueryBuilder
|
|
15
|
+
insert: (data: Record<string, unknown>) => SupabaseQueryBuilder
|
|
16
|
+
update: (data: Record<string, unknown>) => SupabaseQueryBuilder
|
|
17
|
+
delete: () => SupabaseQueryBuilder
|
|
18
|
+
eq: (column: string, value: unknown) => SupabaseQueryBuilder
|
|
19
|
+
neq: (column: string, value: unknown) => SupabaseQueryBuilder
|
|
20
|
+
gt: (column: string, value: unknown) => SupabaseQueryBuilder
|
|
21
|
+
gte: (column: string, value: unknown) => SupabaseQueryBuilder
|
|
22
|
+
lt: (column: string, value: unknown) => SupabaseQueryBuilder
|
|
23
|
+
lte: (column: string, value: unknown) => SupabaseQueryBuilder
|
|
24
|
+
in: (column: string, values: unknown[]) => SupabaseQueryBuilder
|
|
25
|
+
ilike: (column: string, pattern: string) => SupabaseQueryBuilder
|
|
26
|
+
order: (column: string, options?: { ascending?: boolean }) => SupabaseQueryBuilder
|
|
27
|
+
range: (from: number, to: number) => SupabaseQueryBuilder
|
|
28
|
+
limit: (count: number) => SupabaseQueryBuilder
|
|
29
|
+
single: () => SupabaseQueryBuilder
|
|
30
|
+
then: (resolve: (value: { data: unknown; error: { message: string } | null }) => void) => Promise<unknown>
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
export interface SupabaseDbAdapterOptions {
|
|
34
|
+
/** Pre-configured Supabase client instance */
|
|
35
|
+
client: SupabaseClient
|
|
36
|
+
/** Optional table name mapping: { collectionName: 'actual_table_name' } */
|
|
37
|
+
tableNames?: Record<string, string>
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
/**
|
|
41
|
+
* Creates a Supabase-backed DbAdapter.
|
|
42
|
+
*
|
|
43
|
+
* @example
|
|
44
|
+
* ```ts
|
|
45
|
+
* import { createClient } from '@supabase/supabase-js'
|
|
46
|
+
*
|
|
47
|
+
* const supabase = createClient(url, key)
|
|
48
|
+
* const db = createSupabaseDbAdapter({ client: supabase })
|
|
49
|
+
* ```
|
|
50
|
+
*/
|
|
51
|
+
export function createSupabaseDbAdapter(options: SupabaseDbAdapterOptions): DbAdapter {
|
|
52
|
+
const { client, tableNames = {} } = options
|
|
53
|
+
|
|
54
|
+
function tableName(collection: string): string {
|
|
55
|
+
return tableNames[collection] || collection
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
// Supabase query builder returns { data, error } — the builder chain is
|
|
59
|
+
// thenable, so we await it directly. The intermediate chain types don't
|
|
60
|
+
// carry our generic T, so we cast at the result boundary.
|
|
61
|
+
/* eslint-disable @typescript-eslint/no-explicit-any */
|
|
62
|
+
return {
|
|
63
|
+
async create<T extends Record<string, unknown>>(collection: string, data: Omit<T, 'id'>) {
|
|
64
|
+
const { data: result, error } = await (client
|
|
65
|
+
.from(tableName(collection))
|
|
66
|
+
.insert(data as Record<string, unknown>)
|
|
67
|
+
.select()
|
|
68
|
+
.single() as any)
|
|
69
|
+
|
|
70
|
+
if (error) throw new DbError(`Insert into "${collection}" failed: ${error.message}`, 'QUERY_ERROR', error)
|
|
71
|
+
return result as T & { id: string }
|
|
72
|
+
},
|
|
73
|
+
|
|
74
|
+
async get<T>(collection: string, id: string) {
|
|
75
|
+
const { data, error } = await (client
|
|
76
|
+
.from(tableName(collection))
|
|
77
|
+
.select()
|
|
78
|
+
.eq('id', id)
|
|
79
|
+
.single() as any)
|
|
80
|
+
|
|
81
|
+
if (error) return null
|
|
82
|
+
return data as T
|
|
83
|
+
},
|
|
84
|
+
|
|
85
|
+
async update<T extends Record<string, unknown>>(collection: string, id: string, data: Partial<T>) {
|
|
86
|
+
const { data: result, error } = await (client
|
|
87
|
+
.from(tableName(collection))
|
|
88
|
+
.update(data as Record<string, unknown>)
|
|
89
|
+
.eq('id', id)
|
|
90
|
+
.select()
|
|
91
|
+
.single() as any)
|
|
92
|
+
|
|
93
|
+
if (error) return null
|
|
94
|
+
return result as T
|
|
95
|
+
},
|
|
96
|
+
|
|
97
|
+
async delete(collection: string, id: string) {
|
|
98
|
+
const { error } = await (client
|
|
99
|
+
.from(tableName(collection))
|
|
100
|
+
.delete()
|
|
101
|
+
.eq('id', id) as any)
|
|
102
|
+
|
|
103
|
+
return !error
|
|
104
|
+
},
|
|
105
|
+
|
|
106
|
+
async list<T>(collection: string, options?: ListOptions) {
|
|
107
|
+
let query: any = client.from(tableName(collection)).select()
|
|
108
|
+
|
|
109
|
+
if (options?.orderBy) {
|
|
110
|
+
query = query.order(options.orderBy, {
|
|
111
|
+
ascending: options.order !== 'desc',
|
|
112
|
+
})
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
if (options?.offset !== undefined && options?.limit !== undefined) {
|
|
116
|
+
query = query.range(options.offset, options.offset + options.limit - 1)
|
|
117
|
+
} else if (options?.limit) {
|
|
118
|
+
query = query.limit(options.limit)
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
const { data, error } = await query
|
|
122
|
+
if (error) throw new DbError(`List "${collection}" failed: ${error.message}`, 'QUERY_ERROR', error)
|
|
123
|
+
return (data || []) as T[]
|
|
124
|
+
},
|
|
125
|
+
|
|
126
|
+
async query<T>(collection: string, filter: QueryFilter) {
|
|
127
|
+
let query: any = client.from(tableName(collection)).select()
|
|
128
|
+
|
|
129
|
+
for (const cond of filter) {
|
|
130
|
+
switch (cond.operator) {
|
|
131
|
+
case 'eq': query = query.eq(cond.field, cond.value); break
|
|
132
|
+
case 'neq': query = query.neq(cond.field, cond.value); break
|
|
133
|
+
case 'gt': query = query.gt(cond.field, cond.value); break
|
|
134
|
+
case 'gte': query = query.gte(cond.field, cond.value); break
|
|
135
|
+
case 'lt': query = query.lt(cond.field, cond.value); break
|
|
136
|
+
case 'lte': query = query.lte(cond.field, cond.value); break
|
|
137
|
+
case 'in': query = query.in(cond.field, cond.value as unknown[]); break
|
|
138
|
+
case 'contains': query = query.ilike(cond.field, `%${cond.value}%`); break
|
|
139
|
+
}
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
const { data, error } = await query
|
|
143
|
+
if (error) throw new DbError(`Query "${collection}" failed: ${error.message}`, 'QUERY_ERROR', error)
|
|
144
|
+
return (data || []) as T[]
|
|
145
|
+
},
|
|
146
|
+
|
|
147
|
+
async count(collection: string, filter?: QueryFilter) {
|
|
148
|
+
if (!filter || filter.length === 0) {
|
|
149
|
+
const { data } = await (client
|
|
150
|
+
.from(tableName(collection))
|
|
151
|
+
.select('id') as any)
|
|
152
|
+
return data?.length ?? 0
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
const items = await this.query(collection, filter)
|
|
156
|
+
return items.length
|
|
157
|
+
},
|
|
158
|
+
}
|
|
159
|
+
/* eslint-enable @typescript-eslint/no-explicit-any */
|
|
160
|
+
}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
{
|
|
2
|
+
"extends": "../../tsconfig.json",
|
|
3
|
+
"compilerOptions": {
|
|
4
|
+
"ignoreDeprecations": "6.0",
|
|
5
|
+
"outDir": "dist",
|
|
6
|
+
"rootDir": "src",
|
|
7
|
+
"strict": true,
|
|
8
|
+
"skipLibCheck": true,
|
|
9
|
+
"forceConsistentCasingInFileNames": true,
|
|
10
|
+
"resolveJsonModule": true,
|
|
11
|
+
"isolatedModules": true,
|
|
12
|
+
"target": "ES2022",
|
|
13
|
+
"module": "ESNext",
|
|
14
|
+
"moduleResolution": "bundler"
|
|
15
|
+
},
|
|
16
|
+
"include": [
|
|
17
|
+
"src"
|
|
18
|
+
]
|
|
19
|
+
}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import { defineConfig } from 'tsup'
|
|
2
|
+
|
|
3
|
+
export default defineConfig({
|
|
4
|
+
entry: { index: 'src/index.ts' },
|
|
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
|
+
'@neondatabase/serverless',
|
|
14
|
+
'@supabase/supabase-js',
|
|
15
|
+
'convex',
|
|
16
|
+
'mongodb',
|
|
17
|
+
],
|
|
18
|
+
})
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@geenius/db-solidjs",
|
|
3
|
+
"version": "0.2.0",
|
|
4
|
+
"private": false,
|
|
5
|
+
"type": "module",
|
|
6
|
+
"description": "Geenius DB — SolidJS primitives 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
|
+
"solid-js": ">=1.9.0"
|
|
35
|
+
},
|
|
36
|
+
"devDependencies": {
|
|
37
|
+
"@geenius/adapters-shared": "link:../../../geenius-adapters/packages/shared",
|
|
38
|
+
"@geenius/db-shared": "workspace:*",
|
|
39
|
+
"solid-js": "^1.9.8",
|
|
40
|
+
"tsup": "^8.5.1",
|
|
41
|
+
"typescript": "~6.0.2"
|
|
42
|
+
},
|
|
43
|
+
"engines": {
|
|
44
|
+
"node": ">=20.0.0"
|
|
45
|
+
}
|
|
46
|
+
}
|
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
// @geenius/db-solidjs — SolidJS primitives and providers for database adapters
|
|
2
|
+
|
|
3
|
+
import { createContext, useContext, type JSX } from 'solid-js'
|
|
4
|
+
import type { DbAdapter } from '@geenius/adapters-shared'
|
|
5
|
+
|
|
6
|
+
// ─── Context ─────────────────────────────────────────────────────────────────
|
|
7
|
+
|
|
8
|
+
const DbContext = createContext<DbAdapter>()
|
|
9
|
+
|
|
10
|
+
// ─── Provider ────────────────────────────────────────────────────────────────
|
|
11
|
+
|
|
12
|
+
export interface DbProviderProps {
|
|
13
|
+
adapter: DbAdapter
|
|
14
|
+
children: JSX.Element
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
/**
|
|
18
|
+
* Provides a DbAdapter instance to all child components via SolidJS context.
|
|
19
|
+
*
|
|
20
|
+
* @example
|
|
21
|
+
* ```tsx
|
|
22
|
+
* import { DbProvider } from '@geenius/db/solidjs'
|
|
23
|
+
* import { createConvexDbAdapter } from '@geenius/db'
|
|
24
|
+
*
|
|
25
|
+
* function App() {
|
|
26
|
+
* const db = createConvexDbAdapter({ client: convex, functions: myFns })
|
|
27
|
+
* return (
|
|
28
|
+
* <DbProvider adapter={db}>
|
|
29
|
+
* <Router />
|
|
30
|
+
* </DbProvider>
|
|
31
|
+
* )
|
|
32
|
+
* }
|
|
33
|
+
* ```
|
|
34
|
+
*/
|
|
35
|
+
export function DbProvider(props: DbProviderProps) {
|
|
36
|
+
return (
|
|
37
|
+
<DbContext.Provider value={props.adapter}>
|
|
38
|
+
{props.children}
|
|
39
|
+
</DbContext.Provider>
|
|
40
|
+
)
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
// ─── Primitives ──────────────────────────────────────────────────────────────
|
|
44
|
+
|
|
45
|
+
/**
|
|
46
|
+
* Access the DbAdapter from context.
|
|
47
|
+
*
|
|
48
|
+
* @throws Error if used outside of a `<DbProvider>`
|
|
49
|
+
*
|
|
50
|
+
* @example
|
|
51
|
+
* ```tsx
|
|
52
|
+
* function UserList() {
|
|
53
|
+
* const db = createDb()
|
|
54
|
+
* const [users, setUsers] = createSignal([])
|
|
55
|
+
*
|
|
56
|
+
* onMount(async () => {
|
|
57
|
+
* setUsers(await db.list('users'))
|
|
58
|
+
* })
|
|
59
|
+
* }
|
|
60
|
+
* ```
|
|
61
|
+
*/
|
|
62
|
+
export function createDb(): DbAdapter {
|
|
63
|
+
const ctx = useContext(DbContext)
|
|
64
|
+
if (!ctx) {
|
|
65
|
+
throw new Error(
|
|
66
|
+
'[@geenius/db] createDb() must be used inside a <DbProvider>. '
|
|
67
|
+
+ 'Wrap your app with <DbProvider adapter={...}>.',
|
|
68
|
+
)
|
|
69
|
+
}
|
|
70
|
+
return ctx
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
/**
|
|
74
|
+
* Access the DbAdapter from context, returning undefined if not available.
|
|
75
|
+
* Useful for optional DB access in shared components.
|
|
76
|
+
*/
|
|
77
|
+
export function createDbOptional(): DbAdapter | undefined {
|
|
78
|
+
return useContext(DbContext)
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
// ─── Re-exports ──────────────────────────────────────────────────────────────
|
|
82
|
+
|
|
83
|
+
export type { DbAdapter } from '@geenius/adapters-shared'
|
|
@@ -0,0 +1,18 @@
|
|
|
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
|
+
'solid-js',
|
|
15
|
+
'solid-js/web',
|
|
16
|
+
'solid-js/store',
|
|
17
|
+
],
|
|
18
|
+
})
|
package/tsconfig.json
ADDED
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
{
|
|
2
|
+
"compilerOptions": {
|
|
3
|
+
"target": "ES2022",
|
|
4
|
+
"module": "ESNext",
|
|
5
|
+
"moduleResolution": "bundler",
|
|
6
|
+
"esModuleInterop": true,
|
|
7
|
+
"strict": true,
|
|
8
|
+
"skipLibCheck": true,
|
|
9
|
+
"declaration": true,
|
|
10
|
+
"declarationMap": true,
|
|
11
|
+
"sourceMap": true,
|
|
12
|
+
"outDir": "dist",
|
|
13
|
+
"rootDir": "src",
|
|
14
|
+
"jsx": "react-jsx",
|
|
15
|
+
"forceConsistentCasingInFileNames": true,
|
|
16
|
+
"resolveJsonModule": true,
|
|
17
|
+
"isolatedModules": true
|
|
18
|
+
},
|
|
19
|
+
"exclude": [
|
|
20
|
+
"node_modules",
|
|
21
|
+
"dist"
|
|
22
|
+
]
|
|
23
|
+
}
|