resora 0.2.2 → 0.2.3
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/index.cjs +6 -1861
- package/dist/index.mjs +6 -1779
- package/package.json +1 -1
package/dist/index.cjs
CHANGED
|
@@ -1,907 +1,10 @@
|
|
|
1
|
-
Object.defineProperty(exports,
|
|
2
|
-
//#region \0rolldown/runtime.js
|
|
3
|
-
var __create = Object.create;
|
|
4
|
-
var __defProp = Object.defineProperty;
|
|
5
|
-
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
6
|
-
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
7
|
-
var __getProtoOf = Object.getPrototypeOf;
|
|
8
|
-
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
9
|
-
var __copyProps = (to, from, except, desc) => {
|
|
10
|
-
if (from && typeof from === "object" || typeof from === "function") {
|
|
11
|
-
for (var keys = __getOwnPropNames(from), i = 0, n = keys.length, key; i < n; i++) {
|
|
12
|
-
key = keys[i];
|
|
13
|
-
if (!__hasOwnProp.call(to, key) && key !== except) {
|
|
14
|
-
__defProp(to, key, {
|
|
15
|
-
get: ((k) => from[k]).bind(null, key),
|
|
16
|
-
enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable
|
|
17
|
-
});
|
|
18
|
-
}
|
|
19
|
-
}
|
|
20
|
-
}
|
|
21
|
-
return to;
|
|
22
|
-
};
|
|
23
|
-
var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", {
|
|
24
|
-
value: mod,
|
|
25
|
-
enumerable: true
|
|
26
|
-
}) : target, mod));
|
|
27
|
-
|
|
28
|
-
//#endregion
|
|
29
|
-
let fs = require("fs");
|
|
30
|
-
let path = require("path");
|
|
31
|
-
path = __toESM(path);
|
|
32
|
-
let module$1 = require("module");
|
|
33
|
-
let url = require("url");
|
|
34
|
-
let _h3ravel_musket = require("@h3ravel/musket");
|
|
35
|
-
|
|
36
|
-
//#region src/ApiResource.ts
|
|
37
|
-
/**
|
|
38
|
-
* ApiResource function to return the Resource instance
|
|
39
|
-
*
|
|
40
|
-
* @param instance Resource instance
|
|
41
|
-
* @returns Resource instance
|
|
42
|
-
*/
|
|
43
|
-
function ApiResource(instance) {
|
|
44
|
-
return instance;
|
|
45
|
-
}
|
|
46
|
-
|
|
47
|
-
//#endregion
|
|
48
|
-
//#region src/utilities/state.ts
|
|
49
|
-
let globalPreferredCase;
|
|
50
|
-
let globalResponseStructure;
|
|
51
|
-
let globalPaginatedExtras = ["meta", "links"];
|
|
52
|
-
let globalPaginatedLinks = {
|
|
53
|
-
first: "first",
|
|
54
|
-
last: "last",
|
|
55
|
-
prev: "prev",
|
|
56
|
-
next: "next"
|
|
57
|
-
};
|
|
58
|
-
let globalBaseUrl = "https://localhost";
|
|
59
|
-
let globalPageName = "page";
|
|
60
|
-
let globalPaginatedMeta = {
|
|
61
|
-
to: "to",
|
|
62
|
-
from: "from",
|
|
63
|
-
links: "links",
|
|
64
|
-
path: "path",
|
|
65
|
-
total: "total",
|
|
66
|
-
per_page: "per_page",
|
|
67
|
-
last_page: "last_page",
|
|
68
|
-
current_page: "current_page"
|
|
69
|
-
};
|
|
70
|
-
let globalCursorMeta = {
|
|
71
|
-
previous: "previous",
|
|
72
|
-
next: "next"
|
|
73
|
-
};
|
|
74
|
-
/**
|
|
75
|
-
* Sets the global case style for response keys, which will be applied
|
|
76
|
-
* to all responses unless overridden by individual resource configurations.
|
|
77
|
-
*
|
|
78
|
-
* @param style The case style to set as the global default for response keys.
|
|
79
|
-
*/
|
|
80
|
-
const setGlobalCase = (style) => {
|
|
81
|
-
globalPreferredCase = style;
|
|
82
|
-
};
|
|
83
|
-
/**
|
|
84
|
-
* Retrieves the global case style for response keys, which is used
|
|
85
|
-
* to determine how keys in responses should be formatted.
|
|
86
|
-
*
|
|
87
|
-
* @returns
|
|
88
|
-
*/
|
|
89
|
-
const getGlobalCase = () => {
|
|
90
|
-
return globalPreferredCase;
|
|
91
|
-
};
|
|
92
|
-
/**
|
|
93
|
-
* Sets the global response structure configuration, which defines how
|
|
94
|
-
* responses should be structured across the application.
|
|
95
|
-
*
|
|
96
|
-
* @param config The response structure configuration object.
|
|
97
|
-
*/
|
|
98
|
-
const setGlobalResponseStructure = (config) => {
|
|
99
|
-
globalResponseStructure = config;
|
|
100
|
-
};
|
|
101
|
-
/**
|
|
102
|
-
* Retrieves the global response structure configuration, which
|
|
103
|
-
* defines how responses should be structured across the application.
|
|
104
|
-
*
|
|
105
|
-
* @returns
|
|
106
|
-
*/
|
|
107
|
-
const getGlobalResponseStructure = () => {
|
|
108
|
-
return globalResponseStructure;
|
|
109
|
-
};
|
|
110
|
-
/**
|
|
111
|
-
* Sets the global response root key, which is the key under which
|
|
112
|
-
* the main data will be nested in responses if wrapping is enabled.
|
|
113
|
-
*
|
|
114
|
-
* @param rootKey The root key to set for response data.
|
|
115
|
-
*/
|
|
116
|
-
const setGlobalResponseRootKey = (rootKey) => {
|
|
117
|
-
globalResponseStructure = {
|
|
118
|
-
...globalResponseStructure || {},
|
|
119
|
-
rootKey
|
|
120
|
-
};
|
|
121
|
-
};
|
|
122
|
-
/**
|
|
123
|
-
* Sets the global response wrap option, which determines whether responses
|
|
124
|
-
* should be wrapped in a root key or returned unwrapped when possible.
|
|
125
|
-
*
|
|
126
|
-
* @param wrap The wrap option to set for responses.
|
|
127
|
-
*/
|
|
128
|
-
const setGlobalResponseWrap = (wrap) => {
|
|
129
|
-
globalResponseStructure = {
|
|
130
|
-
...globalResponseStructure || {},
|
|
131
|
-
wrap
|
|
132
|
-
};
|
|
133
|
-
};
|
|
134
|
-
/**
|
|
135
|
-
* Retrieves the global response wrap option, which indicates whether responses
|
|
136
|
-
* should be wrapped in a root key or returned unwrapped when possible.
|
|
137
|
-
*
|
|
138
|
-
* @returns
|
|
139
|
-
*/
|
|
140
|
-
const getGlobalResponseWrap = () => {
|
|
141
|
-
return globalResponseStructure?.wrap;
|
|
142
|
-
};
|
|
143
|
-
/**
|
|
144
|
-
* Retrieves the global response root key, which is the key under which the main
|
|
145
|
-
* data will be nested in responses if wrapping is enabled.
|
|
146
|
-
*
|
|
147
|
-
* @returns
|
|
148
|
-
*/
|
|
149
|
-
const getGlobalResponseRootKey = () => {
|
|
150
|
-
return globalResponseStructure?.rootKey;
|
|
151
|
-
};
|
|
152
|
-
/**
|
|
153
|
-
* Sets the global response factory, which is a custom function that can be used
|
|
154
|
-
* to produce a completely custom response structure based on the provided
|
|
155
|
-
* payload and context.
|
|
156
|
-
*
|
|
157
|
-
* @param factory The response factory function to set as the global default for response construction.
|
|
158
|
-
*/
|
|
159
|
-
const setGlobalResponseFactory = (factory) => {
|
|
160
|
-
globalResponseStructure = {
|
|
161
|
-
...globalResponseStructure || {},
|
|
162
|
-
factory
|
|
163
|
-
};
|
|
164
|
-
};
|
|
165
|
-
/**
|
|
166
|
-
* Retrieves the global response factory, which is a custom function that
|
|
167
|
-
* can be used to produce a completely custom response structure based on
|
|
168
|
-
* the provided payload and context.
|
|
169
|
-
*
|
|
170
|
-
* @returns
|
|
171
|
-
*/
|
|
172
|
-
const getGlobalResponseFactory = () => {
|
|
173
|
-
return globalResponseStructure?.factory;
|
|
174
|
-
};
|
|
175
|
-
/**
|
|
176
|
-
* Sets the global paginated extras configuration, which defines the keys
|
|
177
|
-
* to use for pagination metadata, links, and cursor information in paginated responses.
|
|
178
|
-
*
|
|
179
|
-
* @param extras The paginated extras configuration object.
|
|
180
|
-
*/
|
|
181
|
-
const setGlobalPaginatedExtras = (extras) => {
|
|
182
|
-
globalPaginatedExtras = extras;
|
|
183
|
-
};
|
|
184
|
-
/**
|
|
185
|
-
* Retrieves the global paginated extras configuration, which defines the keys to use for pagination metadata, links, and cursor information in paginated responses.
|
|
186
|
-
*
|
|
187
|
-
* @returns
|
|
188
|
-
*/
|
|
189
|
-
const getGlobalPaginatedExtras = () => {
|
|
190
|
-
return globalPaginatedExtras;
|
|
191
|
-
};
|
|
192
|
-
/**
|
|
193
|
-
* Sets the global paginated links configuration, which defines the keys to
|
|
194
|
-
* use for pagination links (first, last, prev, next) in paginated responses.
|
|
195
|
-
*
|
|
196
|
-
* @param links The paginated links configuration object.
|
|
197
|
-
*/
|
|
198
|
-
const setGlobalPaginatedLinks = (links) => {
|
|
199
|
-
globalPaginatedLinks = {
|
|
200
|
-
...globalPaginatedLinks,
|
|
201
|
-
...links
|
|
202
|
-
};
|
|
203
|
-
};
|
|
204
|
-
/**
|
|
205
|
-
* Retrieves the global paginated links configuration, which defines the keys to use for pagination links (first, last, prev, next) in paginated responses.
|
|
206
|
-
*
|
|
207
|
-
* @returns
|
|
208
|
-
*/
|
|
209
|
-
const getGlobalPaginatedLinks = () => {
|
|
210
|
-
return globalPaginatedLinks;
|
|
211
|
-
};
|
|
212
|
-
/**
|
|
213
|
-
* Sets the global base URL, which is used for generating pagination links in responses.
|
|
214
|
-
*
|
|
215
|
-
* @param baseUrl The base URL to set for pagination link generation.
|
|
216
|
-
*/
|
|
217
|
-
const setGlobalBaseUrl = (baseUrl) => {
|
|
218
|
-
globalBaseUrl = baseUrl;
|
|
219
|
-
};
|
|
220
|
-
/**
|
|
221
|
-
* Retrieves the global base URL, which is used for generating pagination links in responses.
|
|
222
|
-
*
|
|
223
|
-
* @returns
|
|
224
|
-
*/
|
|
225
|
-
const getGlobalBaseUrl = () => {
|
|
226
|
-
return globalBaseUrl;
|
|
227
|
-
};
|
|
228
|
-
/**
|
|
229
|
-
* Sets the global page name, which is the query parameter name used for the page number in paginated requests and link generation.
|
|
230
|
-
*
|
|
231
|
-
* @param pageName
|
|
232
|
-
*/
|
|
233
|
-
const setGlobalPageName = (pageName) => {
|
|
234
|
-
globalPageName = pageName;
|
|
235
|
-
};
|
|
236
|
-
/**
|
|
237
|
-
* Retrieves the global page name, which is the query parameter name
|
|
238
|
-
* used for the page number in paginated requests and link generation.
|
|
239
|
-
*
|
|
240
|
-
* @returns
|
|
241
|
-
*/
|
|
242
|
-
const getGlobalPageName = () => {
|
|
243
|
-
return globalPageName;
|
|
244
|
-
};
|
|
245
|
-
/**
|
|
246
|
-
* Retrieves the keys to use for pagination extras (meta, links, cursor) based
|
|
247
|
-
* on the global configuration.
|
|
248
|
-
*
|
|
249
|
-
* @param meta Whether to include pagination metadata in the response.
|
|
250
|
-
*/
|
|
251
|
-
const setGlobalPaginatedMeta = (meta) => {
|
|
252
|
-
globalPaginatedMeta = {
|
|
253
|
-
...globalPaginatedMeta,
|
|
254
|
-
...meta
|
|
255
|
-
};
|
|
256
|
-
};
|
|
257
|
-
/**
|
|
258
|
-
* Retrieves the keys to use for pagination extras (meta, links, cursor) based
|
|
259
|
-
* on the global configuration.
|
|
260
|
-
*
|
|
261
|
-
* @returns The global pagination metadata configuration.
|
|
262
|
-
*/
|
|
263
|
-
const getGlobalPaginatedMeta = () => {
|
|
264
|
-
return globalPaginatedMeta;
|
|
265
|
-
};
|
|
266
|
-
/**
|
|
267
|
-
* Sets the global cursor meta configuration, which defines the keys to use
|
|
268
|
-
* for cursor pagination metadata (previous, next) in responses.
|
|
269
|
-
*
|
|
270
|
-
* @param meta The cursor meta configuration object.
|
|
271
|
-
*/
|
|
272
|
-
const setGlobalCursorMeta = (meta) => {
|
|
273
|
-
globalCursorMeta = {
|
|
274
|
-
...globalCursorMeta,
|
|
275
|
-
...meta
|
|
276
|
-
};
|
|
277
|
-
};
|
|
278
|
-
/**
|
|
279
|
-
* Retrieves the keys to use for cursor pagination metadata (previous, next) in
|
|
280
|
-
* responses based on the global configuration.
|
|
281
|
-
*
|
|
282
|
-
* @returns The global cursor pagination metadata configuration.
|
|
283
|
-
*/
|
|
284
|
-
const getGlobalCursorMeta = () => {
|
|
285
|
-
return globalCursorMeta;
|
|
286
|
-
};
|
|
287
|
-
|
|
288
|
-
//#endregion
|
|
289
|
-
//#region src/utilities/pagination.ts
|
|
290
|
-
/**
|
|
291
|
-
* Retrieves the configured keys for pagination extras (meta, links, cursor) based on the application's configuration.
|
|
292
|
-
*
|
|
293
|
-
* @returns An object containing the keys for meta, links, and cursor extras, or `undefined` if not configured.
|
|
294
|
-
*/
|
|
295
|
-
const getPaginationExtraKeys = () => {
|
|
296
|
-
const extras = getGlobalPaginatedExtras();
|
|
297
|
-
if (Array.isArray(extras)) return {
|
|
298
|
-
metaKey: extras.includes("meta") ? "meta" : void 0,
|
|
299
|
-
linksKey: extras.includes("links") ? "links" : void 0,
|
|
300
|
-
cursorKey: extras.includes("cursor") ? "cursor" : void 0
|
|
301
|
-
};
|
|
302
|
-
return {
|
|
303
|
-
metaKey: extras.meta,
|
|
304
|
-
linksKey: extras.links,
|
|
305
|
-
cursorKey: extras.cursor
|
|
306
|
-
};
|
|
307
|
-
};
|
|
308
|
-
/**
|
|
309
|
-
* Builds a pagination URL for a given page number and path, using the global base URL and page name configuration.
|
|
310
|
-
*
|
|
311
|
-
* @param page The page number for which to build the URL. If `undefined`, the function will return `undefined`.
|
|
312
|
-
* @param pathName The path to use for the URL. If not provided, it will default to an empty string.
|
|
313
|
-
* @returns
|
|
314
|
-
*/
|
|
315
|
-
const buildPageUrl = (page, pathName) => {
|
|
316
|
-
if (typeof page === "undefined") return;
|
|
317
|
-
const rawPath = pathName || "";
|
|
318
|
-
const base = getGlobalBaseUrl() || "";
|
|
319
|
-
const isAbsolutePath = /^https?:\/\//i.test(rawPath);
|
|
320
|
-
const normalizedBase = base.replace(/\/$/, "");
|
|
321
|
-
const normalizedPath = rawPath.replace(/^\//, "");
|
|
322
|
-
const root = isAbsolutePath ? rawPath : normalizedBase ? normalizedPath ? `${normalizedBase}/${normalizedPath}` : normalizedBase : "";
|
|
323
|
-
if (!root) return;
|
|
324
|
-
const url = new URL(root);
|
|
325
|
-
url.searchParams.set(getGlobalPageName() || "page", String(page));
|
|
326
|
-
return url.toString();
|
|
327
|
-
};
|
|
328
|
-
/**
|
|
329
|
-
* Builds pagination extras (meta, links, cursor) for a given resource based on
|
|
330
|
-
* its pagination and cursor properties, using the configured keys for each type of extra.
|
|
331
|
-
*
|
|
332
|
-
* @param resource The resource for which to build pagination extras.
|
|
333
|
-
* @returns An object containing the pagination extras (meta, links, cursor) for the resource.
|
|
334
|
-
*/
|
|
335
|
-
const buildPaginationExtras = (resource) => {
|
|
336
|
-
const { metaKey, linksKey, cursorKey } = getPaginationExtraKeys();
|
|
337
|
-
const extra = {};
|
|
338
|
-
const pagination = resource?.pagination;
|
|
339
|
-
const cursor = resource?.cursor;
|
|
340
|
-
const metaBlock = {};
|
|
341
|
-
const linksBlock = {};
|
|
342
|
-
if (pagination) {
|
|
343
|
-
const metaSource = {
|
|
344
|
-
to: pagination.to,
|
|
345
|
-
from: pagination.from,
|
|
346
|
-
links: pagination.links,
|
|
347
|
-
path: pagination.path,
|
|
348
|
-
total: pagination.total,
|
|
349
|
-
per_page: pagination.perPage,
|
|
350
|
-
last_page: pagination.lastPage,
|
|
351
|
-
current_page: pagination.currentPage
|
|
352
|
-
};
|
|
353
|
-
for (const [sourceKey, outputKey] of Object.entries(getGlobalPaginatedMeta())) {
|
|
354
|
-
if (!outputKey) continue;
|
|
355
|
-
const value = metaSource[sourceKey];
|
|
356
|
-
if (typeof value !== "undefined") metaBlock[outputKey] = value;
|
|
357
|
-
}
|
|
358
|
-
const linksSource = {
|
|
359
|
-
first: buildPageUrl(pagination.firstPage, pagination.path),
|
|
360
|
-
last: buildPageUrl(pagination.lastPage, pagination.path),
|
|
361
|
-
prev: buildPageUrl(pagination.prevPage, pagination.path),
|
|
362
|
-
next: buildPageUrl(pagination.nextPage, pagination.path)
|
|
363
|
-
};
|
|
364
|
-
for (const [sourceKey, outputKey] of Object.entries(getGlobalPaginatedLinks())) {
|
|
365
|
-
if (!outputKey) continue;
|
|
366
|
-
const value = linksSource[sourceKey];
|
|
367
|
-
if (typeof value !== "undefined") linksBlock[outputKey] = value;
|
|
368
|
-
}
|
|
369
|
-
}
|
|
370
|
-
if (cursor) {
|
|
371
|
-
const cursorBlock = {};
|
|
372
|
-
const cursorSource = {
|
|
373
|
-
previous: cursor.previous,
|
|
374
|
-
next: cursor.next
|
|
375
|
-
};
|
|
376
|
-
for (const [sourceKey, outputKey] of Object.entries(getGlobalCursorMeta())) {
|
|
377
|
-
if (!outputKey) continue;
|
|
378
|
-
const value = cursorSource[sourceKey];
|
|
379
|
-
if (typeof value !== "undefined") cursorBlock[outputKey] = value;
|
|
380
|
-
}
|
|
381
|
-
if (cursorKey && Object.keys(cursorBlock).length > 0) extra[cursorKey] = cursorBlock;
|
|
382
|
-
else if (Object.keys(cursorBlock).length > 0) metaBlock.cursor = cursorBlock;
|
|
383
|
-
}
|
|
384
|
-
if (metaKey && Object.keys(metaBlock).length > 0) extra[metaKey] = metaBlock;
|
|
385
|
-
if (linksKey && Object.keys(linksBlock).length > 0) extra[linksKey] = linksBlock;
|
|
386
|
-
return extra;
|
|
387
|
-
};
|
|
388
|
-
|
|
389
|
-
//#endregion
|
|
390
|
-
//#region src/utilities/objects.ts
|
|
391
|
-
/**
|
|
392
|
-
* Utility functions for working with objects, including type checking, merging, and property manipulation.
|
|
393
|
-
*
|
|
394
|
-
* @param value The value to check.
|
|
395
|
-
* @returns `true` if the value is a plain object, `false` otherwise.
|
|
396
|
-
*/
|
|
397
|
-
const isPlainObject = (value) => {
|
|
398
|
-
if (typeof value !== "object" || value === null) return false;
|
|
399
|
-
if (Array.isArray(value) || value instanceof Date || value instanceof RegExp) return false;
|
|
400
|
-
const proto = Object.getPrototypeOf(value);
|
|
401
|
-
return proto === Object.prototype || proto === null;
|
|
402
|
-
};
|
|
403
|
-
/**
|
|
404
|
-
* Appends extra properties to a response body, ensuring that the main data is wrapped under a specified root key if necessary.
|
|
405
|
-
*
|
|
406
|
-
* @param body The original response body, which can be an object, array, or primitive value.
|
|
407
|
-
* @param extra Extra properties to append to the response body.
|
|
408
|
-
* @param rootKey The root key under which to wrap the main data if necessary.
|
|
409
|
-
* @returns The response body with the extra properties appended.
|
|
410
|
-
*/
|
|
411
|
-
const appendRootProperties = (body, extra, rootKey = "data") => {
|
|
412
|
-
if (!extra || Object.keys(extra).length === 0) return body;
|
|
413
|
-
if (Array.isArray(body)) return {
|
|
414
|
-
[rootKey]: body,
|
|
415
|
-
...extra
|
|
416
|
-
};
|
|
417
|
-
if (isPlainObject(body)) return {
|
|
418
|
-
...body,
|
|
419
|
-
...extra
|
|
420
|
-
};
|
|
421
|
-
return {
|
|
422
|
-
[rootKey]: body,
|
|
423
|
-
...extra
|
|
424
|
-
};
|
|
425
|
-
};
|
|
426
|
-
/**
|
|
427
|
-
* Deeply merges two metadata objects, combining nested objects recursively.
|
|
428
|
-
*
|
|
429
|
-
* @param base The base metadata object to merge into.
|
|
430
|
-
* @param incoming The incoming metadata object to merge from.
|
|
431
|
-
* @returns
|
|
432
|
-
*/
|
|
433
|
-
const mergeMetadata = (base, incoming) => {
|
|
434
|
-
if (!incoming) return base;
|
|
435
|
-
if (!base) return incoming;
|
|
436
|
-
const merged = { ...base };
|
|
437
|
-
for (const [key, value] of Object.entries(incoming)) {
|
|
438
|
-
const existing = merged[key];
|
|
439
|
-
if (isPlainObject(existing) && isPlainObject(value)) merged[key] = mergeMetadata(existing, value);
|
|
440
|
-
else merged[key] = value;
|
|
441
|
-
}
|
|
442
|
-
return merged;
|
|
443
|
-
};
|
|
444
|
-
|
|
445
|
-
//#endregion
|
|
446
|
-
//#region src/utilities/response.ts
|
|
447
|
-
/**
|
|
448
|
-
* Builds a response envelope based on the provided payload,
|
|
449
|
-
* metadata, and configuration options.
|
|
450
|
-
*
|
|
451
|
-
* @param config The configuration object containing payload, metadata, and other options for building the response.
|
|
452
|
-
*/
|
|
453
|
-
const buildResponseEnvelope = ({ payload, meta, metaKey = "meta", wrap = true, rootKey = "data", factory, context }) => {
|
|
454
|
-
if (factory) return factory(payload, {
|
|
455
|
-
...context,
|
|
456
|
-
rootKey,
|
|
457
|
-
meta
|
|
458
|
-
});
|
|
459
|
-
if (!wrap) {
|
|
460
|
-
if (typeof meta === "undefined") return payload;
|
|
461
|
-
if (isPlainObject(payload)) return {
|
|
462
|
-
...payload,
|
|
463
|
-
[metaKey]: meta
|
|
464
|
-
};
|
|
465
|
-
return {
|
|
466
|
-
[rootKey]: payload,
|
|
467
|
-
[metaKey]: meta
|
|
468
|
-
};
|
|
469
|
-
}
|
|
470
|
-
const body = { [rootKey]: payload };
|
|
471
|
-
if (typeof meta !== "undefined") body[metaKey] = meta;
|
|
472
|
-
return body;
|
|
473
|
-
};
|
|
474
|
-
|
|
475
|
-
//#endregion
|
|
476
|
-
//#region src/utilities/metadata.ts
|
|
477
|
-
/**
|
|
478
|
-
* Resolves metadata from a resource instance by checking for a custom `with` method.
|
|
479
|
-
* If the method exists and is different from the base method, it calls it to retrieve metadata.
|
|
480
|
-
* This allows resources to provide additional metadata for response construction.
|
|
481
|
-
*
|
|
482
|
-
* @param instance The resource instance to check for a custom `with` method.
|
|
483
|
-
* @param baseWithMethod The base `with` method to compare against.
|
|
484
|
-
* @returns The resolved metadata or `undefined` if no custom metadata is provided.
|
|
485
|
-
*/
|
|
486
|
-
const resolveWithHookMetadata = (instance, baseWithMethod) => {
|
|
487
|
-
const candidate = instance?.with;
|
|
488
|
-
if (typeof candidate !== "function" || candidate === baseWithMethod) return;
|
|
489
|
-
if (candidate.length > 0) return;
|
|
490
|
-
const result = candidate.call(instance);
|
|
491
|
-
return isPlainObject(result) ? result : void 0;
|
|
492
|
-
};
|
|
493
|
-
|
|
494
|
-
//#endregion
|
|
495
|
-
//#region src/utilities/conditional.ts
|
|
496
|
-
const CONDITIONAL_ATTRIBUTE_MISSING = Symbol("resora.conditional.missing");
|
|
497
|
-
/**
|
|
498
|
-
* Resolves a value based on a condition. If the condition is falsy, it returns a special symbol indicating that the attribute is missing.
|
|
499
|
-
*
|
|
500
|
-
*
|
|
501
|
-
* @param condition The condition to evaluate.
|
|
502
|
-
* @param value The value or function to resolve if the condition is truthy.
|
|
503
|
-
* @returns The resolved value or a symbol indicating the attribute is missing.
|
|
504
|
-
*/
|
|
505
|
-
const resolveWhen = (condition, value) => {
|
|
506
|
-
if (!condition) return CONDITIONAL_ATTRIBUTE_MISSING;
|
|
507
|
-
return typeof value === "function" ? value() : value;
|
|
508
|
-
};
|
|
509
|
-
/**
|
|
510
|
-
* Resolves a value only if it is not null or undefined.
|
|
511
|
-
*
|
|
512
|
-
* @param value The value to resolve.
|
|
513
|
-
* @returns The resolved value or a symbol indicating the attribute is missing.
|
|
514
|
-
*/
|
|
515
|
-
const resolveWhenNotNull = (value) => {
|
|
516
|
-
return value === null || typeof value === "undefined" ? CONDITIONAL_ATTRIBUTE_MISSING : value;
|
|
517
|
-
};
|
|
518
|
-
/**
|
|
519
|
-
* Conditionally merges object attributes based on a condition.
|
|
520
|
-
*
|
|
521
|
-
* @param condition
|
|
522
|
-
* @param value
|
|
523
|
-
* @returns
|
|
524
|
-
*/
|
|
525
|
-
const resolveMergeWhen = (condition, value) => {
|
|
526
|
-
if (!condition) return {};
|
|
527
|
-
const resolved = typeof value === "function" ? value() : value;
|
|
528
|
-
return isPlainObject(resolved) ? resolved : {};
|
|
529
|
-
};
|
|
530
|
-
/**
|
|
531
|
-
* Recursively sanitizes an object or array by removing any attributes that are marked as missing using the special symbol.
|
|
532
|
-
*
|
|
533
|
-
* @param value The value to sanitize.
|
|
534
|
-
* @returns The sanitized value.
|
|
535
|
-
*/
|
|
536
|
-
const sanitizeConditionalAttributes = (value) => {
|
|
537
|
-
if (value === CONDITIONAL_ATTRIBUTE_MISSING) return CONDITIONAL_ATTRIBUTE_MISSING;
|
|
538
|
-
if (Array.isArray(value)) return value.map((item) => sanitizeConditionalAttributes(item)).filter((item) => item !== CONDITIONAL_ATTRIBUTE_MISSING);
|
|
539
|
-
if (isPlainObject(value)) {
|
|
540
|
-
const result = {};
|
|
541
|
-
for (const [key, nestedValue] of Object.entries(value)) {
|
|
542
|
-
const sanitizedValue = sanitizeConditionalAttributes(nestedValue);
|
|
543
|
-
if (sanitizedValue === CONDITIONAL_ATTRIBUTE_MISSING) continue;
|
|
544
|
-
result[key] = sanitizedValue;
|
|
545
|
-
}
|
|
546
|
-
return result;
|
|
547
|
-
}
|
|
548
|
-
return value;
|
|
549
|
-
};
|
|
550
|
-
|
|
551
|
-
//#endregion
|
|
552
|
-
//#region src/utilities/case.ts
|
|
553
|
-
/**
|
|
554
|
-
* Splits a string into words based on common delimiters and capitalization patterns.
|
|
555
|
-
* Handles camelCase, PascalCase, snake_case, kebab-case, and space-separated words.
|
|
556
|
-
*
|
|
557
|
-
* @param str
|
|
558
|
-
* @returns
|
|
559
|
-
*/
|
|
560
|
-
const splitWords = (str) => {
|
|
561
|
-
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);
|
|
562
|
-
};
|
|
563
|
-
/**
|
|
564
|
-
* Transforms a string to camelCase.
|
|
565
|
-
*
|
|
566
|
-
* @param str
|
|
567
|
-
* @returns
|
|
568
|
-
*/
|
|
569
|
-
const toCamelCase = (str) => {
|
|
570
|
-
return splitWords(str).map((w, i) => i === 0 ? w : w.charAt(0).toUpperCase() + w.slice(1)).join("");
|
|
571
|
-
};
|
|
572
|
-
/**
|
|
573
|
-
* Transforms a string to snake_case.
|
|
574
|
-
*
|
|
575
|
-
* @param str
|
|
576
|
-
* @returns
|
|
577
|
-
*/
|
|
578
|
-
const toSnakeCase = (str) => {
|
|
579
|
-
return splitWords(str).join("_");
|
|
580
|
-
};
|
|
581
|
-
/**
|
|
582
|
-
* Transforms a string to PascalCase.
|
|
583
|
-
*
|
|
584
|
-
* @param str
|
|
585
|
-
* @returns
|
|
586
|
-
*/
|
|
587
|
-
const toPascalCase = (str) => {
|
|
588
|
-
return splitWords(str).map((w) => w.charAt(0).toUpperCase() + w.slice(1)).join("");
|
|
589
|
-
};
|
|
590
|
-
/**
|
|
591
|
-
* Transforms a string to kebab-case.
|
|
592
|
-
*
|
|
593
|
-
* @param str
|
|
594
|
-
* @returns
|
|
595
|
-
*/
|
|
596
|
-
const toKebabCase = (str) => {
|
|
597
|
-
return splitWords(str).join("-");
|
|
598
|
-
};
|
|
599
|
-
/**
|
|
600
|
-
* Retrieves the appropriate case transformation function based on the provided style.
|
|
601
|
-
*
|
|
602
|
-
* @param style
|
|
603
|
-
* @returns
|
|
604
|
-
*/
|
|
605
|
-
const getCaseTransformer = (style) => {
|
|
606
|
-
if (typeof style === "function") return style;
|
|
607
|
-
switch (style) {
|
|
608
|
-
case "camel": return toCamelCase;
|
|
609
|
-
case "snake": return toSnakeCase;
|
|
610
|
-
case "pascal": return toPascalCase;
|
|
611
|
-
case "kebab": return toKebabCase;
|
|
612
|
-
}
|
|
613
|
-
};
|
|
614
|
-
/**
|
|
615
|
-
* Transforms the keys of an object (including nested objects and arrays) using
|
|
616
|
-
* the provided transformer function.
|
|
617
|
-
*
|
|
618
|
-
* @param obj
|
|
619
|
-
* @param transformer
|
|
620
|
-
* @returns
|
|
621
|
-
*/
|
|
622
|
-
const transformKeys = (obj, transformer) => {
|
|
623
|
-
if (obj === null || obj === void 0) return obj;
|
|
624
|
-
if (Array.isArray(obj)) return obj.map((item) => transformKeys(item, transformer));
|
|
625
|
-
if (obj instanceof Date || obj instanceof RegExp) return obj;
|
|
626
|
-
if (typeof obj === "object") return Object.fromEntries(Object.entries(obj).map(([key, value]) => [transformer(key), transformKeys(value, transformer)]));
|
|
627
|
-
return obj;
|
|
628
|
-
};
|
|
629
|
-
|
|
630
|
-
//#endregion
|
|
631
|
-
//#region src/utilities/config.ts
|
|
632
|
-
let stubsDir = path.default.resolve(process.cwd(), "node_modules/resora/stubs");
|
|
633
|
-
if (!(0, fs.existsSync)(stubsDir)) stubsDir = path.default.resolve(process.cwd(), "stubs");
|
|
634
|
-
const getDefaultConfig = () => {
|
|
635
|
-
return {
|
|
636
|
-
stubsDir,
|
|
637
|
-
preferredCase: "camel",
|
|
638
|
-
responseStructure: {
|
|
639
|
-
wrap: true,
|
|
640
|
-
rootKey: "data"
|
|
641
|
-
},
|
|
642
|
-
paginatedExtras: ["meta", "links"],
|
|
643
|
-
baseUrl: "https://localhost",
|
|
644
|
-
pageName: "page",
|
|
645
|
-
paginatedLinks: {
|
|
646
|
-
first: "first",
|
|
647
|
-
last: "last",
|
|
648
|
-
prev: "prev",
|
|
649
|
-
next: "next"
|
|
650
|
-
},
|
|
651
|
-
paginatedMeta: {
|
|
652
|
-
to: "to",
|
|
653
|
-
from: "from",
|
|
654
|
-
links: "links",
|
|
655
|
-
path: "path",
|
|
656
|
-
total: "total",
|
|
657
|
-
per_page: "per_page",
|
|
658
|
-
last_page: "last_page",
|
|
659
|
-
current_page: "current_page"
|
|
660
|
-
},
|
|
661
|
-
cursorMeta: {
|
|
662
|
-
previous: "previous",
|
|
663
|
-
next: "next"
|
|
664
|
-
},
|
|
665
|
-
resourcesDir: "src/resources",
|
|
666
|
-
stubs: {
|
|
667
|
-
config: "resora.config.stub",
|
|
668
|
-
resource: "resource.stub",
|
|
669
|
-
collection: "resource.collection.stub"
|
|
670
|
-
}
|
|
671
|
-
};
|
|
672
|
-
};
|
|
673
|
-
/**
|
|
674
|
-
* 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.
|
|
675
|
-
*
|
|
676
|
-
* @param config
|
|
677
|
-
* @returns
|
|
678
|
-
*/
|
|
679
|
-
const defineConfig = (config) => {
|
|
680
|
-
const defConf = getDefaultConfig();
|
|
681
|
-
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 || {}) });
|
|
682
|
-
};
|
|
683
|
-
|
|
684
|
-
//#endregion
|
|
685
|
-
//#region src/utilities/runtime-config.ts
|
|
686
|
-
let runtimeConfigLoaded = false;
|
|
687
|
-
let runtimeConfigLoadingPromise;
|
|
688
|
-
/**
|
|
689
|
-
* Resets the runtime configuration state for testing purposes.
|
|
690
|
-
*
|
|
691
|
-
* @returns
|
|
692
|
-
*/
|
|
693
|
-
const resetRuntimeConfigForTests = () => {
|
|
694
|
-
runtimeConfigLoaded = false;
|
|
695
|
-
runtimeConfigLoadingPromise = void 0;
|
|
696
|
-
};
|
|
697
|
-
/**
|
|
698
|
-
* Applies the provided configuration to the global state of the application.
|
|
699
|
-
*
|
|
700
|
-
* @param config The complete configuration object to apply.
|
|
701
|
-
*/
|
|
702
|
-
const applyRuntimeConfig = (config) => {
|
|
703
|
-
if (config.preferredCase !== "camel") setGlobalCase(config.preferredCase);
|
|
704
|
-
setGlobalResponseStructure(config.responseStructure);
|
|
705
|
-
setGlobalPaginatedExtras(config.paginatedExtras);
|
|
706
|
-
setGlobalPaginatedLinks(config.paginatedLinks);
|
|
707
|
-
setGlobalPaginatedMeta(config.paginatedMeta);
|
|
708
|
-
setGlobalCursorMeta(config.cursorMeta);
|
|
709
|
-
setGlobalBaseUrl(config.baseUrl);
|
|
710
|
-
setGlobalPageName(config.pageName);
|
|
711
|
-
};
|
|
712
|
-
/**
|
|
713
|
-
* Loads the runtime configuration by searching for configuration files in the current working directory.
|
|
714
|
-
* @param configPath The path to the configuration file to load.
|
|
715
|
-
* @returns
|
|
716
|
-
*/
|
|
717
|
-
const importConfigFile = async (configPath) => {
|
|
718
|
-
return await import(`${(0, url.pathToFileURL)(configPath).href}?resora_runtime=${Date.now()}`);
|
|
719
|
-
};
|
|
720
|
-
/**
|
|
721
|
-
* Resolves the imported configuration and applies it to the global state.
|
|
722
|
-
*
|
|
723
|
-
* @param imported
|
|
724
|
-
*/
|
|
725
|
-
const resolveAndApply = (imported) => {
|
|
726
|
-
applyRuntimeConfig(defineConfig((imported?.default ?? imported) || {}));
|
|
727
|
-
runtimeConfigLoaded = true;
|
|
728
|
-
};
|
|
729
|
-
/**
|
|
730
|
-
* Loads the runtime configuration synchronously by searching for CommonJS configuration files in the current working directory.
|
|
731
|
-
*
|
|
732
|
-
* @returns
|
|
733
|
-
*/
|
|
734
|
-
const loadRuntimeConfigSync = () => {
|
|
735
|
-
const require = (0, module$1.createRequire)(require("url").pathToFileURL(__filename).href);
|
|
736
|
-
const syncConfigPaths = [path.default.join(process.cwd(), "resora.config.cjs")];
|
|
737
|
-
for (const configPath of syncConfigPaths) {
|
|
738
|
-
if (!(0, fs.existsSync)(configPath)) continue;
|
|
739
|
-
try {
|
|
740
|
-
resolveAndApply(require(configPath));
|
|
741
|
-
return true;
|
|
742
|
-
} catch {
|
|
743
|
-
continue;
|
|
744
|
-
}
|
|
745
|
-
}
|
|
746
|
-
return false;
|
|
747
|
-
};
|
|
748
|
-
/**
|
|
749
|
-
* Loads the runtime configuration by searching for configuration files in the current working directory.
|
|
750
|
-
*
|
|
751
|
-
* @returns
|
|
752
|
-
*/
|
|
753
|
-
const loadRuntimeConfig = async () => {
|
|
754
|
-
if (runtimeConfigLoaded) return;
|
|
755
|
-
if (runtimeConfigLoadingPromise) return await runtimeConfigLoadingPromise;
|
|
756
|
-
if (loadRuntimeConfigSync()) return;
|
|
757
|
-
runtimeConfigLoadingPromise = (async () => {
|
|
758
|
-
const possibleConfigPaths = [path.default.join(process.cwd(), "resora.config.js"), path.default.join(process.cwd(), "resora.config.ts")];
|
|
759
|
-
for (const configPath of possibleConfigPaths) {
|
|
760
|
-
if (!(0, fs.existsSync)(configPath)) continue;
|
|
761
|
-
try {
|
|
762
|
-
resolveAndApply(await importConfigFile(configPath));
|
|
763
|
-
return;
|
|
764
|
-
} catch {
|
|
765
|
-
continue;
|
|
766
|
-
}
|
|
767
|
-
}
|
|
768
|
-
runtimeConfigLoaded = true;
|
|
769
|
-
})();
|
|
770
|
-
await runtimeConfigLoadingPromise;
|
|
771
|
-
};
|
|
772
|
-
loadRuntimeConfig();
|
|
773
|
-
|
|
774
|
-
//#endregion
|
|
775
|
-
//#region src/cli/CliApp.ts
|
|
776
|
-
var CliApp = class {
|
|
777
|
-
command;
|
|
778
|
-
config = {};
|
|
779
|
-
constructor(config = {}) {
|
|
780
|
-
this.config = defineConfig(config);
|
|
781
|
-
}
|
|
782
|
-
async loadConfig(config = {}) {
|
|
783
|
-
this.config = defineConfig(config);
|
|
784
|
-
const possibleConfigPaths = [
|
|
785
|
-
(0, path.join)(process.cwd(), "resora.config.ts"),
|
|
786
|
-
(0, path.join)(process.cwd(), "resora.config.js"),
|
|
787
|
-
(0, path.join)(process.cwd(), "resora.config.cjs")
|
|
788
|
-
];
|
|
789
|
-
for (const configPath of possibleConfigPaths) if ((0, fs.existsSync)(configPath)) try {
|
|
790
|
-
const { default: userConfig } = await import(configPath);
|
|
791
|
-
Object.assign(this.config, userConfig);
|
|
792
|
-
break;
|
|
793
|
-
} catch (e) {
|
|
794
|
-
console.error(`Error loading config file at ${configPath}:`, e);
|
|
795
|
-
}
|
|
796
|
-
return this;
|
|
797
|
-
}
|
|
798
|
-
/**
|
|
799
|
-
* Get the current configuration object
|
|
800
|
-
* @returns
|
|
801
|
-
*/
|
|
802
|
-
getConfig() {
|
|
803
|
-
return this.config;
|
|
804
|
-
}
|
|
805
|
-
/**
|
|
806
|
-
* Initialize Resora by creating a default config file in the current directory
|
|
807
|
-
*
|
|
808
|
-
* @returns
|
|
809
|
-
*/
|
|
810
|
-
init() {
|
|
811
|
-
const outputPath = (0, path.join)(process.cwd(), "resora.config.js");
|
|
812
|
-
const stubPath = (0, path.join)(this.config.stubsDir, this.config.stubs.config);
|
|
813
|
-
if ((0, fs.existsSync)(outputPath) && !this.command.option("force")) {
|
|
814
|
-
this.command.error(`Error: ${outputPath} already exists.`);
|
|
815
|
-
process.exit(1);
|
|
816
|
-
}
|
|
817
|
-
this.ensureDirectory(outputPath);
|
|
818
|
-
if ((0, fs.existsSync)(outputPath) && this.command.option("force")) (0, fs.copyFileSync)(outputPath, outputPath.replace(/\.js$/, `.backup.${Date.now()}.js`));
|
|
819
|
-
(0, fs.writeFileSync)(outputPath, (0, fs.readFileSync)(stubPath, "utf-8"));
|
|
820
|
-
return { path: outputPath };
|
|
821
|
-
}
|
|
822
|
-
/**
|
|
823
|
-
* Utility to ensure directory exists
|
|
824
|
-
*
|
|
825
|
-
* @param filePath
|
|
826
|
-
*/
|
|
827
|
-
ensureDirectory(filePath) {
|
|
828
|
-
const dir = (0, path.dirname)(filePath);
|
|
829
|
-
if (!(0, fs.existsSync)(dir)) (0, fs.mkdirSync)(dir, { recursive: true });
|
|
830
|
-
}
|
|
831
|
-
/**
|
|
832
|
-
* Utility to generate file from stub
|
|
833
|
-
*
|
|
834
|
-
* @param stubPath
|
|
835
|
-
* @param outputPath
|
|
836
|
-
* @param replacements
|
|
837
|
-
*/
|
|
838
|
-
generateFile(stubPath, outputPath, replacements, options) {
|
|
839
|
-
if ((0, fs.existsSync)(outputPath) && !options?.force) {
|
|
840
|
-
this.command.error(`Error: ${outputPath} already exists.`);
|
|
841
|
-
process.exit(1);
|
|
842
|
-
} else if ((0, fs.existsSync)(outputPath) && options?.force) (0, fs.rmSync)(outputPath);
|
|
843
|
-
let content = (0, fs.readFileSync)(stubPath, "utf-8");
|
|
844
|
-
for (const [key, value] of Object.entries(replacements)) content = content.replace(new RegExp(`{{${key}}}`, "g"), value);
|
|
845
|
-
this.ensureDirectory(outputPath);
|
|
846
|
-
(0, fs.writeFileSync)(outputPath, content);
|
|
847
|
-
return outputPath;
|
|
848
|
-
}
|
|
849
|
-
/**
|
|
850
|
-
* Command to create a new resource or resource collection file
|
|
851
|
-
*
|
|
852
|
-
* @param name
|
|
853
|
-
* @param options
|
|
854
|
-
*/
|
|
855
|
-
makeResource(name, options) {
|
|
856
|
-
let resourceName = name;
|
|
857
|
-
if (options?.collection && !name.endsWith("Collection") && !name.endsWith("Resource")) resourceName += "Collection";
|
|
858
|
-
else if (!options?.collection && !name.endsWith("Resource") && !name.endsWith("Collection")) resourceName += "Resource";
|
|
859
|
-
const fileName = `${resourceName}.ts`;
|
|
860
|
-
const outputPath = (0, path.join)(this.config.resourcesDir, fileName);
|
|
861
|
-
const stubPath = (0, path.join)(this.config.stubsDir, options?.collection || name.endsWith("Collection") ? this.config.stubs.collection : this.config.stubs.resource);
|
|
862
|
-
if (!(0, fs.existsSync)(stubPath)) {
|
|
863
|
-
this.command.error(`Error: Stub file ${stubPath} not found.`);
|
|
864
|
-
process.exit(1);
|
|
865
|
-
}
|
|
866
|
-
const collectsName = resourceName.replace(/(Resource|Collection)$/, "") + "Resource";
|
|
867
|
-
const collects = `/**
|
|
1
|
+
Object.defineProperty(exports,Symbol.toStringTag,{value:`Module`});var e=Object.create,t=Object.defineProperty,n=Object.getOwnPropertyDescriptor,r=Object.getOwnPropertyNames,i=Object.getPrototypeOf,a=Object.prototype.hasOwnProperty,o=(e,i,o,s)=>{if(i&&typeof i==`object`||typeof i==`function`)for(var c=r(i),l=0,u=c.length,d;l<u;l++)d=c[l],!a.call(e,d)&&d!==o&&t(e,d,{get:(e=>i[e]).bind(null,d),enumerable:!(s=n(i,d))||s.enumerable});return e},s=(n,r,a)=>(a=n==null?{}:e(i(n)),o(r||!n||!n.__esModule?t(a,`default`,{value:n,enumerable:!0}):a,n));let c=require(`fs`),l=require(`path`);l=s(l);let u=require(`module`),d=require(`url`),f=require(`@h3ravel/musket`);function ee(e){return e}let p,m,h=[`meta`,`links`],g={first:`first`,last:`last`,prev:`prev`,next:`next`},_=`https://localhost`,te=`page`,v={to:`to`,from:`from`,links:`links`,path:`path`,total:`total`,per_page:`per_page`,last_page:`last_page`,current_page:`current_page`},y={previous:`previous`,next:`next`};const b=e=>{p=e},x=()=>p,S=e=>{m=e},C=()=>m,ne=e=>{m={...m||{},rootKey:e}},re=e=>{m={...m||{},wrap:e}},ie=()=>m?.wrap,ae=()=>m?.rootKey,oe=e=>{m={...m||{},factory:e}},se=()=>m?.factory,w=e=>{h=e},T=()=>h,E=e=>{g={...g,...e}},ce=()=>g,le=e=>{_=e},ue=()=>_,de=e=>{te=e},fe=()=>te,pe=e=>{v={...v,...e}},me=()=>v,D=e=>{y={...y,...e}},O=()=>y,k=()=>{let e=T();return Array.isArray(e)?{metaKey:e.includes(`meta`)?`meta`:void 0,linksKey:e.includes(`links`)?`links`:void 0,cursorKey:e.includes(`cursor`)?`cursor`:void 0}:{metaKey:e.meta,linksKey:e.links,cursorKey:e.cursor}},A=(e,t)=>{if(e===void 0)return;let n=t||``,r=ue()||``,i=/^https?:\/\//i.test(n),a=r.replace(/\/$/,``),o=n.replace(/^\//,``),s=i?n:a?o?`${a}/${o}`:a:``;if(!s)return;let c=new URL(s);return c.searchParams.set(fe()||`page`,String(e)),c.toString()},j=e=>{let{metaKey:t,linksKey:n,cursorKey:r}=k(),i={},a=e?.pagination,o=e?.cursor,s={},c={};if(a){let e={to:a.to,from:a.from,links:a.links,path:a.path,total:a.total,per_page:a.perPage,last_page:a.lastPage,current_page:a.currentPage};for(let[t,n]of Object.entries(me())){if(!n)continue;let r=e[t];r!==void 0&&(s[n]=r)}let t={first:A(a.firstPage,a.path),last:A(a.lastPage,a.path),prev:A(a.prevPage,a.path),next:A(a.nextPage,a.path)};for(let[e,n]of Object.entries(ce())){if(!n)continue;let r=t[e];r!==void 0&&(c[n]=r)}}if(o){let e={},t={previous:o.previous,next:o.next};for(let[n,r]of Object.entries(O())){if(!r)continue;let i=t[n];i!==void 0&&(e[r]=i)}r&&Object.keys(e).length>0?i[r]=e:Object.keys(e).length>0&&(s.cursor=e)}return t&&Object.keys(s).length>0&&(i[t]=s),n&&Object.keys(c).length>0&&(i[n]=c),i},M=e=>{if(typeof e!=`object`||!e||Array.isArray(e)||e instanceof Date||e instanceof RegExp)return!1;let t=Object.getPrototypeOf(e);return t===Object.prototype||t===null},N=(e,t,n=`data`)=>!t||Object.keys(t).length===0?e:Array.isArray(e)?{[n]:e,...t}:M(e)?{...e,...t}:{[n]:e,...t},P=(e,t)=>{if(!t)return e;if(!e)return t;let n={...e};for(let[e,r]of Object.entries(t)){let t=n[e];M(t)&&M(r)?n[e]=P(t,r):n[e]=r}return n},F=({payload:e,meta:t,metaKey:n=`meta`,wrap:r=!0,rootKey:i=`data`,factory:a,context:o})=>{if(a)return a(e,{...o,rootKey:i,meta:t});if(!r)return t===void 0?e:M(e)?{...e,[n]:t}:{[i]:e,[n]:t};let s={[i]:e};return t!==void 0&&(s[n]=t),s},I=(e,t)=>{let n=e?.with;if(typeof n!=`function`||n===t||n.length>0)return;let r=n.call(e);return M(r)?r:void 0},L=Symbol(`resora.conditional.missing`),R=(e,t)=>e?typeof t==`function`?t():t:L,z=e=>e??L,B=(e,t)=>{if(!e)return{};let n=typeof t==`function`?t():t;return M(n)?n:{}},V=e=>{if(e===L)return L;if(Array.isArray(e))return e.map(e=>V(e)).filter(e=>e!==L);if(M(e)){let t={};for(let[n,r]of Object.entries(e)){let e=V(r);e!==L&&(t[n]=e)}return t}return e},H=e=>e.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),U=e=>H(e).map((e,t)=>t===0?e:e.charAt(0).toUpperCase()+e.slice(1)).join(``),W=e=>H(e).join(`_`),G=e=>H(e).map(e=>e.charAt(0).toUpperCase()+e.slice(1)).join(``),K=e=>H(e).join(`-`),q=e=>{if(typeof e==`function`)return e;switch(e){case`camel`:return U;case`snake`:return W;case`pascal`:return G;case`kebab`:return K}},J=(e,t)=>e==null?e:Array.isArray(e)?e.map(e=>J(e,t)):e instanceof Date||e instanceof RegExp?e:typeof e==`object`?Object.fromEntries(Object.entries(e).map(([e,n])=>[t(e),J(n,t)])):e;let Y=l.default.resolve(process.cwd(),`node_modules/resora/stubs`);(0,c.existsSync)(Y)||(Y=l.default.resolve(process.cwd(),`stubs`));const he=()=>({stubsDir:Y,preferredCase:`camel`,responseStructure:{wrap:!0,rootKey:`data`},paginatedExtras:[`meta`,`links`],baseUrl:`https://localhost`,pageName:`page`,paginatedLinks:{first:`first`,last:`last`,prev:`prev`,next:`next`},paginatedMeta:{to:`to`,from:`from`,links:`links`,path:`path`,total:`total`,per_page:`per_page`,last_page:`last_page`,current_page:`current_page`},cursorMeta:{previous:`previous`,next:`next`},resourcesDir:`src/resources`,stubs:{config:`resora.config.stub`,resource:`resource.stub`,collection:`resource.collection.stub`}}),X=e=>{let t=he();return Object.assign(t,e,{stubs:Object.assign(t.stubs,e.stubs||{})},{cursorMeta:Object.assign(t.cursorMeta,e.cursorMeta||{})},{paginatedMeta:Object.assign(t.paginatedMeta,e.paginatedMeta||{})},{paginatedLinks:Object.assign(t.paginatedLinks,e.paginatedLinks||{})},{responseStructure:Object.assign(t.responseStructure,e.responseStructure||{})})};let Z=!1,Q;const ge=()=>{Z=!1,Q=void 0},_e=e=>{e.preferredCase!==`camel`&&b(e.preferredCase),S(e.responseStructure),w(e.paginatedExtras),E(e.paginatedLinks),pe(e.paginatedMeta),D(e.cursorMeta),le(e.baseUrl),de(e.pageName)},ve=async e=>await import(`${(0,d.pathToFileURL)(e).href}?resora_runtime=${Date.now()}`),ye=e=>{_e(X((e?.default??e)||{})),Z=!0},be=()=>{let e=(0,u.createRequire)(e(`url`).pathToFileURL(__filename).href),t=[l.default.join(process.cwd(),`resora.config.cjs`)];for(let n of t)if((0,c.existsSync)(n))try{return ye(e(n)),!0}catch{continue}return!1},xe=async()=>{if(!Z){if(Q)return await Q;be()||(Q=(async()=>{let e=[l.default.join(process.cwd(),`resora.config.js`),l.default.join(process.cwd(),`resora.config.ts`)];for(let t of e)if((0,c.existsSync)(t))try{ye(await ve(t));return}catch{continue}Z=!0})(),await Q)}};xe();var Se=class{command;config={};constructor(e={}){this.config=X(e)}async loadConfig(e={}){this.config=X(e);let t=[(0,l.join)(process.cwd(),`resora.config.ts`),(0,l.join)(process.cwd(),`resora.config.js`),(0,l.join)(process.cwd(),`resora.config.cjs`)];for(let e of t)if((0,c.existsSync)(e))try{let{default:t}=await import(e);Object.assign(this.config,t);break}catch(t){console.error(`Error loading config file at ${e}:`,t)}return this}getConfig(){return this.config}init(){let e=(0,l.join)(process.cwd(),`resora.config.js`),t=(0,l.join)(this.config.stubsDir,this.config.stubs.config);return(0,c.existsSync)(e)&&!this.command.option(`force`)&&(this.command.error(`Error: ${e} already exists.`),process.exit(1)),this.ensureDirectory(e),(0,c.existsSync)(e)&&this.command.option(`force`)&&(0,c.copyFileSync)(e,e.replace(/\.js$/,`.backup.${Date.now()}.js`)),(0,c.writeFileSync)(e,(0,c.readFileSync)(t,`utf-8`)),{path:e}}ensureDirectory(e){let t=(0,l.dirname)(e);(0,c.existsSync)(t)||(0,c.mkdirSync)(t,{recursive:!0})}generateFile(e,t,n,r){(0,c.existsSync)(t)&&!r?.force?(this.command.error(`Error: ${t} already exists.`),process.exit(1)):(0,c.existsSync)(t)&&r?.force&&(0,c.rmSync)(t);let i=(0,c.readFileSync)(e,`utf-8`);for(let[e,t]of Object.entries(n))i=i.replace(RegExp(`{{${e}}}`,`g`),t);return this.ensureDirectory(t),(0,c.writeFileSync)(t,i),t}makeResource(e,t){let n=e;t?.collection&&!e.endsWith(`Collection`)&&!e.endsWith(`Resource`)?n+=`Collection`:!t?.collection&&!e.endsWith(`Resource`)&&!e.endsWith(`Collection`)&&(n+=`Resource`);let r=`${n}.ts`,i=(0,l.join)(this.config.resourcesDir,r),a=(0,l.join)(this.config.stubsDir,t?.collection||e.endsWith(`Collection`)?this.config.stubs.collection:this.config.stubs.resource);(0,c.existsSync)(a)||(this.command.error(`Error: Stub file ${a} not found.`),process.exit(1));let o=n.replace(/(Resource|Collection)$/,``)+`Resource`,s=`/**
|
|
868
2
|
* The resource that this collection collects.
|
|
869
3
|
*/
|
|
870
|
-
collects = ${
|
|
871
|
-
|
|
872
|
-
const collectsImport = `import ${collectsName} from './${collectsName}'\n`;
|
|
873
|
-
const hasCollects = (!!options?.collection || name.endsWith("Collection")) && (0, fs.existsSync)((0, path.join)(this.config.resourcesDir, `${collectsName}.ts`));
|
|
874
|
-
const path$3 = this.generateFile(stubPath, outputPath, {
|
|
875
|
-
ResourceName: resourceName,
|
|
876
|
-
CollectionResourceName: resourceName.replace(/(Resource|Collection)$/, "") + "Resource",
|
|
877
|
-
"collects = Resource": hasCollects ? collects : "",
|
|
878
|
-
"import = Resource": hasCollects ? collectsImport : ""
|
|
879
|
-
}, options);
|
|
880
|
-
return {
|
|
881
|
-
name: resourceName,
|
|
882
|
-
path: path$3
|
|
883
|
-
};
|
|
884
|
-
}
|
|
885
|
-
};
|
|
886
|
-
|
|
887
|
-
//#endregion
|
|
888
|
-
//#region src/cli/commands/InitCommand.ts
|
|
889
|
-
var InitCommand = class extends _h3ravel_musket.Command {
|
|
890
|
-
signature = `init
|
|
4
|
+
collects = ${o}
|
|
5
|
+
`,u=`import ${o} from './${o}'\n`,d=(!!t?.collection||e.endsWith(`Collection`))&&(0,c.existsSync)((0,l.join)(this.config.resourcesDir,`${o}.ts`)),f=this.generateFile(a,i,{ResourceName:n,CollectionResourceName:n.replace(/(Resource|Collection)$/,``)+`Resource`,"collects = Resource":d?s:``,"import = Resource":d?u:``},t);return{name:n,path:f}}},Ce=class extends f.Command{signature=`init
|
|
891
6
|
{--force : Force overwrite if config file already exists (existing file will be backed up) }
|
|
892
|
-
`;
|
|
893
|
-
description = "Initialize Resora";
|
|
894
|
-
async handle() {
|
|
895
|
-
this.app.command = this;
|
|
896
|
-
this.app.init();
|
|
897
|
-
this.success("Resora initialized");
|
|
898
|
-
}
|
|
899
|
-
};
|
|
900
|
-
|
|
901
|
-
//#endregion
|
|
902
|
-
//#region src/cli/commands/MakeResource.ts
|
|
903
|
-
var MakeResource = class extends _h3ravel_musket.Command {
|
|
904
|
-
signature = `#create:
|
|
7
|
+
`;description=`Initialize Resora`;async handle(){this.app.command=this,this.app.init(),this.success(`Resora initialized`)}},we=class extends f.Command{signature=`#create:
|
|
905
8
|
{resource : Generates a new resource file.
|
|
906
9
|
| {name : Name of the resource to create}
|
|
907
10
|
| {--c|collection : Make a resource collection}
|
|
@@ -915,969 +18,11 @@ var MakeResource = class extends _h3ravel_musket.Command {
|
|
|
915
18
|
| {prefix : prefix of the resources to create, "Admin" will create AdminResource, AdminCollection}
|
|
916
19
|
| {--force : Create the resource or collection file even if it already exists.}
|
|
917
20
|
}
|
|
918
|
-
`;
|
|
919
|
-
description = "Create a new resource or resource collection file";
|
|
920
|
-
async handle() {
|
|
921
|
-
this.app.command = this;
|
|
922
|
-
let path = "";
|
|
923
|
-
const action = this.dictionary.name || this.dictionary.baseCommand;
|
|
924
|
-
if (["resource", "collection"].includes(action) && !this.argument("name")) return void this.error("Error: Name argument is required.");
|
|
925
|
-
if (action === "all" && !this.argument("prefix")) return void this.error("Error: Prefix argument is required.");
|
|
926
|
-
switch (action) {
|
|
927
|
-
case "resource":
|
|
928
|
-
({path} = this.app.makeResource(this.argument("name"), this.options()));
|
|
929
|
-
break;
|
|
930
|
-
case "collection":
|
|
931
|
-
({path} = this.app.makeResource(this.argument("name") + "Collection", this.options()));
|
|
932
|
-
break;
|
|
933
|
-
case "all": {
|
|
934
|
-
const o1 = this.app.makeResource(this.argument("prefix"), { force: this.option("force") });
|
|
935
|
-
const o2 = this.app.makeResource(this.argument("prefix") + "Collection", {
|
|
936
|
-
collection: true,
|
|
937
|
-
force: this.option("force")
|
|
938
|
-
});
|
|
939
|
-
path = `${o1.path}, ${o2.path}`;
|
|
940
|
-
break;
|
|
941
|
-
}
|
|
942
|
-
default: this.fail(`Unknown action: ${action}`);
|
|
943
|
-
}
|
|
944
|
-
this.success(`Created: ${path}`);
|
|
945
|
-
}
|
|
946
|
-
};
|
|
947
|
-
|
|
948
|
-
//#endregion
|
|
949
|
-
//#region src/cli/logo.ts
|
|
950
|
-
var logo_default = String.raw`
|
|
21
|
+
`;description=`Create a new resource or resource collection file`;async handle(){this.app.command=this;let e=``,t=this.dictionary.name||this.dictionary.baseCommand;if([`resource`,`collection`].includes(t)&&!this.argument(`name`))return void this.error(`Error: Name argument is required.`);if(t===`all`&&!this.argument(`prefix`))return void this.error(`Error: Prefix argument is required.`);switch(t){case`resource`:({path:e}=this.app.makeResource(this.argument(`name`),this.options()));break;case`collection`:({path:e}=this.app.makeResource(this.argument(`name`)+`Collection`,this.options()));break;case`all`:{let t=this.app.makeResource(this.argument(`prefix`),{force:this.option(`force`)}),n=this.app.makeResource(this.argument(`prefix`)+`Collection`,{collection:!0,force:this.option(`force`)});e=`${t.path}, ${n.path}`;break}default:this.fail(`Unknown action: ${t}`)}this.success(`Created: ${e}`)}};String.raw`
|
|
951
22
|
_____
|
|
952
23
|
| __ \
|
|
953
24
|
| |__) |___ ___ ___ _ __ __ _
|
|
954
25
|
| _ // _ \/ __|/ _ \| '__/ _, |
|
|
955
26
|
| | \ \ __/\__ \ (_) | | | (_| |
|
|
956
27
|
|_| \_\___||___/\___/|_| \__,_|
|
|
957
|
-
`;
|
|
958
|
-
|
|
959
|
-
//#endregion
|
|
960
|
-
//#region src/ServerResponse.ts
|
|
961
|
-
var ServerResponse = class {
|
|
962
|
-
_status = 200;
|
|
963
|
-
headers = {};
|
|
964
|
-
constructor(response, body) {
|
|
965
|
-
this.response = response;
|
|
966
|
-
this.body = body;
|
|
967
|
-
}
|
|
968
|
-
/**
|
|
969
|
-
* Set the HTTP status code for the response
|
|
970
|
-
*
|
|
971
|
-
* @param status
|
|
972
|
-
* @returns The current ServerResponse instance
|
|
973
|
-
*/
|
|
974
|
-
setStatusCode(status) {
|
|
975
|
-
this._status = status;
|
|
976
|
-
if ("status" in this.response && typeof this.response.status === "function") this.response.status(status);
|
|
977
|
-
else if ("status" in this.response) this.response.status = status;
|
|
978
|
-
return this;
|
|
979
|
-
}
|
|
980
|
-
/**
|
|
981
|
-
* Get the current HTTP status code for the response
|
|
982
|
-
*
|
|
983
|
-
* @returns
|
|
984
|
-
*/
|
|
985
|
-
status() {
|
|
986
|
-
return this._status;
|
|
987
|
-
}
|
|
988
|
-
/**
|
|
989
|
-
* Get the current HTTP status text for the response
|
|
990
|
-
*
|
|
991
|
-
* @returns
|
|
992
|
-
*/
|
|
993
|
-
statusText() {
|
|
994
|
-
if ("statusMessage" in this.response) return this.response.statusMessage;
|
|
995
|
-
else if ("statusText" in this.response) return this.response.statusText;
|
|
996
|
-
}
|
|
997
|
-
/**
|
|
998
|
-
* Set a cookie in the response header
|
|
999
|
-
*
|
|
1000
|
-
* @param name The name of the cookie
|
|
1001
|
-
* @param value The value of the cookie
|
|
1002
|
-
* @param options Optional cookie attributes (e.g., path, domain, maxAge)
|
|
1003
|
-
* @returns The current ServerResponse instance
|
|
1004
|
-
*/
|
|
1005
|
-
setCookie(name, value, options) {
|
|
1006
|
-
this.#addHeader("Set-Cookie", `${name}=${value}; ${Object.entries(options || {}).map(([key, val]) => `${key}=${val}`).join("; ")}`);
|
|
1007
|
-
return this;
|
|
1008
|
-
}
|
|
1009
|
-
/**
|
|
1010
|
-
* Convert the resource to a JSON response body
|
|
1011
|
-
*
|
|
1012
|
-
* @param headers Optional headers to add to the response
|
|
1013
|
-
* @returns The current ServerResponse instance
|
|
1014
|
-
*/
|
|
1015
|
-
setHeaders(headers) {
|
|
1016
|
-
for (const [key, value] of Object.entries(headers)) this.#addHeader(key, value);
|
|
1017
|
-
return this;
|
|
1018
|
-
}
|
|
1019
|
-
/**
|
|
1020
|
-
* Add a single header to the response
|
|
1021
|
-
*
|
|
1022
|
-
* @param key The name of the header
|
|
1023
|
-
* @param value The value of the header
|
|
1024
|
-
* @returns The current ServerResponse instance
|
|
1025
|
-
*/
|
|
1026
|
-
header(key, value) {
|
|
1027
|
-
this.#addHeader(key, value);
|
|
1028
|
-
return this;
|
|
1029
|
-
}
|
|
1030
|
-
/**
|
|
1031
|
-
* Add a single header to the response
|
|
1032
|
-
*
|
|
1033
|
-
* @param key The name of the header
|
|
1034
|
-
* @param value The value of the header
|
|
1035
|
-
*/
|
|
1036
|
-
#addHeader(key, value) {
|
|
1037
|
-
this.headers[key] = value;
|
|
1038
|
-
if ("headers" in this.response) this.response.headers.set(key, value);
|
|
1039
|
-
else if ("setHeader" in this.response) this.response.setHeader(key, value);
|
|
1040
|
-
}
|
|
1041
|
-
/**
|
|
1042
|
-
* Promise-like then method to allow chaining with async/await or .then() syntax
|
|
1043
|
-
*
|
|
1044
|
-
* @param onfulfilled Callback to handle the fulfilled state of the promise, receiving the response body
|
|
1045
|
-
* @param onrejected Callback to handle the rejected state of the promise, receiving the error reason
|
|
1046
|
-
* @returns A promise that resolves to the result of the onfulfilled or onrejected callback
|
|
1047
|
-
*/
|
|
1048
|
-
then(onfulfilled, onrejected) {
|
|
1049
|
-
const resolved = Promise.resolve(this.body).then(onfulfilled, onrejected);
|
|
1050
|
-
if ("send" in this.response) this.response.send(this.body);
|
|
1051
|
-
return resolved;
|
|
1052
|
-
}
|
|
1053
|
-
/**
|
|
1054
|
-
* Promise-like catch method to handle rejected state of the promise
|
|
1055
|
-
*
|
|
1056
|
-
* @param onrejected
|
|
1057
|
-
* @returns
|
|
1058
|
-
*/
|
|
1059
|
-
catch(onrejected) {
|
|
1060
|
-
return this.then(void 0, onrejected);
|
|
1061
|
-
}
|
|
1062
|
-
/**
|
|
1063
|
-
* Promise-like finally method to handle cleanup after promise is settled
|
|
1064
|
-
*
|
|
1065
|
-
* @param onfinally
|
|
1066
|
-
* @returns
|
|
1067
|
-
*/
|
|
1068
|
-
finally(onfinally) {
|
|
1069
|
-
return this.then(onfinally, onfinally);
|
|
1070
|
-
}
|
|
1071
|
-
};
|
|
1072
|
-
|
|
1073
|
-
//#endregion
|
|
1074
|
-
//#region src/GenericResource.ts
|
|
1075
|
-
/**
|
|
1076
|
-
* GenericResource class to handle API resource transformation and response building
|
|
1077
|
-
*/
|
|
1078
|
-
var GenericResource = class GenericResource {
|
|
1079
|
-
body = { data: {} };
|
|
1080
|
-
resource;
|
|
1081
|
-
collects;
|
|
1082
|
-
additionalMeta;
|
|
1083
|
-
withResponseContext;
|
|
1084
|
-
/**
|
|
1085
|
-
* Preferred case style for this resource's output keys.
|
|
1086
|
-
* Set on a subclass to override the global default.
|
|
1087
|
-
*/
|
|
1088
|
-
static preferredCase;
|
|
1089
|
-
/**
|
|
1090
|
-
* Response structure override for this generic resource class.
|
|
1091
|
-
*/
|
|
1092
|
-
static responseStructure;
|
|
1093
|
-
called = {};
|
|
1094
|
-
constructor(rsc, res) {
|
|
1095
|
-
this.res = res;
|
|
1096
|
-
this.resource = rsc;
|
|
1097
|
-
/**
|
|
1098
|
-
* Copy properties from rsc to this instance for easy
|
|
1099
|
-
* access, but only if data is not an array
|
|
1100
|
-
*/
|
|
1101
|
-
if (!Array.isArray(this.resource.data ?? this.resource)) {
|
|
1102
|
-
for (const key of Object.keys(this.resource.data ?? this.resource)) if (!(key in this)) Object.defineProperty(this, key, {
|
|
1103
|
-
enumerable: true,
|
|
1104
|
-
configurable: true,
|
|
1105
|
-
get: () => {
|
|
1106
|
-
return this.resource.data?.[key] ?? this.resource[key];
|
|
1107
|
-
},
|
|
1108
|
-
set: (value) => {
|
|
1109
|
-
if (this.resource.data && this.resource.data[key]) this.resource.data[key] = value;
|
|
1110
|
-
else this.resource[key] = value;
|
|
1111
|
-
}
|
|
1112
|
-
});
|
|
1113
|
-
}
|
|
1114
|
-
}
|
|
1115
|
-
/**
|
|
1116
|
-
* Get the original resource data
|
|
1117
|
-
*/
|
|
1118
|
-
data() {
|
|
1119
|
-
return this.resource;
|
|
1120
|
-
}
|
|
1121
|
-
/**
|
|
1122
|
-
* Get the current serialized output body.
|
|
1123
|
-
*/
|
|
1124
|
-
getBody() {
|
|
1125
|
-
this.json();
|
|
1126
|
-
return this.body;
|
|
1127
|
-
}
|
|
1128
|
-
/**
|
|
1129
|
-
* Replace the current serialized output body.
|
|
1130
|
-
*/
|
|
1131
|
-
setBody(body) {
|
|
1132
|
-
this.body = body;
|
|
1133
|
-
return this;
|
|
1134
|
-
}
|
|
1135
|
-
/**
|
|
1136
|
-
* Conditionally include a value in serialized output.
|
|
1137
|
-
*/
|
|
1138
|
-
when(condition, value) {
|
|
1139
|
-
return resolveWhen(condition, value);
|
|
1140
|
-
}
|
|
1141
|
-
/**
|
|
1142
|
-
* Include a value only when it is not null/undefined.
|
|
1143
|
-
*/
|
|
1144
|
-
whenNotNull(value) {
|
|
1145
|
-
return resolveWhenNotNull(value);
|
|
1146
|
-
}
|
|
1147
|
-
/**
|
|
1148
|
-
* Conditionally merge object attributes into serialized output.
|
|
1149
|
-
*/
|
|
1150
|
-
mergeWhen(condition, value) {
|
|
1151
|
-
return resolveMergeWhen(condition, value);
|
|
1152
|
-
}
|
|
1153
|
-
resolveResponseStructure() {
|
|
1154
|
-
const local = this.constructor.responseStructure;
|
|
1155
|
-
const collectsLocal = this.collects?.responseStructure;
|
|
1156
|
-
const global = getGlobalResponseStructure();
|
|
1157
|
-
return {
|
|
1158
|
-
wrap: local?.wrap ?? collectsLocal?.wrap ?? global?.wrap ?? true,
|
|
1159
|
-
rootKey: local?.rootKey ?? collectsLocal?.rootKey ?? global?.rootKey ?? "data",
|
|
1160
|
-
factory: local?.factory ?? collectsLocal?.factory ?? global?.factory
|
|
1161
|
-
};
|
|
1162
|
-
}
|
|
1163
|
-
getPayloadKey() {
|
|
1164
|
-
const { wrap, rootKey, factory } = this.resolveResponseStructure();
|
|
1165
|
-
return factory || !wrap ? void 0 : rootKey;
|
|
1166
|
-
}
|
|
1167
|
-
/**
|
|
1168
|
-
* Convert resource to JSON response format
|
|
1169
|
-
*
|
|
1170
|
-
* @returns
|
|
1171
|
-
*/
|
|
1172
|
-
json() {
|
|
1173
|
-
if (!this.called.json) {
|
|
1174
|
-
this.called.json = true;
|
|
1175
|
-
const resource = this.data();
|
|
1176
|
-
let data = Array.isArray(resource) ? [...resource] : { ...resource };
|
|
1177
|
-
if (Array.isArray(data) && this.collects) {
|
|
1178
|
-
data = data.map((item) => new this.collects(item).data());
|
|
1179
|
-
this.resource = data;
|
|
1180
|
-
}
|
|
1181
|
-
if (typeof data.data !== "undefined") data = data.data;
|
|
1182
|
-
data = sanitizeConditionalAttributes(data);
|
|
1183
|
-
const paginationExtras = buildPaginationExtras(this.resource);
|
|
1184
|
-
const { metaKey } = getPaginationExtraKeys();
|
|
1185
|
-
const configuredMeta = metaKey ? paginationExtras[metaKey] : void 0;
|
|
1186
|
-
if (metaKey) delete paginationExtras[metaKey];
|
|
1187
|
-
const caseStyle = this.constructor.preferredCase ?? getGlobalCase();
|
|
1188
|
-
if (caseStyle) {
|
|
1189
|
-
const transformer = getCaseTransformer(caseStyle);
|
|
1190
|
-
data = transformKeys(data, transformer);
|
|
1191
|
-
}
|
|
1192
|
-
const customMeta = mergeMetadata(resolveWithHookMetadata(this, GenericResource.prototype.with), this.additionalMeta);
|
|
1193
|
-
const { wrap, rootKey, factory } = this.resolveResponseStructure();
|
|
1194
|
-
this.body = buildResponseEnvelope({
|
|
1195
|
-
payload: data,
|
|
1196
|
-
meta: configuredMeta,
|
|
1197
|
-
metaKey,
|
|
1198
|
-
wrap,
|
|
1199
|
-
rootKey,
|
|
1200
|
-
factory,
|
|
1201
|
-
context: {
|
|
1202
|
-
type: "generic",
|
|
1203
|
-
resource: this.resource
|
|
1204
|
-
}
|
|
1205
|
-
});
|
|
1206
|
-
this.body = appendRootProperties(this.body, {
|
|
1207
|
-
...paginationExtras,
|
|
1208
|
-
...customMeta || {}
|
|
1209
|
-
}, rootKey);
|
|
1210
|
-
}
|
|
1211
|
-
return this;
|
|
1212
|
-
}
|
|
1213
|
-
/**
|
|
1214
|
-
* Append structured metadata to the response body.
|
|
1215
|
-
*
|
|
1216
|
-
* @param meta Metadata object or metadata factory
|
|
1217
|
-
* @returns
|
|
1218
|
-
*/
|
|
1219
|
-
with(meta) {
|
|
1220
|
-
this.called.with = true;
|
|
1221
|
-
if (typeof meta === "undefined") return this.additionalMeta || {};
|
|
1222
|
-
const resolvedMeta = typeof meta === "function" ? meta(this.resource) : meta;
|
|
1223
|
-
this.additionalMeta = mergeMetadata(this.additionalMeta, resolvedMeta);
|
|
1224
|
-
if (this.called.json) {
|
|
1225
|
-
const { rootKey } = this.resolveResponseStructure();
|
|
1226
|
-
this.body = appendRootProperties(this.body, resolvedMeta, rootKey);
|
|
1227
|
-
}
|
|
1228
|
-
return this;
|
|
1229
|
-
}
|
|
1230
|
-
/**
|
|
1231
|
-
* Typed fluent metadata helper.
|
|
1232
|
-
*
|
|
1233
|
-
* @param meta Metadata object or metadata factory
|
|
1234
|
-
* @returns
|
|
1235
|
-
*/
|
|
1236
|
-
withMeta(meta) {
|
|
1237
|
-
this.with(meta);
|
|
1238
|
-
return this;
|
|
1239
|
-
}
|
|
1240
|
-
/**
|
|
1241
|
-
* Convert resource to array format (for collections)
|
|
1242
|
-
*
|
|
1243
|
-
* @returns
|
|
1244
|
-
*/
|
|
1245
|
-
toArray() {
|
|
1246
|
-
this.called.toArray = true;
|
|
1247
|
-
this.json();
|
|
1248
|
-
let data = Array.isArray(this.resource) ? [...this.resource] : { ...this.resource };
|
|
1249
|
-
if (typeof data.data !== "undefined") data = data.data;
|
|
1250
|
-
return data;
|
|
1251
|
-
}
|
|
1252
|
-
/**
|
|
1253
|
-
* Add additional properties to the response body
|
|
1254
|
-
*
|
|
1255
|
-
* @param extra Additional properties to merge into the response body
|
|
1256
|
-
* @returns
|
|
1257
|
-
*/
|
|
1258
|
-
additional(extra) {
|
|
1259
|
-
this.called.additional = true;
|
|
1260
|
-
this.json();
|
|
1261
|
-
const extraData = extra.data;
|
|
1262
|
-
delete extra.data;
|
|
1263
|
-
delete extra.pagination;
|
|
1264
|
-
const payloadKey = this.getPayloadKey();
|
|
1265
|
-
if (extraData && payloadKey && typeof this.body[payloadKey] !== "undefined") this.body[payloadKey] = Array.isArray(this.body[payloadKey]) ? [...this.body[payloadKey], ...extraData] : {
|
|
1266
|
-
...this.body[payloadKey],
|
|
1267
|
-
...extraData
|
|
1268
|
-
};
|
|
1269
|
-
this.body = {
|
|
1270
|
-
...this.body,
|
|
1271
|
-
...extra
|
|
1272
|
-
};
|
|
1273
|
-
return this;
|
|
1274
|
-
}
|
|
1275
|
-
response(res) {
|
|
1276
|
-
this.called.toResponse = true;
|
|
1277
|
-
this.json();
|
|
1278
|
-
const rawResponse = res ?? this.res;
|
|
1279
|
-
const response = new ServerResponse(rawResponse, this.body);
|
|
1280
|
-
this.withResponseContext = {
|
|
1281
|
-
response,
|
|
1282
|
-
raw: rawResponse
|
|
1283
|
-
};
|
|
1284
|
-
this.called.withResponse = true;
|
|
1285
|
-
this.withResponse(response, rawResponse);
|
|
1286
|
-
return response;
|
|
1287
|
-
}
|
|
1288
|
-
/**
|
|
1289
|
-
* Customize the outgoing transport response right before dispatch.
|
|
1290
|
-
*
|
|
1291
|
-
* Override in custom classes to mutate headers/status/body.
|
|
1292
|
-
*/
|
|
1293
|
-
withResponse(_response, _rawResponse) {
|
|
1294
|
-
return this;
|
|
1295
|
-
}
|
|
1296
|
-
/**
|
|
1297
|
-
* Promise-like then method to allow chaining with async/await or .then() syntax
|
|
1298
|
-
*
|
|
1299
|
-
* @param onfulfilled Callback to handle the fulfilled state of the promise, receiving the response body
|
|
1300
|
-
* @param onrejected Callback to handle the rejected state of the promise, receiving the error reason
|
|
1301
|
-
* @returns A promise that resolves to the result of the onfulfilled or onrejected callback
|
|
1302
|
-
*/
|
|
1303
|
-
then(onfulfilled, onrejected) {
|
|
1304
|
-
this.called.then = true;
|
|
1305
|
-
this.json();
|
|
1306
|
-
if (this.res) {
|
|
1307
|
-
const response = new ServerResponse(this.res, this.body);
|
|
1308
|
-
this.withResponseContext = {
|
|
1309
|
-
response,
|
|
1310
|
-
raw: this.res
|
|
1311
|
-
};
|
|
1312
|
-
this.called.withResponse = true;
|
|
1313
|
-
this.withResponse(response, this.res);
|
|
1314
|
-
} else {
|
|
1315
|
-
this.called.withResponse = true;
|
|
1316
|
-
this.withResponse();
|
|
1317
|
-
}
|
|
1318
|
-
const resolved = Promise.resolve(this.body).then(onfulfilled, onrejected);
|
|
1319
|
-
if (this.res) this.res.send(this.body);
|
|
1320
|
-
return resolved;
|
|
1321
|
-
}
|
|
1322
|
-
};
|
|
1323
|
-
|
|
1324
|
-
//#endregion
|
|
1325
|
-
//#region src/ResourceCollection.ts
|
|
1326
|
-
/**
|
|
1327
|
-
* ResourceCollection class to handle API resource transformation and response building for collections
|
|
1328
|
-
*/
|
|
1329
|
-
var ResourceCollection = class ResourceCollection {
|
|
1330
|
-
body = { data: [] };
|
|
1331
|
-
resource;
|
|
1332
|
-
collects;
|
|
1333
|
-
additionalMeta;
|
|
1334
|
-
withResponseContext;
|
|
1335
|
-
/**
|
|
1336
|
-
* Preferred case style for this collection's output keys.
|
|
1337
|
-
* Set on a subclass to override the global default.
|
|
1338
|
-
*/
|
|
1339
|
-
static preferredCase;
|
|
1340
|
-
/**
|
|
1341
|
-
* Response structure override for this collection class.
|
|
1342
|
-
*/
|
|
1343
|
-
static responseStructure;
|
|
1344
|
-
called = {};
|
|
1345
|
-
constructor(rsc, res) {
|
|
1346
|
-
this.res = res;
|
|
1347
|
-
this.resource = rsc;
|
|
1348
|
-
}
|
|
1349
|
-
/**
|
|
1350
|
-
* Get the original resource data
|
|
1351
|
-
*/
|
|
1352
|
-
data() {
|
|
1353
|
-
return this.toArray();
|
|
1354
|
-
}
|
|
1355
|
-
/**
|
|
1356
|
-
* Get the current serialized output body.
|
|
1357
|
-
*/
|
|
1358
|
-
getBody() {
|
|
1359
|
-
this.json();
|
|
1360
|
-
return this.body;
|
|
1361
|
-
}
|
|
1362
|
-
/**
|
|
1363
|
-
* Replace the current serialized output body.
|
|
1364
|
-
*/
|
|
1365
|
-
setBody(body) {
|
|
1366
|
-
this.body = body;
|
|
1367
|
-
return this;
|
|
1368
|
-
}
|
|
1369
|
-
/**
|
|
1370
|
-
* Conditionally include a value in serialized output.
|
|
1371
|
-
*/
|
|
1372
|
-
when(condition, value) {
|
|
1373
|
-
return resolveWhen(condition, value);
|
|
1374
|
-
}
|
|
1375
|
-
/**
|
|
1376
|
-
* Include a value only when it is not null/undefined.
|
|
1377
|
-
*/
|
|
1378
|
-
whenNotNull(value) {
|
|
1379
|
-
return resolveWhenNotNull(value);
|
|
1380
|
-
}
|
|
1381
|
-
/**
|
|
1382
|
-
* Conditionally merge object attributes into serialized output.
|
|
1383
|
-
*/
|
|
1384
|
-
mergeWhen(condition, value) {
|
|
1385
|
-
return resolveMergeWhen(condition, value);
|
|
1386
|
-
}
|
|
1387
|
-
resolveResponseStructure() {
|
|
1388
|
-
const local = this.constructor.responseStructure;
|
|
1389
|
-
const collectsLocal = this.collects?.responseStructure;
|
|
1390
|
-
const global = getGlobalResponseStructure();
|
|
1391
|
-
return {
|
|
1392
|
-
wrap: local?.wrap ?? collectsLocal?.wrap ?? global?.wrap ?? true,
|
|
1393
|
-
rootKey: local?.rootKey ?? collectsLocal?.rootKey ?? global?.rootKey ?? "data",
|
|
1394
|
-
factory: local?.factory ?? collectsLocal?.factory ?? global?.factory
|
|
1395
|
-
};
|
|
1396
|
-
}
|
|
1397
|
-
getPayloadKey() {
|
|
1398
|
-
const { wrap, rootKey, factory } = this.resolveResponseStructure();
|
|
1399
|
-
return factory || !wrap ? void 0 : rootKey;
|
|
1400
|
-
}
|
|
1401
|
-
/**
|
|
1402
|
-
* Convert resource to JSON response format
|
|
1403
|
-
*
|
|
1404
|
-
* @returns
|
|
1405
|
-
*/
|
|
1406
|
-
json() {
|
|
1407
|
-
if (!this.called.json) {
|
|
1408
|
-
this.called.json = true;
|
|
1409
|
-
let data = this.data();
|
|
1410
|
-
if (this.collects) data = data.map((item) => new this.collects(item).data());
|
|
1411
|
-
data = sanitizeConditionalAttributes(data);
|
|
1412
|
-
const paginationExtras = !Array.isArray(this.resource) ? buildPaginationExtras(this.resource) : {};
|
|
1413
|
-
const { metaKey } = getPaginationExtraKeys();
|
|
1414
|
-
const configuredMeta = metaKey ? paginationExtras[metaKey] : void 0;
|
|
1415
|
-
if (metaKey) delete paginationExtras[metaKey];
|
|
1416
|
-
const caseStyle = this.constructor.preferredCase ?? this.collects?.preferredCase ?? getGlobalCase();
|
|
1417
|
-
if (caseStyle) {
|
|
1418
|
-
const transformer = getCaseTransformer(caseStyle);
|
|
1419
|
-
data = transformKeys(data, transformer);
|
|
1420
|
-
}
|
|
1421
|
-
const customMeta = mergeMetadata(resolveWithHookMetadata(this, ResourceCollection.prototype.with), this.additionalMeta);
|
|
1422
|
-
const { wrap, rootKey, factory } = this.resolveResponseStructure();
|
|
1423
|
-
this.body = buildResponseEnvelope({
|
|
1424
|
-
payload: data,
|
|
1425
|
-
meta: configuredMeta,
|
|
1426
|
-
metaKey,
|
|
1427
|
-
wrap,
|
|
1428
|
-
rootKey,
|
|
1429
|
-
factory,
|
|
1430
|
-
context: {
|
|
1431
|
-
type: "collection",
|
|
1432
|
-
resource: this.resource
|
|
1433
|
-
}
|
|
1434
|
-
});
|
|
1435
|
-
this.body = appendRootProperties(this.body, {
|
|
1436
|
-
...paginationExtras,
|
|
1437
|
-
...customMeta || {}
|
|
1438
|
-
}, rootKey);
|
|
1439
|
-
}
|
|
1440
|
-
return this;
|
|
1441
|
-
}
|
|
1442
|
-
/**
|
|
1443
|
-
* Append structured metadata to the response body.
|
|
1444
|
-
*
|
|
1445
|
-
* @param meta Metadata object or metadata factory
|
|
1446
|
-
* @returns
|
|
1447
|
-
*/
|
|
1448
|
-
with(meta) {
|
|
1449
|
-
this.called.with = true;
|
|
1450
|
-
if (typeof meta === "undefined") return this.additionalMeta || {};
|
|
1451
|
-
const resolvedMeta = typeof meta === "function" ? meta(this.resource) : meta;
|
|
1452
|
-
this.additionalMeta = mergeMetadata(this.additionalMeta, resolvedMeta);
|
|
1453
|
-
if (this.called.json) {
|
|
1454
|
-
const { rootKey } = this.resolveResponseStructure();
|
|
1455
|
-
this.body = appendRootProperties(this.body, resolvedMeta, rootKey);
|
|
1456
|
-
}
|
|
1457
|
-
return this;
|
|
1458
|
-
}
|
|
1459
|
-
/**
|
|
1460
|
-
* Typed fluent metadata helper.
|
|
1461
|
-
*
|
|
1462
|
-
* @param meta Metadata object or metadata factory
|
|
1463
|
-
* @returns
|
|
1464
|
-
*/
|
|
1465
|
-
withMeta(meta) {
|
|
1466
|
-
this.with(meta);
|
|
1467
|
-
return this;
|
|
1468
|
-
}
|
|
1469
|
-
/**
|
|
1470
|
-
* Flatten resource to return original data
|
|
1471
|
-
*
|
|
1472
|
-
* @returns
|
|
1473
|
-
*/
|
|
1474
|
-
toArray() {
|
|
1475
|
-
this.called.toArray = true;
|
|
1476
|
-
this.json();
|
|
1477
|
-
return Array.isArray(this.resource) ? [...this.resource] : [...this.resource.data];
|
|
1478
|
-
}
|
|
1479
|
-
/**
|
|
1480
|
-
* Add additional properties to the response body
|
|
1481
|
-
*
|
|
1482
|
-
* @param extra Additional properties to merge into the response body
|
|
1483
|
-
* @returns
|
|
1484
|
-
*/
|
|
1485
|
-
additional(extra) {
|
|
1486
|
-
this.called.additional = true;
|
|
1487
|
-
this.json();
|
|
1488
|
-
delete extra.cursor;
|
|
1489
|
-
delete extra.pagination;
|
|
1490
|
-
const payloadKey = this.getPayloadKey();
|
|
1491
|
-
if (extra.data && payloadKey && Array.isArray(this.body[payloadKey])) this.body[payloadKey] = [...this.body[payloadKey], ...extra.data];
|
|
1492
|
-
this.body = {
|
|
1493
|
-
...this.body,
|
|
1494
|
-
...extra
|
|
1495
|
-
};
|
|
1496
|
-
return this;
|
|
1497
|
-
}
|
|
1498
|
-
response(res) {
|
|
1499
|
-
this.called.toResponse = true;
|
|
1500
|
-
this.json();
|
|
1501
|
-
const rawResponse = res ?? this.res;
|
|
1502
|
-
const response = new ServerResponse(rawResponse, this.body);
|
|
1503
|
-
this.withResponseContext = {
|
|
1504
|
-
response,
|
|
1505
|
-
raw: rawResponse
|
|
1506
|
-
};
|
|
1507
|
-
this.called.withResponse = true;
|
|
1508
|
-
this.withResponse(response, rawResponse);
|
|
1509
|
-
return response;
|
|
1510
|
-
}
|
|
1511
|
-
/**
|
|
1512
|
-
* Customize the outgoing transport response right before dispatch.
|
|
1513
|
-
*
|
|
1514
|
-
* Override in custom classes to mutate headers/status/body.
|
|
1515
|
-
*/
|
|
1516
|
-
withResponse(_response, _rawResponse) {
|
|
1517
|
-
return this;
|
|
1518
|
-
}
|
|
1519
|
-
setCollects(collects) {
|
|
1520
|
-
this.collects = collects;
|
|
1521
|
-
return this;
|
|
1522
|
-
}
|
|
1523
|
-
/**
|
|
1524
|
-
* Promise-like then method to allow chaining with async/await or .then() syntax
|
|
1525
|
-
*
|
|
1526
|
-
* @param onfulfilled Callback to handle the fulfilled state of the promise, receiving the response body
|
|
1527
|
-
* @param onrejected Callback to handle the rejected state of the promise, receiving the error reason
|
|
1528
|
-
* @returns A promise that resolves to the result of the onfulfilled or onrejected callback
|
|
1529
|
-
*/
|
|
1530
|
-
then(onfulfilled, onrejected) {
|
|
1531
|
-
this.called.then = true;
|
|
1532
|
-
this.json();
|
|
1533
|
-
if (this.res) {
|
|
1534
|
-
const response = new ServerResponse(this.res, this.body);
|
|
1535
|
-
this.withResponseContext = {
|
|
1536
|
-
response,
|
|
1537
|
-
raw: this.res
|
|
1538
|
-
};
|
|
1539
|
-
this.called.withResponse = true;
|
|
1540
|
-
this.withResponse(response, this.res);
|
|
1541
|
-
} else {
|
|
1542
|
-
this.called.withResponse = true;
|
|
1543
|
-
this.withResponse();
|
|
1544
|
-
}
|
|
1545
|
-
const resolved = Promise.resolve(this.body).then(onfulfilled, onrejected);
|
|
1546
|
-
if (this.res) this.res.send(this.body);
|
|
1547
|
-
return resolved;
|
|
1548
|
-
}
|
|
1549
|
-
/**
|
|
1550
|
-
* Promise-like catch method to handle rejected state of the promise
|
|
1551
|
-
*
|
|
1552
|
-
* @param onrejected
|
|
1553
|
-
* @returns
|
|
1554
|
-
*/
|
|
1555
|
-
catch(onrejected) {
|
|
1556
|
-
return this.then(void 0, onrejected);
|
|
1557
|
-
}
|
|
1558
|
-
/**
|
|
1559
|
-
* Promise-like finally method to handle cleanup after promise is settled
|
|
1560
|
-
*
|
|
1561
|
-
* @param onfinally
|
|
1562
|
-
* @returns
|
|
1563
|
-
*/
|
|
1564
|
-
finally(onfinally) {
|
|
1565
|
-
return this.then(onfinally, onfinally);
|
|
1566
|
-
}
|
|
1567
|
-
};
|
|
1568
|
-
|
|
1569
|
-
//#endregion
|
|
1570
|
-
//#region src/Resource.ts
|
|
1571
|
-
/**
|
|
1572
|
-
* Resource class to handle API resource transformation and response building
|
|
1573
|
-
*/
|
|
1574
|
-
var Resource = class Resource {
|
|
1575
|
-
body = { data: {} };
|
|
1576
|
-
resource;
|
|
1577
|
-
additionalMeta;
|
|
1578
|
-
withResponseContext;
|
|
1579
|
-
/**
|
|
1580
|
-
* Preferred case style for this resource's output keys.
|
|
1581
|
-
* Set on a subclass to override the global default.
|
|
1582
|
-
*/
|
|
1583
|
-
static preferredCase;
|
|
1584
|
-
/**
|
|
1585
|
-
* Response structure override for this resource class.
|
|
1586
|
-
*/
|
|
1587
|
-
static responseStructure;
|
|
1588
|
-
called = {};
|
|
1589
|
-
constructor(rsc, res) {
|
|
1590
|
-
this.res = res;
|
|
1591
|
-
this.resource = rsc;
|
|
1592
|
-
/**
|
|
1593
|
-
* Copy properties from rsc to this instance for easy
|
|
1594
|
-
* access, but only if data is not an array
|
|
1595
|
-
*/
|
|
1596
|
-
if (!Array.isArray(this.resource.data ?? this.resource)) {
|
|
1597
|
-
for (const key of Object.keys(this.resource.data ?? this.resource)) if (!(key in this)) Object.defineProperty(this, key, {
|
|
1598
|
-
enumerable: true,
|
|
1599
|
-
configurable: true,
|
|
1600
|
-
get: () => {
|
|
1601
|
-
return this.resource.data?.[key] ?? this.resource[key];
|
|
1602
|
-
},
|
|
1603
|
-
set: (value) => {
|
|
1604
|
-
if (this.resource.data && this.resource.data[key]) this.resource.data[key] = value;
|
|
1605
|
-
else this.resource[key] = value;
|
|
1606
|
-
}
|
|
1607
|
-
});
|
|
1608
|
-
}
|
|
1609
|
-
}
|
|
1610
|
-
/**
|
|
1611
|
-
* Create a ResourceCollection from an array of resource data or a Collectible instance
|
|
1612
|
-
*
|
|
1613
|
-
* @param data
|
|
1614
|
-
* @returns
|
|
1615
|
-
*/
|
|
1616
|
-
static collection(data) {
|
|
1617
|
-
return new ResourceCollection(data).setCollects(this);
|
|
1618
|
-
}
|
|
1619
|
-
/**
|
|
1620
|
-
* Get the original resource data
|
|
1621
|
-
*/
|
|
1622
|
-
data() {
|
|
1623
|
-
return this.toArray();
|
|
1624
|
-
}
|
|
1625
|
-
/**
|
|
1626
|
-
* Get the current serialized output body.
|
|
1627
|
-
*/
|
|
1628
|
-
getBody() {
|
|
1629
|
-
this.json();
|
|
1630
|
-
return this.body;
|
|
1631
|
-
}
|
|
1632
|
-
/**
|
|
1633
|
-
* Replace the current serialized output body.
|
|
1634
|
-
*/
|
|
1635
|
-
setBody(body) {
|
|
1636
|
-
this.body = body;
|
|
1637
|
-
return this;
|
|
1638
|
-
}
|
|
1639
|
-
/**
|
|
1640
|
-
* Conditionally include a value in serialized output.
|
|
1641
|
-
*/
|
|
1642
|
-
when(condition, value) {
|
|
1643
|
-
return resolveWhen(condition, value);
|
|
1644
|
-
}
|
|
1645
|
-
/**
|
|
1646
|
-
* Include a value only when it is not null/undefined.
|
|
1647
|
-
*/
|
|
1648
|
-
whenNotNull(value) {
|
|
1649
|
-
return resolveWhenNotNull(value);
|
|
1650
|
-
}
|
|
1651
|
-
/**
|
|
1652
|
-
* Conditionally merge object attributes into serialized output.
|
|
1653
|
-
*/
|
|
1654
|
-
mergeWhen(condition, value) {
|
|
1655
|
-
return resolveMergeWhen(condition, value);
|
|
1656
|
-
}
|
|
1657
|
-
resolveResponseStructure() {
|
|
1658
|
-
const local = this.constructor.responseStructure;
|
|
1659
|
-
const global = getGlobalResponseStructure();
|
|
1660
|
-
return {
|
|
1661
|
-
wrap: local?.wrap ?? global?.wrap ?? true,
|
|
1662
|
-
rootKey: local?.rootKey ?? global?.rootKey ?? "data",
|
|
1663
|
-
factory: local?.factory ?? global?.factory
|
|
1664
|
-
};
|
|
1665
|
-
}
|
|
1666
|
-
getPayloadKey() {
|
|
1667
|
-
const { wrap, rootKey, factory } = this.resolveResponseStructure();
|
|
1668
|
-
return factory || !wrap ? void 0 : rootKey;
|
|
1669
|
-
}
|
|
1670
|
-
/**
|
|
1671
|
-
* Convert resource to JSON response format
|
|
1672
|
-
*
|
|
1673
|
-
* @returns
|
|
1674
|
-
*/
|
|
1675
|
-
json() {
|
|
1676
|
-
if (!this.called.json) {
|
|
1677
|
-
this.called.json = true;
|
|
1678
|
-
const resource = this.data();
|
|
1679
|
-
let data = Array.isArray(resource) ? [...resource] : { ...resource };
|
|
1680
|
-
if (typeof data.data !== "undefined") data = data.data;
|
|
1681
|
-
data = sanitizeConditionalAttributes(data);
|
|
1682
|
-
const caseStyle = this.constructor.preferredCase ?? getGlobalCase();
|
|
1683
|
-
if (caseStyle) {
|
|
1684
|
-
const transformer = getCaseTransformer(caseStyle);
|
|
1685
|
-
data = transformKeys(data, transformer);
|
|
1686
|
-
}
|
|
1687
|
-
const customMeta = mergeMetadata(resolveWithHookMetadata(this, Resource.prototype.with), this.additionalMeta);
|
|
1688
|
-
const { wrap, rootKey, factory } = this.resolveResponseStructure();
|
|
1689
|
-
this.body = buildResponseEnvelope({
|
|
1690
|
-
payload: data,
|
|
1691
|
-
wrap,
|
|
1692
|
-
rootKey,
|
|
1693
|
-
factory,
|
|
1694
|
-
context: {
|
|
1695
|
-
type: "resource",
|
|
1696
|
-
resource: this.resource
|
|
1697
|
-
}
|
|
1698
|
-
});
|
|
1699
|
-
this.body = appendRootProperties(this.body, customMeta, rootKey);
|
|
1700
|
-
}
|
|
1701
|
-
return this;
|
|
1702
|
-
}
|
|
1703
|
-
/**
|
|
1704
|
-
* Append structured metadata to the response body.
|
|
1705
|
-
*
|
|
1706
|
-
* @param meta Metadata object or metadata factory
|
|
1707
|
-
* @returns
|
|
1708
|
-
*/
|
|
1709
|
-
with(meta) {
|
|
1710
|
-
this.called.with = true;
|
|
1711
|
-
if (typeof meta === "undefined") return this.additionalMeta || {};
|
|
1712
|
-
const resolvedMeta = typeof meta === "function" ? meta(this.resource) : meta;
|
|
1713
|
-
this.additionalMeta = mergeMetadata(this.additionalMeta, resolvedMeta);
|
|
1714
|
-
if (this.called.json) {
|
|
1715
|
-
const { rootKey } = this.resolveResponseStructure();
|
|
1716
|
-
this.body = appendRootProperties(this.body, resolvedMeta, rootKey);
|
|
1717
|
-
}
|
|
1718
|
-
return this;
|
|
1719
|
-
}
|
|
1720
|
-
/**
|
|
1721
|
-
* Typed fluent metadata helper.
|
|
1722
|
-
*
|
|
1723
|
-
* @param meta Metadata object or metadata factory
|
|
1724
|
-
* @returns
|
|
1725
|
-
*/
|
|
1726
|
-
withMeta(meta) {
|
|
1727
|
-
this.with(meta);
|
|
1728
|
-
return this;
|
|
1729
|
-
}
|
|
1730
|
-
/**
|
|
1731
|
-
* Flatten resource to array format (for collections) or return original data for single resources
|
|
1732
|
-
*
|
|
1733
|
-
* @returns
|
|
1734
|
-
*/
|
|
1735
|
-
toArray() {
|
|
1736
|
-
this.called.toArray = true;
|
|
1737
|
-
this.json();
|
|
1738
|
-
let data = Array.isArray(this.resource) ? [...this.resource] : { ...this.resource };
|
|
1739
|
-
if (!Array.isArray(data) && typeof data.data !== "undefined") data = data.data;
|
|
1740
|
-
return data;
|
|
1741
|
-
}
|
|
1742
|
-
/**
|
|
1743
|
-
* Add additional properties to the response body
|
|
1744
|
-
*
|
|
1745
|
-
* @param extra Additional properties to merge into the response body
|
|
1746
|
-
* @returns
|
|
1747
|
-
*/
|
|
1748
|
-
additional(extra) {
|
|
1749
|
-
this.called.additional = true;
|
|
1750
|
-
this.json();
|
|
1751
|
-
const payloadKey = this.getPayloadKey();
|
|
1752
|
-
if (extra.data && payloadKey && typeof this.body[payloadKey] !== "undefined") this.body[payloadKey] = Array.isArray(this.body[payloadKey]) ? [...this.body[payloadKey], ...extra.data] : {
|
|
1753
|
-
...this.body[payloadKey],
|
|
1754
|
-
...extra.data
|
|
1755
|
-
};
|
|
1756
|
-
this.body = {
|
|
1757
|
-
...this.body,
|
|
1758
|
-
...extra
|
|
1759
|
-
};
|
|
1760
|
-
return this;
|
|
1761
|
-
}
|
|
1762
|
-
response(res) {
|
|
1763
|
-
this.called.toResponse = true;
|
|
1764
|
-
this.json();
|
|
1765
|
-
const rawResponse = res ?? this.res;
|
|
1766
|
-
const response = new ServerResponse(rawResponse, this.body);
|
|
1767
|
-
this.withResponseContext = {
|
|
1768
|
-
response,
|
|
1769
|
-
raw: rawResponse
|
|
1770
|
-
};
|
|
1771
|
-
this.called.withResponse = true;
|
|
1772
|
-
this.withResponse(response, rawResponse);
|
|
1773
|
-
return response;
|
|
1774
|
-
}
|
|
1775
|
-
/**
|
|
1776
|
-
* Customize the outgoing transport response right before dispatch.
|
|
1777
|
-
*
|
|
1778
|
-
* Override in custom classes to mutate headers/status/body.
|
|
1779
|
-
*/
|
|
1780
|
-
withResponse(_response, _rawResponse) {
|
|
1781
|
-
return this;
|
|
1782
|
-
}
|
|
1783
|
-
/**
|
|
1784
|
-
* Promise-like then method to allow chaining with async/await or .then() syntax
|
|
1785
|
-
*
|
|
1786
|
-
* @param onfulfilled Callback to handle the fulfilled state of the promise, receiving the response body
|
|
1787
|
-
* @param onrejected Callback to handle the rejected state of the promise, receiving the error reason
|
|
1788
|
-
* @returns A promise that resolves to the result of the onfulfilled or onrejected callback
|
|
1789
|
-
*/
|
|
1790
|
-
then(onfulfilled, onrejected) {
|
|
1791
|
-
this.called.then = true;
|
|
1792
|
-
this.json();
|
|
1793
|
-
if (this.res) {
|
|
1794
|
-
const response = new ServerResponse(this.res, this.body);
|
|
1795
|
-
this.withResponseContext = {
|
|
1796
|
-
response,
|
|
1797
|
-
raw: this.res
|
|
1798
|
-
};
|
|
1799
|
-
this.called.withResponse = true;
|
|
1800
|
-
this.withResponse(response, this.res);
|
|
1801
|
-
} else {
|
|
1802
|
-
this.called.withResponse = true;
|
|
1803
|
-
this.withResponse();
|
|
1804
|
-
}
|
|
1805
|
-
const resolved = Promise.resolve(this.body).then(onfulfilled, onrejected);
|
|
1806
|
-
if (this.res) this.res.send(this.body);
|
|
1807
|
-
return resolved;
|
|
1808
|
-
}
|
|
1809
|
-
/**
|
|
1810
|
-
* Promise-like catch method to handle rejected state of the promise
|
|
1811
|
-
*
|
|
1812
|
-
* @param onrejected
|
|
1813
|
-
* @returns
|
|
1814
|
-
*/
|
|
1815
|
-
catch(onrejected) {
|
|
1816
|
-
return this.then(void 0, onrejected);
|
|
1817
|
-
}
|
|
1818
|
-
/**
|
|
1819
|
-
* Promise-like finally method to handle cleanup after promise is settled
|
|
1820
|
-
*
|
|
1821
|
-
* @param onfinally
|
|
1822
|
-
* @returns
|
|
1823
|
-
*/
|
|
1824
|
-
finally(onfinally) {
|
|
1825
|
-
return this.then(onfinally, onfinally);
|
|
1826
|
-
}
|
|
1827
|
-
};
|
|
1828
|
-
|
|
1829
|
-
//#endregion
|
|
1830
|
-
exports.ApiResource = ApiResource;
|
|
1831
|
-
exports.CONDITIONAL_ATTRIBUTE_MISSING = CONDITIONAL_ATTRIBUTE_MISSING;
|
|
1832
|
-
exports.CliApp = CliApp;
|
|
1833
|
-
exports.GenericResource = GenericResource;
|
|
1834
|
-
exports.InitCommand = InitCommand;
|
|
1835
|
-
exports.MakeResource = MakeResource;
|
|
1836
|
-
exports.Resource = Resource;
|
|
1837
|
-
exports.ResourceCollection = ResourceCollection;
|
|
1838
|
-
exports.ServerResponse = ServerResponse;
|
|
1839
|
-
exports.appendRootProperties = appendRootProperties;
|
|
1840
|
-
exports.applyRuntimeConfig = applyRuntimeConfig;
|
|
1841
|
-
exports.buildPaginationExtras = buildPaginationExtras;
|
|
1842
|
-
exports.buildResponseEnvelope = buildResponseEnvelope;
|
|
1843
|
-
exports.defineConfig = defineConfig;
|
|
1844
|
-
exports.getCaseTransformer = getCaseTransformer;
|
|
1845
|
-
exports.getDefaultConfig = getDefaultConfig;
|
|
1846
|
-
exports.getGlobalBaseUrl = getGlobalBaseUrl;
|
|
1847
|
-
exports.getGlobalCase = getGlobalCase;
|
|
1848
|
-
exports.getGlobalCursorMeta = getGlobalCursorMeta;
|
|
1849
|
-
exports.getGlobalPageName = getGlobalPageName;
|
|
1850
|
-
exports.getGlobalPaginatedExtras = getGlobalPaginatedExtras;
|
|
1851
|
-
exports.getGlobalPaginatedLinks = getGlobalPaginatedLinks;
|
|
1852
|
-
exports.getGlobalPaginatedMeta = getGlobalPaginatedMeta;
|
|
1853
|
-
exports.getGlobalResponseFactory = getGlobalResponseFactory;
|
|
1854
|
-
exports.getGlobalResponseRootKey = getGlobalResponseRootKey;
|
|
1855
|
-
exports.getGlobalResponseStructure = getGlobalResponseStructure;
|
|
1856
|
-
exports.getGlobalResponseWrap = getGlobalResponseWrap;
|
|
1857
|
-
exports.getPaginationExtraKeys = getPaginationExtraKeys;
|
|
1858
|
-
exports.isPlainObject = isPlainObject;
|
|
1859
|
-
exports.loadRuntimeConfig = loadRuntimeConfig;
|
|
1860
|
-
exports.mergeMetadata = mergeMetadata;
|
|
1861
|
-
exports.resetRuntimeConfigForTests = resetRuntimeConfigForTests;
|
|
1862
|
-
exports.resolveMergeWhen = resolveMergeWhen;
|
|
1863
|
-
exports.resolveWhen = resolveWhen;
|
|
1864
|
-
exports.resolveWhenNotNull = resolveWhenNotNull;
|
|
1865
|
-
exports.resolveWithHookMetadata = resolveWithHookMetadata;
|
|
1866
|
-
exports.sanitizeConditionalAttributes = sanitizeConditionalAttributes;
|
|
1867
|
-
exports.setGlobalBaseUrl = setGlobalBaseUrl;
|
|
1868
|
-
exports.setGlobalCase = setGlobalCase;
|
|
1869
|
-
exports.setGlobalCursorMeta = setGlobalCursorMeta;
|
|
1870
|
-
exports.setGlobalPageName = setGlobalPageName;
|
|
1871
|
-
exports.setGlobalPaginatedExtras = setGlobalPaginatedExtras;
|
|
1872
|
-
exports.setGlobalPaginatedLinks = setGlobalPaginatedLinks;
|
|
1873
|
-
exports.setGlobalPaginatedMeta = setGlobalPaginatedMeta;
|
|
1874
|
-
exports.setGlobalResponseFactory = setGlobalResponseFactory;
|
|
1875
|
-
exports.setGlobalResponseRootKey = setGlobalResponseRootKey;
|
|
1876
|
-
exports.setGlobalResponseStructure = setGlobalResponseStructure;
|
|
1877
|
-
exports.setGlobalResponseWrap = setGlobalResponseWrap;
|
|
1878
|
-
exports.splitWords = splitWords;
|
|
1879
|
-
exports.toCamelCase = toCamelCase;
|
|
1880
|
-
exports.toKebabCase = toKebabCase;
|
|
1881
|
-
exports.toPascalCase = toPascalCase;
|
|
1882
|
-
exports.toSnakeCase = toSnakeCase;
|
|
1883
|
-
exports.transformKeys = transformKeys;
|
|
28
|
+
`;var $=class{_status=200;headers={};constructor(e,t){this.response=e,this.body=t}setStatusCode(e){return this._status=e,`status`in this.response&&typeof this.response.status==`function`?this.response.status(e):`status`in this.response&&(this.response.status=e),this}status(){return this._status}statusText(){if(`statusMessage`in this.response)return this.response.statusMessage;if(`statusText`in this.response)return this.response.statusText}setCookie(e,t,n){return this.#e(`Set-Cookie`,`${e}=${t}; ${Object.entries(n||{}).map(([e,t])=>`${e}=${t}`).join(`; `)}`),this}setHeaders(e){for(let[t,n]of Object.entries(e))this.#e(t,n);return this}header(e,t){return this.#e(e,t),this}#e(e,t){this.headers[e]=t,`headers`in this.response?this.response.headers.set(e,t):`setHeader`in this.response&&this.response.setHeader(e,t)}then(e,t){let n=Promise.resolve(this.body).then(e,t);return`send`in this.response&&this.response.send(this.body),n}catch(e){return this.then(void 0,e)}finally(e){return this.then(e,e)}},Te=class e{body={data:{}};resource;collects;additionalMeta;withResponseContext;static preferredCase;static responseStructure;called={};constructor(e,t){if(this.res=t,this.resource=e,!Array.isArray(this.resource.data??this.resource))for(let e of Object.keys(this.resource.data??this.resource))e in this||Object.defineProperty(this,e,{enumerable:!0,configurable:!0,get:()=>this.resource.data?.[e]??this.resource[e],set:t=>{this.resource.data&&this.resource.data[e]?this.resource.data[e]=t:this.resource[e]=t}})}data(){return this.resource}getBody(){return this.json(),this.body}setBody(e){return this.body=e,this}when(e,t){return R(e,t)}whenNotNull(e){return z(e)}mergeWhen(e,t){return B(e,t)}resolveResponseStructure(){let e=this.constructor.responseStructure,t=this.collects?.responseStructure,n=C();return{wrap:e?.wrap??t?.wrap??n?.wrap??!0,rootKey:e?.rootKey??t?.rootKey??n?.rootKey??`data`,factory:e?.factory??t?.factory??n?.factory}}getPayloadKey(){let{wrap:e,rootKey:t,factory:n}=this.resolveResponseStructure();return n||!e?void 0:t}json(){if(!this.called.json){this.called.json=!0;let t=this.data(),n=Array.isArray(t)?[...t]:{...t};Array.isArray(n)&&this.collects&&(n=n.map(e=>new this.collects(e).data()),this.resource=n),n.data!==void 0&&(n=n.data),n=V(n);let r=j(this.resource),{metaKey:i}=k(),a=i?r[i]:void 0;i&&delete r[i];let o=this.constructor.preferredCase??x();if(o){let e=q(o);n=J(n,e)}let s=P(I(this,e.prototype.with),this.additionalMeta),{wrap:c,rootKey:l,factory:u}=this.resolveResponseStructure();this.body=F({payload:n,meta:a,metaKey:i,wrap:c,rootKey:l,factory:u,context:{type:`generic`,resource:this.resource}}),this.body=N(this.body,{...r,...s||{}},l)}return this}with(e){if(this.called.with=!0,e===void 0)return this.additionalMeta||{};let t=typeof e==`function`?e(this.resource):e;if(this.additionalMeta=P(this.additionalMeta,t),this.called.json){let{rootKey:e}=this.resolveResponseStructure();this.body=N(this.body,t,e)}return this}withMeta(e){return this.with(e),this}toArray(){this.called.toArray=!0,this.json();let e=Array.isArray(this.resource)?[...this.resource]:{...this.resource};return e.data!==void 0&&(e=e.data),e}additional(e){this.called.additional=!0,this.json();let t=e.data;delete e.data,delete e.pagination;let n=this.getPayloadKey();return t&&n&&this.body[n]!==void 0&&(this.body[n]=Array.isArray(this.body[n])?[...this.body[n],...t]:{...this.body[n],...t}),this.body={...this.body,...e},this}response(e){this.called.toResponse=!0,this.json();let t=e??this.res,n=new $(t,this.body);return this.withResponseContext={response:n,raw:t},this.called.withResponse=!0,this.withResponse(n,t),n}withResponse(e,t){return this}then(e,t){if(this.called.then=!0,this.json(),this.res){let e=new $(this.res,this.body);this.withResponseContext={response:e,raw:this.res},this.called.withResponse=!0,this.withResponse(e,this.res)}else this.called.withResponse=!0,this.withResponse();let n=Promise.resolve(this.body).then(e,t);return this.res&&this.res.send(this.body),n}},Ee=class e{body={data:[]};resource;collects;additionalMeta;withResponseContext;static preferredCase;static responseStructure;called={};constructor(e,t){this.res=t,this.resource=e}data(){return this.toArray()}getBody(){return this.json(),this.body}setBody(e){return this.body=e,this}when(e,t){return R(e,t)}whenNotNull(e){return z(e)}mergeWhen(e,t){return B(e,t)}resolveResponseStructure(){let e=this.constructor.responseStructure,t=this.collects?.responseStructure,n=C();return{wrap:e?.wrap??t?.wrap??n?.wrap??!0,rootKey:e?.rootKey??t?.rootKey??n?.rootKey??`data`,factory:e?.factory??t?.factory??n?.factory}}getPayloadKey(){let{wrap:e,rootKey:t,factory:n}=this.resolveResponseStructure();return n||!e?void 0:t}json(){if(!this.called.json){this.called.json=!0;let t=this.data();this.collects&&(t=t.map(e=>new this.collects(e).data())),t=V(t);let n=Array.isArray(this.resource)?{}:j(this.resource),{metaKey:r}=k(),i=r?n[r]:void 0;r&&delete n[r];let a=this.constructor.preferredCase??this.collects?.preferredCase??x();if(a){let e=q(a);t=J(t,e)}let o=P(I(this,e.prototype.with),this.additionalMeta),{wrap:s,rootKey:c,factory:l}=this.resolveResponseStructure();this.body=F({payload:t,meta:i,metaKey:r,wrap:s,rootKey:c,factory:l,context:{type:`collection`,resource:this.resource}}),this.body=N(this.body,{...n,...o||{}},c)}return this}with(e){if(this.called.with=!0,e===void 0)return this.additionalMeta||{};let t=typeof e==`function`?e(this.resource):e;if(this.additionalMeta=P(this.additionalMeta,t),this.called.json){let{rootKey:e}=this.resolveResponseStructure();this.body=N(this.body,t,e)}return this}withMeta(e){return this.with(e),this}toArray(){return this.called.toArray=!0,this.json(),Array.isArray(this.resource)?[...this.resource]:[...this.resource.data]}additional(e){this.called.additional=!0,this.json(),delete e.cursor,delete e.pagination;let t=this.getPayloadKey();return e.data&&t&&Array.isArray(this.body[t])&&(this.body[t]=[...this.body[t],...e.data]),this.body={...this.body,...e},this}response(e){this.called.toResponse=!0,this.json();let t=e??this.res,n=new $(t,this.body);return this.withResponseContext={response:n,raw:t},this.called.withResponse=!0,this.withResponse(n,t),n}withResponse(e,t){return this}setCollects(e){return this.collects=e,this}then(e,t){if(this.called.then=!0,this.json(),this.res){let e=new $(this.res,this.body);this.withResponseContext={response:e,raw:this.res},this.called.withResponse=!0,this.withResponse(e,this.res)}else this.called.withResponse=!0,this.withResponse();let n=Promise.resolve(this.body).then(e,t);return this.res&&this.res.send(this.body),n}catch(e){return this.then(void 0,e)}finally(e){return this.then(e,e)}},De=class e{body={data:{}};resource;additionalMeta;withResponseContext;static preferredCase;static responseStructure;called={};constructor(e,t){if(this.res=t,this.resource=e,!Array.isArray(this.resource.data??this.resource))for(let e of Object.keys(this.resource.data??this.resource))e in this||Object.defineProperty(this,e,{enumerable:!0,configurable:!0,get:()=>this.resource.data?.[e]??this.resource[e],set:t=>{this.resource.data&&this.resource.data[e]?this.resource.data[e]=t:this.resource[e]=t}})}static collection(e){return new Ee(e).setCollects(this)}data(){return this.toArray()}getBody(){return this.json(),this.body}setBody(e){return this.body=e,this}when(e,t){return R(e,t)}whenNotNull(e){return z(e)}mergeWhen(e,t){return B(e,t)}resolveResponseStructure(){let e=this.constructor.responseStructure,t=C();return{wrap:e?.wrap??t?.wrap??!0,rootKey:e?.rootKey??t?.rootKey??`data`,factory:e?.factory??t?.factory}}getPayloadKey(){let{wrap:e,rootKey:t,factory:n}=this.resolveResponseStructure();return n||!e?void 0:t}json(){if(!this.called.json){this.called.json=!0;let t=this.data(),n=Array.isArray(t)?[...t]:{...t};n.data!==void 0&&(n=n.data),n=V(n);let r=this.constructor.preferredCase??x();if(r){let e=q(r);n=J(n,e)}let i=P(I(this,e.prototype.with),this.additionalMeta),{wrap:a,rootKey:o,factory:s}=this.resolveResponseStructure();this.body=F({payload:n,wrap:a,rootKey:o,factory:s,context:{type:`resource`,resource:this.resource}}),this.body=N(this.body,i,o)}return this}with(e){if(this.called.with=!0,e===void 0)return this.additionalMeta||{};let t=typeof e==`function`?e(this.resource):e;if(this.additionalMeta=P(this.additionalMeta,t),this.called.json){let{rootKey:e}=this.resolveResponseStructure();this.body=N(this.body,t,e)}return this}withMeta(e){return this.with(e),this}toArray(){this.called.toArray=!0,this.json();let e=Array.isArray(this.resource)?[...this.resource]:{...this.resource};return!Array.isArray(e)&&e.data!==void 0&&(e=e.data),e}additional(e){this.called.additional=!0,this.json();let t=this.getPayloadKey();return e.data&&t&&this.body[t]!==void 0&&(this.body[t]=Array.isArray(this.body[t])?[...this.body[t],...e.data]:{...this.body[t],...e.data}),this.body={...this.body,...e},this}response(e){this.called.toResponse=!0,this.json();let t=e??this.res,n=new $(t,this.body);return this.withResponseContext={response:n,raw:t},this.called.withResponse=!0,this.withResponse(n,t),n}withResponse(e,t){return this}then(e,t){if(this.called.then=!0,this.json(),this.res){let e=new $(this.res,this.body);this.withResponseContext={response:e,raw:this.res},this.called.withResponse=!0,this.withResponse(e,this.res)}else this.called.withResponse=!0,this.withResponse();let n=Promise.resolve(this.body).then(e,t);return this.res&&this.res.send(this.body),n}catch(e){return this.then(void 0,e)}finally(e){return this.then(e,e)}};exports.ApiResource=ee,exports.CONDITIONAL_ATTRIBUTE_MISSING=L,exports.CliApp=Se,exports.GenericResource=Te,exports.InitCommand=Ce,exports.MakeResource=we,exports.Resource=De,exports.ResourceCollection=Ee,exports.ServerResponse=$,exports.appendRootProperties=N,exports.applyRuntimeConfig=_e,exports.buildPaginationExtras=j,exports.buildResponseEnvelope=F,exports.defineConfig=X,exports.getCaseTransformer=q,exports.getDefaultConfig=he,exports.getGlobalBaseUrl=ue,exports.getGlobalCase=x,exports.getGlobalCursorMeta=O,exports.getGlobalPageName=fe,exports.getGlobalPaginatedExtras=T,exports.getGlobalPaginatedLinks=ce,exports.getGlobalPaginatedMeta=me,exports.getGlobalResponseFactory=se,exports.getGlobalResponseRootKey=ae,exports.getGlobalResponseStructure=C,exports.getGlobalResponseWrap=ie,exports.getPaginationExtraKeys=k,exports.isPlainObject=M,exports.loadRuntimeConfig=xe,exports.mergeMetadata=P,exports.resetRuntimeConfigForTests=ge,exports.resolveMergeWhen=B,exports.resolveWhen=R,exports.resolveWhenNotNull=z,exports.resolveWithHookMetadata=I,exports.sanitizeConditionalAttributes=V,exports.setGlobalBaseUrl=le,exports.setGlobalCase=b,exports.setGlobalCursorMeta=D,exports.setGlobalPageName=de,exports.setGlobalPaginatedExtras=w,exports.setGlobalPaginatedLinks=E,exports.setGlobalPaginatedMeta=pe,exports.setGlobalResponseFactory=oe,exports.setGlobalResponseRootKey=ne,exports.setGlobalResponseStructure=S,exports.setGlobalResponseWrap=re,exports.splitWords=H,exports.toCamelCase=U,exports.toKebabCase=K,exports.toPascalCase=G,exports.toSnakeCase=W,exports.transformKeys=J;
|