resora 0.2.1 → 0.2.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/bin/index.mjs +5 -5
- package/dist/index.cjs +362 -5
- package/dist/index.d.cts +375 -4
- package/dist/index.d.mts +375 -4
- package/dist/index.mjs +358 -4
- package/package.json +1 -1
package/dist/index.mjs
CHANGED
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
import { copyFileSync, existsSync, mkdirSync, readFileSync, rmSync, writeFileSync } from "fs";
|
|
2
2
|
import path, { dirname, join } from "path";
|
|
3
|
+
import { createRequire } from "module";
|
|
4
|
+
import { pathToFileURL } from "url";
|
|
3
5
|
import { Command } from "@h3ravel/musket";
|
|
4
6
|
|
|
5
7
|
//#region src/ApiResource.ts
|
|
@@ -40,93 +42,227 @@ let globalCursorMeta = {
|
|
|
40
42
|
previous: "previous",
|
|
41
43
|
next: "next"
|
|
42
44
|
};
|
|
45
|
+
/**
|
|
46
|
+
* Sets the global case style for response keys, which will be applied
|
|
47
|
+
* to all responses unless overridden by individual resource configurations.
|
|
48
|
+
*
|
|
49
|
+
* @param style The case style to set as the global default for response keys.
|
|
50
|
+
*/
|
|
43
51
|
const setGlobalCase = (style) => {
|
|
44
52
|
globalPreferredCase = style;
|
|
45
53
|
};
|
|
54
|
+
/**
|
|
55
|
+
* Retrieves the global case style for response keys, which is used
|
|
56
|
+
* to determine how keys in responses should be formatted.
|
|
57
|
+
*
|
|
58
|
+
* @returns
|
|
59
|
+
*/
|
|
46
60
|
const getGlobalCase = () => {
|
|
47
61
|
return globalPreferredCase;
|
|
48
62
|
};
|
|
63
|
+
/**
|
|
64
|
+
* Sets the global response structure configuration, which defines how
|
|
65
|
+
* responses should be structured across the application.
|
|
66
|
+
*
|
|
67
|
+
* @param config The response structure configuration object.
|
|
68
|
+
*/
|
|
49
69
|
const setGlobalResponseStructure = (config) => {
|
|
50
70
|
globalResponseStructure = config;
|
|
51
71
|
};
|
|
72
|
+
/**
|
|
73
|
+
* Retrieves the global response structure configuration, which
|
|
74
|
+
* defines how responses should be structured across the application.
|
|
75
|
+
*
|
|
76
|
+
* @returns
|
|
77
|
+
*/
|
|
52
78
|
const getGlobalResponseStructure = () => {
|
|
53
79
|
return globalResponseStructure;
|
|
54
80
|
};
|
|
81
|
+
/**
|
|
82
|
+
* Sets the global response root key, which is the key under which
|
|
83
|
+
* the main data will be nested in responses if wrapping is enabled.
|
|
84
|
+
*
|
|
85
|
+
* @param rootKey The root key to set for response data.
|
|
86
|
+
*/
|
|
55
87
|
const setGlobalResponseRootKey = (rootKey) => {
|
|
56
88
|
globalResponseStructure = {
|
|
57
89
|
...globalResponseStructure || {},
|
|
58
90
|
rootKey
|
|
59
91
|
};
|
|
60
92
|
};
|
|
93
|
+
/**
|
|
94
|
+
* Sets the global response wrap option, which determines whether responses
|
|
95
|
+
* should be wrapped in a root key or returned unwrapped when possible.
|
|
96
|
+
*
|
|
97
|
+
* @param wrap The wrap option to set for responses.
|
|
98
|
+
*/
|
|
61
99
|
const setGlobalResponseWrap = (wrap) => {
|
|
62
100
|
globalResponseStructure = {
|
|
63
101
|
...globalResponseStructure || {},
|
|
64
102
|
wrap
|
|
65
103
|
};
|
|
66
104
|
};
|
|
105
|
+
/**
|
|
106
|
+
* Retrieves the global response wrap option, which indicates whether responses
|
|
107
|
+
* should be wrapped in a root key or returned unwrapped when possible.
|
|
108
|
+
*
|
|
109
|
+
* @returns
|
|
110
|
+
*/
|
|
67
111
|
const getGlobalResponseWrap = () => {
|
|
68
112
|
return globalResponseStructure?.wrap;
|
|
69
113
|
};
|
|
114
|
+
/**
|
|
115
|
+
* Retrieves the global response root key, which is the key under which the main
|
|
116
|
+
* data will be nested in responses if wrapping is enabled.
|
|
117
|
+
*
|
|
118
|
+
* @returns
|
|
119
|
+
*/
|
|
70
120
|
const getGlobalResponseRootKey = () => {
|
|
71
121
|
return globalResponseStructure?.rootKey;
|
|
72
122
|
};
|
|
123
|
+
/**
|
|
124
|
+
* Sets the global response factory, which is a custom function that can be used
|
|
125
|
+
* to produce a completely custom response structure based on the provided
|
|
126
|
+
* payload and context.
|
|
127
|
+
*
|
|
128
|
+
* @param factory The response factory function to set as the global default for response construction.
|
|
129
|
+
*/
|
|
73
130
|
const setGlobalResponseFactory = (factory) => {
|
|
74
131
|
globalResponseStructure = {
|
|
75
132
|
...globalResponseStructure || {},
|
|
76
133
|
factory
|
|
77
134
|
};
|
|
78
135
|
};
|
|
136
|
+
/**
|
|
137
|
+
* Retrieves the global response factory, which is a custom function that
|
|
138
|
+
* can be used to produce a completely custom response structure based on
|
|
139
|
+
* the provided payload and context.
|
|
140
|
+
*
|
|
141
|
+
* @returns
|
|
142
|
+
*/
|
|
79
143
|
const getGlobalResponseFactory = () => {
|
|
80
144
|
return globalResponseStructure?.factory;
|
|
81
145
|
};
|
|
146
|
+
/**
|
|
147
|
+
* Sets the global paginated extras configuration, which defines the keys
|
|
148
|
+
* to use for pagination metadata, links, and cursor information in paginated responses.
|
|
149
|
+
*
|
|
150
|
+
* @param extras The paginated extras configuration object.
|
|
151
|
+
*/
|
|
82
152
|
const setGlobalPaginatedExtras = (extras) => {
|
|
83
153
|
globalPaginatedExtras = extras;
|
|
84
154
|
};
|
|
155
|
+
/**
|
|
156
|
+
* Retrieves the global paginated extras configuration, which defines the keys to use for pagination metadata, links, and cursor information in paginated responses.
|
|
157
|
+
*
|
|
158
|
+
* @returns
|
|
159
|
+
*/
|
|
85
160
|
const getGlobalPaginatedExtras = () => {
|
|
86
161
|
return globalPaginatedExtras;
|
|
87
162
|
};
|
|
163
|
+
/**
|
|
164
|
+
* Sets the global paginated links configuration, which defines the keys to
|
|
165
|
+
* use for pagination links (first, last, prev, next) in paginated responses.
|
|
166
|
+
*
|
|
167
|
+
* @param links The paginated links configuration object.
|
|
168
|
+
*/
|
|
88
169
|
const setGlobalPaginatedLinks = (links) => {
|
|
89
170
|
globalPaginatedLinks = {
|
|
90
171
|
...globalPaginatedLinks,
|
|
91
172
|
...links
|
|
92
173
|
};
|
|
93
174
|
};
|
|
175
|
+
/**
|
|
176
|
+
* Retrieves the global paginated links configuration, which defines the keys to use for pagination links (first, last, prev, next) in paginated responses.
|
|
177
|
+
*
|
|
178
|
+
* @returns
|
|
179
|
+
*/
|
|
94
180
|
const getGlobalPaginatedLinks = () => {
|
|
95
181
|
return globalPaginatedLinks;
|
|
96
182
|
};
|
|
183
|
+
/**
|
|
184
|
+
* Sets the global base URL, which is used for generating pagination links in responses.
|
|
185
|
+
*
|
|
186
|
+
* @param baseUrl The base URL to set for pagination link generation.
|
|
187
|
+
*/
|
|
97
188
|
const setGlobalBaseUrl = (baseUrl) => {
|
|
98
189
|
globalBaseUrl = baseUrl;
|
|
99
190
|
};
|
|
191
|
+
/**
|
|
192
|
+
* Retrieves the global base URL, which is used for generating pagination links in responses.
|
|
193
|
+
*
|
|
194
|
+
* @returns
|
|
195
|
+
*/
|
|
100
196
|
const getGlobalBaseUrl = () => {
|
|
101
197
|
return globalBaseUrl;
|
|
102
198
|
};
|
|
199
|
+
/**
|
|
200
|
+
* Sets the global page name, which is the query parameter name used for the page number in paginated requests and link generation.
|
|
201
|
+
*
|
|
202
|
+
* @param pageName
|
|
203
|
+
*/
|
|
103
204
|
const setGlobalPageName = (pageName) => {
|
|
104
205
|
globalPageName = pageName;
|
|
105
206
|
};
|
|
207
|
+
/**
|
|
208
|
+
* Retrieves the global page name, which is the query parameter name
|
|
209
|
+
* used for the page number in paginated requests and link generation.
|
|
210
|
+
*
|
|
211
|
+
* @returns
|
|
212
|
+
*/
|
|
106
213
|
const getGlobalPageName = () => {
|
|
107
214
|
return globalPageName;
|
|
108
215
|
};
|
|
216
|
+
/**
|
|
217
|
+
* Retrieves the keys to use for pagination extras (meta, links, cursor) based
|
|
218
|
+
* on the global configuration.
|
|
219
|
+
*
|
|
220
|
+
* @param meta Whether to include pagination metadata in the response.
|
|
221
|
+
*/
|
|
109
222
|
const setGlobalPaginatedMeta = (meta) => {
|
|
110
223
|
globalPaginatedMeta = {
|
|
111
224
|
...globalPaginatedMeta,
|
|
112
225
|
...meta
|
|
113
226
|
};
|
|
114
227
|
};
|
|
228
|
+
/**
|
|
229
|
+
* Retrieves the keys to use for pagination extras (meta, links, cursor) based
|
|
230
|
+
* on the global configuration.
|
|
231
|
+
*
|
|
232
|
+
* @returns The global pagination metadata configuration.
|
|
233
|
+
*/
|
|
115
234
|
const getGlobalPaginatedMeta = () => {
|
|
116
235
|
return globalPaginatedMeta;
|
|
117
236
|
};
|
|
237
|
+
/**
|
|
238
|
+
* Sets the global cursor meta configuration, which defines the keys to use
|
|
239
|
+
* for cursor pagination metadata (previous, next) in responses.
|
|
240
|
+
*
|
|
241
|
+
* @param meta The cursor meta configuration object.
|
|
242
|
+
*/
|
|
118
243
|
const setGlobalCursorMeta = (meta) => {
|
|
119
244
|
globalCursorMeta = {
|
|
120
245
|
...globalCursorMeta,
|
|
121
246
|
...meta
|
|
122
247
|
};
|
|
123
248
|
};
|
|
249
|
+
/**
|
|
250
|
+
* Retrieves the keys to use for cursor pagination metadata (previous, next) in
|
|
251
|
+
* responses based on the global configuration.
|
|
252
|
+
*
|
|
253
|
+
* @returns The global cursor pagination metadata configuration.
|
|
254
|
+
*/
|
|
124
255
|
const getGlobalCursorMeta = () => {
|
|
125
256
|
return globalCursorMeta;
|
|
126
257
|
};
|
|
127
258
|
|
|
128
259
|
//#endregion
|
|
129
260
|
//#region src/utilities/pagination.ts
|
|
261
|
+
/**
|
|
262
|
+
* Retrieves the configured keys for pagination extras (meta, links, cursor) based on the application's configuration.
|
|
263
|
+
*
|
|
264
|
+
* @returns An object containing the keys for meta, links, and cursor extras, or `undefined` if not configured.
|
|
265
|
+
*/
|
|
130
266
|
const getPaginationExtraKeys = () => {
|
|
131
267
|
const extras = getGlobalPaginatedExtras();
|
|
132
268
|
if (Array.isArray(extras)) return {
|
|
@@ -140,6 +276,13 @@ const getPaginationExtraKeys = () => {
|
|
|
140
276
|
cursorKey: extras.cursor
|
|
141
277
|
};
|
|
142
278
|
};
|
|
279
|
+
/**
|
|
280
|
+
* Builds a pagination URL for a given page number and path, using the global base URL and page name configuration.
|
|
281
|
+
*
|
|
282
|
+
* @param page The page number for which to build the URL. If `undefined`, the function will return `undefined`.
|
|
283
|
+
* @param pathName The path to use for the URL. If not provided, it will default to an empty string.
|
|
284
|
+
* @returns
|
|
285
|
+
*/
|
|
143
286
|
const buildPageUrl = (page, pathName) => {
|
|
144
287
|
if (typeof page === "undefined") return;
|
|
145
288
|
const rawPath = pathName || "";
|
|
@@ -153,6 +296,13 @@ const buildPageUrl = (page, pathName) => {
|
|
|
153
296
|
url.searchParams.set(getGlobalPageName() || "page", String(page));
|
|
154
297
|
return url.toString();
|
|
155
298
|
};
|
|
299
|
+
/**
|
|
300
|
+
* Builds pagination extras (meta, links, cursor) for a given resource based on
|
|
301
|
+
* its pagination and cursor properties, using the configured keys for each type of extra.
|
|
302
|
+
*
|
|
303
|
+
* @param resource The resource for which to build pagination extras.
|
|
304
|
+
* @returns An object containing the pagination extras (meta, links, cursor) for the resource.
|
|
305
|
+
*/
|
|
156
306
|
const buildPaginationExtras = (resource) => {
|
|
157
307
|
const { metaKey, linksKey, cursorKey } = getPaginationExtraKeys();
|
|
158
308
|
const extra = {};
|
|
@@ -209,12 +359,26 @@ const buildPaginationExtras = (resource) => {
|
|
|
209
359
|
|
|
210
360
|
//#endregion
|
|
211
361
|
//#region src/utilities/objects.ts
|
|
362
|
+
/**
|
|
363
|
+
* Utility functions for working with objects, including type checking, merging, and property manipulation.
|
|
364
|
+
*
|
|
365
|
+
* @param value The value to check.
|
|
366
|
+
* @returns `true` if the value is a plain object, `false` otherwise.
|
|
367
|
+
*/
|
|
212
368
|
const isPlainObject = (value) => {
|
|
213
369
|
if (typeof value !== "object" || value === null) return false;
|
|
214
370
|
if (Array.isArray(value) || value instanceof Date || value instanceof RegExp) return false;
|
|
215
371
|
const proto = Object.getPrototypeOf(value);
|
|
216
372
|
return proto === Object.prototype || proto === null;
|
|
217
373
|
};
|
|
374
|
+
/**
|
|
375
|
+
* Appends extra properties to a response body, ensuring that the main data is wrapped under a specified root key if necessary.
|
|
376
|
+
*
|
|
377
|
+
* @param body The original response body, which can be an object, array, or primitive value.
|
|
378
|
+
* @param extra Extra properties to append to the response body.
|
|
379
|
+
* @param rootKey The root key under which to wrap the main data if necessary.
|
|
380
|
+
* @returns The response body with the extra properties appended.
|
|
381
|
+
*/
|
|
218
382
|
const appendRootProperties = (body, extra, rootKey = "data") => {
|
|
219
383
|
if (!extra || Object.keys(extra).length === 0) return body;
|
|
220
384
|
if (Array.isArray(body)) return {
|
|
@@ -230,6 +394,13 @@ const appendRootProperties = (body, extra, rootKey = "data") => {
|
|
|
230
394
|
...extra
|
|
231
395
|
};
|
|
232
396
|
};
|
|
397
|
+
/**
|
|
398
|
+
* Deeply merges two metadata objects, combining nested objects recursively.
|
|
399
|
+
*
|
|
400
|
+
* @param base The base metadata object to merge into.
|
|
401
|
+
* @param incoming The incoming metadata object to merge from.
|
|
402
|
+
* @returns
|
|
403
|
+
*/
|
|
233
404
|
const mergeMetadata = (base, incoming) => {
|
|
234
405
|
if (!incoming) return base;
|
|
235
406
|
if (!base) return incoming;
|
|
@@ -244,6 +415,12 @@ const mergeMetadata = (base, incoming) => {
|
|
|
244
415
|
|
|
245
416
|
//#endregion
|
|
246
417
|
//#region src/utilities/response.ts
|
|
418
|
+
/**
|
|
419
|
+
* Builds a response envelope based on the provided payload,
|
|
420
|
+
* metadata, and configuration options.
|
|
421
|
+
*
|
|
422
|
+
* @param config The configuration object containing payload, metadata, and other options for building the response.
|
|
423
|
+
*/
|
|
247
424
|
const buildResponseEnvelope = ({ payload, meta, metaKey = "meta", wrap = true, rootKey = "data", factory, context }) => {
|
|
248
425
|
if (factory) return factory(payload, {
|
|
249
426
|
...context,
|
|
@@ -268,6 +445,15 @@ const buildResponseEnvelope = ({ payload, meta, metaKey = "meta", wrap = true, r
|
|
|
268
445
|
|
|
269
446
|
//#endregion
|
|
270
447
|
//#region src/utilities/metadata.ts
|
|
448
|
+
/**
|
|
449
|
+
* Resolves metadata from a resource instance by checking for a custom `with` method.
|
|
450
|
+
* If the method exists and is different from the base method, it calls it to retrieve metadata.
|
|
451
|
+
* This allows resources to provide additional metadata for response construction.
|
|
452
|
+
*
|
|
453
|
+
* @param instance The resource instance to check for a custom `with` method.
|
|
454
|
+
* @param baseWithMethod The base `with` method to compare against.
|
|
455
|
+
* @returns The resolved metadata or `undefined` if no custom metadata is provided.
|
|
456
|
+
*/
|
|
271
457
|
const resolveWithHookMetadata = (instance, baseWithMethod) => {
|
|
272
458
|
const candidate = instance?.with;
|
|
273
459
|
if (typeof candidate !== "function" || candidate === baseWithMethod) return;
|
|
@@ -279,18 +465,45 @@ const resolveWithHookMetadata = (instance, baseWithMethod) => {
|
|
|
279
465
|
//#endregion
|
|
280
466
|
//#region src/utilities/conditional.ts
|
|
281
467
|
const CONDITIONAL_ATTRIBUTE_MISSING = Symbol("resora.conditional.missing");
|
|
468
|
+
/**
|
|
469
|
+
* Resolves a value based on a condition. If the condition is falsy, it returns a special symbol indicating that the attribute is missing.
|
|
470
|
+
*
|
|
471
|
+
*
|
|
472
|
+
* @param condition The condition to evaluate.
|
|
473
|
+
* @param value The value or function to resolve if the condition is truthy.
|
|
474
|
+
* @returns The resolved value or a symbol indicating the attribute is missing.
|
|
475
|
+
*/
|
|
282
476
|
const resolveWhen = (condition, value) => {
|
|
283
477
|
if (!condition) return CONDITIONAL_ATTRIBUTE_MISSING;
|
|
284
478
|
return typeof value === "function" ? value() : value;
|
|
285
479
|
};
|
|
480
|
+
/**
|
|
481
|
+
* Resolves a value only if it is not null or undefined.
|
|
482
|
+
*
|
|
483
|
+
* @param value The value to resolve.
|
|
484
|
+
* @returns The resolved value or a symbol indicating the attribute is missing.
|
|
485
|
+
*/
|
|
286
486
|
const resolveWhenNotNull = (value) => {
|
|
287
487
|
return value === null || typeof value === "undefined" ? CONDITIONAL_ATTRIBUTE_MISSING : value;
|
|
288
488
|
};
|
|
489
|
+
/**
|
|
490
|
+
* Conditionally merges object attributes based on a condition.
|
|
491
|
+
*
|
|
492
|
+
* @param condition
|
|
493
|
+
* @param value
|
|
494
|
+
* @returns
|
|
495
|
+
*/
|
|
289
496
|
const resolveMergeWhen = (condition, value) => {
|
|
290
497
|
if (!condition) return {};
|
|
291
498
|
const resolved = typeof value === "function" ? value() : value;
|
|
292
499
|
return isPlainObject(resolved) ? resolved : {};
|
|
293
500
|
};
|
|
501
|
+
/**
|
|
502
|
+
* Recursively sanitizes an object or array by removing any attributes that are marked as missing using the special symbol.
|
|
503
|
+
*
|
|
504
|
+
* @param value The value to sanitize.
|
|
505
|
+
* @returns The sanitized value.
|
|
506
|
+
*/
|
|
294
507
|
const sanitizeConditionalAttributes = (value) => {
|
|
295
508
|
if (value === CONDITIONAL_ATTRIBUTE_MISSING) return CONDITIONAL_ATTRIBUTE_MISSING;
|
|
296
509
|
if (Array.isArray(value)) return value.map((item) => sanitizeConditionalAttributes(item)).filter((item) => item !== CONDITIONAL_ATTRIBUTE_MISSING);
|
|
@@ -308,21 +521,58 @@ const sanitizeConditionalAttributes = (value) => {
|
|
|
308
521
|
|
|
309
522
|
//#endregion
|
|
310
523
|
//#region src/utilities/case.ts
|
|
524
|
+
/**
|
|
525
|
+
* Splits a string into words based on common delimiters and capitalization patterns.
|
|
526
|
+
* Handles camelCase, PascalCase, snake_case, kebab-case, and space-separated words.
|
|
527
|
+
*
|
|
528
|
+
* @param str
|
|
529
|
+
* @returns
|
|
530
|
+
*/
|
|
311
531
|
const splitWords = (str) => {
|
|
312
532
|
return str.replace(/([a-z0-9])([A-Z])/g, "$1 $2").replace(/([A-Z]+)([A-Z][a-z])/g, "$1 $2").replace(/[-_\s]+/g, " ").trim().toLowerCase().split(" ").filter(Boolean);
|
|
313
533
|
};
|
|
534
|
+
/**
|
|
535
|
+
* Transforms a string to camelCase.
|
|
536
|
+
*
|
|
537
|
+
* @param str
|
|
538
|
+
* @returns
|
|
539
|
+
*/
|
|
314
540
|
const toCamelCase = (str) => {
|
|
315
541
|
return splitWords(str).map((w, i) => i === 0 ? w : w.charAt(0).toUpperCase() + w.slice(1)).join("");
|
|
316
542
|
};
|
|
543
|
+
/**
|
|
544
|
+
* Transforms a string to snake_case.
|
|
545
|
+
*
|
|
546
|
+
* @param str
|
|
547
|
+
* @returns
|
|
548
|
+
*/
|
|
317
549
|
const toSnakeCase = (str) => {
|
|
318
550
|
return splitWords(str).join("_");
|
|
319
551
|
};
|
|
552
|
+
/**
|
|
553
|
+
* Transforms a string to PascalCase.
|
|
554
|
+
*
|
|
555
|
+
* @param str
|
|
556
|
+
* @returns
|
|
557
|
+
*/
|
|
320
558
|
const toPascalCase = (str) => {
|
|
321
559
|
return splitWords(str).map((w) => w.charAt(0).toUpperCase() + w.slice(1)).join("");
|
|
322
560
|
};
|
|
561
|
+
/**
|
|
562
|
+
* Transforms a string to kebab-case.
|
|
563
|
+
*
|
|
564
|
+
* @param str
|
|
565
|
+
* @returns
|
|
566
|
+
*/
|
|
323
567
|
const toKebabCase = (str) => {
|
|
324
568
|
return splitWords(str).join("-");
|
|
325
569
|
};
|
|
570
|
+
/**
|
|
571
|
+
* Retrieves the appropriate case transformation function based on the provided style.
|
|
572
|
+
*
|
|
573
|
+
* @param style
|
|
574
|
+
* @returns
|
|
575
|
+
*/
|
|
326
576
|
const getCaseTransformer = (style) => {
|
|
327
577
|
if (typeof style === "function") return style;
|
|
328
578
|
switch (style) {
|
|
@@ -332,6 +582,14 @@ const getCaseTransformer = (style) => {
|
|
|
332
582
|
case "kebab": return toKebabCase;
|
|
333
583
|
}
|
|
334
584
|
};
|
|
585
|
+
/**
|
|
586
|
+
* Transforms the keys of an object (including nested objects and arrays) using
|
|
587
|
+
* the provided transformer function.
|
|
588
|
+
*
|
|
589
|
+
* @param obj
|
|
590
|
+
* @param transformer
|
|
591
|
+
* @returns
|
|
592
|
+
*/
|
|
335
593
|
const transformKeys = (obj, transformer) => {
|
|
336
594
|
if (obj === null || obj === void 0) return obj;
|
|
337
595
|
if (Array.isArray(obj)) return obj.map((item) => transformKeys(item, transformer));
|
|
@@ -383,10 +641,106 @@ const getDefaultConfig = () => {
|
|
|
383
641
|
}
|
|
384
642
|
};
|
|
385
643
|
};
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
644
|
+
/**
|
|
645
|
+
* Defines the configuration for the application by merging the provided configuration with the default configuration. This function takes a partial configuration object as input and returns a complete configuration object that includes all required properties, using default values for any properties that are not specified in the input.
|
|
646
|
+
*
|
|
647
|
+
* @param config
|
|
648
|
+
* @returns
|
|
649
|
+
*/
|
|
650
|
+
const defineConfig = (config) => {
|
|
651
|
+
const defConf = getDefaultConfig();
|
|
652
|
+
return Object.assign(defConf, config, { stubs: Object.assign(defConf.stubs, config.stubs || {}) }, { cursorMeta: Object.assign(defConf.cursorMeta, config.cursorMeta || {}) }, { paginatedMeta: Object.assign(defConf.paginatedMeta, config.paginatedMeta || {}) }, { paginatedLinks: Object.assign(defConf.paginatedLinks, config.paginatedLinks || {}) }, { responseStructure: Object.assign(defConf.responseStructure, config.responseStructure || {}) });
|
|
653
|
+
};
|
|
654
|
+
|
|
655
|
+
//#endregion
|
|
656
|
+
//#region src/utilities/runtime-config.ts
|
|
657
|
+
let runtimeConfigLoaded = false;
|
|
658
|
+
let runtimeConfigLoadingPromise;
|
|
659
|
+
/**
|
|
660
|
+
* Resets the runtime configuration state for testing purposes.
|
|
661
|
+
*
|
|
662
|
+
* @returns
|
|
663
|
+
*/
|
|
664
|
+
const resetRuntimeConfigForTests = () => {
|
|
665
|
+
runtimeConfigLoaded = false;
|
|
666
|
+
runtimeConfigLoadingPromise = void 0;
|
|
667
|
+
};
|
|
668
|
+
/**
|
|
669
|
+
* Applies the provided configuration to the global state of the application.
|
|
670
|
+
*
|
|
671
|
+
* @param config The complete configuration object to apply.
|
|
672
|
+
*/
|
|
673
|
+
const applyRuntimeConfig = (config) => {
|
|
674
|
+
if (config.preferredCase !== "camel") setGlobalCase(config.preferredCase);
|
|
675
|
+
setGlobalResponseStructure(config.responseStructure);
|
|
676
|
+
setGlobalPaginatedExtras(config.paginatedExtras);
|
|
677
|
+
setGlobalPaginatedLinks(config.paginatedLinks);
|
|
678
|
+
setGlobalPaginatedMeta(config.paginatedMeta);
|
|
679
|
+
setGlobalCursorMeta(config.cursorMeta);
|
|
680
|
+
setGlobalBaseUrl(config.baseUrl);
|
|
681
|
+
setGlobalPageName(config.pageName);
|
|
682
|
+
};
|
|
683
|
+
/**
|
|
684
|
+
* Loads the runtime configuration by searching for configuration files in the current working directory.
|
|
685
|
+
* @param configPath The path to the configuration file to load.
|
|
686
|
+
* @returns
|
|
687
|
+
*/
|
|
688
|
+
const importConfigFile = async (configPath) => {
|
|
689
|
+
return await import(`${pathToFileURL(configPath).href}?resora_runtime=${Date.now()}`);
|
|
690
|
+
};
|
|
691
|
+
/**
|
|
692
|
+
* Resolves the imported configuration and applies it to the global state.
|
|
693
|
+
*
|
|
694
|
+
* @param imported
|
|
695
|
+
*/
|
|
696
|
+
const resolveAndApply = (imported) => {
|
|
697
|
+
applyRuntimeConfig(defineConfig((imported?.default ?? imported) || {}));
|
|
698
|
+
runtimeConfigLoaded = true;
|
|
699
|
+
};
|
|
700
|
+
/**
|
|
701
|
+
* Loads the runtime configuration synchronously by searching for CommonJS configuration files in the current working directory.
|
|
702
|
+
*
|
|
703
|
+
* @returns
|
|
704
|
+
*/
|
|
705
|
+
const loadRuntimeConfigSync = () => {
|
|
706
|
+
const require = createRequire(import.meta.url);
|
|
707
|
+
const syncConfigPaths = [path.join(process.cwd(), "resora.config.cjs")];
|
|
708
|
+
for (const configPath of syncConfigPaths) {
|
|
709
|
+
if (!existsSync(configPath)) continue;
|
|
710
|
+
try {
|
|
711
|
+
resolveAndApply(require(configPath));
|
|
712
|
+
return true;
|
|
713
|
+
} catch {
|
|
714
|
+
continue;
|
|
715
|
+
}
|
|
716
|
+
}
|
|
717
|
+
return false;
|
|
718
|
+
};
|
|
719
|
+
/**
|
|
720
|
+
* Loads the runtime configuration by searching for configuration files in the current working directory.
|
|
721
|
+
*
|
|
722
|
+
* @returns
|
|
723
|
+
*/
|
|
724
|
+
const loadRuntimeConfig = async () => {
|
|
725
|
+
if (runtimeConfigLoaded) return;
|
|
726
|
+
if (runtimeConfigLoadingPromise) return await runtimeConfigLoadingPromise;
|
|
727
|
+
if (loadRuntimeConfigSync()) return;
|
|
728
|
+
runtimeConfigLoadingPromise = (async () => {
|
|
729
|
+
const possibleConfigPaths = [path.join(process.cwd(), "resora.config.js"), path.join(process.cwd(), "resora.config.ts")];
|
|
730
|
+
for (const configPath of possibleConfigPaths) {
|
|
731
|
+
if (!existsSync(configPath)) continue;
|
|
732
|
+
try {
|
|
733
|
+
resolveAndApply(await importConfigFile(configPath));
|
|
734
|
+
return;
|
|
735
|
+
} catch {
|
|
736
|
+
continue;
|
|
737
|
+
}
|
|
738
|
+
}
|
|
739
|
+
runtimeConfigLoaded = true;
|
|
740
|
+
})();
|
|
741
|
+
await runtimeConfigLoadingPromise;
|
|
389
742
|
};
|
|
743
|
+
loadRuntimeConfig();
|
|
390
744
|
|
|
391
745
|
//#endregion
|
|
392
746
|
//#region src/cli/CliApp.ts
|
|
@@ -1444,4 +1798,4 @@ var Resource = class Resource {
|
|
|
1444
1798
|
};
|
|
1445
1799
|
|
|
1446
1800
|
//#endregion
|
|
1447
|
-
export { ApiResource, CONDITIONAL_ATTRIBUTE_MISSING, CliApp, GenericResource, InitCommand, MakeResource, Resource, ResourceCollection, ServerResponse, appendRootProperties, buildPaginationExtras, buildResponseEnvelope, defineConfig, getCaseTransformer, getDefaultConfig, getGlobalBaseUrl, getGlobalCase, getGlobalCursorMeta, getGlobalPageName, getGlobalPaginatedExtras, getGlobalPaginatedLinks, getGlobalPaginatedMeta, getGlobalResponseFactory, getGlobalResponseRootKey, getGlobalResponseStructure, getGlobalResponseWrap, getPaginationExtraKeys, isPlainObject, mergeMetadata, resolveMergeWhen, resolveWhen, resolveWhenNotNull, resolveWithHookMetadata, sanitizeConditionalAttributes, setGlobalBaseUrl, setGlobalCase, setGlobalCursorMeta, setGlobalPageName, setGlobalPaginatedExtras, setGlobalPaginatedLinks, setGlobalPaginatedMeta, setGlobalResponseFactory, setGlobalResponseRootKey, setGlobalResponseStructure, setGlobalResponseWrap, splitWords, toCamelCase, toKebabCase, toPascalCase, toSnakeCase, transformKeys };
|
|
1801
|
+
export { ApiResource, CONDITIONAL_ATTRIBUTE_MISSING, CliApp, GenericResource, InitCommand, MakeResource, Resource, ResourceCollection, ServerResponse, appendRootProperties, applyRuntimeConfig, buildPaginationExtras, buildResponseEnvelope, defineConfig, getCaseTransformer, getDefaultConfig, getGlobalBaseUrl, getGlobalCase, getGlobalCursorMeta, getGlobalPageName, getGlobalPaginatedExtras, getGlobalPaginatedLinks, getGlobalPaginatedMeta, getGlobalResponseFactory, getGlobalResponseRootKey, getGlobalResponseStructure, getGlobalResponseWrap, getPaginationExtraKeys, isPlainObject, loadRuntimeConfig, mergeMetadata, resetRuntimeConfigForTests, resolveMergeWhen, resolveWhen, resolveWhenNotNull, resolveWithHookMetadata, sanitizeConditionalAttributes, setGlobalBaseUrl, setGlobalCase, setGlobalCursorMeta, setGlobalPageName, setGlobalPaginatedExtras, setGlobalPaginatedLinks, setGlobalPaginatedMeta, setGlobalResponseFactory, setGlobalResponseRootKey, setGlobalResponseStructure, setGlobalResponseWrap, splitWords, toCamelCase, toKebabCase, toPascalCase, toSnakeCase, transformKeys };
|
package/package.json
CHANGED