hazo_connect 2.2.0 → 2.3.2
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 +563 -0
- package/dist/adapters/sqlite-adapter.d.ts.map +1 -1
- package/dist/adapters/sqlite-adapter.js +37 -2
- package/dist/adapters/sqlite-adapter.js.map +1 -1
- package/dist/factory.d.ts +1 -0
- package/dist/factory.d.ts.map +1 -1
- package/dist/factory.js +51 -9
- package/dist/factory.js.map +1 -1
- package/dist/nextjs/index.d.ts +2 -0
- package/dist/nextjs/index.d.ts.map +1 -1
- package/dist/nextjs/index.js +10 -1
- package/dist/nextjs/index.js.map +1 -1
- package/dist/nextjs/route-setup.d.ts +46 -0
- package/dist/nextjs/route-setup.d.ts.map +1 -0
- package/dist/nextjs/route-setup.js +141 -0
- package/dist/nextjs/route-setup.js.map +1 -0
- package/dist/nextjs/setup-helpers.d.ts +86 -0
- package/dist/nextjs/setup-helpers.d.ts.map +1 -0
- package/dist/nextjs/setup-helpers.js +174 -0
- package/dist/nextjs/setup-helpers.js.map +1 -0
- package/dist/server/index.d.ts +2 -1
- package/dist/server/index.d.ts.map +1 -1
- package/dist/server/index.js +7 -1
- package/dist/server/index.js.map +1 -1
- package/dist/sqlite/admin-service.d.ts +8 -0
- package/dist/sqlite/admin-service.d.ts.map +1 -1
- package/dist/sqlite/admin-service.js +39 -1
- package/dist/sqlite/admin-service.js.map +1 -1
- package/dist/utils/config-validator.d.ts +39 -0
- package/dist/utils/config-validator.d.ts.map +1 -0
- package/dist/utils/config-validator.js +78 -0
- package/dist/utils/config-validator.js.map +1 -0
- package/docs/examples/nextjs-admin-ui-setup.ts +199 -0
- package/docs/examples/nextjs-api-route.ts +205 -0
- package/docs/examples/nextjs-crud-service.ts +257 -0
- package/docs/examples/nextjs-server-component.tsx +123 -0
- package/docs/examples/nextjs-singleton-pattern.ts +166 -0
- package/docs/migration-guide.md +272 -0
- package/docs/nextjs-setup.md +471 -0
- package/docs/techdoc.md +824 -0
- package/docs/troubleshooting.md +442 -0
- package/docs/types.md +490 -0
- package/package.json +16 -4
- package/scripts/postinstall-setup.js +72 -0
- package/scripts/setup-routes.js +123 -0
package/docs/techdoc.md
ADDED
|
@@ -0,0 +1,824 @@
|
|
|
1
|
+
# Hazo Connect - Technical Documentation
|
|
2
|
+
|
|
3
|
+
This document provides technical details about the internal architecture, file structure, services, key assumptions, and dependencies of the `hazo_connect` package. It is intended for developers maintaining or extending the codebase.
|
|
4
|
+
|
|
5
|
+
## Table of Contents
|
|
6
|
+
|
|
7
|
+
- [Overview](#overview)
|
|
8
|
+
- [Architecture](#architecture)
|
|
9
|
+
- [File Structure](#file-structure)
|
|
10
|
+
- [Entry Points](#entry-points)
|
|
11
|
+
- [Core Components](#core-components)
|
|
12
|
+
- [Factory](#factory)
|
|
13
|
+
- [Query Builder](#query-builder)
|
|
14
|
+
- [Adapters](#adapters)
|
|
15
|
+
- [Helpers](#helpers)
|
|
16
|
+
- [SQLite Subsystem](#sqlite-subsystem)
|
|
17
|
+
- [SQLite Adapter](#sqlite-adapter)
|
|
18
|
+
- [Query Translator](#query-translator)
|
|
19
|
+
- [Admin Service](#admin-service)
|
|
20
|
+
- [Next.js Integration](#nextjs-integration)
|
|
21
|
+
- [Utility Modules](#utility-modules)
|
|
22
|
+
- [Key Assumptions](#key-assumptions)
|
|
23
|
+
- [Dependencies](#dependencies)
|
|
24
|
+
- [Build Process](#build-process)
|
|
25
|
+
- [Testing Strategy](#testing-strategy)
|
|
26
|
+
- [Extension Points](#extension-points)
|
|
27
|
+
|
|
28
|
+
---
|
|
29
|
+
|
|
30
|
+
## Overview
|
|
31
|
+
|
|
32
|
+
`hazo_connect` is a database abstraction layer that provides a unified interface for interacting with different database backends. It follows the adapter pattern, where each database type (PostgREST, SQLite, Supabase, File) has its own adapter implementation that conforms to the `HazoConnectAdapter` interface.
|
|
33
|
+
|
|
34
|
+
### Design Principles
|
|
35
|
+
|
|
36
|
+
1. **Zero Dependencies in Core**: The core types and query builder have no external dependencies
|
|
37
|
+
2. **Dependency Injection**: Logger and configuration are injected, not imported
|
|
38
|
+
3. **Server-Side Only**: Runtime guards prevent client-side usage
|
|
39
|
+
4. **Type Safety**: Full TypeScript support with strict typing
|
|
40
|
+
5. **Adapter Pattern**: Each database type implements a common interface
|
|
41
|
+
|
|
42
|
+
---
|
|
43
|
+
|
|
44
|
+
## Architecture
|
|
45
|
+
|
|
46
|
+
```
|
|
47
|
+
┌─────────────────────────────────────────────────────────────────────────┐
|
|
48
|
+
│ Application Layer │
|
|
49
|
+
│ (Next.js API Routes, Server Components, Server Actions) │
|
|
50
|
+
└─────────────────────────────────────────────────────────────────────────┘
|
|
51
|
+
│
|
|
52
|
+
▼
|
|
53
|
+
┌─────────────────────────────────────────────────────────────────────────┐
|
|
54
|
+
│ Entry Points │
|
|
55
|
+
│ ┌─────────────┐ ┌─────────────┐ ┌─────────────┐ ┌─────────────┐ │
|
|
56
|
+
│ │ /server │ │ /nextjs │ │ /nextjs/ │ │ /ui │ │
|
|
57
|
+
│ │ (runtime) │ │ (helpers) │ │ setup │ │ (types) │ │
|
|
58
|
+
│ └─────────────┘ └─────────────┘ └─────────────┘ └─────────────┘ │
|
|
59
|
+
└─────────────────────────────────────────────────────────────────────────┘
|
|
60
|
+
│
|
|
61
|
+
▼
|
|
62
|
+
┌─────────────────────────────────────────────────────────────────────────┐
|
|
63
|
+
│ Core Layer │
|
|
64
|
+
│ ┌─────────────────┐ ┌─────────────────┐ ┌─────────────────┐ │
|
|
65
|
+
│ │ Factory │ │ Query Builder │ │ Helpers │ │
|
|
66
|
+
│ │ createHazoConnect│ │ QueryBuilder │ │ createCrudService│ │
|
|
67
|
+
│ └─────────────────┘ └─────────────────┘ └─────────────────┘ │
|
|
68
|
+
└─────────────────────────────────────────────────────────────────────────┘
|
|
69
|
+
│
|
|
70
|
+
▼
|
|
71
|
+
┌─────────────────────────────────────────────────────────────────────────┐
|
|
72
|
+
│ Adapter Layer │
|
|
73
|
+
│ ┌─────────────┐ ┌─────────────┐ ┌─────────────┐ ┌─────────────┐ │
|
|
74
|
+
│ │ PostgrestAd │ │ SqliteAdapt │ │ SupabaseAd │ │ FileAdapt │ │
|
|
75
|
+
│ │ apter │ │ er │ │ apter │ │ er │ │
|
|
76
|
+
│ └─────────────┘ └─────────────┘ └─────────────┘ └─────────────┘ │
|
|
77
|
+
│ │ │ │ │ │
|
|
78
|
+
│ └────────────────┴────────────────┴────────────────┘ │
|
|
79
|
+
│ extends BaseAdapter │
|
|
80
|
+
└─────────────────────────────────────────────────────────────────────────┘
|
|
81
|
+
│
|
|
82
|
+
▼
|
|
83
|
+
┌─────────────────────────────────────────────────────────────────────────┐
|
|
84
|
+
│ Database Layer │
|
|
85
|
+
│ ┌─────────────┐ ┌─────────────┐ ┌─────────────┐ ┌─────────────┐ │
|
|
86
|
+
│ │ PostgREST │ │ sql.js │ │ Supabase │ │ File System │ │
|
|
87
|
+
│ │ API │ │ (WASM) │ │ API │ │ │ │
|
|
88
|
+
│ └─────────────┘ └─────────────┘ └─────────────┘ └─────────────┘ │
|
|
89
|
+
└─────────────────────────────────────────────────────────────────────────┘
|
|
90
|
+
```
|
|
91
|
+
|
|
92
|
+
---
|
|
93
|
+
|
|
94
|
+
## File Structure
|
|
95
|
+
|
|
96
|
+
```
|
|
97
|
+
src/lib/
|
|
98
|
+
├── index.ts # Main entry point (types only)
|
|
99
|
+
├── types.ts # Core TypeScript interfaces and types
|
|
100
|
+
├── factory.ts # Factory function to create adapters
|
|
101
|
+
├── query-builder.ts # Query builder with fluent API
|
|
102
|
+
├── helpers.ts # CRUD service and query helpers
|
|
103
|
+
├── README.md # Library documentation
|
|
104
|
+
│
|
|
105
|
+
├── adapters/ # Database adapter implementations
|
|
106
|
+
│ ├── base-adapter.ts # Abstract base class for adapters
|
|
107
|
+
│ ├── postgrest-adapter.ts # PostgREST API adapter
|
|
108
|
+
│ ├── sqlite-adapter.ts # SQLite adapter (sql.js)
|
|
109
|
+
│ ├── supabase-adapter.ts # Supabase adapter (stub)
|
|
110
|
+
│ └── file-adapter.ts # File system adapter (stub)
|
|
111
|
+
│
|
|
112
|
+
├── server/ # Server-only entry point
|
|
113
|
+
│ └── index.ts # Exports with 'use server' directive
|
|
114
|
+
│
|
|
115
|
+
├── nextjs/ # Next.js-specific helpers
|
|
116
|
+
│ ├── index.ts # Main Next.js exports
|
|
117
|
+
│ ├── api-route-helpers.ts # API route handler utilities
|
|
118
|
+
│ ├── setup-helpers.ts # Environment-based setup, singleton
|
|
119
|
+
│ └── server-only.ts # Server-only guard utilities
|
|
120
|
+
│
|
|
121
|
+
├── sqlite/ # SQLite-specific modules
|
|
122
|
+
│ ├── admin-service.ts # Admin UI service functions
|
|
123
|
+
│ └── query-translator.ts # QueryBuilder to SQL translator
|
|
124
|
+
│
|
|
125
|
+
├── ui/ # UI-safe entry point
|
|
126
|
+
│ └── index.ts # Type-only exports for client components
|
|
127
|
+
│
|
|
128
|
+
├── utils/ # Utility modules
|
|
129
|
+
│ ├── config-validator.ts # Configuration validation
|
|
130
|
+
│ ├── sqlite-utils.ts # SQLite helper functions
|
|
131
|
+
│ ├── wasm-resolver.ts # WASM file resolution
|
|
132
|
+
│ └── where-builder.ts # WHERE clause builder
|
|
133
|
+
│
|
|
134
|
+
└── __tests__/ # Unit tests
|
|
135
|
+
├── factory.test.ts
|
|
136
|
+
├── postgrest_adapter.test.ts
|
|
137
|
+
├── query_builder.test.ts
|
|
138
|
+
└── sqlite_adapter.test.ts
|
|
139
|
+
|
|
140
|
+
app/ # Next.js app directory (Admin UI)
|
|
141
|
+
└── hazo_connect/
|
|
142
|
+
├── api/sqlite/ # Admin API routes
|
|
143
|
+
│ ├── data/route.ts # CRUD operations
|
|
144
|
+
│ ├── schema/route.ts # Schema retrieval
|
|
145
|
+
│ └── tables/route.ts # Table listing
|
|
146
|
+
└── sqlite_admin/ # Admin UI pages
|
|
147
|
+
├── page.tsx # Server component
|
|
148
|
+
└── sqlite-admin-client.tsx # Client component
|
|
149
|
+
|
|
150
|
+
docs/ # Documentation
|
|
151
|
+
├── examples/ # Code examples
|
|
152
|
+
├── migration-guide.md
|
|
153
|
+
├── nextjs-setup.md
|
|
154
|
+
├── troubleshooting.md
|
|
155
|
+
└── types.md
|
|
156
|
+
|
|
157
|
+
tests/ # Integration tests
|
|
158
|
+
└── integration/
|
|
159
|
+
├── postgrest.integration.test.ts
|
|
160
|
+
├── sqlite.integration.test.ts
|
|
161
|
+
└── sqlite-singleton.test.ts
|
|
162
|
+
```
|
|
163
|
+
|
|
164
|
+
---
|
|
165
|
+
|
|
166
|
+
## Entry Points
|
|
167
|
+
|
|
168
|
+
### `hazo_connect` (Main)
|
|
169
|
+
|
|
170
|
+
**File:** `src/lib/index.ts`
|
|
171
|
+
|
|
172
|
+
Exports types only. Safe for client-side imports.
|
|
173
|
+
|
|
174
|
+
```typescript
|
|
175
|
+
export type { HazoConnectConfig, HazoConnectAdapter, Logger, ... }
|
|
176
|
+
```
|
|
177
|
+
|
|
178
|
+
### `hazo_connect/server`
|
|
179
|
+
|
|
180
|
+
**File:** `src/lib/server/index.ts`
|
|
181
|
+
|
|
182
|
+
Server-side runtime exports with `'use server'` directive.
|
|
183
|
+
|
|
184
|
+
```typescript
|
|
185
|
+
export { createHazoConnect, QueryBuilder, createCrudService, ... }
|
|
186
|
+
export type { HazoConnectConfig, CrudService, ... }
|
|
187
|
+
```
|
|
188
|
+
|
|
189
|
+
**Runtime Guard:** Throws error if imported in browser environment.
|
|
190
|
+
|
|
191
|
+
### `hazo_connect/nextjs`
|
|
192
|
+
|
|
193
|
+
**File:** `src/lib/nextjs/index.ts`
|
|
194
|
+
|
|
195
|
+
Next.js-specific helpers for API routes.
|
|
196
|
+
|
|
197
|
+
```typescript
|
|
198
|
+
export { createApiRouteHandler, getServerHazoConnect }
|
|
199
|
+
```
|
|
200
|
+
|
|
201
|
+
### `hazo_connect/nextjs/setup`
|
|
202
|
+
|
|
203
|
+
**File:** `src/lib/nextjs/setup-helpers.ts`
|
|
204
|
+
|
|
205
|
+
Environment-based configuration and singleton pattern.
|
|
206
|
+
|
|
207
|
+
```typescript
|
|
208
|
+
export { createHazoConnectFromEnv, getHazoConnectSingleton }
|
|
209
|
+
```
|
|
210
|
+
|
|
211
|
+
### `hazo_connect/ui`
|
|
212
|
+
|
|
213
|
+
**File:** `src/lib/ui/index.ts`
|
|
214
|
+
|
|
215
|
+
UI-safe types for client components.
|
|
216
|
+
|
|
217
|
+
```typescript
|
|
218
|
+
export type { TableSummary, TableSchema, SqliteFilterOperator, ... }
|
|
219
|
+
```
|
|
220
|
+
|
|
221
|
+
---
|
|
222
|
+
|
|
223
|
+
## Core Components
|
|
224
|
+
|
|
225
|
+
### Factory
|
|
226
|
+
|
|
227
|
+
**File:** `src/lib/factory.ts`
|
|
228
|
+
|
|
229
|
+
The `createHazoConnect` function is the main entry point for creating adapter instances.
|
|
230
|
+
|
|
231
|
+
**Responsibilities:**
|
|
232
|
+
- Parse and validate configuration
|
|
233
|
+
- Extract configuration from ConfigProvider if provided
|
|
234
|
+
- Resolve environment variables for paths and credentials
|
|
235
|
+
- Instantiate the appropriate adapter based on `type`
|
|
236
|
+
- Register SQLite adapters with admin service when `enable_admin_ui` is true
|
|
237
|
+
|
|
238
|
+
**Flow:**
|
|
239
|
+
```
|
|
240
|
+
createHazoConnect(config)
|
|
241
|
+
│
|
|
242
|
+
├─── Validate type
|
|
243
|
+
│
|
|
244
|
+
├─── Extract config from ConfigProvider or direct config
|
|
245
|
+
│
|
|
246
|
+
├─── Resolve environment variables (paths, API keys)
|
|
247
|
+
│
|
|
248
|
+
├─── Switch on type:
|
|
249
|
+
│ ├─── 'postgrest' → new PostgrestAdapter(...)
|
|
250
|
+
│ ├─── 'sqlite' → new SqliteAdapter(...)
|
|
251
|
+
│ ├─── 'supabase' → new SupabaseAdapter(...)
|
|
252
|
+
│ └─── 'file' → new FileAdapter(...)
|
|
253
|
+
│
|
|
254
|
+
└─── If SQLite + enable_admin_ui → registerSqliteAdapter(adapter)
|
|
255
|
+
```
|
|
256
|
+
|
|
257
|
+
### Query Builder
|
|
258
|
+
|
|
259
|
+
**File:** `src/lib/query-builder.ts`
|
|
260
|
+
|
|
261
|
+
Fluent API for building database queries.
|
|
262
|
+
|
|
263
|
+
**State:**
|
|
264
|
+
- `_table`: Target table name
|
|
265
|
+
- `_selectFields`: Fields to select
|
|
266
|
+
- `_whereConditions`: WHERE conditions (AND)
|
|
267
|
+
- `_whereOrConditions`: OR condition groups
|
|
268
|
+
- `_orderBy`: ORDER BY clauses
|
|
269
|
+
- `_limitValue`: LIMIT value
|
|
270
|
+
- `_offsetValue`: OFFSET value
|
|
271
|
+
- `_joins`: JOIN clauses
|
|
272
|
+
- `_nestedSelects`: Nested select for PostgREST
|
|
273
|
+
|
|
274
|
+
**Methods:**
|
|
275
|
+
```typescript
|
|
276
|
+
from(table: string): this
|
|
277
|
+
select(fields: string | string[]): this
|
|
278
|
+
where(field: string, operator: QueryOperator, value: any): this
|
|
279
|
+
whereIn(field: string, values: any[]): this
|
|
280
|
+
whereOr(conditions: Array<{...}>): this
|
|
281
|
+
order(field: string, direction: OrderDirection): this
|
|
282
|
+
limit(count: number): this
|
|
283
|
+
offset(count: number): this
|
|
284
|
+
join(table: string, on: string, type: JoinType): this
|
|
285
|
+
nestedSelect(table: string, fields: string[]): this
|
|
286
|
+
clone(): QueryBuilder
|
|
287
|
+
```
|
|
288
|
+
|
|
289
|
+
### Adapters
|
|
290
|
+
|
|
291
|
+
**Base Class:** `src/lib/adapters/base-adapter.ts`
|
|
292
|
+
|
|
293
|
+
Abstract base class providing common functionality:
|
|
294
|
+
- Configuration storage
|
|
295
|
+
- Logger integration
|
|
296
|
+
- Error handling utilities
|
|
297
|
+
- Query logging
|
|
298
|
+
|
|
299
|
+
**Interface:** `HazoConnectAdapter`
|
|
300
|
+
```typescript
|
|
301
|
+
interface HazoConnectAdapter {
|
|
302
|
+
query(builder: QueryBuilder, method?: string, body?: any): Promise<any>
|
|
303
|
+
rawQuery(endpoint: string, options?: RequestInit): Promise<any>
|
|
304
|
+
getConfig(): Promise<any>
|
|
305
|
+
}
|
|
306
|
+
```
|
|
307
|
+
|
|
308
|
+
#### PostgrestAdapter
|
|
309
|
+
|
|
310
|
+
**File:** `src/lib/adapters/postgrest-adapter.ts`
|
|
311
|
+
|
|
312
|
+
Translates QueryBuilder to PostgREST URL syntax and makes HTTP requests.
|
|
313
|
+
|
|
314
|
+
**URL Building:**
|
|
315
|
+
- `from('users')` → `/users`
|
|
316
|
+
- `where('id', 'eq', '123')` → `?id=eq.123`
|
|
317
|
+
- `select(['id', 'name'])` → `?select=id,name`
|
|
318
|
+
- `nestedSelect('posts', ['title'])` → `?select=*,posts(title)`
|
|
319
|
+
|
|
320
|
+
#### SqliteAdapter
|
|
321
|
+
|
|
322
|
+
**File:** `src/lib/adapters/sqlite-adapter.ts`
|
|
323
|
+
|
|
324
|
+
Uses sql.js (WebAssembly SQLite) for local database operations.
|
|
325
|
+
|
|
326
|
+
**Key Features:**
|
|
327
|
+
- In-memory and file-backed databases
|
|
328
|
+
- Read-only mode support
|
|
329
|
+
- Initial SQL execution for schema setup
|
|
330
|
+
- Automatic persistence after writes
|
|
331
|
+
- WASM file resolution
|
|
332
|
+
|
|
333
|
+
**Initialization Flow:**
|
|
334
|
+
```
|
|
335
|
+
constructor()
|
|
336
|
+
│
|
|
337
|
+
├─── normalizeConfig()
|
|
338
|
+
│
|
|
339
|
+
├─── resolveWasmDirectory()
|
|
340
|
+
│
|
|
341
|
+
├─── loadSqlJs() → Promise<SqlJsStatic>
|
|
342
|
+
│
|
|
343
|
+
└─── initializeDatabase() → Promise<SqlJsDatabase>
|
|
344
|
+
│
|
|
345
|
+
├─── If file exists → Load from file
|
|
346
|
+
│
|
|
347
|
+
├─── Else → Create new database
|
|
348
|
+
│ │
|
|
349
|
+
│ └─── Execute initial_sql if provided
|
|
350
|
+
│
|
|
351
|
+
└─── If database_path → Persist to file
|
|
352
|
+
```
|
|
353
|
+
|
|
354
|
+
### Helpers
|
|
355
|
+
|
|
356
|
+
**File:** `src/lib/helpers.ts`
|
|
357
|
+
|
|
358
|
+
#### createCrudService
|
|
359
|
+
|
|
360
|
+
Creates a CRUD service wrapper for a table:
|
|
361
|
+
|
|
362
|
+
```typescript
|
|
363
|
+
function createCrudService<T>(
|
|
364
|
+
adapter: HazoConnectAdapter,
|
|
365
|
+
table: string,
|
|
366
|
+
options?: { primaryKeys?: string[]; logger?: Logger }
|
|
367
|
+
): CrudService<T>
|
|
368
|
+
```
|
|
369
|
+
|
|
370
|
+
**Methods:**
|
|
371
|
+
- `list()`: Get all records
|
|
372
|
+
- `findBy(criteria)`: Find by criteria
|
|
373
|
+
- `findOneBy(criteria)`: Find single record
|
|
374
|
+
- `findById(id)`: Find by primary key
|
|
375
|
+
- `insert(data)`: Insert record(s)
|
|
376
|
+
- `updateById(id, patch)`: Update by primary key
|
|
377
|
+
- `deleteById(id)`: Delete by primary key
|
|
378
|
+
- `query()`: Get executable query builder
|
|
379
|
+
|
|
380
|
+
#### attachExecute
|
|
381
|
+
|
|
382
|
+
Attaches `execute()` method to a QueryBuilder:
|
|
383
|
+
|
|
384
|
+
```typescript
|
|
385
|
+
function attachExecute(
|
|
386
|
+
builder: QueryBuilder,
|
|
387
|
+
adapter: HazoConnectAdapter,
|
|
388
|
+
logger?: Logger
|
|
389
|
+
): ExecutableQueryBuilder
|
|
390
|
+
```
|
|
391
|
+
|
|
392
|
+
---
|
|
393
|
+
|
|
394
|
+
## SQLite Subsystem
|
|
395
|
+
|
|
396
|
+
### SQLite Adapter
|
|
397
|
+
|
|
398
|
+
**File:** `src/lib/adapters/sqlite-adapter.ts`
|
|
399
|
+
|
|
400
|
+
**Query Execution Flow:**
|
|
401
|
+
```
|
|
402
|
+
query(builder, method, body)
|
|
403
|
+
│
|
|
404
|
+
├─── GET → executeSelect(builder)
|
|
405
|
+
│ │
|
|
406
|
+
│ └─── translateSelect(builder) → SQL + params
|
|
407
|
+
│ │
|
|
408
|
+
│ └─── executeStatements(database, [translation])
|
|
409
|
+
│
|
|
410
|
+
├─── POST → executeInsert(builder, body)
|
|
411
|
+
│ │
|
|
412
|
+
│ └─── translateInsert(builder, payload) → SQL statements
|
|
413
|
+
│ │
|
|
414
|
+
│ └─── executeStatements() → persistDatabase()
|
|
415
|
+
│
|
|
416
|
+
├─── PATCH/PUT → executeUpdate(builder, body)
|
|
417
|
+
│ │
|
|
418
|
+
│ └─── translateUpdate(builder, updates) → SQL
|
|
419
|
+
│ │
|
|
420
|
+
│ └─── executeStatements() → persistDatabase()
|
|
421
|
+
│
|
|
422
|
+
└─── DELETE → executeDelete(builder)
|
|
423
|
+
│
|
|
424
|
+
└─── translateDelete(builder) → SQL
|
|
425
|
+
│
|
|
426
|
+
└─── executeStatements() → persistDatabase()
|
|
427
|
+
```
|
|
428
|
+
|
|
429
|
+
### Query Translator
|
|
430
|
+
|
|
431
|
+
**File:** `src/lib/sqlite/query-translator.ts`
|
|
432
|
+
|
|
433
|
+
Translates QueryBuilder state to parameterized SQL statements.
|
|
434
|
+
|
|
435
|
+
**Functions:**
|
|
436
|
+
- `translateSelect(builder)`: SELECT query
|
|
437
|
+
- `translateInsert(builder, payload)`: INSERT statement(s)
|
|
438
|
+
- `translateUpdate(builder, updates)`: UPDATE statement
|
|
439
|
+
- `translateDelete(builder)`: DELETE statement
|
|
440
|
+
|
|
441
|
+
**SQL Generation:**
|
|
442
|
+
```typescript
|
|
443
|
+
// QueryBuilder state
|
|
444
|
+
builder.from('users').where('status', 'eq', 'active').limit(10)
|
|
445
|
+
|
|
446
|
+
// Translated SQL
|
|
447
|
+
{
|
|
448
|
+
sql: 'SELECT * FROM "users" WHERE "status" = ? LIMIT 10',
|
|
449
|
+
params: ['active']
|
|
450
|
+
}
|
|
451
|
+
```
|
|
452
|
+
|
|
453
|
+
### Admin Service
|
|
454
|
+
|
|
455
|
+
**File:** `src/lib/sqlite/admin-service.ts`
|
|
456
|
+
|
|
457
|
+
Provides admin functionality for the SQLite Admin UI.
|
|
458
|
+
|
|
459
|
+
**Singleton Pattern:**
|
|
460
|
+
- `registeredAdapter`: Adapter registered by factory/singleton
|
|
461
|
+
- `cachedAdapter`: Fallback adapter from environment variables
|
|
462
|
+
- `adminUiEnabled`: Flag to enable/disable admin UI
|
|
463
|
+
|
|
464
|
+
**Functions:**
|
|
465
|
+
```typescript
|
|
466
|
+
getSqliteAdminService(): SqliteAdminService
|
|
467
|
+
initializeAdminService(config): void
|
|
468
|
+
registerSqliteAdapter(adapter): void
|
|
469
|
+
clearRegisteredAdapter(): void // For testing
|
|
470
|
+
```
|
|
471
|
+
|
|
472
|
+
**Service Methods:**
|
|
473
|
+
- `listTables()`: List all tables and views
|
|
474
|
+
- `getTableSchema(table)`: Get column and FK definitions
|
|
475
|
+
- `getTableData(table, options)`: Paginated data retrieval
|
|
476
|
+
- `insertRow(table, data)`: Insert single row
|
|
477
|
+
- `updateRows(table, criteria, data)`: Update matching rows
|
|
478
|
+
- `deleteRows(table, criteria)`: Delete matching rows
|
|
479
|
+
|
|
480
|
+
---
|
|
481
|
+
|
|
482
|
+
## Next.js Integration
|
|
483
|
+
|
|
484
|
+
### Setup Helpers
|
|
485
|
+
|
|
486
|
+
**File:** `src/lib/nextjs/setup-helpers.ts`
|
|
487
|
+
|
|
488
|
+
#### createHazoConnectFromEnv
|
|
489
|
+
|
|
490
|
+
Creates adapter from environment variables:
|
|
491
|
+
|
|
492
|
+
```typescript
|
|
493
|
+
function createHazoConnectFromEnv(options?: {
|
|
494
|
+
type?: 'sqlite' | 'postgrest' | 'supabase' | 'file'
|
|
495
|
+
sqlitePath?: string
|
|
496
|
+
readOnly?: boolean
|
|
497
|
+
enableAdminUi?: boolean
|
|
498
|
+
logger?: Logger
|
|
499
|
+
}): HazoConnectAdapter
|
|
500
|
+
```
|
|
501
|
+
|
|
502
|
+
**Environment Variable Resolution:**
|
|
503
|
+
1. Check option overrides
|
|
504
|
+
2. Fall back to `HAZO_CONNECT_*` variables
|
|
505
|
+
3. Fall back to legacy variable names
|
|
506
|
+
4. Apply defaults
|
|
507
|
+
|
|
508
|
+
#### getHazoConnectSingleton
|
|
509
|
+
|
|
510
|
+
Creates/returns singleton adapter instance:
|
|
511
|
+
|
|
512
|
+
```typescript
|
|
513
|
+
function getHazoConnectSingleton(options?: CreateHazoConnectFromEnvOptions): HazoConnectAdapter
|
|
514
|
+
```
|
|
515
|
+
|
|
516
|
+
**Singleton Behavior:**
|
|
517
|
+
- Creates adapter on first call
|
|
518
|
+
- Returns cached instance on subsequent calls
|
|
519
|
+
- Recreates if configuration signature changes
|
|
520
|
+
- Registers SQLite adapter with admin service
|
|
521
|
+
|
|
522
|
+
### API Route Helpers
|
|
523
|
+
|
|
524
|
+
**File:** `src/lib/nextjs/api-route-helpers.ts`
|
|
525
|
+
|
|
526
|
+
#### createApiRouteHandler
|
|
527
|
+
|
|
528
|
+
Wraps handler with automatic adapter initialization:
|
|
529
|
+
|
|
530
|
+
```typescript
|
|
531
|
+
function createApiRouteHandler(
|
|
532
|
+
handler: (hazo: HazoConnectAdapter, request: NextRequest) => Promise<Response>,
|
|
533
|
+
options?: {
|
|
534
|
+
config?: ApiRouteHazoConfig
|
|
535
|
+
getConfig?: (request: NextRequest) => ApiRouteHazoConfig
|
|
536
|
+
logger?: Logger
|
|
537
|
+
}
|
|
538
|
+
): (request: NextRequest) => Promise<Response>
|
|
539
|
+
```
|
|
540
|
+
|
|
541
|
+
#### getServerHazoConnect
|
|
542
|
+
|
|
543
|
+
Creates adapter for manual use in API routes:
|
|
544
|
+
|
|
545
|
+
```typescript
|
|
546
|
+
function getServerHazoConnect(config: ApiRouteHazoConfig, logger?: Logger): HazoConnectAdapter
|
|
547
|
+
```
|
|
548
|
+
|
|
549
|
+
---
|
|
550
|
+
|
|
551
|
+
## Utility Modules
|
|
552
|
+
|
|
553
|
+
### Config Validator
|
|
554
|
+
|
|
555
|
+
**File:** `src/lib/utils/config-validator.ts`
|
|
556
|
+
|
|
557
|
+
Validates Next.js configuration for SQLite compatibility.
|
|
558
|
+
|
|
559
|
+
```typescript
|
|
560
|
+
function validateNextJsConfig(): ValidationResult
|
|
561
|
+
function getConfigurationTips(): string[]
|
|
562
|
+
```
|
|
563
|
+
|
|
564
|
+
### SQLite Utils
|
|
565
|
+
|
|
566
|
+
**File:** `src/lib/utils/sqlite-utils.ts`
|
|
567
|
+
|
|
568
|
+
Helper functions for SQLite operations:
|
|
569
|
+
|
|
570
|
+
```typescript
|
|
571
|
+
function quoteIdentifier(name: string): string
|
|
572
|
+
function normalizeValue(value: unknown): unknown
|
|
573
|
+
function sanitizeTableName(table: string): string
|
|
574
|
+
function isPlainObject(value: unknown): boolean
|
|
575
|
+
```
|
|
576
|
+
|
|
577
|
+
### WASM Resolver
|
|
578
|
+
|
|
579
|
+
**File:** `src/lib/utils/wasm-resolver.ts`
|
|
580
|
+
|
|
581
|
+
Resolves sql-wasm.wasm file location:
|
|
582
|
+
|
|
583
|
+
```typescript
|
|
584
|
+
function resolveWasmDirectory(config: SqliteAdapterOptions): string
|
|
585
|
+
function createSqlJsConfig(wasmDirectory: string): SqlJsConfig
|
|
586
|
+
```
|
|
587
|
+
|
|
588
|
+
**Resolution Order:**
|
|
589
|
+
1. Explicit `wasm_directory` in config
|
|
590
|
+
2. `HAZO_CONNECT_SQLITE_WASM_DIR` environment variable
|
|
591
|
+
3. Auto-detect from node_modules
|
|
592
|
+
|
|
593
|
+
### Where Builder
|
|
594
|
+
|
|
595
|
+
**File:** `src/lib/utils/where-builder.ts`
|
|
596
|
+
|
|
597
|
+
Builds WHERE clauses from filter specifications:
|
|
598
|
+
|
|
599
|
+
```typescript
|
|
600
|
+
function buildWhereClause(filters: SqliteWhereFilter[]): { clause: string; params: unknown[] }
|
|
601
|
+
function buildFiltersFromCriteria(criteria: Record<string, unknown>): SqliteWhereFilter[]
|
|
602
|
+
```
|
|
603
|
+
|
|
604
|
+
---
|
|
605
|
+
|
|
606
|
+
## Key Assumptions
|
|
607
|
+
|
|
608
|
+
### Database Assumptions
|
|
609
|
+
|
|
610
|
+
1. **SQLite**: Uses sql.js (WebAssembly), not native SQLite bindings
|
|
611
|
+
2. **PostgREST**: Assumes standard PostgREST API format
|
|
612
|
+
3. **Primary Keys**: Defaults to `id` column if not specified
|
|
613
|
+
4. **Table Names**: Must be valid SQL identifiers
|
|
614
|
+
|
|
615
|
+
### Environment Assumptions
|
|
616
|
+
|
|
617
|
+
1. **Server-Side Only**: All runtime code assumes Node.js environment
|
|
618
|
+
2. **Next.js 14+**: Next.js helpers assume App Router
|
|
619
|
+
3. **File System Access**: SQLite file persistence requires write access
|
|
620
|
+
4. **WASM Support**: sql.js requires WebAssembly support
|
|
621
|
+
|
|
622
|
+
### Configuration Assumptions
|
|
623
|
+
|
|
624
|
+
1. **Environment Variables**: Loaded via dotenv from `.env.local`
|
|
625
|
+
2. **Path Resolution**: Relative paths resolved from `process.cwd()`
|
|
626
|
+
3. **Admin UI Disabled**: By default for security
|
|
627
|
+
|
|
628
|
+
---
|
|
629
|
+
|
|
630
|
+
## Dependencies
|
|
631
|
+
|
|
632
|
+
### Production Dependencies
|
|
633
|
+
|
|
634
|
+
| Package | Version | Purpose |
|
|
635
|
+
|---------|---------|---------|
|
|
636
|
+
| `dotenv` | ^16.4.5 | Environment variable loading |
|
|
637
|
+
| `hazo_config` | ^1.3.0 | Configuration provider interface |
|
|
638
|
+
| `sql.js` | ^1.13.0 | WebAssembly SQLite implementation |
|
|
639
|
+
|
|
640
|
+
### Peer Dependencies (Optional)
|
|
641
|
+
|
|
642
|
+
| Package | Version | Purpose |
|
|
643
|
+
|---------|---------|---------|
|
|
644
|
+
| `next` | >=14.0.0 | Next.js framework (Admin UI) |
|
|
645
|
+
| `react` | >=18.0.0 | React library (Admin UI) |
|
|
646
|
+
| `react-dom` | >=18.0.0 | React DOM (Admin UI) |
|
|
647
|
+
| `lucide-react` | ^0.553.0 | Icons (Admin UI) |
|
|
648
|
+
| `sonner` | ^2.0.7 | Toast notifications (Admin UI) |
|
|
649
|
+
|
|
650
|
+
### Development Dependencies
|
|
651
|
+
|
|
652
|
+
- TypeScript 5.5+
|
|
653
|
+
- Jest 30+ for testing
|
|
654
|
+
- Next.js 14+ for development server
|
|
655
|
+
- Storybook for component development
|
|
656
|
+
|
|
657
|
+
---
|
|
658
|
+
|
|
659
|
+
## Build Process
|
|
660
|
+
|
|
661
|
+
### Library Build
|
|
662
|
+
|
|
663
|
+
```bash
|
|
664
|
+
npm run build:lib
|
|
665
|
+
```
|
|
666
|
+
|
|
667
|
+
Uses `tsconfig.lib.json`:
|
|
668
|
+
- Compiles `src/lib/**/*.ts` to `dist/`
|
|
669
|
+
- Generates `.d.ts` type declarations
|
|
670
|
+
- Excludes tests and app directory
|
|
671
|
+
|
|
672
|
+
### Next.js Build
|
|
673
|
+
|
|
674
|
+
```bash
|
|
675
|
+
npm run build
|
|
676
|
+
```
|
|
677
|
+
|
|
678
|
+
Builds the Next.js application including Admin UI.
|
|
679
|
+
|
|
680
|
+
### Package Exports
|
|
681
|
+
|
|
682
|
+
Defined in `package.json`:
|
|
683
|
+
|
|
684
|
+
```json
|
|
685
|
+
{
|
|
686
|
+
"exports": {
|
|
687
|
+
".": { "types": "./dist/index.d.ts", "default": "./dist/index.js" },
|
|
688
|
+
"./server": { "types": "./dist/server/index.d.ts", "default": "./dist/server/index.js" },
|
|
689
|
+
"./nextjs": { "types": "./dist/nextjs/index.d.ts", "default": "./dist/nextjs/index.js" },
|
|
690
|
+
"./nextjs/setup": { "types": "./dist/nextjs/setup-helpers.d.ts", "default": "./dist/nextjs/setup-helpers.js" },
|
|
691
|
+
"./ui": { "types": "./dist/ui/index.d.ts", "default": "./dist/ui/index.js" }
|
|
692
|
+
}
|
|
693
|
+
}
|
|
694
|
+
```
|
|
695
|
+
|
|
696
|
+
---
|
|
697
|
+
|
|
698
|
+
## Testing Strategy
|
|
699
|
+
|
|
700
|
+
### Unit Tests
|
|
701
|
+
|
|
702
|
+
**Location:** `src/lib/__tests__/`
|
|
703
|
+
|
|
704
|
+
- `factory.test.ts`: Factory function tests
|
|
705
|
+
- `query_builder.test.ts`: QueryBuilder API tests
|
|
706
|
+
- `postgrest_adapter.test.ts`: PostgREST adapter tests
|
|
707
|
+
- `sqlite_adapter.test.ts`: SQLite adapter tests
|
|
708
|
+
|
|
709
|
+
### Integration Tests
|
|
710
|
+
|
|
711
|
+
**Location:** `tests/integration/`
|
|
712
|
+
|
|
713
|
+
- `sqlite.integration.test.ts`: SQLite CRUD operations
|
|
714
|
+
- `sqlite-singleton.test.ts`: Singleton pattern and admin service
|
|
715
|
+
- `postgrest.integration.test.ts`: PostgREST API integration
|
|
716
|
+
|
|
717
|
+
### Running Tests
|
|
718
|
+
|
|
719
|
+
```bash
|
|
720
|
+
# All tests
|
|
721
|
+
npm test
|
|
722
|
+
|
|
723
|
+
# Watch mode
|
|
724
|
+
npm run test:watch
|
|
725
|
+
|
|
726
|
+
# Coverage
|
|
727
|
+
npm run test:coverage
|
|
728
|
+
|
|
729
|
+
# Integration only
|
|
730
|
+
npm run test:integration:sqlite
|
|
731
|
+
```
|
|
732
|
+
|
|
733
|
+
---
|
|
734
|
+
|
|
735
|
+
## Extension Points
|
|
736
|
+
|
|
737
|
+
### Adding a New Adapter
|
|
738
|
+
|
|
739
|
+
1. Create adapter class extending `BaseAdapter`:
|
|
740
|
+
|
|
741
|
+
```typescript
|
|
742
|
+
// src/lib/adapters/new-adapter.ts
|
|
743
|
+
export class NewAdapter extends BaseAdapter implements HazoConnectAdapter {
|
|
744
|
+
async query(builder: QueryBuilder, method?: string, body?: any): Promise<any> {
|
|
745
|
+
// Implementation
|
|
746
|
+
}
|
|
747
|
+
|
|
748
|
+
async rawQuery(endpoint: string, options?: RequestInit): Promise<any> {
|
|
749
|
+
// Implementation
|
|
750
|
+
}
|
|
751
|
+
|
|
752
|
+
async getConfig(): Promise<any> {
|
|
753
|
+
return { ...this.config }
|
|
754
|
+
}
|
|
755
|
+
}
|
|
756
|
+
```
|
|
757
|
+
|
|
758
|
+
2. Add to factory:
|
|
759
|
+
|
|
760
|
+
```typescript
|
|
761
|
+
// src/lib/factory.ts
|
|
762
|
+
case 'new':
|
|
763
|
+
adapter = new NewAdapter(providerConfig, logger)
|
|
764
|
+
break
|
|
765
|
+
```
|
|
766
|
+
|
|
767
|
+
3. Update types:
|
|
768
|
+
|
|
769
|
+
```typescript
|
|
770
|
+
// src/lib/types.ts
|
|
771
|
+
export type ConnectionType = 'postgrest' | 'supabase' | 'sqlite' | 'file' | 'new'
|
|
772
|
+
```
|
|
773
|
+
|
|
774
|
+
4. Export from server entry point:
|
|
775
|
+
|
|
776
|
+
```typescript
|
|
777
|
+
// src/lib/server/index.ts
|
|
778
|
+
export { NewAdapter } from '../adapters/new-adapter'
|
|
779
|
+
```
|
|
780
|
+
|
|
781
|
+
### Adding New Query Operators
|
|
782
|
+
|
|
783
|
+
1. Update types:
|
|
784
|
+
|
|
785
|
+
```typescript
|
|
786
|
+
// src/lib/types.ts
|
|
787
|
+
export type QueryOperator = 'eq' | 'neq' | ... | 'new_operator'
|
|
788
|
+
```
|
|
789
|
+
|
|
790
|
+
2. Update query translator (for SQLite):
|
|
791
|
+
|
|
792
|
+
```typescript
|
|
793
|
+
// src/lib/sqlite/query-translator.ts
|
|
794
|
+
function translateOperator(operator: QueryOperator, value: unknown): string {
|
|
795
|
+
switch (operator) {
|
|
796
|
+
case 'new_operator':
|
|
797
|
+
return 'NEW_SQL_SYNTAX ?'
|
|
798
|
+
// ...
|
|
799
|
+
}
|
|
800
|
+
}
|
|
801
|
+
```
|
|
802
|
+
|
|
803
|
+
3. Update PostgREST adapter (for PostgREST):
|
|
804
|
+
|
|
805
|
+
```typescript
|
|
806
|
+
// src/lib/adapters/postgrest-adapter.ts
|
|
807
|
+
// Add operator to URL building logic
|
|
808
|
+
```
|
|
809
|
+
|
|
810
|
+
---
|
|
811
|
+
|
|
812
|
+
## Version History
|
|
813
|
+
|
|
814
|
+
See [CHANGELOG.md](../CHANGELOG.md) for version history.
|
|
815
|
+
|
|
816
|
+
---
|
|
817
|
+
|
|
818
|
+
## Contributing
|
|
819
|
+
|
|
820
|
+
1. Follow existing code style and conventions
|
|
821
|
+
2. Add tests for new functionality
|
|
822
|
+
3. Update documentation for API changes
|
|
823
|
+
4. Run `npm run build:lib` and `npm test` before submitting
|
|
824
|
+
|