@promakeai/orm 1.3.2 → 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.
@@ -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;
package/dist/index.js CHANGED
@@ -621,6 +621,7 @@ class RestAdapter {
621
621
  token;
622
622
  getTokenFn;
623
623
  customHeaders;
624
+ getHeadersFn;
624
625
  fetchFn;
625
626
  timeout;
626
627
  schema;
@@ -631,6 +632,7 @@ class RestAdapter {
631
632
  this.token = config.token;
632
633
  this.getTokenFn = config.getToken;
633
634
  this.customHeaders = config.headers ?? {};
635
+ this.getHeadersFn = config.getHeaders;
634
636
  const gf = globalThis;
635
637
  this.fetchFn = config.fetch ?? gf.fetch.bind(gf);
636
638
  this.timeout = config.timeout ?? 30000;
@@ -646,6 +648,9 @@ class RestAdapter {
646
648
  Accept: "application/json",
647
649
  ...this.customHeaders
648
650
  };
651
+ if (this.getHeadersFn) {
652
+ Object.assign(headers, this.getHeadersFn());
653
+ }
649
654
  const token = this.getTokenFn ? this.getTokenFn() : this.token;
650
655
  if (token) {
651
656
  headers["Authorization"] = `Bearer ${token}`;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@promakeai/orm",
3
- "version": "1.3.2",
3
+ "version": "1.4.0",
4
4
  "description": "Database-agnostic ORM core - works in browser and Node.js",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",
@@ -5,10 +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 { deserializeRow } from "../utils/deserializer";
11
- 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";
12
12
 
13
13
  type FetchFn = (input: string, init?: any) => Promise<any>;
14
14
 
@@ -23,6 +23,8 @@ export interface RestAdapterConfig {
23
23
  getToken?: () => string | null;
24
24
  /** Custom headers to include in all requests */
25
25
  headers?: Record<string, string>;
26
+ /** Dynamic headers getter - called on every request. Merged after static headers. */
27
+ getHeaders?: () => Record<string, string>;
26
28
  /** Schema definition for translation support */
27
29
  schema?: SchemaDefinition;
28
30
  /** Default language for translations */
@@ -39,6 +41,7 @@ export class RestAdapter implements IDataAdapter {
39
41
  private token?: string;
40
42
  private getTokenFn?: () => string | null;
41
43
  private customHeaders: Record<string, string>;
44
+ private getHeadersFn?: () => Record<string, string>;
42
45
  private fetchFn: FetchFn;
43
46
  private timeout: number;
44
47
 
@@ -51,6 +54,7 @@ export class RestAdapter implements IDataAdapter {
51
54
  this.token = config.token;
52
55
  this.getTokenFn = config.getToken;
53
56
  this.customHeaders = config.headers ?? {};
57
+ this.getHeadersFn = config.getHeaders;
54
58
  const gf = globalThis as any;
55
59
  this.fetchFn = config.fetch ?? gf.fetch.bind(gf);
56
60
  this.timeout = config.timeout ?? 30000;
@@ -70,6 +74,9 @@ export class RestAdapter implements IDataAdapter {
70
74
  Accept: "application/json",
71
75
  ...this.customHeaders,
72
76
  };
77
+ if (this.getHeadersFn) {
78
+ Object.assign(headers, this.getHeadersFn());
79
+ }
73
80
  const token = this.getTokenFn ? this.getTokenFn() : this.token;
74
81
  if (token) {
75
82
  headers["Authorization"] = `Bearer ${token}`;
@@ -130,34 +137,34 @@ export class RestAdapter implements IDataAdapter {
130
137
  return params;
131
138
  }
132
139
 
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
- }
140
+ private urlWithParams(base: string, params: URLSearchParams): string {
141
+ const qs = params.toString();
142
+ return qs ? `${base}?${qs}` : base;
143
+ }
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
+ }
161
168
 
162
169
  // ==================== Lifecycle ====================
163
170
 
@@ -229,12 +236,12 @@ export class RestAdapter implements IDataAdapter {
229
236
 
230
237
  // ==================== Query Methods ====================
231
238
 
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
- }
239
+ async list<T = unknown>(table: string, options?: QueryOptions): Promise<T[]> {
240
+ const params = this.buildQueryParams(options);
241
+ const url = this.urlWithParams(this.buildUrl(`/${table}/list`), params);
242
+ const res = await this.request<{ data: T[] }>(url);
243
+ return this.deserializeRecords(table, res.data);
244
+ }
238
245
 
239
246
  async get<T = unknown>(
240
247
  table: string,
@@ -246,12 +253,12 @@ export class RestAdapter implements IDataAdapter {
246
253
  if (options?.fallbackLang) params.set("fallback_lang", options.fallbackLang);
247
254
  const url = this.urlWithParams(this.buildUrl(`/${table}/${id}`), params);
248
255
 
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;
256
+ try {
257
+ const res = await this.request<{ data: T }>(url);
258
+ return this.deserializeRecord(table, res.data);
259
+ } catch (err) {
260
+ if (err instanceof Error && err.message.includes("404")) {
261
+ return null;
255
262
  }
256
263
  throw err;
257
264
  }
@@ -304,17 +311,17 @@ export class RestAdapter implements IDataAdapter {
304
311
  data: Record<string, unknown>
305
312
  ): Promise<T> {
306
313
  const url = this.buildUrl(`/${table}/create`);
307
- const res = await this.request<{ data: T; id: unknown }>(url, {
308
- method: "POST",
309
- body: JSON.stringify({ data }),
310
- });
314
+ const res = await this.request<{ data: T; id: unknown }>(url, {
315
+ method: "POST",
316
+ body: JSON.stringify({ data }),
317
+ });
311
318
  // Backend returns id at top level, merge it into data
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
- }
319
+ const result = (res.data ?? {}) as Record<string, unknown>;
320
+ if (res.id !== undefined && !("id" in result)) {
321
+ result.id = res.id;
322
+ }
323
+ return this.deserializeRecord(table, result as T) as T;
324
+ }
318
325
 
319
326
  async update<T = unknown>(
320
327
  table: string,
@@ -327,12 +334,12 @@ export class RestAdapter implements IDataAdapter {
327
334
  if (options?.translations) {
328
335
  body.translations = options.translations;
329
336
  }
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
- }
337
+ const res = await this.request<{ data: T }>(url, {
338
+ method: "PUT",
339
+ body: JSON.stringify(body),
340
+ });
341
+ return this.deserializeRecord(table, res.data) as T;
342
+ }
336
343
 
337
344
  async delete(table: string, id: string | number): Promise<boolean> {
338
345
  const url = this.buildUrl(`/${table}/${id}`);
@@ -408,17 +415,17 @@ export class RestAdapter implements IDataAdapter {
408
415
  translations?: Record<string, Record<string, unknown>>
409
416
  ): Promise<T> {
410
417
  const url = this.buildUrl(`/${table}/create`);
411
- const res = await this.request<{ data: T; id: unknown }>(url, {
412
- method: "POST",
413
- body: JSON.stringify({ data, translations }),
414
- });
418
+ const res = await this.request<{ data: T; id: unknown }>(url, {
419
+ method: "POST",
420
+ body: JSON.stringify({ data, translations }),
421
+ });
415
422
  // Backend returns id at top level, merge it into data
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
- }
423
+ const result = (res.data ?? {}) as Record<string, unknown>;
424
+ if (res.id !== undefined && !("id" in result)) {
425
+ result.id = res.id;
426
+ }
427
+ return this.deserializeRecord(table, result as T) as T;
428
+ }
422
429
 
423
430
  async upsertTranslation(
424
431
  table: string,
@@ -441,13 +448,13 @@ export class RestAdapter implements IDataAdapter {
441
448
  const fkName = toTranslationFKName(table);
442
449
  const params = new URLSearchParams();
443
450
  params.set("where", JSON.stringify({ [fkName]: id }));
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
- }
451
+ const url = this.urlWithParams(
452
+ this.buildUrl(`/${translationTable}/list`),
453
+ params
454
+ );
455
+ const res = await this.request<{ data: T[] }>(url);
456
+ return this.deserializeRecords(table, res.data);
457
+ }
451
458
 
452
459
  // ==================== Raw Query Methods ====================
453
460