@promakeai/dbreact 1.0.3 → 1.0.4
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/README.md +122 -106
- package/dist/adapters/SqliteAdapter.d.ts +3 -0
- package/dist/adapters/SqliteAdapter.d.ts.map +1 -1
- package/dist/index.js +24 -1
- package/package.json +2 -2
package/README.md
CHANGED
|
@@ -4,12 +4,12 @@ React hooks and providers for schema-driven, multi-language databases. Type-safe
|
|
|
4
4
|
|
|
5
5
|
## Features
|
|
6
6
|
|
|
7
|
-
- **Type-Safe Hooks** -
|
|
7
|
+
- **Type-Safe Hooks** - Generic React hooks with generated TypeScript types
|
|
8
8
|
- **Multi-Language Support** - Automatic translation queries with fallback
|
|
9
9
|
- **Browser SQLite** - sql.js WASM adapter for offline-first apps
|
|
10
|
-
- **React Query Integration** - Built-in caching, loading states, optimistic updates
|
|
11
|
-
- **Zero-Config Language Switching** - Change language, queries refetch automatically
|
|
12
|
-
- **MongoDB-Style Queries** - Intuitive filter syntax (`$gt`, `$in`, `$like`, etc.)
|
|
10
|
+
- **React Query Integration** - Built-in caching, loading states, optimistic updates
|
|
11
|
+
- **Zero-Config Language Switching** - Change language, queries refetch automatically
|
|
12
|
+
- **MongoDB-Style Queries** - Intuitive filter syntax (`$gt`, `$in`, `$like`, `$contains`, etc.)
|
|
13
13
|
|
|
14
14
|
## Installation
|
|
15
15
|
|
|
@@ -26,11 +26,16 @@ npm install @promakeai/dbreact @tanstack/react-query
|
|
|
26
26
|
### 1. Setup Adapter
|
|
27
27
|
|
|
28
28
|
```tsx
|
|
29
|
-
import { SqliteAdapter } from '@promakeai/dbreact';
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
29
|
+
import { SqliteAdapter } from '@promakeai/dbreact';
|
|
30
|
+
import { parseJSONSchema } from '@promakeai/dbreact';
|
|
31
|
+
import schemaJson from './schema.json';
|
|
32
|
+
|
|
33
|
+
const schema = parseJSONSchema(schemaJson as any);
|
|
34
|
+
|
|
35
|
+
const adapter = new SqliteAdapter({
|
|
36
|
+
storageKey: 'myapp-db', // localStorage key for persistence
|
|
37
|
+
schema,
|
|
38
|
+
});
|
|
34
39
|
```
|
|
35
40
|
|
|
36
41
|
### 2. Wrap App with Provider
|
|
@@ -98,24 +103,27 @@ Main provider component that wraps your application.
|
|
|
98
103
|
|
|
99
104
|
```tsx
|
|
100
105
|
<DbProvider
|
|
101
|
-
adapter={adapter}
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
+
adapter={adapter}
|
|
107
|
+
schema={schema}
|
|
108
|
+
lang="tr"
|
|
109
|
+
fallbackLang="en"
|
|
110
|
+
autoConnect={true}
|
|
111
|
+
>
|
|
106
112
|
<App />
|
|
107
113
|
</DbProvider>
|
|
108
114
|
```
|
|
109
115
|
|
|
110
116
|
**Props:**
|
|
111
117
|
|
|
112
|
-
| Prop | Type | Required | Default | Description |
|
|
113
|
-
|------|------|----------|---------|-------------|
|
|
114
|
-
| `adapter` | `IDataAdapter` | Yes | - | Database adapter instance |
|
|
115
|
-
| `
|
|
116
|
-
| `
|
|
117
|
-
| `
|
|
118
|
-
| `
|
|
118
|
+
| Prop | Type | Required | Default | Description |
|
|
119
|
+
|------|------|----------|---------|-------------|
|
|
120
|
+
| `adapter` | `IDataAdapter` | Yes | - | Database adapter instance |
|
|
121
|
+
| `schema` | `SchemaDefinition` | No | - | Enables populate + typed serialization |
|
|
122
|
+
| `lang` | `string` | No | `'en'` | Current language code |
|
|
123
|
+
| `fallbackLang` | `string` | No | `'en'` | Fallback language |
|
|
124
|
+
| `autoConnect` | `boolean` | No | `true` | Auto-connect on mount |
|
|
125
|
+
| `queryClient` | `QueryClient` | No | Internal | Provide a custom React Query client |
|
|
126
|
+
| `children` | `ReactNode` | Yes | - | Child components |
|
|
119
127
|
|
|
120
128
|
---
|
|
121
129
|
|
|
@@ -143,19 +151,29 @@ const { data, isLoading, error, refetch } = useDbList<Product>('products', {
|
|
|
143
151
|
| `orderBy` | `array` | Sort order `[{ field, direction }]` |
|
|
144
152
|
| `limit` | `number` | Max records to return |
|
|
145
153
|
| `offset` | `number` | Skip records |
|
|
146
|
-
| `populate` | `
|
|
154
|
+
| `populate` | `PopulateOption` | Resolve foreign key references (string, array, or object) |
|
|
147
155
|
| `enabled` | `boolean` | Enable/disable query |
|
|
148
156
|
|
|
149
|
-
#### useDbGet
|
|
150
|
-
|
|
151
|
-
Fetch single record by ID.
|
|
152
|
-
|
|
153
|
-
```tsx
|
|
154
|
-
const { data: product, isLoading } = useDbGet<Product>('products', productId, {
|
|
155
|
-
populate: ['categoryId'],
|
|
156
|
-
enabled: !!productId,
|
|
157
|
-
});
|
|
158
|
-
```
|
|
157
|
+
#### useDbGet
|
|
158
|
+
|
|
159
|
+
Fetch single record by ID.
|
|
160
|
+
|
|
161
|
+
```tsx
|
|
162
|
+
const { data: product, isLoading } = useDbGet<Product>('products', productId, {
|
|
163
|
+
populate: ['categoryId'],
|
|
164
|
+
enabled: !!productId,
|
|
165
|
+
});
|
|
166
|
+
```
|
|
167
|
+
|
|
168
|
+
Find-one style (by where clause):
|
|
169
|
+
|
|
170
|
+
```tsx
|
|
171
|
+
const { data: product } = useDbGet<Product>('products', {
|
|
172
|
+
where: { slug: 'my-product' },
|
|
173
|
+
enabled: !!slug,
|
|
174
|
+
populate: { categoryId: true },
|
|
175
|
+
});
|
|
176
|
+
```
|
|
159
177
|
|
|
160
178
|
---
|
|
161
179
|
|
|
@@ -182,13 +200,13 @@ createProduct.mutate(
|
|
|
182
200
|
Update an existing record.
|
|
183
201
|
|
|
184
202
|
```tsx
|
|
185
|
-
const updateProduct = useDbUpdate<Product>('products'
|
|
186
|
-
|
|
187
|
-
updateProduct.mutate(
|
|
188
|
-
{ price: 89.99 },
|
|
189
|
-
{
|
|
190
|
-
onSuccess: () => console.log('Updated'),
|
|
191
|
-
}
|
|
203
|
+
const updateProduct = useDbUpdate<Product>('products');
|
|
204
|
+
|
|
205
|
+
updateProduct.mutate(
|
|
206
|
+
{ id: productId, data: { price: 89.99 } },
|
|
207
|
+
{
|
|
208
|
+
onSuccess: () => console.log('Updated'),
|
|
209
|
+
}
|
|
192
210
|
);
|
|
193
211
|
```
|
|
194
212
|
|
|
@@ -197,11 +215,11 @@ updateProduct.mutate(
|
|
|
197
215
|
Delete a record.
|
|
198
216
|
|
|
199
217
|
```tsx
|
|
200
|
-
const deleteProduct = useDbDelete('products'
|
|
201
|
-
|
|
202
|
-
deleteProduct.mutate(
|
|
203
|
-
onSuccess: () => console.log('Deleted'),
|
|
204
|
-
});
|
|
218
|
+
const deleteProduct = useDbDelete('products');
|
|
219
|
+
|
|
220
|
+
deleteProduct.mutate(productId, {
|
|
221
|
+
onSuccess: () => console.log('Deleted'),
|
|
222
|
+
});
|
|
205
223
|
```
|
|
206
224
|
|
|
207
225
|
---
|
|
@@ -294,12 +312,16 @@ const adapter = new SqliteAdapter({
|
|
|
294
312
|
{ name: { $like: '%shirt%' } } // LIKE '%shirt%'
|
|
295
313
|
{ name: { $notLike: '%test%' } } // NOT LIKE '%test%'
|
|
296
314
|
|
|
297
|
-
// Range
|
|
298
|
-
{ price: { $between: [10, 100] } } // BETWEEN 10 AND 100
|
|
299
|
-
|
|
300
|
-
// Null
|
|
301
|
-
{ description: { $isNull: true } } // IS NULL
|
|
302
|
-
{ description: { $isNull: false } } // IS NOT NULL
|
|
315
|
+
// Range
|
|
316
|
+
{ price: { $between: [10, 100] } } // BETWEEN 10 AND 100
|
|
317
|
+
|
|
318
|
+
// Null
|
|
319
|
+
{ description: { $isNull: true } } // IS NULL
|
|
320
|
+
{ description: { $isNull: false } } // IS NOT NULL
|
|
321
|
+
|
|
322
|
+
// JSON array contains
|
|
323
|
+
{ tags: { $contains: "sale" } }
|
|
324
|
+
{ tags: { $containsAny: ["sale", "new"] } }
|
|
303
325
|
|
|
304
326
|
// Logical
|
|
305
327
|
{ $and: [
|
|
@@ -318,18 +340,18 @@ const adapter = new SqliteAdapter({
|
|
|
318
340
|
### Query Interface
|
|
319
341
|
|
|
320
342
|
```typescript
|
|
321
|
-
interface QueryOptions {
|
|
322
|
-
where?: Record<string, unknown>;
|
|
323
|
-
orderBy?: Array<{
|
|
324
|
-
field: string;
|
|
325
|
-
direction: 'ASC' | 'DESC';
|
|
326
|
-
}>;
|
|
327
|
-
limit?: number;
|
|
328
|
-
offset?: number;
|
|
329
|
-
populate?:
|
|
330
|
-
lang?: string;
|
|
331
|
-
fallbackLang?: string;
|
|
332
|
-
}
|
|
343
|
+
interface QueryOptions {
|
|
344
|
+
where?: Record<string, unknown>;
|
|
345
|
+
orderBy?: Array<{
|
|
346
|
+
field: string;
|
|
347
|
+
direction: 'ASC' | 'DESC';
|
|
348
|
+
}>;
|
|
349
|
+
limit?: number;
|
|
350
|
+
offset?: number;
|
|
351
|
+
populate?: PopulateOption;
|
|
352
|
+
lang?: string;
|
|
353
|
+
fallbackLang?: string;
|
|
354
|
+
}
|
|
333
355
|
```
|
|
334
356
|
|
|
335
357
|
---
|
|
@@ -387,36 +409,32 @@ function LanguageSwitcher() {
|
|
|
387
409
|
|
|
388
410
|
---
|
|
389
411
|
|
|
390
|
-
## Generated Hooks
|
|
391
|
-
|
|
392
|
-
Generate
|
|
393
|
-
|
|
394
|
-
```bash
|
|
395
|
-
dbcli generate --schema ./schema.
|
|
396
|
-
```
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
});
|
|
410
|
-
|
|
411
|
-
const
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
// ...
|
|
417
|
-
);
|
|
418
|
-
}
|
|
419
|
-
```
|
|
412
|
+
## Generated Types + Generic Hooks
|
|
413
|
+
|
|
414
|
+
Generate runtime schema and TypeScript interfaces:
|
|
415
|
+
|
|
416
|
+
```bash
|
|
417
|
+
dbcli generate --schema ./schema.json --output ./src/db
|
|
418
|
+
```
|
|
419
|
+
|
|
420
|
+
`dbcli generate` writes `schema.json` and `types.ts`. React hooks are imported from `@promakeai/dbreact`:
|
|
421
|
+
|
|
422
|
+
```tsx
|
|
423
|
+
import { useDbList, useDbGet, useDbCreate } from '@promakeai/dbreact';
|
|
424
|
+
import type { DbProduct, DbProductInput } from './db/types';
|
|
425
|
+
|
|
426
|
+
function ProductManager() {
|
|
427
|
+
const { data: products } = useDbList<DbProduct>('products', {
|
|
428
|
+
where: { stock: { $gt: 0 } },
|
|
429
|
+
});
|
|
430
|
+
|
|
431
|
+
const { data: product } = useDbGet<DbProduct>('products', 1);
|
|
432
|
+
|
|
433
|
+
const createProduct = useDbCreate<DbProduct, DbProductInput>('products');
|
|
434
|
+
|
|
435
|
+
return null;
|
|
436
|
+
}
|
|
437
|
+
```
|
|
420
438
|
|
|
421
439
|
---
|
|
422
440
|
|
|
@@ -425,15 +443,10 @@ function ProductManager() {
|
|
|
425
443
|
### Custom React Query Options
|
|
426
444
|
|
|
427
445
|
```tsx
|
|
428
|
-
const { data } = useDbList('products', {
|
|
429
|
-
where: { active: true },
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
staleTime: 1000 * 60 * 5, // 5 minutes
|
|
433
|
-
gcTime: 1000 * 60 * 30, // 30 minutes
|
|
434
|
-
refetchOnWindowFocus: false,
|
|
435
|
-
retry: 3,
|
|
436
|
-
});
|
|
446
|
+
const { data } = useDbList('products', {
|
|
447
|
+
where: { active: true },
|
|
448
|
+
limit: 50,
|
|
449
|
+
});
|
|
437
450
|
```
|
|
438
451
|
|
|
439
452
|
### Direct Adapter Access
|
|
@@ -467,7 +480,7 @@ function AdvancedSearch() {
|
|
|
467
480
|
```tsx
|
|
468
481
|
function ProductPrice({ product }) {
|
|
469
482
|
const queryClient = useQueryClient();
|
|
470
|
-
const updateProduct = useDbUpdate('products'
|
|
483
|
+
const updateProduct = useDbUpdate('products');
|
|
471
484
|
|
|
472
485
|
const handlePriceChange = async (newPrice: number) => {
|
|
473
486
|
// Optimistic update
|
|
@@ -476,7 +489,10 @@ function ProductPrice({ product }) {
|
|
|
476
489
|
{ ...product, price: newPrice }
|
|
477
490
|
);
|
|
478
491
|
|
|
479
|
-
await updateProduct.mutateAsync({
|
|
492
|
+
await updateProduct.mutateAsync({
|
|
493
|
+
id: product.id,
|
|
494
|
+
data: { price: newPrice },
|
|
495
|
+
});
|
|
480
496
|
};
|
|
481
497
|
|
|
482
498
|
return <PriceInput value={product.price} onChange={handlePriceChange} />;
|
|
@@ -568,13 +584,13 @@ function ProductSearch() {
|
|
|
568
584
|
Full TypeScript support with generated types:
|
|
569
585
|
|
|
570
586
|
```typescript
|
|
571
|
-
import type {
|
|
587
|
+
import type { DbProduct, DbProductInput } from './db/types';
|
|
572
588
|
|
|
573
589
|
// Type-safe queries
|
|
574
|
-
const products:
|
|
590
|
+
const products: DbProduct[] = await adapter.list('products');
|
|
575
591
|
|
|
576
592
|
// Type-safe creates
|
|
577
|
-
const newProduct:
|
|
593
|
+
const newProduct: DbProductInput = {
|
|
578
594
|
sku: 'SHIRT-001',
|
|
579
595
|
price: 99.99,
|
|
580
596
|
};
|
|
@@ -32,6 +32,7 @@ export interface SqliteAdapterConfig {
|
|
|
32
32
|
export declare class SqliteAdapter implements IDataAdapter {
|
|
33
33
|
private db;
|
|
34
34
|
private SQL;
|
|
35
|
+
private tableColumnsCache;
|
|
35
36
|
private config;
|
|
36
37
|
schema?: SchemaDefinition;
|
|
37
38
|
defaultLang?: string;
|
|
@@ -141,6 +142,8 @@ export declare class SqliteAdapter implements IDataAdapter {
|
|
|
141
142
|
created: number;
|
|
142
143
|
ids: (number | bigint)[];
|
|
143
144
|
}>;
|
|
145
|
+
private getAvailableMainFallbackFields;
|
|
146
|
+
private getTableColumns;
|
|
144
147
|
updateMany(table: string, updates: {
|
|
145
148
|
id: number | string;
|
|
146
149
|
data: Record<string, unknown>;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"SqliteAdapter.d.ts","sourceRoot":"","sources":["../../adapters/SqliteAdapter.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAGH,OAAO,KAAK,EAAE,YAAY,EAAE,YAAY,EAAE,eAAe,EAAE,gBAAgB,EAAE,MAAM,gBAAgB,CAAC;AAepG,MAAM,WAAW,mBAAmB;IAClC,+DAA+D;IAC/D,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,sCAAsC;IACtC,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,gDAAgD;IAChD,WAAW,CAAC,EAAE,UAAU,CAAC;IACzB,gDAAgD;IAChD,MAAM,CAAC,EAAE,gBAAgB,CAAC;IAC1B,wCAAwC;IACxC,WAAW,CAAC,EAAE,MAAM,CAAC;CACtB;AAED;;;;;;;;;;;GAWG;AACH,qBAAa,aAAc,YAAW,YAAY;IAChD,OAAO,CAAC,EAAE,CAAyB;IACnC,OAAO,CAAC,GAAG,CAA4B;IACvC,OAAO,CAAC,MAAM,CAGZ;IAEF,MAAM,CAAC,EAAE,gBAAgB,CAAC;IAC1B,WAAW,CAAC,EAAE,MAAM,CAAC;gBAET,MAAM,GAAE,mBAAwB;IAc5C,SAAS,CAAC,MAAM,EAAE,gBAAgB,GAAG,IAAI;
|
|
1
|
+
{"version":3,"file":"SqliteAdapter.d.ts","sourceRoot":"","sources":["../../adapters/SqliteAdapter.ts"],"names":[],"mappings":"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"}
|
package/dist/index.js
CHANGED
|
@@ -224,6 +224,7 @@ import {
|
|
|
224
224
|
class SqliteAdapter {
|
|
225
225
|
db = null;
|
|
226
226
|
SQL = null;
|
|
227
|
+
tableColumnsCache = new Map;
|
|
227
228
|
config;
|
|
228
229
|
schema;
|
|
229
230
|
defaultLang;
|
|
@@ -241,6 +242,7 @@ class SqliteAdapter {
|
|
|
241
242
|
setSchema(schema) {
|
|
242
243
|
this.schema = schema;
|
|
243
244
|
this.config.schema = schema;
|
|
245
|
+
this.tableColumnsCache.clear();
|
|
244
246
|
}
|
|
245
247
|
async connect() {
|
|
246
248
|
this.SQL = await initSqlJs({
|
|
@@ -348,6 +350,7 @@ class SqliteAdapter {
|
|
|
348
350
|
schema: this.schema,
|
|
349
351
|
lang: options.lang,
|
|
350
352
|
fallbackLang: options.fallbackLang ?? this.defaultLang,
|
|
353
|
+
mainFallbackFields: await this.getAvailableMainFallbackFields(table),
|
|
351
354
|
where: options.where,
|
|
352
355
|
orderBy: options.orderBy,
|
|
353
356
|
limit: options.limit,
|
|
@@ -377,7 +380,7 @@ class SqliteAdapter {
|
|
|
377
380
|
const results = await this.list(table, { where: { id }, limit: 1 });
|
|
378
381
|
return results[0] ?? null;
|
|
379
382
|
}
|
|
380
|
-
const { sql, params } = buildTranslationQueryById(table, this.schema, id, options.lang, options.fallbackLang ?? this.defaultLang);
|
|
383
|
+
const { sql, params } = buildTranslationQueryById(table, this.schema, id, options.lang, options.fallbackLang ?? this.defaultLang, await this.getAvailableMainFallbackFields(table));
|
|
381
384
|
const rows = this.runQuery(sql, params);
|
|
382
385
|
const deserialized = this.deserializeResults(table, rows);
|
|
383
386
|
return deserialized[0] ?? null;
|
|
@@ -573,6 +576,7 @@ class SqliteAdapter {
|
|
|
573
576
|
this.persist();
|
|
574
577
|
this.db.close();
|
|
575
578
|
this.db = null;
|
|
579
|
+
this.tableColumnsCache.clear();
|
|
576
580
|
}
|
|
577
581
|
}
|
|
578
582
|
async beginTransaction() {
|
|
@@ -615,6 +619,25 @@ class SqliteAdapter {
|
|
|
615
619
|
this.persist();
|
|
616
620
|
return { created: ids.length, ids };
|
|
617
621
|
}
|
|
622
|
+
async getAvailableMainFallbackFields(table) {
|
|
623
|
+
const tableSchema = this.schema?.tables[table];
|
|
624
|
+
if (!tableSchema)
|
|
625
|
+
return [];
|
|
626
|
+
const translatableFields = getTranslatableFields(tableSchema);
|
|
627
|
+
if (translatableFields.length === 0)
|
|
628
|
+
return [];
|
|
629
|
+
const tableColumns = await this.getTableColumns(table);
|
|
630
|
+
return translatableFields.filter((field) => tableColumns.has(field));
|
|
631
|
+
}
|
|
632
|
+
async getTableColumns(table) {
|
|
633
|
+
const cached = this.tableColumnsCache.get(table);
|
|
634
|
+
if (cached)
|
|
635
|
+
return cached;
|
|
636
|
+
const schema = await this.getTableSchema(table);
|
|
637
|
+
const columnSet = new Set(schema.map((col) => col.name));
|
|
638
|
+
this.tableColumnsCache.set(table, columnSet);
|
|
639
|
+
return columnSet;
|
|
640
|
+
}
|
|
618
641
|
async updateMany(table, updates) {
|
|
619
642
|
let updated = 0;
|
|
620
643
|
for (const { id, data } of updates) {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@promakeai/dbreact",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.4",
|
|
4
4
|
"description": "React client for schema-driven multi-language database",
|
|
5
5
|
"main": "./dist/index.js",
|
|
6
6
|
"module": "./dist/index.js",
|
|
@@ -40,7 +40,7 @@
|
|
|
40
40
|
"sql.js": ">=1.11.0"
|
|
41
41
|
},
|
|
42
42
|
"dependencies": {
|
|
43
|
-
"@promakeai/orm": "1.0.
|
|
43
|
+
"@promakeai/orm": "1.0.4"
|
|
44
44
|
},
|
|
45
45
|
"devDependencies": {
|
|
46
46
|
"@tanstack/query-core": "5.90.20",
|