@promakeai/dbreact 1.0.1 → 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/RestAdapter.d.ts +83 -0
- package/dist/adapters/SqliteAdapter.d.ts +169 -0
- package/dist/adapters/SqliteAdapter.d.ts.map +1 -1
- 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 +61 -12
- 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,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
|
}
|
|
@@ -198,6 +224,7 @@ import {
|
|
|
198
224
|
class SqliteAdapter {
|
|
199
225
|
db = null;
|
|
200
226
|
SQL = null;
|
|
227
|
+
tableColumnsCache = new Map;
|
|
201
228
|
config;
|
|
202
229
|
schema;
|
|
203
230
|
defaultLang;
|
|
@@ -215,6 +242,7 @@ class SqliteAdapter {
|
|
|
215
242
|
setSchema(schema) {
|
|
216
243
|
this.schema = schema;
|
|
217
244
|
this.config.schema = schema;
|
|
245
|
+
this.tableColumnsCache.clear();
|
|
218
246
|
}
|
|
219
247
|
async connect() {
|
|
220
248
|
this.SQL = await initSqlJs({
|
|
@@ -322,6 +350,7 @@ class SqliteAdapter {
|
|
|
322
350
|
schema: this.schema,
|
|
323
351
|
lang: options.lang,
|
|
324
352
|
fallbackLang: options.fallbackLang ?? this.defaultLang,
|
|
353
|
+
mainFallbackFields: await this.getAvailableMainFallbackFields(table),
|
|
325
354
|
where: options.where,
|
|
326
355
|
orderBy: options.orderBy,
|
|
327
356
|
limit: options.limit,
|
|
@@ -351,7 +380,7 @@ class SqliteAdapter {
|
|
|
351
380
|
const results = await this.list(table, { where: { id }, limit: 1 });
|
|
352
381
|
return results[0] ?? null;
|
|
353
382
|
}
|
|
354
|
-
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));
|
|
355
384
|
const rows = this.runQuery(sql, params);
|
|
356
385
|
const deserialized = this.deserializeResults(table, rows);
|
|
357
386
|
return deserialized[0] ?? null;
|
|
@@ -547,6 +576,7 @@ class SqliteAdapter {
|
|
|
547
576
|
this.persist();
|
|
548
577
|
this.db.close();
|
|
549
578
|
this.db = null;
|
|
579
|
+
this.tableColumnsCache.clear();
|
|
550
580
|
}
|
|
551
581
|
}
|
|
552
582
|
async beginTransaction() {
|
|
@@ -589,6 +619,25 @@ class SqliteAdapter {
|
|
|
589
619
|
this.persist();
|
|
590
620
|
return { created: ids.length, ids };
|
|
591
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
|
+
}
|
|
592
641
|
async updateMany(table, updates) {
|
|
593
642
|
let updated = 0;
|
|
594
643
|
for (const { id, data } of updates) {
|
|
@@ -636,7 +685,7 @@ export {
|
|
|
636
685
|
useDbCreate,
|
|
637
686
|
useDb,
|
|
638
687
|
useAdapter,
|
|
639
|
-
resolvePopulate,
|
|
688
|
+
resolvePopulate2 as resolvePopulate,
|
|
640
689
|
parseJSONSchema,
|
|
641
690
|
getPopulatableFields,
|
|
642
691
|
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.4",
|
|
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.4"
|
|
41
44
|
},
|
|
42
45
|
"devDependencies": {
|
|
43
46
|
"@tanstack/query-core": "5.90.20",
|