@promakeai/orm 1.3.0 → 1.3.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/adapters/RestAdapter.d.ts +2 -0
- package/dist/index.js +70 -51
- package/package.json +1 -1
- package/src/adapters/RestAdapter.ts +77 -52
|
@@ -43,6 +43,8 @@ export declare class RestAdapter implements IDataAdapter {
|
|
|
43
43
|
private request;
|
|
44
44
|
private buildQueryParams;
|
|
45
45
|
private urlWithParams;
|
|
46
|
+
private deserializeRecord;
|
|
47
|
+
private deserializeRecords;
|
|
46
48
|
setSchema(schema: SchemaDefinition): void;
|
|
47
49
|
connect(): Promise<void>;
|
|
48
50
|
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);
|
|
@@ -653,6 +699,24 @@ class RestAdapter {
|
|
|
653
699
|
const qs = params.toString();
|
|
654
700
|
return qs ? `${base}?${qs}` : base;
|
|
655
701
|
}
|
|
702
|
+
deserializeRecord(table, record) {
|
|
703
|
+
if (record == null)
|
|
704
|
+
return null;
|
|
705
|
+
const tableSchema = this.schema?.tables[table];
|
|
706
|
+
if (!tableSchema) {
|
|
707
|
+
return record;
|
|
708
|
+
}
|
|
709
|
+
return deserializeRow(record, tableSchema.fields);
|
|
710
|
+
}
|
|
711
|
+
deserializeRecords(table, records) {
|
|
712
|
+
if (!records?.length)
|
|
713
|
+
return records ?? [];
|
|
714
|
+
const tableSchema = this.schema?.tables[table];
|
|
715
|
+
if (!tableSchema) {
|
|
716
|
+
return records;
|
|
717
|
+
}
|
|
718
|
+
return records.map((record) => deserializeRow(record, tableSchema.fields));
|
|
719
|
+
}
|
|
656
720
|
setSchema(schema) {
|
|
657
721
|
this.schema = schema;
|
|
658
722
|
}
|
|
@@ -719,7 +783,7 @@ class RestAdapter {
|
|
|
719
783
|
const params = this.buildQueryParams(options);
|
|
720
784
|
const url = this.urlWithParams(this.buildUrl(`/${table}/list`), params);
|
|
721
785
|
const res = await this.request(url);
|
|
722
|
-
return res.data;
|
|
786
|
+
return this.deserializeRecords(table, res.data);
|
|
723
787
|
}
|
|
724
788
|
async get(table, id, options) {
|
|
725
789
|
const params = new URLSearchParams;
|
|
@@ -730,7 +794,7 @@ class RestAdapter {
|
|
|
730
794
|
const url = this.urlWithParams(this.buildUrl(`/${table}/${id}`), params);
|
|
731
795
|
try {
|
|
732
796
|
const res = await this.request(url);
|
|
733
|
-
return res.data
|
|
797
|
+
return this.deserializeRecord(table, res.data);
|
|
734
798
|
} catch (err) {
|
|
735
799
|
if (err instanceof Error && err.message.includes("404")) {
|
|
736
800
|
return null;
|
|
@@ -777,7 +841,7 @@ class RestAdapter {
|
|
|
777
841
|
if (res.id !== undefined && !("id" in result)) {
|
|
778
842
|
result.id = res.id;
|
|
779
843
|
}
|
|
780
|
-
return result;
|
|
844
|
+
return this.deserializeRecord(table, result);
|
|
781
845
|
}
|
|
782
846
|
async update(table, id, data, options) {
|
|
783
847
|
const url = this.buildUrl(`/${table}/${id}`);
|
|
@@ -789,7 +853,7 @@ class RestAdapter {
|
|
|
789
853
|
method: "PUT",
|
|
790
854
|
body: JSON.stringify(body)
|
|
791
855
|
});
|
|
792
|
-
return res.data;
|
|
856
|
+
return this.deserializeRecord(table, res.data);
|
|
793
857
|
}
|
|
794
858
|
async delete(table, id) {
|
|
795
859
|
const url = this.buildUrl(`/${table}/${id}`);
|
|
@@ -849,7 +913,7 @@ class RestAdapter {
|
|
|
849
913
|
if (res.id !== undefined && !("id" in result)) {
|
|
850
914
|
result.id = res.id;
|
|
851
915
|
}
|
|
852
|
-
return result;
|
|
916
|
+
return this.deserializeRecord(table, result);
|
|
853
917
|
}
|
|
854
918
|
async upsertTranslation(table, id, lang, data) {
|
|
855
919
|
const url = this.buildUrl(`/${table}/${id}`);
|
|
@@ -865,7 +929,7 @@ class RestAdapter {
|
|
|
865
929
|
params.set("where", JSON.stringify({ [fkName]: id }));
|
|
866
930
|
const url = this.urlWithParams(this.buildUrl(`/${translationTable}/list`), params);
|
|
867
931
|
const res = await this.request(url);
|
|
868
|
-
return res.data;
|
|
932
|
+
return this.deserializeRecords(table, res.data);
|
|
869
933
|
}
|
|
870
934
|
async raw(query, params) {
|
|
871
935
|
const url = this.buildUrl("/exec");
|
|
@@ -1319,51 +1383,6 @@ function convertJSONField(jsonField) {
|
|
|
1319
1383
|
ref
|
|
1320
1384
|
};
|
|
1321
1385
|
}
|
|
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
1386
|
export {
|
|
1368
1387
|
validateTable,
|
|
1369
1388
|
validateSchema,
|
package/package.json
CHANGED
|
@@ -5,9 +5,10 @@
|
|
|
5
5
|
* Works in both Node.js and browser environments (uses fetch).
|
|
6
6
|
*/
|
|
7
7
|
|
|
8
|
-
import type { IDataAdapter } from "./IDataAdapter";
|
|
9
|
-
import type { QueryOptions, PaginatedResult, SchemaDefinition } from "../types";
|
|
10
|
-
import {
|
|
8
|
+
import type { IDataAdapter } from "./IDataAdapter";
|
|
9
|
+
import type { QueryOptions, PaginatedResult, SchemaDefinition } from "../types";
|
|
10
|
+
import { deserializeRow } from "../utils/deserializer";
|
|
11
|
+
import { toTranslationTableName, toTranslationFKName } from "../schema";
|
|
11
12
|
|
|
12
13
|
type FetchFn = (input: string, init?: any) => Promise<any>;
|
|
13
14
|
|
|
@@ -129,10 +130,34 @@ export class RestAdapter implements IDataAdapter {
|
|
|
129
130
|
return params;
|
|
130
131
|
}
|
|
131
132
|
|
|
132
|
-
private urlWithParams(base: string, params: URLSearchParams): string {
|
|
133
|
-
const qs = params.toString();
|
|
134
|
-
return qs ? `${base}?${qs}` : base;
|
|
135
|
-
}
|
|
133
|
+
private urlWithParams(base: string, params: URLSearchParams): string {
|
|
134
|
+
const qs = params.toString();
|
|
135
|
+
return qs ? `${base}?${qs}` : base;
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
private deserializeRecord<T>(table: string, record: T | null | undefined): T | null {
|
|
139
|
+
if (record == null) return null;
|
|
140
|
+
|
|
141
|
+
const tableSchema = this.schema?.tables[table];
|
|
142
|
+
if (!tableSchema) {
|
|
143
|
+
return record;
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
return deserializeRow<T>(record as Record<string, unknown>, tableSchema.fields);
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
private deserializeRecords<T>(table: string, records: T[] | undefined): T[] {
|
|
150
|
+
if (!records?.length) return records ?? [];
|
|
151
|
+
|
|
152
|
+
const tableSchema = this.schema?.tables[table];
|
|
153
|
+
if (!tableSchema) {
|
|
154
|
+
return records;
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
return records.map((record) =>
|
|
158
|
+
deserializeRow<T>(record as Record<string, unknown>, tableSchema.fields)
|
|
159
|
+
);
|
|
160
|
+
}
|
|
136
161
|
|
|
137
162
|
// ==================== Lifecycle ====================
|
|
138
163
|
|
|
@@ -204,12 +229,12 @@ export class RestAdapter implements IDataAdapter {
|
|
|
204
229
|
|
|
205
230
|
// ==================== Query Methods ====================
|
|
206
231
|
|
|
207
|
-
async list<T = unknown>(table: string, options?: QueryOptions): Promise<T[]> {
|
|
208
|
-
const params = this.buildQueryParams(options);
|
|
209
|
-
const url = this.urlWithParams(this.buildUrl(`/${table}/list`), params);
|
|
210
|
-
const res = await this.request<{ data: T[] }>(url);
|
|
211
|
-
return res.data;
|
|
212
|
-
}
|
|
232
|
+
async list<T = unknown>(table: string, options?: QueryOptions): Promise<T[]> {
|
|
233
|
+
const params = this.buildQueryParams(options);
|
|
234
|
+
const url = this.urlWithParams(this.buildUrl(`/${table}/list`), params);
|
|
235
|
+
const res = await this.request<{ data: T[] }>(url);
|
|
236
|
+
return this.deserializeRecords(table, res.data);
|
|
237
|
+
}
|
|
213
238
|
|
|
214
239
|
async get<T = unknown>(
|
|
215
240
|
table: string,
|
|
@@ -221,12 +246,12 @@ export class RestAdapter implements IDataAdapter {
|
|
|
221
246
|
if (options?.fallbackLang) params.set("fallback_lang", options.fallbackLang);
|
|
222
247
|
const url = this.urlWithParams(this.buildUrl(`/${table}/${id}`), params);
|
|
223
248
|
|
|
224
|
-
try {
|
|
225
|
-
const res = await this.request<{ data: T }>(url);
|
|
226
|
-
return res.data
|
|
227
|
-
} catch (err) {
|
|
228
|
-
if (err instanceof Error && err.message.includes("404")) {
|
|
229
|
-
return null;
|
|
249
|
+
try {
|
|
250
|
+
const res = await this.request<{ data: T }>(url);
|
|
251
|
+
return this.deserializeRecord(table, res.data);
|
|
252
|
+
} catch (err) {
|
|
253
|
+
if (err instanceof Error && err.message.includes("404")) {
|
|
254
|
+
return null;
|
|
230
255
|
}
|
|
231
256
|
throw err;
|
|
232
257
|
}
|
|
@@ -279,17 +304,17 @@ export class RestAdapter implements IDataAdapter {
|
|
|
279
304
|
data: Record<string, unknown>
|
|
280
305
|
): Promise<T> {
|
|
281
306
|
const url = this.buildUrl(`/${table}/create`);
|
|
282
|
-
const res = await this.request<{ data: T; id: unknown }>(url, {
|
|
283
|
-
method: "POST",
|
|
284
|
-
body: JSON.stringify({ data }),
|
|
285
|
-
});
|
|
307
|
+
const res = await this.request<{ data: T; id: unknown }>(url, {
|
|
308
|
+
method: "POST",
|
|
309
|
+
body: JSON.stringify({ data }),
|
|
310
|
+
});
|
|
286
311
|
// Backend returns id at top level, merge it into data
|
|
287
|
-
const result = (res.data ?? {}) as Record<string, unknown>;
|
|
288
|
-
if (res.id !== undefined && !("id" in result)) {
|
|
289
|
-
result.id = res.id;
|
|
290
|
-
}
|
|
291
|
-
return result as T;
|
|
292
|
-
}
|
|
312
|
+
const result = (res.data ?? {}) as Record<string, unknown>;
|
|
313
|
+
if (res.id !== undefined && !("id" in result)) {
|
|
314
|
+
result.id = res.id;
|
|
315
|
+
}
|
|
316
|
+
return this.deserializeRecord(table, result as T) as T;
|
|
317
|
+
}
|
|
293
318
|
|
|
294
319
|
async update<T = unknown>(
|
|
295
320
|
table: string,
|
|
@@ -302,12 +327,12 @@ export class RestAdapter implements IDataAdapter {
|
|
|
302
327
|
if (options?.translations) {
|
|
303
328
|
body.translations = options.translations;
|
|
304
329
|
}
|
|
305
|
-
const res = await this.request<{ data: T }>(url, {
|
|
306
|
-
method: "PUT",
|
|
307
|
-
body: JSON.stringify(body),
|
|
308
|
-
});
|
|
309
|
-
return res.data;
|
|
310
|
-
}
|
|
330
|
+
const res = await this.request<{ data: T }>(url, {
|
|
331
|
+
method: "PUT",
|
|
332
|
+
body: JSON.stringify(body),
|
|
333
|
+
});
|
|
334
|
+
return this.deserializeRecord(table, res.data) as T;
|
|
335
|
+
}
|
|
311
336
|
|
|
312
337
|
async delete(table: string, id: string | number): Promise<boolean> {
|
|
313
338
|
const url = this.buildUrl(`/${table}/${id}`);
|
|
@@ -383,17 +408,17 @@ export class RestAdapter implements IDataAdapter {
|
|
|
383
408
|
translations?: Record<string, Record<string, unknown>>
|
|
384
409
|
): Promise<T> {
|
|
385
410
|
const url = this.buildUrl(`/${table}/create`);
|
|
386
|
-
const res = await this.request<{ data: T; id: unknown }>(url, {
|
|
387
|
-
method: "POST",
|
|
388
|
-
body: JSON.stringify({ data, translations }),
|
|
389
|
-
});
|
|
411
|
+
const res = await this.request<{ data: T; id: unknown }>(url, {
|
|
412
|
+
method: "POST",
|
|
413
|
+
body: JSON.stringify({ data, translations }),
|
|
414
|
+
});
|
|
390
415
|
// Backend returns id at top level, merge it into data
|
|
391
|
-
const result = (res.data ?? {}) as Record<string, unknown>;
|
|
392
|
-
if (res.id !== undefined && !("id" in result)) {
|
|
393
|
-
result.id = res.id;
|
|
394
|
-
}
|
|
395
|
-
return result as T;
|
|
396
|
-
}
|
|
416
|
+
const result = (res.data ?? {}) as Record<string, unknown>;
|
|
417
|
+
if (res.id !== undefined && !("id" in result)) {
|
|
418
|
+
result.id = res.id;
|
|
419
|
+
}
|
|
420
|
+
return this.deserializeRecord(table, result as T) as T;
|
|
421
|
+
}
|
|
397
422
|
|
|
398
423
|
async upsertTranslation(
|
|
399
424
|
table: string,
|
|
@@ -416,13 +441,13 @@ export class RestAdapter implements IDataAdapter {
|
|
|
416
441
|
const fkName = toTranslationFKName(table);
|
|
417
442
|
const params = new URLSearchParams();
|
|
418
443
|
params.set("where", JSON.stringify({ [fkName]: id }));
|
|
419
|
-
const url = this.urlWithParams(
|
|
420
|
-
this.buildUrl(`/${translationTable}/list`),
|
|
421
|
-
params
|
|
422
|
-
);
|
|
423
|
-
const res = await this.request<{ data: T[] }>(url);
|
|
424
|
-
return res.data;
|
|
425
|
-
}
|
|
444
|
+
const url = this.urlWithParams(
|
|
445
|
+
this.buildUrl(`/${translationTable}/list`),
|
|
446
|
+
params
|
|
447
|
+
);
|
|
448
|
+
const res = await this.request<{ data: T[] }>(url);
|
|
449
|
+
return this.deserializeRecords(table, res.data);
|
|
450
|
+
}
|
|
426
451
|
|
|
427
452
|
// ==================== Raw Query Methods ====================
|
|
428
453
|
|