@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.
@@ -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,11 @@
1
+ {
2
+ "extends": "../../tsconfig.json",
3
+ "compilerOptions": {
4
+ "ignoreDeprecations": "6.0",
5
+ "outDir": "dist",
6
+ "rootDir": "src",
7
+ "jsx": "preserve",
8
+ "jsxImportSource": "solid-js"
9
+ },
10
+ "include": ["src"]
11
+ }
@@ -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
+ })
@@ -0,0 +1,2 @@
1
+ packages:
2
+ - 'packages/*'
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
+ }