@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.
@@ -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 ?? null;
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
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@promakeai/orm",
3
- "version": "1.3.0",
3
+ "version": "1.3.2",
4
4
  "description": "Database-agnostic ORM core - works in browser and Node.js",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",
@@ -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 { toTranslationTableName, toTranslationFKName } from "../schema";
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 ?? null;
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