@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.
- package/dist/adapters/RestAdapter.d.ts +3 -0
- package/dist/index.js +5 -0
- package/package.json +1 -1
- package/src/adapters/RestAdapter.ts +84 -77
|
@@ -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
|
@@ -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
|
|