@promakeai/orm 1.3.1 → 1.4.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/adapters/RestAdapter.d.ts +5 -0
- package/dist/index.js +75 -51
- package/package.json +1 -1
- package/src/adapters/RestAdapter.ts +38 -6
|
@@ -18,6 +18,8 @@ export interface RestAdapterConfig {
|
|
|
18
18
|
getToken?: () => string | null;
|
|
19
19
|
/** Custom headers to include in all requests */
|
|
20
20
|
headers?: Record<string, string>;
|
|
21
|
+
/** Dynamic headers getter - called on every request. Merged after static headers. */
|
|
22
|
+
getHeaders?: () => Record<string, string>;
|
|
21
23
|
/** Schema definition for translation support */
|
|
22
24
|
schema?: SchemaDefinition;
|
|
23
25
|
/** Default language for translations */
|
|
@@ -33,6 +35,7 @@ export declare class RestAdapter implements IDataAdapter {
|
|
|
33
35
|
private token?;
|
|
34
36
|
private getTokenFn?;
|
|
35
37
|
private customHeaders;
|
|
38
|
+
private getHeadersFn?;
|
|
36
39
|
private fetchFn;
|
|
37
40
|
private timeout;
|
|
38
41
|
schema?: SchemaDefinition;
|
|
@@ -43,6 +46,8 @@ export declare class RestAdapter implements IDataAdapter {
|
|
|
43
46
|
private request;
|
|
44
47
|
private buildQueryParams;
|
|
45
48
|
private urlWithParams;
|
|
49
|
+
private deserializeRecord;
|
|
50
|
+
private deserializeRecords;
|
|
46
51
|
setSchema(schema: SchemaDefinition): void;
|
|
47
52
|
connect(): Promise<void>;
|
|
48
53
|
private mapFieldType;
|
package/dist/index.js
CHANGED
|
@@ -293,6 +293,52 @@ class ORM {
|
|
|
293
293
|
this.schema = schema;
|
|
294
294
|
}
|
|
295
295
|
}
|
|
296
|
+
// src/utils/deserializer.ts
|
|
297
|
+
function deserializeValue(value, fieldType) {
|
|
298
|
+
if (value === null || value === undefined)
|
|
299
|
+
return value;
|
|
300
|
+
if (fieldType === "bool") {
|
|
301
|
+
return value === 1 || value === true;
|
|
302
|
+
}
|
|
303
|
+
if (isJsonType(fieldType) && typeof value === "string") {
|
|
304
|
+
try {
|
|
305
|
+
return JSON.parse(value);
|
|
306
|
+
} catch {
|
|
307
|
+
return value;
|
|
308
|
+
}
|
|
309
|
+
}
|
|
310
|
+
return value;
|
|
311
|
+
}
|
|
312
|
+
function deserializeRow(row, fields) {
|
|
313
|
+
const result = { ...row };
|
|
314
|
+
for (const [fieldName, field] of Object.entries(fields)) {
|
|
315
|
+
if (fieldName in result) {
|
|
316
|
+
result[fieldName] = deserializeValue(result[fieldName], field.type);
|
|
317
|
+
}
|
|
318
|
+
}
|
|
319
|
+
return result;
|
|
320
|
+
}
|
|
321
|
+
function serializeValue(value, fieldType) {
|
|
322
|
+
if (value === null || value === undefined)
|
|
323
|
+
return value;
|
|
324
|
+
if (fieldType === "bool") {
|
|
325
|
+
return value ? 1 : 0;
|
|
326
|
+
}
|
|
327
|
+
if (isJsonType(fieldType) && typeof value !== "string") {
|
|
328
|
+
return JSON.stringify(value);
|
|
329
|
+
}
|
|
330
|
+
return value;
|
|
331
|
+
}
|
|
332
|
+
function serializeRow(data, fields) {
|
|
333
|
+
const result = { ...data };
|
|
334
|
+
for (const [fieldName, field] of Object.entries(fields)) {
|
|
335
|
+
if (fieldName in result) {
|
|
336
|
+
result[fieldName] = serializeValue(result[fieldName], field.type);
|
|
337
|
+
}
|
|
338
|
+
}
|
|
339
|
+
return result;
|
|
340
|
+
}
|
|
341
|
+
|
|
296
342
|
// src/schema/schemaHelpers.ts
|
|
297
343
|
function getTranslatableFields(table) {
|
|
298
344
|
return Object.entries(table.fields).filter(([_, field]) => field.translatable).map(([name]) => name);
|
|
@@ -575,6 +621,7 @@ class RestAdapter {
|
|
|
575
621
|
token;
|
|
576
622
|
getTokenFn;
|
|
577
623
|
customHeaders;
|
|
624
|
+
getHeadersFn;
|
|
578
625
|
fetchFn;
|
|
579
626
|
timeout;
|
|
580
627
|
schema;
|
|
@@ -585,6 +632,7 @@ class RestAdapter {
|
|
|
585
632
|
this.token = config.token;
|
|
586
633
|
this.getTokenFn = config.getToken;
|
|
587
634
|
this.customHeaders = config.headers ?? {};
|
|
635
|
+
this.getHeadersFn = config.getHeaders;
|
|
588
636
|
const gf = globalThis;
|
|
589
637
|
this.fetchFn = config.fetch ?? gf.fetch.bind(gf);
|
|
590
638
|
this.timeout = config.timeout ?? 30000;
|
|
@@ -600,6 +648,9 @@ class RestAdapter {
|
|
|
600
648
|
Accept: "application/json",
|
|
601
649
|
...this.customHeaders
|
|
602
650
|
};
|
|
651
|
+
if (this.getHeadersFn) {
|
|
652
|
+
Object.assign(headers, this.getHeadersFn());
|
|
653
|
+
}
|
|
603
654
|
const token = this.getTokenFn ? this.getTokenFn() : this.token;
|
|
604
655
|
if (token) {
|
|
605
656
|
headers["Authorization"] = `Bearer ${token}`;
|
|
@@ -653,6 +704,24 @@ class RestAdapter {
|
|
|
653
704
|
const qs = params.toString();
|
|
654
705
|
return qs ? `${base}?${qs}` : base;
|
|
655
706
|
}
|
|
707
|
+
deserializeRecord(table, record) {
|
|
708
|
+
if (record == null)
|
|
709
|
+
return null;
|
|
710
|
+
const tableSchema = this.schema?.tables[table];
|
|
711
|
+
if (!tableSchema) {
|
|
712
|
+
return record;
|
|
713
|
+
}
|
|
714
|
+
return deserializeRow(record, tableSchema.fields);
|
|
715
|
+
}
|
|
716
|
+
deserializeRecords(table, records) {
|
|
717
|
+
if (!records?.length)
|
|
718
|
+
return records ?? [];
|
|
719
|
+
const tableSchema = this.schema?.tables[table];
|
|
720
|
+
if (!tableSchema) {
|
|
721
|
+
return records;
|
|
722
|
+
}
|
|
723
|
+
return records.map((record) => deserializeRow(record, tableSchema.fields));
|
|
724
|
+
}
|
|
656
725
|
setSchema(schema) {
|
|
657
726
|
this.schema = schema;
|
|
658
727
|
}
|
|
@@ -719,7 +788,7 @@ class RestAdapter {
|
|
|
719
788
|
const params = this.buildQueryParams(options);
|
|
720
789
|
const url = this.urlWithParams(this.buildUrl(`/${table}/list`), params);
|
|
721
790
|
const res = await this.request(url);
|
|
722
|
-
return res.data;
|
|
791
|
+
return this.deserializeRecords(table, res.data);
|
|
723
792
|
}
|
|
724
793
|
async get(table, id, options) {
|
|
725
794
|
const params = new URLSearchParams;
|
|
@@ -730,7 +799,7 @@ class RestAdapter {
|
|
|
730
799
|
const url = this.urlWithParams(this.buildUrl(`/${table}/${id}`), params);
|
|
731
800
|
try {
|
|
732
801
|
const res = await this.request(url);
|
|
733
|
-
return res.data
|
|
802
|
+
return this.deserializeRecord(table, res.data);
|
|
734
803
|
} catch (err) {
|
|
735
804
|
if (err instanceof Error && err.message.includes("404")) {
|
|
736
805
|
return null;
|
|
@@ -777,7 +846,7 @@ class RestAdapter {
|
|
|
777
846
|
if (res.id !== undefined && !("id" in result)) {
|
|
778
847
|
result.id = res.id;
|
|
779
848
|
}
|
|
780
|
-
return result;
|
|
849
|
+
return this.deserializeRecord(table, result);
|
|
781
850
|
}
|
|
782
851
|
async update(table, id, data, options) {
|
|
783
852
|
const url = this.buildUrl(`/${table}/${id}`);
|
|
@@ -789,7 +858,7 @@ class RestAdapter {
|
|
|
789
858
|
method: "PUT",
|
|
790
859
|
body: JSON.stringify(body)
|
|
791
860
|
});
|
|
792
|
-
return res.data;
|
|
861
|
+
return this.deserializeRecord(table, res.data);
|
|
793
862
|
}
|
|
794
863
|
async delete(table, id) {
|
|
795
864
|
const url = this.buildUrl(`/${table}/${id}`);
|
|
@@ -849,7 +918,7 @@ class RestAdapter {
|
|
|
849
918
|
if (res.id !== undefined && !("id" in result)) {
|
|
850
919
|
result.id = res.id;
|
|
851
920
|
}
|
|
852
|
-
return result;
|
|
921
|
+
return this.deserializeRecord(table, result);
|
|
853
922
|
}
|
|
854
923
|
async upsertTranslation(table, id, lang, data) {
|
|
855
924
|
const url = this.buildUrl(`/${table}/${id}`);
|
|
@@ -865,7 +934,7 @@ class RestAdapter {
|
|
|
865
934
|
params.set("where", JSON.stringify({ [fkName]: id }));
|
|
866
935
|
const url = this.urlWithParams(this.buildUrl(`/${translationTable}/list`), params);
|
|
867
936
|
const res = await this.request(url);
|
|
868
|
-
return res.data;
|
|
937
|
+
return this.deserializeRecords(table, res.data);
|
|
869
938
|
}
|
|
870
939
|
async raw(query, params) {
|
|
871
940
|
const url = this.buildUrl("/exec");
|
|
@@ -1319,51 +1388,6 @@ function convertJSONField(jsonField) {
|
|
|
1319
1388
|
ref
|
|
1320
1389
|
};
|
|
1321
1390
|
}
|
|
1322
|
-
// src/utils/deserializer.ts
|
|
1323
|
-
function deserializeValue(value, fieldType) {
|
|
1324
|
-
if (value === null || value === undefined)
|
|
1325
|
-
return value;
|
|
1326
|
-
if (fieldType === "bool") {
|
|
1327
|
-
return value === 1 || value === true;
|
|
1328
|
-
}
|
|
1329
|
-
if (isJsonType(fieldType) && typeof value === "string") {
|
|
1330
|
-
try {
|
|
1331
|
-
return JSON.parse(value);
|
|
1332
|
-
} catch {
|
|
1333
|
-
return value;
|
|
1334
|
-
}
|
|
1335
|
-
}
|
|
1336
|
-
return value;
|
|
1337
|
-
}
|
|
1338
|
-
function deserializeRow(row, fields) {
|
|
1339
|
-
const result = { ...row };
|
|
1340
|
-
for (const [fieldName, field] of Object.entries(fields)) {
|
|
1341
|
-
if (fieldName in result) {
|
|
1342
|
-
result[fieldName] = deserializeValue(result[fieldName], field.type);
|
|
1343
|
-
}
|
|
1344
|
-
}
|
|
1345
|
-
return result;
|
|
1346
|
-
}
|
|
1347
|
-
function serializeValue(value, fieldType) {
|
|
1348
|
-
if (value === null || value === undefined)
|
|
1349
|
-
return value;
|
|
1350
|
-
if (fieldType === "bool") {
|
|
1351
|
-
return value ? 1 : 0;
|
|
1352
|
-
}
|
|
1353
|
-
if (isJsonType(fieldType) && typeof value !== "string") {
|
|
1354
|
-
return JSON.stringify(value);
|
|
1355
|
-
}
|
|
1356
|
-
return value;
|
|
1357
|
-
}
|
|
1358
|
-
function serializeRow(data, fields) {
|
|
1359
|
-
const result = { ...data };
|
|
1360
|
-
for (const [fieldName, field] of Object.entries(fields)) {
|
|
1361
|
-
if (fieldName in result) {
|
|
1362
|
-
result[fieldName] = serializeValue(result[fieldName], field.type);
|
|
1363
|
-
}
|
|
1364
|
-
}
|
|
1365
|
-
return result;
|
|
1366
|
-
}
|
|
1367
1391
|
export {
|
|
1368
1392
|
validateTable,
|
|
1369
1393
|
validateSchema,
|
package/package.json
CHANGED
|
@@ -7,6 +7,7 @@
|
|
|
7
7
|
|
|
8
8
|
import type { IDataAdapter } from "./IDataAdapter";
|
|
9
9
|
import type { QueryOptions, PaginatedResult, SchemaDefinition } from "../types";
|
|
10
|
+
import { deserializeRow } from "../utils/deserializer";
|
|
10
11
|
import { toTranslationTableName, toTranslationFKName } from "../schema";
|
|
11
12
|
|
|
12
13
|
type FetchFn = (input: string, init?: any) => Promise<any>;
|
|
@@ -22,6 +23,8 @@ export interface RestAdapterConfig {
|
|
|
22
23
|
getToken?: () => string | null;
|
|
23
24
|
/** Custom headers to include in all requests */
|
|
24
25
|
headers?: Record<string, string>;
|
|
26
|
+
/** Dynamic headers getter - called on every request. Merged after static headers. */
|
|
27
|
+
getHeaders?: () => Record<string, string>;
|
|
25
28
|
/** Schema definition for translation support */
|
|
26
29
|
schema?: SchemaDefinition;
|
|
27
30
|
/** Default language for translations */
|
|
@@ -38,6 +41,7 @@ export class RestAdapter implements IDataAdapter {
|
|
|
38
41
|
private token?: string;
|
|
39
42
|
private getTokenFn?: () => string | null;
|
|
40
43
|
private customHeaders: Record<string, string>;
|
|
44
|
+
private getHeadersFn?: () => Record<string, string>;
|
|
41
45
|
private fetchFn: FetchFn;
|
|
42
46
|
private timeout: number;
|
|
43
47
|
|
|
@@ -50,6 +54,7 @@ export class RestAdapter implements IDataAdapter {
|
|
|
50
54
|
this.token = config.token;
|
|
51
55
|
this.getTokenFn = config.getToken;
|
|
52
56
|
this.customHeaders = config.headers ?? {};
|
|
57
|
+
this.getHeadersFn = config.getHeaders;
|
|
53
58
|
const gf = globalThis as any;
|
|
54
59
|
this.fetchFn = config.fetch ?? gf.fetch.bind(gf);
|
|
55
60
|
this.timeout = config.timeout ?? 30000;
|
|
@@ -69,6 +74,9 @@ export class RestAdapter implements IDataAdapter {
|
|
|
69
74
|
Accept: "application/json",
|
|
70
75
|
...this.customHeaders,
|
|
71
76
|
};
|
|
77
|
+
if (this.getHeadersFn) {
|
|
78
|
+
Object.assign(headers, this.getHeadersFn());
|
|
79
|
+
}
|
|
72
80
|
const token = this.getTokenFn ? this.getTokenFn() : this.token;
|
|
73
81
|
if (token) {
|
|
74
82
|
headers["Authorization"] = `Bearer ${token}`;
|
|
@@ -134,6 +142,30 @@ export class RestAdapter implements IDataAdapter {
|
|
|
134
142
|
return qs ? `${base}?${qs}` : base;
|
|
135
143
|
}
|
|
136
144
|
|
|
145
|
+
private deserializeRecord<T>(table: string, record: T | null | undefined): T | null {
|
|
146
|
+
if (record == null) return null;
|
|
147
|
+
|
|
148
|
+
const tableSchema = this.schema?.tables[table];
|
|
149
|
+
if (!tableSchema) {
|
|
150
|
+
return record;
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
return deserializeRow<T>(record as Record<string, unknown>, tableSchema.fields);
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
private deserializeRecords<T>(table: string, records: T[] | undefined): T[] {
|
|
157
|
+
if (!records?.length) return records ?? [];
|
|
158
|
+
|
|
159
|
+
const tableSchema = this.schema?.tables[table];
|
|
160
|
+
if (!tableSchema) {
|
|
161
|
+
return records;
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
return records.map((record) =>
|
|
165
|
+
deserializeRow<T>(record as Record<string, unknown>, tableSchema.fields)
|
|
166
|
+
);
|
|
167
|
+
}
|
|
168
|
+
|
|
137
169
|
// ==================== Lifecycle ====================
|
|
138
170
|
|
|
139
171
|
setSchema(schema: SchemaDefinition): void {
|
|
@@ -208,7 +240,7 @@ export class RestAdapter implements IDataAdapter {
|
|
|
208
240
|
const params = this.buildQueryParams(options);
|
|
209
241
|
const url = this.urlWithParams(this.buildUrl(`/${table}/list`), params);
|
|
210
242
|
const res = await this.request<{ data: T[] }>(url);
|
|
211
|
-
return res.data;
|
|
243
|
+
return this.deserializeRecords(table, res.data);
|
|
212
244
|
}
|
|
213
245
|
|
|
214
246
|
async get<T = unknown>(
|
|
@@ -223,7 +255,7 @@ export class RestAdapter implements IDataAdapter {
|
|
|
223
255
|
|
|
224
256
|
try {
|
|
225
257
|
const res = await this.request<{ data: T }>(url);
|
|
226
|
-
return res.data
|
|
258
|
+
return this.deserializeRecord(table, res.data);
|
|
227
259
|
} catch (err) {
|
|
228
260
|
if (err instanceof Error && err.message.includes("404")) {
|
|
229
261
|
return null;
|
|
@@ -288,7 +320,7 @@ export class RestAdapter implements IDataAdapter {
|
|
|
288
320
|
if (res.id !== undefined && !("id" in result)) {
|
|
289
321
|
result.id = res.id;
|
|
290
322
|
}
|
|
291
|
-
return result as T;
|
|
323
|
+
return this.deserializeRecord(table, result as T) as T;
|
|
292
324
|
}
|
|
293
325
|
|
|
294
326
|
async update<T = unknown>(
|
|
@@ -306,7 +338,7 @@ export class RestAdapter implements IDataAdapter {
|
|
|
306
338
|
method: "PUT",
|
|
307
339
|
body: JSON.stringify(body),
|
|
308
340
|
});
|
|
309
|
-
return res.data;
|
|
341
|
+
return this.deserializeRecord(table, res.data) as T;
|
|
310
342
|
}
|
|
311
343
|
|
|
312
344
|
async delete(table: string, id: string | number): Promise<boolean> {
|
|
@@ -392,7 +424,7 @@ export class RestAdapter implements IDataAdapter {
|
|
|
392
424
|
if (res.id !== undefined && !("id" in result)) {
|
|
393
425
|
result.id = res.id;
|
|
394
426
|
}
|
|
395
|
-
return result as T;
|
|
427
|
+
return this.deserializeRecord(table, result as T) as T;
|
|
396
428
|
}
|
|
397
429
|
|
|
398
430
|
async upsertTranslation(
|
|
@@ -421,7 +453,7 @@ export class RestAdapter implements IDataAdapter {
|
|
|
421
453
|
params
|
|
422
454
|
);
|
|
423
455
|
const res = await this.request<{ data: T[] }>(url);
|
|
424
|
-
return res.data;
|
|
456
|
+
return this.deserializeRecords(table, res.data);
|
|
425
457
|
}
|
|
426
458
|
|
|
427
459
|
// ==================== Raw Query Methods ====================
|