@promakeai/dbreact 1.0.1 → 1.0.3
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/dist/adapters/RestAdapter.d.ts +83 -0
- package/dist/adapters/SqliteAdapter.d.ts +166 -0
- package/dist/core/DataManager.d.ts +147 -0
- package/dist/hooks/useDataManager.d.ts +7 -0
- package/dist/hooks/useDbHooks.d.ts +132 -0
- package/dist/hooks/useDbHooks.d.ts.map +1 -1
- package/dist/hooks/useDbLang.d.ts +7 -0
- package/dist/index.d.ts +13 -0
- package/dist/index.js +37 -11
- package/dist/providers/DbProvider.d.ts +47 -0
- package/dist/providers/DbProvider.d.ts.map +1 -1
- package/dist/types.d.ts +36 -0
- package/dist/types.d.ts.map +1 -1
- package/dist/utils/whereBuilder.d.ts +39 -0
- package/package.json +5 -2
- package/SKILL.md +0 -311
- package/providers/DbProvider.tsx +0 -191
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* REST API Adapter
|
|
3
|
+
*
|
|
4
|
+
* Fetches data from a REST API backend.
|
|
5
|
+
* Sends Accept-Language header for multi-language support.
|
|
6
|
+
*/
|
|
7
|
+
import type { IDataAdapter, QueryOptions, PaginatedResult } from "../types";
|
|
8
|
+
export interface RestAdapterConfig {
|
|
9
|
+
/** Base URL for API (e.g., "https://api.example.com") */
|
|
10
|
+
baseUrl: string;
|
|
11
|
+
/** Language code for Accept-Language header */
|
|
12
|
+
lang?: string;
|
|
13
|
+
/** Custom headers to include in all requests */
|
|
14
|
+
headers?: Record<string, string>;
|
|
15
|
+
/** Custom fetch function (for testing/SSR) */
|
|
16
|
+
fetch?: typeof fetch;
|
|
17
|
+
}
|
|
18
|
+
/**
|
|
19
|
+
* REST API Adapter
|
|
20
|
+
*
|
|
21
|
+
* Makes HTTP requests to a REST API backend.
|
|
22
|
+
*
|
|
23
|
+
* @example
|
|
24
|
+
* ```ts
|
|
25
|
+
* const adapter = new RestAdapter({
|
|
26
|
+
* baseUrl: 'https://api.example.com',
|
|
27
|
+
* lang: 'tr'
|
|
28
|
+
* });
|
|
29
|
+
* const users = await adapter.findMany('users');
|
|
30
|
+
* ```
|
|
31
|
+
*/
|
|
32
|
+
export declare class RestAdapter implements IDataAdapter {
|
|
33
|
+
private config;
|
|
34
|
+
constructor(config: RestAdapterConfig);
|
|
35
|
+
connect(): Promise<void>;
|
|
36
|
+
private buildUrl;
|
|
37
|
+
private getHeaders;
|
|
38
|
+
private request;
|
|
39
|
+
private buildQueryParams;
|
|
40
|
+
findMany<T = unknown>(table: string, options?: QueryOptions): Promise<T[]>;
|
|
41
|
+
findOne<T = unknown>(table: string, options?: QueryOptions): Promise<T | null>;
|
|
42
|
+
findById<T = unknown>(table: string, id: number | string): Promise<T | null>;
|
|
43
|
+
count(table: string, options?: QueryOptions): Promise<number>;
|
|
44
|
+
paginate<T = unknown>(table: string, page: number, limit: number, options?: QueryOptions): Promise<PaginatedResult<T>>;
|
|
45
|
+
create<T = unknown>(table: string, data: Record<string, unknown>): Promise<T>;
|
|
46
|
+
update<T = unknown>(table: string, id: number | string, data: Record<string, unknown>): Promise<T>;
|
|
47
|
+
delete(table: string, id: number | string): Promise<boolean>;
|
|
48
|
+
execute(_query: string, _params?: unknown[]): Promise<void>;
|
|
49
|
+
executeQuery<T = unknown>(_query: string, _params?: unknown[]): Promise<T[]>;
|
|
50
|
+
seed(data: Record<string, Record<string, unknown>[]>): Promise<void>;
|
|
51
|
+
getTables(): Promise<string[]>;
|
|
52
|
+
getTableSchema(table: string): Promise<{
|
|
53
|
+
name: string;
|
|
54
|
+
type: string;
|
|
55
|
+
notnull: number;
|
|
56
|
+
pk: number;
|
|
57
|
+
}[]>;
|
|
58
|
+
close(): void;
|
|
59
|
+
/**
|
|
60
|
+
* Update language for subsequent requests
|
|
61
|
+
*/
|
|
62
|
+
setLang(lang: string): void;
|
|
63
|
+
/**
|
|
64
|
+
* Get current language
|
|
65
|
+
*/
|
|
66
|
+
getLang(): string;
|
|
67
|
+
createMany<T = unknown>(table: string, records: Record<string, unknown>[], options?: {
|
|
68
|
+
ignore?: boolean;
|
|
69
|
+
}): Promise<{
|
|
70
|
+
created: number;
|
|
71
|
+
ids: (number | bigint)[];
|
|
72
|
+
}>;
|
|
73
|
+
updateMany(table: string, updates: {
|
|
74
|
+
id: number | string;
|
|
75
|
+
data: Record<string, unknown>;
|
|
76
|
+
}[]): Promise<{
|
|
77
|
+
updated: number;
|
|
78
|
+
}>;
|
|
79
|
+
deleteMany(table: string, ids: (number | string)[]): Promise<{
|
|
80
|
+
deleted: number;
|
|
81
|
+
}>;
|
|
82
|
+
}
|
|
83
|
+
//# sourceMappingURL=RestAdapter.d.ts.map
|
|
@@ -0,0 +1,166 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Browser SQLite Adapter
|
|
3
|
+
*
|
|
4
|
+
* Uses sql.js (WASM) for in-browser SQLite database.
|
|
5
|
+
* Persists to localStorage for offline support.
|
|
6
|
+
*/
|
|
7
|
+
import type { IDataAdapter, QueryOptions, PaginatedResult, SchemaDefinition } from "@promakeai/orm";
|
|
8
|
+
export interface SqliteAdapterConfig {
|
|
9
|
+
/** localStorage key for persistence (default: "dbreact_db") */
|
|
10
|
+
storageKey?: string;
|
|
11
|
+
/** sql.js WASM path (default: CDN) */
|
|
12
|
+
wasmPath?: string;
|
|
13
|
+
/** Initial data to seed if database is empty */
|
|
14
|
+
initialData?: Uint8Array;
|
|
15
|
+
/** Schema definition for translation support */
|
|
16
|
+
schema?: SchemaDefinition;
|
|
17
|
+
/** Default language for translations */
|
|
18
|
+
defaultLang?: string;
|
|
19
|
+
}
|
|
20
|
+
/**
|
|
21
|
+
* Browser SQLite Adapter
|
|
22
|
+
*
|
|
23
|
+
* Provides SQLite database in the browser using sql.js WASM.
|
|
24
|
+
*
|
|
25
|
+
* @example
|
|
26
|
+
* ```ts
|
|
27
|
+
* const adapter = new SqliteAdapter({ storageKey: 'myapp' });
|
|
28
|
+
* await adapter.connect();
|
|
29
|
+
* const users = await adapter.list('users');
|
|
30
|
+
* ```
|
|
31
|
+
*/
|
|
32
|
+
export declare class SqliteAdapter implements IDataAdapter {
|
|
33
|
+
private db;
|
|
34
|
+
private SQL;
|
|
35
|
+
private config;
|
|
36
|
+
schema?: SchemaDefinition;
|
|
37
|
+
defaultLang?: string;
|
|
38
|
+
constructor(config?: SqliteAdapterConfig);
|
|
39
|
+
setSchema(schema: SchemaDefinition): void;
|
|
40
|
+
connect(): Promise<void>;
|
|
41
|
+
private persist;
|
|
42
|
+
private getDb;
|
|
43
|
+
private runQuery;
|
|
44
|
+
/**
|
|
45
|
+
* Deserialize rows using schema field definitions (bool 0/1 → boolean, json TEXT → parsed)
|
|
46
|
+
*/
|
|
47
|
+
private deserializeResults;
|
|
48
|
+
/**
|
|
49
|
+
* Serialize data for DB storage (bool → 0/1, json → TEXT)
|
|
50
|
+
*/
|
|
51
|
+
private serializeData;
|
|
52
|
+
private buildSelectQuery;
|
|
53
|
+
/**
|
|
54
|
+
* List records from a table
|
|
55
|
+
*/
|
|
56
|
+
list<T = unknown>(table: string, options?: QueryOptions): Promise<T[]>;
|
|
57
|
+
private listWithLang;
|
|
58
|
+
/**
|
|
59
|
+
* Get a single record by ID
|
|
60
|
+
*/
|
|
61
|
+
get<T = unknown>(table: string, id: number | string, options?: {
|
|
62
|
+
lang?: string;
|
|
63
|
+
fallbackLang?: string;
|
|
64
|
+
}): Promise<T | null>;
|
|
65
|
+
findOne<T = unknown>(table: string, options?: {
|
|
66
|
+
where?: Record<string, unknown>;
|
|
67
|
+
lang?: string;
|
|
68
|
+
fallbackLang?: string;
|
|
69
|
+
}): Promise<T | null>;
|
|
70
|
+
private getWithLang;
|
|
71
|
+
/**
|
|
72
|
+
* Count records in a table
|
|
73
|
+
*/
|
|
74
|
+
count(table: string, options?: QueryOptions): Promise<number>;
|
|
75
|
+
/**
|
|
76
|
+
* Paginate records
|
|
77
|
+
*/
|
|
78
|
+
paginate<T = unknown>(table: string, page: number, limit: number, options?: QueryOptions): Promise<PaginatedResult<T>>;
|
|
79
|
+
/**
|
|
80
|
+
* Create a new record
|
|
81
|
+
*/
|
|
82
|
+
create<T = unknown>(table: string, data: Record<string, unknown>): Promise<T>;
|
|
83
|
+
/**
|
|
84
|
+
* Create a record with explicit translations
|
|
85
|
+
*/
|
|
86
|
+
createWithTranslations<T = unknown>(table: string, data: Record<string, unknown>, translations?: Record<string, Record<string, unknown>>): Promise<T>;
|
|
87
|
+
/**
|
|
88
|
+
* Update a record by ID
|
|
89
|
+
*/
|
|
90
|
+
update<T = unknown>(table: string, id: number | string, data: Record<string, unknown>): Promise<T>;
|
|
91
|
+
/**
|
|
92
|
+
* Upsert a translation for a record
|
|
93
|
+
*/
|
|
94
|
+
upsertTranslation(table: string, id: number | string, lang: string, data: Record<string, unknown>): Promise<void>;
|
|
95
|
+
/**
|
|
96
|
+
* Get all translations for a record
|
|
97
|
+
*/
|
|
98
|
+
getTranslations<T = unknown>(table: string, id: number | string): Promise<T[]>;
|
|
99
|
+
/**
|
|
100
|
+
* Delete a record by ID
|
|
101
|
+
*/
|
|
102
|
+
delete(table: string, id: number | string): Promise<boolean>;
|
|
103
|
+
/**
|
|
104
|
+
* Execute a raw SQL statement
|
|
105
|
+
*/
|
|
106
|
+
execute(query: string, params?: unknown[]): Promise<{
|
|
107
|
+
changes: number;
|
|
108
|
+
lastInsertRowid: number | bigint;
|
|
109
|
+
}>;
|
|
110
|
+
/**
|
|
111
|
+
* Execute a raw SQL query and return results
|
|
112
|
+
*/
|
|
113
|
+
raw<T = unknown>(query: string, params?: unknown[]): Promise<T[]>;
|
|
114
|
+
/**
|
|
115
|
+
* Seed database with initial data
|
|
116
|
+
*/
|
|
117
|
+
seed(data: Record<string, Record<string, unknown>[]>): Promise<void>;
|
|
118
|
+
/**
|
|
119
|
+
* Get all table names
|
|
120
|
+
*/
|
|
121
|
+
getTables(): Promise<string[]>;
|
|
122
|
+
/**
|
|
123
|
+
* Get table schema
|
|
124
|
+
*/
|
|
125
|
+
getTableSchema(table: string): Promise<{
|
|
126
|
+
name: string;
|
|
127
|
+
type: string;
|
|
128
|
+
notnull: number;
|
|
129
|
+
pk: number;
|
|
130
|
+
}[]>;
|
|
131
|
+
/**
|
|
132
|
+
* Close the database connection
|
|
133
|
+
*/
|
|
134
|
+
close(): void;
|
|
135
|
+
beginTransaction(): Promise<void>;
|
|
136
|
+
commit(): Promise<void>;
|
|
137
|
+
rollback(): Promise<void>;
|
|
138
|
+
createMany<T = unknown>(table: string, records: Record<string, unknown>[], options?: {
|
|
139
|
+
ignore?: boolean;
|
|
140
|
+
}): Promise<{
|
|
141
|
+
created: number;
|
|
142
|
+
ids: (number | bigint)[];
|
|
143
|
+
}>;
|
|
144
|
+
updateMany(table: string, updates: {
|
|
145
|
+
id: number | string;
|
|
146
|
+
data: Record<string, unknown>;
|
|
147
|
+
}[]): Promise<{
|
|
148
|
+
updated: number;
|
|
149
|
+
}>;
|
|
150
|
+
deleteMany(table: string, ids: (number | string)[]): Promise<{
|
|
151
|
+
deleted: number;
|
|
152
|
+
}>;
|
|
153
|
+
/**
|
|
154
|
+
* Export database as Uint8Array
|
|
155
|
+
*/
|
|
156
|
+
export(): Uint8Array;
|
|
157
|
+
/**
|
|
158
|
+
* Import database from Uint8Array
|
|
159
|
+
*/
|
|
160
|
+
import(data: Uint8Array): Promise<void>;
|
|
161
|
+
/**
|
|
162
|
+
* Clear all data and reset database
|
|
163
|
+
*/
|
|
164
|
+
clear(): void;
|
|
165
|
+
}
|
|
166
|
+
//# sourceMappingURL=SqliteAdapter.d.ts.map
|
|
@@ -0,0 +1,147 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* DataManager for React Client
|
|
3
|
+
*
|
|
4
|
+
* Central data access layer for the React client.
|
|
5
|
+
* Provides language-aware query methods using raw SQL when needed.
|
|
6
|
+
*/
|
|
7
|
+
import type { IDataAdapter, QueryOptions, PaginatedResult, LangQueryOptions } from "../types";
|
|
8
|
+
/**
|
|
9
|
+
* Schema definition for language-aware queries
|
|
10
|
+
*/
|
|
11
|
+
export interface TableSchema {
|
|
12
|
+
translatableFields?: string[];
|
|
13
|
+
translationTable?: string;
|
|
14
|
+
foreignKey?: string;
|
|
15
|
+
}
|
|
16
|
+
/**
|
|
17
|
+
* DataManager - Central data access layer for React
|
|
18
|
+
*
|
|
19
|
+
* Acts as a proxy to the underlying adapter with language-aware query support.
|
|
20
|
+
*
|
|
21
|
+
* @example
|
|
22
|
+
* ```ts
|
|
23
|
+
* const dm = useDataManager();
|
|
24
|
+
* const products = await dm.query('products', { lang: 'tr' });
|
|
25
|
+
* ```
|
|
26
|
+
*/
|
|
27
|
+
export declare class DataManager {
|
|
28
|
+
private adapter;
|
|
29
|
+
private schemas;
|
|
30
|
+
constructor(adapter: IDataAdapter);
|
|
31
|
+
/**
|
|
32
|
+
* Register a table schema for language-aware queries
|
|
33
|
+
*/
|
|
34
|
+
registerSchema(table: string, schema: TableSchema): void;
|
|
35
|
+
/**
|
|
36
|
+
* Get registered schema for a table
|
|
37
|
+
*/
|
|
38
|
+
getSchema(table: string): TableSchema | undefined;
|
|
39
|
+
/**
|
|
40
|
+
* Query multiple records
|
|
41
|
+
*/
|
|
42
|
+
query<T = unknown>(table: string, options?: QueryOptions): Promise<T[]>;
|
|
43
|
+
/**
|
|
44
|
+
* Query a single record
|
|
45
|
+
*/
|
|
46
|
+
queryOne<T = unknown>(table: string, options?: QueryOptions): Promise<T | null>;
|
|
47
|
+
/**
|
|
48
|
+
* Get a record by ID
|
|
49
|
+
*/
|
|
50
|
+
queryById<T = unknown>(table: string, id: number | string): Promise<T | null>;
|
|
51
|
+
/**
|
|
52
|
+
* Count records
|
|
53
|
+
*/
|
|
54
|
+
count(table: string, options?: QueryOptions): Promise<number>;
|
|
55
|
+
/**
|
|
56
|
+
* Paginated query
|
|
57
|
+
*/
|
|
58
|
+
paginate<T = unknown>(table: string, page: number, limit: number, options?: QueryOptions): Promise<PaginatedResult<T>>;
|
|
59
|
+
/**
|
|
60
|
+
* Create a new record
|
|
61
|
+
*/
|
|
62
|
+
create<T = unknown>(table: string, data: Record<string, unknown>): Promise<T>;
|
|
63
|
+
/**
|
|
64
|
+
* Update a record by ID
|
|
65
|
+
*/
|
|
66
|
+
update<T = unknown>(table: string, id: number | string, data: Record<string, unknown>): Promise<T>;
|
|
67
|
+
/**
|
|
68
|
+
* Delete a record by ID
|
|
69
|
+
*/
|
|
70
|
+
delete(table: string, id: number | string): Promise<boolean>;
|
|
71
|
+
/**
|
|
72
|
+
* Execute raw SQL/query
|
|
73
|
+
*/
|
|
74
|
+
execute(query: string, params?: unknown[]): Promise<void>;
|
|
75
|
+
/**
|
|
76
|
+
* Execute raw SQL query (SELECT)
|
|
77
|
+
*/
|
|
78
|
+
executeQuery<T = unknown>(query: string, params?: unknown[]): Promise<T[]>;
|
|
79
|
+
/**
|
|
80
|
+
* Bulk seed data
|
|
81
|
+
*/
|
|
82
|
+
seed(data: Record<string, Record<string, unknown>[]>): Promise<void>;
|
|
83
|
+
/**
|
|
84
|
+
* Get all table names
|
|
85
|
+
*/
|
|
86
|
+
getTables(): Promise<string[]>;
|
|
87
|
+
/**
|
|
88
|
+
* Get table schema
|
|
89
|
+
*/
|
|
90
|
+
getTableSchema(table: string): Promise<{
|
|
91
|
+
name: string;
|
|
92
|
+
type: string;
|
|
93
|
+
notnull: number;
|
|
94
|
+
pk: number;
|
|
95
|
+
}[]>;
|
|
96
|
+
/**
|
|
97
|
+
* Close connection
|
|
98
|
+
*/
|
|
99
|
+
close(): void;
|
|
100
|
+
/**
|
|
101
|
+
* Get the underlying adapter
|
|
102
|
+
*/
|
|
103
|
+
getAdapter(): IDataAdapter;
|
|
104
|
+
/**
|
|
105
|
+
* Query with automatic translation support
|
|
106
|
+
*
|
|
107
|
+
* If a schema is registered for the table with translatable fields,
|
|
108
|
+
* this will perform a JOIN with the translation table and use COALESCE
|
|
109
|
+
* to fallback to the default language.
|
|
110
|
+
*
|
|
111
|
+
* @example
|
|
112
|
+
* ```ts
|
|
113
|
+
* dm.registerSchema('products', {
|
|
114
|
+
* translatableFields: ['name', 'description'],
|
|
115
|
+
* translationTable: 'product_translations',
|
|
116
|
+
* foreignKey: 'product_id'
|
|
117
|
+
* });
|
|
118
|
+
*
|
|
119
|
+
* const products = await dm.queryWithLang('products', {
|
|
120
|
+
* lang: 'tr',
|
|
121
|
+
* fallbackLang: 'en'
|
|
122
|
+
* });
|
|
123
|
+
* ```
|
|
124
|
+
*/
|
|
125
|
+
queryWithLang<T = unknown>(table: string, options?: LangQueryOptions): Promise<T[]>;
|
|
126
|
+
/**
|
|
127
|
+
* Query single record by ID with translation support
|
|
128
|
+
*/
|
|
129
|
+
queryByIdWithLang<T = unknown>(table: string, id: number | string, options?: {
|
|
130
|
+
lang?: string;
|
|
131
|
+
fallbackLang?: string;
|
|
132
|
+
}): Promise<T | null>;
|
|
133
|
+
/**
|
|
134
|
+
* Create record with translations
|
|
135
|
+
*/
|
|
136
|
+
createWithTranslations<T = unknown>(table: string, data: Record<string, unknown>, translations: Record<string, Record<string, unknown>>): Promise<T>;
|
|
137
|
+
/**
|
|
138
|
+
* Update or insert translation for specific language
|
|
139
|
+
*/
|
|
140
|
+
upsertTranslation(table: string, id: number | string, lang: string, data: Record<string, unknown>): Promise<void>;
|
|
141
|
+
/**
|
|
142
|
+
* Get all translations for a record
|
|
143
|
+
*/
|
|
144
|
+
getTranslations<T = unknown>(table: string, id: number | string): Promise<T[]>;
|
|
145
|
+
private buildTranslationQuery;
|
|
146
|
+
}
|
|
147
|
+
//# sourceMappingURL=DataManager.d.ts.map
|
|
@@ -0,0 +1,132 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Generic Database Hooks
|
|
3
|
+
*
|
|
4
|
+
* Type-safe React hooks for database operations.
|
|
5
|
+
* Works with any table - just pass the table name and type.
|
|
6
|
+
*
|
|
7
|
+
* @example
|
|
8
|
+
* ```tsx
|
|
9
|
+
* import { useDbList, useDbGet, useDbCreate } from '@promakeai/dbreact';
|
|
10
|
+
* import type { DbProduct } from './db/types';
|
|
11
|
+
*
|
|
12
|
+
* // List all products
|
|
13
|
+
* const { data } = useDbList<DbProduct>('products');
|
|
14
|
+
*
|
|
15
|
+
* // Get single product
|
|
16
|
+
* const { data } = useDbGet<DbProduct>('products', productId);
|
|
17
|
+
*
|
|
18
|
+
* // Create product
|
|
19
|
+
* const mutation = useDbCreate<DbProduct>('products');
|
|
20
|
+
* mutation.mutate({ name: 'New Product' });
|
|
21
|
+
* ```
|
|
22
|
+
*/
|
|
23
|
+
import { type PopulateOption } from "@promakeai/orm";
|
|
24
|
+
/**
|
|
25
|
+
* List options for useDbList
|
|
26
|
+
*/
|
|
27
|
+
export interface ListOptions {
|
|
28
|
+
where?: Record<string, unknown>;
|
|
29
|
+
orderBy?: Array<{
|
|
30
|
+
field: string;
|
|
31
|
+
direction: "ASC" | "DESC";
|
|
32
|
+
}>;
|
|
33
|
+
limit?: number;
|
|
34
|
+
offset?: number;
|
|
35
|
+
enabled?: boolean;
|
|
36
|
+
populate?: PopulateOption;
|
|
37
|
+
}
|
|
38
|
+
/**
|
|
39
|
+
* Hook to list all records from a table
|
|
40
|
+
*
|
|
41
|
+
* @param table - Table name
|
|
42
|
+
* @param options - Query options (where, orderBy, limit, offset, enabled)
|
|
43
|
+
* @returns React Query result with data array
|
|
44
|
+
*
|
|
45
|
+
* @example
|
|
46
|
+
* ```tsx
|
|
47
|
+
* const { data: products, isLoading } = useDbList<DbProduct>('products', {
|
|
48
|
+
* where: { price: { $gt: 100 } },
|
|
49
|
+
* orderBy: [{ field: 'name', direction: 'ASC' }],
|
|
50
|
+
* limit: 10,
|
|
51
|
+
* });
|
|
52
|
+
* ```
|
|
53
|
+
*/
|
|
54
|
+
export declare function useDbList<T>(table: string, options?: ListOptions): import("@tanstack/react-query").UseQueryResult<T[], Error>;
|
|
55
|
+
/**
|
|
56
|
+
* Options for findOne-style queries
|
|
57
|
+
*/
|
|
58
|
+
export interface FindOneOptions {
|
|
59
|
+
where: Record<string, unknown>;
|
|
60
|
+
enabled?: boolean;
|
|
61
|
+
populate?: PopulateOption;
|
|
62
|
+
}
|
|
63
|
+
/**
|
|
64
|
+
* Hook to get a single record by ID or where query
|
|
65
|
+
*
|
|
66
|
+
* @param table - Table name
|
|
67
|
+
* @param idOrOptions - Record ID, or FindOneOptions with where clause
|
|
68
|
+
* @param maybeOptions - Query options when using ID form
|
|
69
|
+
* @returns React Query result with single record or null
|
|
70
|
+
*
|
|
71
|
+
* @example
|
|
72
|
+
* ```tsx
|
|
73
|
+
* // By ID
|
|
74
|
+
* const { data: product } = useDbGet<DbProduct>('products', productId);
|
|
75
|
+
*
|
|
76
|
+
* // By where query (findOne style)
|
|
77
|
+
* const { data: product } = useDbGet<DbProduct>('products', {
|
|
78
|
+
* where: { slug: 'my-product' },
|
|
79
|
+
* enabled: !!slug,
|
|
80
|
+
* });
|
|
81
|
+
* ```
|
|
82
|
+
*/
|
|
83
|
+
export declare function useDbGet<T>(table: string, idOrOptions: number | undefined | FindOneOptions, maybeOptions?: {
|
|
84
|
+
enabled?: boolean;
|
|
85
|
+
populate?: PopulateOption;
|
|
86
|
+
}): import("@tanstack/react-query").UseQueryResult<import("@tanstack/query-core").NoInfer<T | null>, Error>;
|
|
87
|
+
/**
|
|
88
|
+
* Hook to create a new record
|
|
89
|
+
*
|
|
90
|
+
* @param table - Table name
|
|
91
|
+
* @returns React Query mutation for creating records
|
|
92
|
+
*
|
|
93
|
+
* @example
|
|
94
|
+
* ```tsx
|
|
95
|
+
* const mutation = useDbCreate<DbProduct>('products');
|
|
96
|
+
*
|
|
97
|
+
* mutation.mutate({ sku: 'ABC', price: 100 });
|
|
98
|
+
* ```
|
|
99
|
+
*/
|
|
100
|
+
export declare function useDbCreate<T, TInput = Partial<T>>(table: string): import("@tanstack/react-query").UseMutationResult<T, Error, TInput, unknown>;
|
|
101
|
+
/**
|
|
102
|
+
* Hook to update an existing record
|
|
103
|
+
*
|
|
104
|
+
* @param table - Table name
|
|
105
|
+
* @returns React Query mutation for updating records
|
|
106
|
+
*
|
|
107
|
+
* @example
|
|
108
|
+
* ```tsx
|
|
109
|
+
* const mutation = useDbUpdate<DbProduct>('products');
|
|
110
|
+
*
|
|
111
|
+
* mutation.mutate({ id: 1, data: { price: 150 } });
|
|
112
|
+
* ```
|
|
113
|
+
*/
|
|
114
|
+
export declare function useDbUpdate<T, TInput = Partial<T>>(table: string): import("@tanstack/react-query").UseMutationResult<T, Error, {
|
|
115
|
+
id: number;
|
|
116
|
+
data: TInput;
|
|
117
|
+
}, unknown>;
|
|
118
|
+
/**
|
|
119
|
+
* Hook to delete a record
|
|
120
|
+
*
|
|
121
|
+
* @param table - Table name
|
|
122
|
+
* @returns React Query mutation for deleting records
|
|
123
|
+
*
|
|
124
|
+
* @example
|
|
125
|
+
* ```tsx
|
|
126
|
+
* const mutation = useDbDelete('products');
|
|
127
|
+
*
|
|
128
|
+
* mutation.mutate(productId);
|
|
129
|
+
* ```
|
|
130
|
+
*/
|
|
131
|
+
export declare function useDbDelete(table: string): import("@tanstack/react-query").UseMutationResult<boolean, Error, number, unknown>;
|
|
132
|
+
//# sourceMappingURL=useDbHooks.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"useDbHooks.d.ts","sourceRoot":"","sources":["../../hooks/useDbHooks.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;GAqBG;
|
|
1
|
+
{"version":3,"file":"useDbHooks.d.ts","sourceRoot":"","sources":["../../hooks/useDbHooks.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;GAqBG;AAIH,OAAO,EAAmB,KAAK,cAAc,EAAE,MAAM,gBAAgB,CAAC;AAEtE;;GAEG;AACH,MAAM,WAAW,WAAW;IAC1B,KAAK,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IAChC,OAAO,CAAC,EAAE,KAAK,CAAC;QAAE,KAAK,EAAE,MAAM,CAAC;QAAC,SAAS,EAAE,KAAK,GAAG,MAAM,CAAA;KAAE,CAAC,CAAC;IAC9D,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB,QAAQ,CAAC,EAAE,cAAc,CAAC;CAC3B;AAED;;;;;;;;;;;;;;;GAeG;AACH,wBAAgB,SAAS,CAAC,CAAC,EAAE,KAAK,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,WAAW,8DA2BhE;AAED;;GAEG;AACH,MAAM,WAAW,cAAc;IAC7B,KAAK,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IAC/B,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB,QAAQ,CAAC,EAAE,cAAc,CAAC;CAC3B;AAED;;;;;;;;;;;;;;;;;;;GAmBG;AACH,wBAAgB,QAAQ,CAAC,CAAC,EACxB,KAAK,EAAE,MAAM,EACb,WAAW,EAAE,MAAM,GAAG,SAAS,GAAG,cAAc,EAChD,YAAY,CAAC,EAAE;IAAE,OAAO,CAAC,EAAE,OAAO,CAAC;IAAC,QAAQ,CAAC,EAAE,cAAc,CAAA;CAAE,2GAqChE;AAED;;;;;;;;;;;;GAYG;AACH,wBAAgB,WAAW,CAAC,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,CAAC,CAAC,EAAE,KAAK,EAAE,MAAM,gFAUhE;AAED;;;;;;;;;;;;GAYG;AACH,wBAAgB,WAAW,CAAC,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,CAAC,CAAC,EAAE,KAAK,EAAE,MAAM;QAO7B,MAAM;UAAQ,MAAM;YAKvD;AAED;;;;;;;;;;;;GAYG;AACH,wBAAgB,WAAW,CAAC,KAAK,EAAE,MAAM,sFAUxC"}
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @promakeai/dbreact
|
|
3
|
+
*
|
|
4
|
+
* React client for schema-driven multi-language database.
|
|
5
|
+
* Works with SQL.js (browser SQLite) or REST API backends.
|
|
6
|
+
*/
|
|
7
|
+
export { ORM, defineSchema, f, buildWhereClause, resolvePopulate, getPopulatableFields, validatePopulate, parseJSONSchema, } from "@promakeai/orm";
|
|
8
|
+
export type { SchemaDefinition, TableDefinition, FieldDefinition, PopulateOption, PopulateNested, ORMConfig, QueryOptions, PaginatedResult, IDataAdapter, WhereResult, } from "@promakeai/orm";
|
|
9
|
+
export type { DbProviderConfig, DbLangContextValue, DbContextValue, } from "./types";
|
|
10
|
+
export { DbProvider, useDb, useAdapter, useDbLang, } from "./providers/DbProvider";
|
|
11
|
+
export { useDbList, useDbGet, useDbCreate, useDbUpdate, useDbDelete, type ListOptions, type FindOneOptions, } from "./hooks/useDbHooks";
|
|
12
|
+
export { SqliteAdapter, type SqliteAdapterConfig } from "./adapters/SqliteAdapter";
|
|
13
|
+
//# sourceMappingURL=index.d.ts.map
|
package/dist/index.js
CHANGED
|
@@ -4,7 +4,7 @@ import {
|
|
|
4
4
|
defineSchema,
|
|
5
5
|
f,
|
|
6
6
|
buildWhereClause as buildWhereClause2,
|
|
7
|
-
resolvePopulate,
|
|
7
|
+
resolvePopulate as resolvePopulate2,
|
|
8
8
|
getPopulatableFields,
|
|
9
9
|
validatePopulate,
|
|
10
10
|
parseJSONSchema
|
|
@@ -35,6 +35,7 @@ var defaultQueryClient = new QueryClient({
|
|
|
35
35
|
});
|
|
36
36
|
function DbProvider({
|
|
37
37
|
adapter,
|
|
38
|
+
schema,
|
|
38
39
|
lang: langProp = "en",
|
|
39
40
|
fallbackLang = "en",
|
|
40
41
|
autoConnect = true,
|
|
@@ -85,9 +86,10 @@ function DbProvider({
|
|
|
85
86
|
}, []);
|
|
86
87
|
const dbContextValue = useMemo(() => ({
|
|
87
88
|
adapter,
|
|
89
|
+
schema,
|
|
88
90
|
isConnected,
|
|
89
91
|
error
|
|
90
|
-
}), [adapter, isConnected, error]);
|
|
92
|
+
}), [adapter, schema, isConnected, error]);
|
|
91
93
|
const langContextValue = useMemo(() => ({
|
|
92
94
|
lang,
|
|
93
95
|
fallbackLang,
|
|
@@ -124,28 +126,52 @@ function useDbLang() {
|
|
|
124
126
|
}
|
|
125
127
|
// hooks/useDbHooks.ts
|
|
126
128
|
import { useQuery, useMutation, useQueryClient } from "@tanstack/react-query";
|
|
129
|
+
import { resolvePopulate } from "@promakeai/orm";
|
|
127
130
|
function useDbList(table, options) {
|
|
128
131
|
const adapter = useAdapter();
|
|
132
|
+
const { schema } = useDb();
|
|
129
133
|
const { lang, fallbackLang } = useDbLang();
|
|
134
|
+
const { populate, ...queryOpts } = options || {};
|
|
130
135
|
return useQuery({
|
|
131
|
-
queryKey: [table, "list",
|
|
132
|
-
queryFn: () =>
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
136
|
+
queryKey: [table, "list", queryOpts, lang, populate],
|
|
137
|
+
queryFn: async () => {
|
|
138
|
+
const records = await adapter.list(table, {
|
|
139
|
+
...queryOpts,
|
|
140
|
+
lang,
|
|
141
|
+
fallbackLang
|
|
142
|
+
});
|
|
143
|
+
if (populate && schema) {
|
|
144
|
+
const adapterWrapper = {
|
|
145
|
+
findMany: (t, opts) => adapter.list(t, { ...opts, lang, fallbackLang })
|
|
146
|
+
};
|
|
147
|
+
return resolvePopulate(records, table, populate, schema, adapterWrapper);
|
|
148
|
+
}
|
|
149
|
+
return records;
|
|
150
|
+
},
|
|
137
151
|
enabled: options?.enabled ?? true
|
|
138
152
|
});
|
|
139
153
|
}
|
|
140
154
|
function useDbGet(table, idOrOptions, maybeOptions) {
|
|
141
155
|
const adapter = useAdapter();
|
|
156
|
+
const { schema } = useDb();
|
|
142
157
|
const { lang, fallbackLang } = useDbLang();
|
|
143
158
|
const isWhereMode = typeof idOrOptions === "object" && idOrOptions !== null && "where" in idOrOptions;
|
|
144
159
|
const where = isWhereMode ? idOrOptions.where : { id: idOrOptions };
|
|
145
160
|
const enabled = isWhereMode ? idOrOptions.enabled ?? true : (maybeOptions?.enabled ?? true) && idOrOptions !== undefined;
|
|
161
|
+
const populate = isWhereMode ? idOrOptions.populate : maybeOptions?.populate;
|
|
146
162
|
return useQuery({
|
|
147
|
-
queryKey: [table, "single", where, lang],
|
|
148
|
-
queryFn: () =>
|
|
163
|
+
queryKey: [table, "single", where, lang, populate],
|
|
164
|
+
queryFn: async () => {
|
|
165
|
+
const record = await adapter.findOne(table, { where, lang, fallbackLang });
|
|
166
|
+
if (record && populate && schema) {
|
|
167
|
+
const adapterWrapper = {
|
|
168
|
+
findMany: (t, opts) => adapter.list(t, { ...opts, lang, fallbackLang })
|
|
169
|
+
};
|
|
170
|
+
const [populated] = await resolvePopulate([record], table, populate, schema, adapterWrapper);
|
|
171
|
+
return populated;
|
|
172
|
+
}
|
|
173
|
+
return record;
|
|
174
|
+
},
|
|
149
175
|
enabled
|
|
150
176
|
});
|
|
151
177
|
}
|
|
@@ -636,7 +662,7 @@ export {
|
|
|
636
662
|
useDbCreate,
|
|
637
663
|
useDb,
|
|
638
664
|
useAdapter,
|
|
639
|
-
resolvePopulate,
|
|
665
|
+
resolvePopulate2 as resolvePopulate,
|
|
640
666
|
parseJSONSchema,
|
|
641
667
|
getPopulatableFields,
|
|
642
668
|
f,
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Database Provider
|
|
3
|
+
*
|
|
4
|
+
* React context provider for database operations with language support.
|
|
5
|
+
*/
|
|
6
|
+
import { type ReactNode } from "react";
|
|
7
|
+
import { QueryClient } from "@tanstack/react-query";
|
|
8
|
+
import type { IDataAdapter, DbProviderConfig, DbLangContextValue, DbContextValue } from "../types";
|
|
9
|
+
interface DbProviderProps extends DbProviderConfig {
|
|
10
|
+
children: ReactNode;
|
|
11
|
+
queryClient?: QueryClient;
|
|
12
|
+
}
|
|
13
|
+
/**
|
|
14
|
+
* Database Provider Component
|
|
15
|
+
*
|
|
16
|
+
* Wraps application with database context, language context, and React Query.
|
|
17
|
+
*
|
|
18
|
+
* @example
|
|
19
|
+
* ```tsx
|
|
20
|
+
* import { DbProvider, SqliteAdapter } from '@promakeai/dbreact';
|
|
21
|
+
*
|
|
22
|
+
* const adapter = new SqliteAdapter({ storageKey: 'myapp' });
|
|
23
|
+
*
|
|
24
|
+
* function App() {
|
|
25
|
+
* return (
|
|
26
|
+
* <DbProvider adapter={adapter} lang="tr" fallbackLang="en">
|
|
27
|
+
* <MyApp />
|
|
28
|
+
* </DbProvider>
|
|
29
|
+
* );
|
|
30
|
+
* }
|
|
31
|
+
* ```
|
|
32
|
+
*/
|
|
33
|
+
export declare function DbProvider({ adapter, schema, lang: langProp, fallbackLang, autoConnect, queryClient, children, }: DbProviderProps): import("react/jsx-runtime").JSX.Element;
|
|
34
|
+
/**
|
|
35
|
+
* Hook to access database context
|
|
36
|
+
*/
|
|
37
|
+
export declare function useDb(): DbContextValue;
|
|
38
|
+
/**
|
|
39
|
+
* Hook to access the raw adapter
|
|
40
|
+
*/
|
|
41
|
+
export declare function useAdapter(): IDataAdapter;
|
|
42
|
+
/**
|
|
43
|
+
* Hook to access language context
|
|
44
|
+
*/
|
|
45
|
+
export declare function useDbLang(): DbLangContextValue;
|
|
46
|
+
export {};
|
|
47
|
+
//# sourceMappingURL=DbProvider.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"DbProvider.d.ts","sourceRoot":"","sources":["../../providers/DbProvider.tsx"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,OAAO,EAQL,KAAK,SAAS,EACf,MAAM,OAAO,CAAC;AACf,OAAO,EAAE,WAAW,EAAuB,MAAM,uBAAuB,CAAC;AACzE,OAAO,KAAK,EACV,YAAY,EACZ,gBAAgB,EAChB,kBAAkB,EAClB,cAAc,EACf,MAAM,UAAU,CAAC;AAqBlB,UAAU,eAAgB,SAAQ,gBAAgB;IAChD,QAAQ,EAAE,SAAS,CAAC;IACpB,WAAW,CAAC,EAAE,WAAW,CAAC;CAC3B;AAED;;;;;;;;;;;;;;;;;;;GAmBG;AACH,wBAAgB,UAAU,CAAC,EACzB,OAAO,EACP,IAAI,EAAE,QAAe,EACrB,YAAmB,EACnB,WAAkB,EAClB,WAAgC,EAChC,QAAQ,GACT,EAAE,eAAe,
|
|
1
|
+
{"version":3,"file":"DbProvider.d.ts","sourceRoot":"","sources":["../../providers/DbProvider.tsx"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,OAAO,EAQL,KAAK,SAAS,EACf,MAAM,OAAO,CAAC;AACf,OAAO,EAAE,WAAW,EAAuB,MAAM,uBAAuB,CAAC;AACzE,OAAO,KAAK,EACV,YAAY,EACZ,gBAAgB,EAChB,kBAAkB,EAClB,cAAc,EACf,MAAM,UAAU,CAAC;AAqBlB,UAAU,eAAgB,SAAQ,gBAAgB;IAChD,QAAQ,EAAE,SAAS,CAAC;IACpB,WAAW,CAAC,EAAE,WAAW,CAAC;CAC3B;AAED;;;;;;;;;;;;;;;;;;;GAmBG;AACH,wBAAgB,UAAU,CAAC,EACzB,OAAO,EACP,MAAM,EACN,IAAI,EAAE,QAAe,EACrB,YAAmB,EACnB,WAAkB,EAClB,WAAgC,EAChC,QAAQ,GACT,EAAE,eAAe,2CAsFjB;AAED;;GAEG;AACH,wBAAgB,KAAK,IAAI,cAAc,CAMtC;AAED;;GAEG;AACH,wBAAgB,UAAU,IAAI,YAAY,CAGzC;AAED;;GAEG;AACH,wBAAgB,SAAS,IAAI,kBAAkB,CAM9C"}
|
package/dist/types.d.ts
ADDED
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* dbreact Types
|
|
3
|
+
*
|
|
4
|
+
* Re-exports from @promakeai/orm and React-specific types.
|
|
5
|
+
*/
|
|
6
|
+
export type { IDataAdapter, QueryOptions, PaginatedResult, SchemaDefinition, } from "@promakeai/orm";
|
|
7
|
+
/**
|
|
8
|
+
* Provider configuration
|
|
9
|
+
*/
|
|
10
|
+
export interface DbProviderConfig {
|
|
11
|
+
adapter: import("@promakeai/orm").IDataAdapter;
|
|
12
|
+
/** Schema definition for populate support */
|
|
13
|
+
schema?: import("@promakeai/orm").SchemaDefinition;
|
|
14
|
+
lang?: string;
|
|
15
|
+
fallbackLang?: string;
|
|
16
|
+
/** Auto-connect on mount (default: true) */
|
|
17
|
+
autoConnect?: boolean;
|
|
18
|
+
}
|
|
19
|
+
/**
|
|
20
|
+
* Language context value
|
|
21
|
+
*/
|
|
22
|
+
export interface DbLangContextValue {
|
|
23
|
+
lang: string;
|
|
24
|
+
fallbackLang: string;
|
|
25
|
+
setLang: (lang: string) => void;
|
|
26
|
+
}
|
|
27
|
+
/**
|
|
28
|
+
* Database context value
|
|
29
|
+
*/
|
|
30
|
+
export interface DbContextValue {
|
|
31
|
+
adapter: import("@promakeai/orm").IDataAdapter;
|
|
32
|
+
schema?: import("@promakeai/orm").SchemaDefinition;
|
|
33
|
+
isConnected: boolean;
|
|
34
|
+
error: Error | null;
|
|
35
|
+
}
|
|
36
|
+
//# sourceMappingURL=types.d.ts.map
|
package/dist/types.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../types.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAGH,YAAY,EACV,YAAY,EACZ,YAAY,EACZ,eAAe,
|
|
1
|
+
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../types.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAGH,YAAY,EACV,YAAY,EACZ,YAAY,EACZ,eAAe,EACf,gBAAgB,GACjB,MAAM,gBAAgB,CAAC;AAExB;;GAEG;AACH,MAAM,WAAW,gBAAgB;IAC/B,OAAO,EAAE,OAAO,gBAAgB,EAAE,YAAY,CAAC;IAC/C,6CAA6C;IAC7C,MAAM,CAAC,EAAE,OAAO,gBAAgB,EAAE,gBAAgB,CAAC;IACnD,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,4CAA4C;IAC5C,WAAW,CAAC,EAAE,OAAO,CAAC;CACvB;AAED;;GAEG;AACH,MAAM,WAAW,kBAAkB;IACjC,IAAI,EAAE,MAAM,CAAC;IACb,YAAY,EAAE,MAAM,CAAC;IACrB,OAAO,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,IAAI,CAAC;CACjC;AAED;;GAEG;AACH,MAAM,WAAW,cAAc;IAC7B,OAAO,EAAE,OAAO,gBAAgB,EAAE,YAAY,CAAC;IAC/C,MAAM,CAAC,EAAE,OAAO,gBAAgB,EAAE,gBAAgB,CAAC;IACnD,WAAW,EAAE,OAAO,CAAC;IACrB,KAAK,EAAE,KAAK,GAAG,IAAI,CAAC;CACrB"}
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* MongoDB-style Where Clause Builder
|
|
3
|
+
*
|
|
4
|
+
* Converts MongoDB/Mongoose-style query objects into SQL WHERE clauses
|
|
5
|
+
* with parameterized values.
|
|
6
|
+
*
|
|
7
|
+
* Supported operators:
|
|
8
|
+
* - Comparison: $eq, $ne, $gt, $gte, $lt, $lte
|
|
9
|
+
* - Array: $in, $nin
|
|
10
|
+
* - String: $like, $notLike
|
|
11
|
+
* - Range: $between
|
|
12
|
+
* - Null: $isNull
|
|
13
|
+
* - Negation: $not (field-level)
|
|
14
|
+
* - Logical: $and, $or, $nor
|
|
15
|
+
*/
|
|
16
|
+
export interface WhereResult {
|
|
17
|
+
sql: string;
|
|
18
|
+
params: unknown[];
|
|
19
|
+
}
|
|
20
|
+
/**
|
|
21
|
+
* Build a SQL WHERE clause from a MongoDB-style query object.
|
|
22
|
+
*
|
|
23
|
+
* @example
|
|
24
|
+
* // Simple equality (backward compatible)
|
|
25
|
+
* buildWhereClause({ active: 1 })
|
|
26
|
+
* // => { sql: "active = ?", params: [1] }
|
|
27
|
+
*
|
|
28
|
+
* @example
|
|
29
|
+
* // Comparison operators
|
|
30
|
+
* buildWhereClause({ age: { $gte: 18, $lt: 65 } })
|
|
31
|
+
* // => { sql: "(age >= ? AND age < ?)", params: [18, 65] }
|
|
32
|
+
*
|
|
33
|
+
* @example
|
|
34
|
+
* // Logical operators
|
|
35
|
+
* buildWhereClause({ $or: [{ active: 1 }, { role: "admin" }] })
|
|
36
|
+
* // => { sql: "(active = ? OR role = ?)", params: [1, "admin"] }
|
|
37
|
+
*/
|
|
38
|
+
export declare function buildWhereClause(where?: Record<string, unknown>): WhereResult;
|
|
39
|
+
//# sourceMappingURL=whereBuilder.d.ts.map
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@promakeai/dbreact",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.3",
|
|
4
4
|
"description": "React client for schema-driven multi-language database",
|
|
5
5
|
"main": "./dist/index.js",
|
|
6
6
|
"module": "./dist/index.js",
|
|
@@ -21,6 +21,9 @@
|
|
|
21
21
|
"typecheck": "tsc --noEmit",
|
|
22
22
|
"release": "bun run build && npm publish --access public"
|
|
23
23
|
},
|
|
24
|
+
"files": [
|
|
25
|
+
"dist"
|
|
26
|
+
],
|
|
24
27
|
"keywords": [
|
|
25
28
|
"react",
|
|
26
29
|
"database",
|
|
@@ -37,7 +40,7 @@
|
|
|
37
40
|
"sql.js": ">=1.11.0"
|
|
38
41
|
},
|
|
39
42
|
"dependencies": {
|
|
40
|
-
"@promakeai/orm": "1.0.
|
|
43
|
+
"@promakeai/orm": "1.0.3"
|
|
41
44
|
},
|
|
42
45
|
"devDependencies": {
|
|
43
46
|
"@tanstack/query-core": "5.90.20",
|
package/SKILL.md
DELETED
|
@@ -1,311 +0,0 @@
|
|
|
1
|
-
---
|
|
2
|
-
name: dbreact
|
|
3
|
-
description: |
|
|
4
|
-
React hooks and providers for schema-driven multi-language databases (@promakeai/dbreact).
|
|
5
|
-
Use when: integrating React apps with SQLite database, using DbProvider setup,
|
|
6
|
-
data hooks (useDbList, useDbGet, useDbCreate, useDbUpdate, useDbDelete),
|
|
7
|
-
language switching (useDbLang), SqliteAdapter for browser, React Query patterns,
|
|
8
|
-
or any @promakeai/dbreact task.
|
|
9
|
-
---
|
|
10
|
-
|
|
11
|
-
# dbreact - React Database Integration
|
|
12
|
-
|
|
13
|
-
## Installation
|
|
14
|
-
|
|
15
|
-
```bash
|
|
16
|
-
npm install @promakeai/dbreact @promakeai/orm @tanstack/react-query sql.js
|
|
17
|
-
```
|
|
18
|
-
|
|
19
|
-
Peer dependencies: `react >= 18`, `react-dom >= 18`, `@tanstack/react-query >= 5`
|
|
20
|
-
|
|
21
|
-
---
|
|
22
|
-
|
|
23
|
-
## Quick Setup
|
|
24
|
-
|
|
25
|
-
```tsx
|
|
26
|
-
import { DbProvider, SqliteAdapter, parseJSONSchema } from '@promakeai/dbreact';
|
|
27
|
-
import schema from './db/schema.json';
|
|
28
|
-
|
|
29
|
-
const adapter = new SqliteAdapter({
|
|
30
|
-
storageKey: 'myapp',
|
|
31
|
-
schema: parseJSONSchema(schema),
|
|
32
|
-
defaultLang: 'en',
|
|
33
|
-
});
|
|
34
|
-
|
|
35
|
-
function App() {
|
|
36
|
-
return (
|
|
37
|
-
<DbProvider adapter={adapter} lang="en" fallbackLang="en">
|
|
38
|
-
<YourApp />
|
|
39
|
-
</DbProvider>
|
|
40
|
-
);
|
|
41
|
-
}
|
|
42
|
-
```
|
|
43
|
-
|
|
44
|
-
---
|
|
45
|
-
|
|
46
|
-
## DbProvider Props
|
|
47
|
-
|
|
48
|
-
```tsx
|
|
49
|
-
interface DbProviderProps {
|
|
50
|
-
adapter: IDataAdapter; // Required: SqliteAdapter instance
|
|
51
|
-
lang?: string; // Current language (default: 'en')
|
|
52
|
-
fallbackLang?: string; // Fallback if translation missing (default: 'en')
|
|
53
|
-
autoConnect?: boolean; // Auto-connect on mount (default: true)
|
|
54
|
-
queryClient?: QueryClient; // Custom React Query client (optional)
|
|
55
|
-
}
|
|
56
|
-
```
|
|
57
|
-
|
|
58
|
-
Language changes automatically invalidate all queries and refetch with new language.
|
|
59
|
-
|
|
60
|
-
---
|
|
61
|
-
|
|
62
|
-
## SqliteAdapter Config
|
|
63
|
-
|
|
64
|
-
```tsx
|
|
65
|
-
const adapter = new SqliteAdapter({
|
|
66
|
-
storageKey: 'myapp', // localStorage key
|
|
67
|
-
schema: parsedSchema, // For translation support
|
|
68
|
-
defaultLang: 'en', // Default language
|
|
69
|
-
initialData?: Uint8Array, // Seed with existing DB
|
|
70
|
-
wasmPath?: string, // Custom sql.js WASM path
|
|
71
|
-
});
|
|
72
|
-
```
|
|
73
|
-
|
|
74
|
-
### Loading Existing Database
|
|
75
|
-
|
|
76
|
-
```tsx
|
|
77
|
-
// Load from base64 file
|
|
78
|
-
const response = await fetch('/app.db.b64');
|
|
79
|
-
const base64 = await response.text();
|
|
80
|
-
const bytes = Uint8Array.from(atob(base64), c => c.charCodeAt(0));
|
|
81
|
-
|
|
82
|
-
const adapter = new SqliteAdapter({
|
|
83
|
-
storageKey: 'myapp',
|
|
84
|
-
initialData: bytes,
|
|
85
|
-
schema: parseJSONSchema(schema),
|
|
86
|
-
});
|
|
87
|
-
```
|
|
88
|
-
|
|
89
|
-
---
|
|
90
|
-
|
|
91
|
-
## Data Hooks
|
|
92
|
-
|
|
93
|
-
### useDbList<T>(table, options?)
|
|
94
|
-
|
|
95
|
-
Query multiple records.
|
|
96
|
-
|
|
97
|
-
```tsx
|
|
98
|
-
interface ListOptions {
|
|
99
|
-
where?: Record<string, unknown>; // MongoDB-style filter
|
|
100
|
-
orderBy?: Array<{ field: string; direction: 'ASC' | 'DESC' }>;
|
|
101
|
-
limit?: number;
|
|
102
|
-
offset?: number;
|
|
103
|
-
enabled?: boolean; // Conditional query
|
|
104
|
-
}
|
|
105
|
-
|
|
106
|
-
function ProductList() {
|
|
107
|
-
const { data, isLoading, error } = useDbList<Product>('products', {
|
|
108
|
-
where: { stock: { $gt: 0 } },
|
|
109
|
-
orderBy: [{ field: 'name', direction: 'ASC' }],
|
|
110
|
-
limit: 10,
|
|
111
|
-
});
|
|
112
|
-
|
|
113
|
-
if (isLoading) return <div>Loading...</div>;
|
|
114
|
-
if (error) return <div>Error: {error.message}</div>;
|
|
115
|
-
|
|
116
|
-
return <ul>{data?.map(p => <li key={p.id}>{p.name}</li>)}</ul>;
|
|
117
|
-
}
|
|
118
|
-
```
|
|
119
|
-
|
|
120
|
-
### useDbGet<T>(table, id, options?)
|
|
121
|
-
|
|
122
|
-
Fetch single record by ID.
|
|
123
|
-
|
|
124
|
-
```tsx
|
|
125
|
-
function ProductDetail({ id }: { id: number }) {
|
|
126
|
-
const { data: product, isLoading } = useDbGet<Product>('products', id);
|
|
127
|
-
|
|
128
|
-
if (isLoading) return <div>Loading...</div>;
|
|
129
|
-
if (!product) return <div>Not found</div>;
|
|
130
|
-
|
|
131
|
-
return <div>{product.name} - ${product.price}</div>;
|
|
132
|
-
}
|
|
133
|
-
```
|
|
134
|
-
|
|
135
|
-
### useDbCreate<T>(table)
|
|
136
|
-
|
|
137
|
-
Create new record.
|
|
138
|
-
|
|
139
|
-
```tsx
|
|
140
|
-
function CreateProduct() {
|
|
141
|
-
const mutation = useDbCreate<Product>('products');
|
|
142
|
-
|
|
143
|
-
const handleCreate = () => {
|
|
144
|
-
mutation.mutate({ name: 'New Product', price: 99 });
|
|
145
|
-
};
|
|
146
|
-
|
|
147
|
-
return (
|
|
148
|
-
<button onClick={handleCreate} disabled={mutation.isPending}>
|
|
149
|
-
{mutation.isPending ? 'Creating...' : 'Create'}
|
|
150
|
-
</button>
|
|
151
|
-
);
|
|
152
|
-
}
|
|
153
|
-
```
|
|
154
|
-
|
|
155
|
-
### useDbUpdate<T>(table)
|
|
156
|
-
|
|
157
|
-
Update existing record.
|
|
158
|
-
|
|
159
|
-
```tsx
|
|
160
|
-
function EditProduct({ id }: { id: number }) {
|
|
161
|
-
const mutation = useDbUpdate<Product>('products');
|
|
162
|
-
|
|
163
|
-
const handleUpdate = () => {
|
|
164
|
-
mutation.mutate({ id, data: { price: 149 } });
|
|
165
|
-
};
|
|
166
|
-
|
|
167
|
-
return <button onClick={handleUpdate}>Update Price</button>;
|
|
168
|
-
}
|
|
169
|
-
```
|
|
170
|
-
|
|
171
|
-
### useDbDelete(table)
|
|
172
|
-
|
|
173
|
-
Delete record by ID.
|
|
174
|
-
|
|
175
|
-
```tsx
|
|
176
|
-
function DeleteProduct({ id }: { id: number }) {
|
|
177
|
-
const mutation = useDbDelete('products');
|
|
178
|
-
|
|
179
|
-
return (
|
|
180
|
-
<button onClick={() => mutation.mutate(id)}>
|
|
181
|
-
{mutation.isPending ? 'Deleting...' : 'Delete'}
|
|
182
|
-
</button>
|
|
183
|
-
);
|
|
184
|
-
}
|
|
185
|
-
```
|
|
186
|
-
|
|
187
|
-
---
|
|
188
|
-
|
|
189
|
-
## Language Hook
|
|
190
|
-
|
|
191
|
-
### useDbLang()
|
|
192
|
-
|
|
193
|
-
Access and change current language.
|
|
194
|
-
|
|
195
|
-
```tsx
|
|
196
|
-
function LanguageSwitcher() {
|
|
197
|
-
const { lang, setLang } = useDbLang();
|
|
198
|
-
|
|
199
|
-
return (
|
|
200
|
-
<select value={lang} onChange={(e) => setLang(e.target.value)}>
|
|
201
|
-
<option value="en">English</option>
|
|
202
|
-
<option value="tr">Turkish</option>
|
|
203
|
-
<option value="de">German</option>
|
|
204
|
-
</select>
|
|
205
|
-
);
|
|
206
|
-
}
|
|
207
|
-
```
|
|
208
|
-
|
|
209
|
-
When `setLang()` is called:
|
|
210
|
-
1. Provider state updates
|
|
211
|
-
2. All queries invalidated
|
|
212
|
-
3. Queries refetch with new language
|
|
213
|
-
4. UI updates automatically
|
|
214
|
-
|
|
215
|
-
---
|
|
216
|
-
|
|
217
|
-
## Context Hooks
|
|
218
|
-
|
|
219
|
-
### useDb()
|
|
220
|
-
|
|
221
|
-
Access connection state.
|
|
222
|
-
|
|
223
|
-
```tsx
|
|
224
|
-
const { adapter, isConnected, error } = useDb();
|
|
225
|
-
```
|
|
226
|
-
|
|
227
|
-
### useAdapter()
|
|
228
|
-
|
|
229
|
-
Direct adapter access for advanced operations.
|
|
230
|
-
|
|
231
|
-
```tsx
|
|
232
|
-
const adapter = useAdapter();
|
|
233
|
-
await adapter.createWithTranslations('products', data, translations);
|
|
234
|
-
```
|
|
235
|
-
|
|
236
|
-
---
|
|
237
|
-
|
|
238
|
-
## Query Operators
|
|
239
|
-
|
|
240
|
-
Same MongoDB-style operators as dbcli:
|
|
241
|
-
|
|
242
|
-
```tsx
|
|
243
|
-
useDbList('products', {
|
|
244
|
-
where: {
|
|
245
|
-
$and: [
|
|
246
|
-
{ price: { $between: [50, 500] } },
|
|
247
|
-
{ stock: { $gt: 0 } },
|
|
248
|
-
{ categoryId: { $in: [1, 5, 10] } },
|
|
249
|
-
{ $or: [{ active: true }, { featured: true }] }
|
|
250
|
-
]
|
|
251
|
-
}
|
|
252
|
-
});
|
|
253
|
-
```
|
|
254
|
-
|
|
255
|
-
Operators: `$eq`, `$ne`, `$gt`, `$gte`, `$lt`, `$lte`, `$in`, `$nin`, `$like`, `$notLike`, `$between`, `$isNull`, `$and`, `$or`, `$nor`, `$not`
|
|
256
|
-
|
|
257
|
-
---
|
|
258
|
-
|
|
259
|
-
## Type Definitions
|
|
260
|
-
|
|
261
|
-
Use generated types from `dbcli generate`:
|
|
262
|
-
|
|
263
|
-
```tsx
|
|
264
|
-
// src/db/types.ts (generated)
|
|
265
|
-
export interface DbProduct {
|
|
266
|
-
id: number;
|
|
267
|
-
name: string;
|
|
268
|
-
price: number;
|
|
269
|
-
stock: number;
|
|
270
|
-
}
|
|
271
|
-
|
|
272
|
-
// Usage
|
|
273
|
-
const { data } = useDbList<DbProduct>('products');
|
|
274
|
-
const mutation = useDbCreate<DbProduct>('products');
|
|
275
|
-
```
|
|
276
|
-
|
|
277
|
-
---
|
|
278
|
-
|
|
279
|
-
## Best Practices
|
|
280
|
-
|
|
281
|
-
1. **Single DbProvider at root** - Don't nest providers
|
|
282
|
-
2. **Use generated types** - Type safety with `DbXxx` interfaces
|
|
283
|
-
3. **Conditional queries** - Use `enabled: false` to prevent unwanted fetches
|
|
284
|
-
4. **Handle all states** - Check `isLoading`, `error`, and empty data
|
|
285
|
-
5. **Language via hook** - Use `setLang()` not prop changes for proper invalidation
|
|
286
|
-
6. **Batch with adapter** - Use `adapter.createMany()` for bulk operations
|
|
287
|
-
7. **Check connection** - Use `useDb().isConnected` before critical operations
|
|
288
|
-
|
|
289
|
-
---
|
|
290
|
-
|
|
291
|
-
## Exports
|
|
292
|
-
|
|
293
|
-
```tsx
|
|
294
|
-
// Provider & Context
|
|
295
|
-
export { DbProvider, useDb, useAdapter, useDbLang };
|
|
296
|
-
|
|
297
|
-
// Data Hooks
|
|
298
|
-
export { useDbList, useDbGet, useDbCreate, useDbUpdate, useDbDelete };
|
|
299
|
-
|
|
300
|
-
// Adapter
|
|
301
|
-
export { SqliteAdapter };
|
|
302
|
-
|
|
303
|
-
// Schema
|
|
304
|
-
export { parseJSONSchema, defineSchema, f };
|
|
305
|
-
|
|
306
|
-
// Query Utilities
|
|
307
|
-
export { buildWhereClause };
|
|
308
|
-
|
|
309
|
-
// Types
|
|
310
|
-
export type { DbProviderConfig, DbContextValue, DbLangContextValue, ListOptions };
|
|
311
|
-
```
|
package/providers/DbProvider.tsx
DELETED
|
@@ -1,191 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Database Provider
|
|
3
|
-
*
|
|
4
|
-
* React context provider for database operations with language support.
|
|
5
|
-
*/
|
|
6
|
-
|
|
7
|
-
import {
|
|
8
|
-
createContext,
|
|
9
|
-
useContext,
|
|
10
|
-
useState,
|
|
11
|
-
useEffect,
|
|
12
|
-
useMemo,
|
|
13
|
-
useCallback,
|
|
14
|
-
useRef,
|
|
15
|
-
type ReactNode,
|
|
16
|
-
} from "react";
|
|
17
|
-
import { QueryClient, QueryClientProvider } from "@tanstack/react-query";
|
|
18
|
-
import type {
|
|
19
|
-
IDataAdapter,
|
|
20
|
-
DbProviderConfig,
|
|
21
|
-
DbLangContextValue,
|
|
22
|
-
DbContextValue,
|
|
23
|
-
} from "../types";
|
|
24
|
-
|
|
25
|
-
// Database context
|
|
26
|
-
const DbContext = createContext<DbContextValue | null>(null);
|
|
27
|
-
|
|
28
|
-
// Language context
|
|
29
|
-
const DbLangContext = createContext<DbLangContextValue | null>(null);
|
|
30
|
-
|
|
31
|
-
/**
|
|
32
|
-
* Default QueryClient for React Query
|
|
33
|
-
*/
|
|
34
|
-
const defaultQueryClient = new QueryClient({
|
|
35
|
-
defaultOptions: {
|
|
36
|
-
queries: {
|
|
37
|
-
staleTime: 1000 * 60 * 5, // 5 minutes
|
|
38
|
-
gcTime: 1000 * 60 * 30, // 30 minutes (formerly cacheTime)
|
|
39
|
-
refetchOnWindowFocus: false,
|
|
40
|
-
},
|
|
41
|
-
},
|
|
42
|
-
});
|
|
43
|
-
|
|
44
|
-
interface DbProviderProps extends DbProviderConfig {
|
|
45
|
-
children: ReactNode;
|
|
46
|
-
queryClient?: QueryClient;
|
|
47
|
-
}
|
|
48
|
-
|
|
49
|
-
/**
|
|
50
|
-
* Database Provider Component
|
|
51
|
-
*
|
|
52
|
-
* Wraps application with database context, language context, and React Query.
|
|
53
|
-
*
|
|
54
|
-
* @example
|
|
55
|
-
* ```tsx
|
|
56
|
-
* import { DbProvider, SqliteAdapter } from '@promakeai/dbreact';
|
|
57
|
-
*
|
|
58
|
-
* const adapter = new SqliteAdapter({ storageKey: 'myapp' });
|
|
59
|
-
*
|
|
60
|
-
* function App() {
|
|
61
|
-
* return (
|
|
62
|
-
* <DbProvider adapter={adapter} lang="tr" fallbackLang="en">
|
|
63
|
-
* <MyApp />
|
|
64
|
-
* </DbProvider>
|
|
65
|
-
* );
|
|
66
|
-
* }
|
|
67
|
-
* ```
|
|
68
|
-
*/
|
|
69
|
-
export function DbProvider({
|
|
70
|
-
adapter,
|
|
71
|
-
lang: langProp = "en",
|
|
72
|
-
fallbackLang = "en",
|
|
73
|
-
autoConnect = true,
|
|
74
|
-
queryClient = defaultQueryClient,
|
|
75
|
-
children,
|
|
76
|
-
}: DbProviderProps) {
|
|
77
|
-
const [isConnected, setIsConnected] = useState(false);
|
|
78
|
-
const [error, setError] = useState<Error | null>(null);
|
|
79
|
-
const [lang, setLangState] = useState(langProp);
|
|
80
|
-
const isFirstRender = useRef(true);
|
|
81
|
-
|
|
82
|
-
// Sync internal state when prop changes
|
|
83
|
-
useEffect(() => {
|
|
84
|
-
if (langProp !== lang) {
|
|
85
|
-
setLangState(langProp);
|
|
86
|
-
}
|
|
87
|
-
}, [langProp]);
|
|
88
|
-
|
|
89
|
-
// Invalidate all queries when language changes (skip first render)
|
|
90
|
-
useEffect(() => {
|
|
91
|
-
if (isFirstRender.current) {
|
|
92
|
-
isFirstRender.current = false;
|
|
93
|
-
return;
|
|
94
|
-
}
|
|
95
|
-
// Invalidate all queries to refetch with new language
|
|
96
|
-
queryClient.invalidateQueries();
|
|
97
|
-
}, [lang]);
|
|
98
|
-
|
|
99
|
-
// Connect to adapter on mount
|
|
100
|
-
useEffect(() => {
|
|
101
|
-
if (!autoConnect) return;
|
|
102
|
-
|
|
103
|
-
let mounted = true;
|
|
104
|
-
|
|
105
|
-
async function connect() {
|
|
106
|
-
try {
|
|
107
|
-
await adapter.connect?.();
|
|
108
|
-
if (mounted) {
|
|
109
|
-
setIsConnected(true);
|
|
110
|
-
setError(null);
|
|
111
|
-
}
|
|
112
|
-
} catch (err) {
|
|
113
|
-
if (mounted) {
|
|
114
|
-
setError(err instanceof Error ? err : new Error(String(err)));
|
|
115
|
-
setIsConnected(false);
|
|
116
|
-
}
|
|
117
|
-
}
|
|
118
|
-
}
|
|
119
|
-
|
|
120
|
-
connect();
|
|
121
|
-
|
|
122
|
-
return () => {
|
|
123
|
-
mounted = false;
|
|
124
|
-
};
|
|
125
|
-
}, [adapter, autoConnect]);
|
|
126
|
-
|
|
127
|
-
// setLang updates internal state (and triggers invalidation via effect)
|
|
128
|
-
const setLang = useCallback((newLang: string) => {
|
|
129
|
-
setLangState(newLang);
|
|
130
|
-
}, []);
|
|
131
|
-
|
|
132
|
-
// Database context value
|
|
133
|
-
const dbContextValue = useMemo<DbContextValue>(
|
|
134
|
-
() => ({
|
|
135
|
-
adapter,
|
|
136
|
-
isConnected,
|
|
137
|
-
error,
|
|
138
|
-
}),
|
|
139
|
-
[adapter, isConnected, error]
|
|
140
|
-
);
|
|
141
|
-
|
|
142
|
-
// Language context value
|
|
143
|
-
const langContextValue = useMemo<DbLangContextValue>(
|
|
144
|
-
() => ({
|
|
145
|
-
lang,
|
|
146
|
-
fallbackLang,
|
|
147
|
-
setLang,
|
|
148
|
-
}),
|
|
149
|
-
[lang, fallbackLang, setLang]
|
|
150
|
-
);
|
|
151
|
-
|
|
152
|
-
return (
|
|
153
|
-
<QueryClientProvider client={queryClient}>
|
|
154
|
-
<DbContext.Provider value={dbContextValue}>
|
|
155
|
-
<DbLangContext.Provider value={langContextValue}>
|
|
156
|
-
{children}
|
|
157
|
-
</DbLangContext.Provider>
|
|
158
|
-
</DbContext.Provider>
|
|
159
|
-
</QueryClientProvider>
|
|
160
|
-
);
|
|
161
|
-
}
|
|
162
|
-
|
|
163
|
-
/**
|
|
164
|
-
* Hook to access database context
|
|
165
|
-
*/
|
|
166
|
-
export function useDb(): DbContextValue {
|
|
167
|
-
const context = useContext(DbContext);
|
|
168
|
-
if (!context) {
|
|
169
|
-
throw new Error("useDb must be used within a DbProvider");
|
|
170
|
-
}
|
|
171
|
-
return context;
|
|
172
|
-
}
|
|
173
|
-
|
|
174
|
-
/**
|
|
175
|
-
* Hook to access the raw adapter
|
|
176
|
-
*/
|
|
177
|
-
export function useAdapter(): IDataAdapter {
|
|
178
|
-
const { adapter } = useDb();
|
|
179
|
-
return adapter;
|
|
180
|
-
}
|
|
181
|
-
|
|
182
|
-
/**
|
|
183
|
-
* Hook to access language context
|
|
184
|
-
*/
|
|
185
|
-
export function useDbLang(): DbLangContextValue {
|
|
186
|
-
const context = useContext(DbLangContext);
|
|
187
|
-
if (!context) {
|
|
188
|
-
throw new Error("useDbLang must be used within a DbProvider");
|
|
189
|
-
}
|
|
190
|
-
return context;
|
|
191
|
-
}
|