resora 0.2.1 → 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/bin/index.mjs +5 -5
- package/dist/index.cjs +6 -1504
- package/dist/index.d.cts +375 -4
- package/dist/index.d.mts +375 -4
- package/dist/index.mjs +6 -1425
- package/package.json +1 -1
package/dist/index.cjs
CHANGED
|
@@ -1,553 +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 _h3ravel_musket = require("@h3ravel/musket");
|
|
33
|
-
|
|
34
|
-
//#region src/ApiResource.ts
|
|
35
|
-
/**
|
|
36
|
-
* ApiResource function to return the Resource instance
|
|
37
|
-
*
|
|
38
|
-
* @param instance Resource instance
|
|
39
|
-
* @returns Resource instance
|
|
40
|
-
*/
|
|
41
|
-
function ApiResource(instance) {
|
|
42
|
-
return instance;
|
|
43
|
-
}
|
|
44
|
-
|
|
45
|
-
//#endregion
|
|
46
|
-
//#region src/utilities/state.ts
|
|
47
|
-
let globalPreferredCase;
|
|
48
|
-
let globalResponseStructure;
|
|
49
|
-
let globalPaginatedExtras = ["meta", "links"];
|
|
50
|
-
let globalPaginatedLinks = {
|
|
51
|
-
first: "first",
|
|
52
|
-
last: "last",
|
|
53
|
-
prev: "prev",
|
|
54
|
-
next: "next"
|
|
55
|
-
};
|
|
56
|
-
let globalBaseUrl = "https://localhost";
|
|
57
|
-
let globalPageName = "page";
|
|
58
|
-
let globalPaginatedMeta = {
|
|
59
|
-
to: "to",
|
|
60
|
-
from: "from",
|
|
61
|
-
links: "links",
|
|
62
|
-
path: "path",
|
|
63
|
-
total: "total",
|
|
64
|
-
per_page: "per_page",
|
|
65
|
-
last_page: "last_page",
|
|
66
|
-
current_page: "current_page"
|
|
67
|
-
};
|
|
68
|
-
let globalCursorMeta = {
|
|
69
|
-
previous: "previous",
|
|
70
|
-
next: "next"
|
|
71
|
-
};
|
|
72
|
-
const setGlobalCase = (style) => {
|
|
73
|
-
globalPreferredCase = style;
|
|
74
|
-
};
|
|
75
|
-
const getGlobalCase = () => {
|
|
76
|
-
return globalPreferredCase;
|
|
77
|
-
};
|
|
78
|
-
const setGlobalResponseStructure = (config) => {
|
|
79
|
-
globalResponseStructure = config;
|
|
80
|
-
};
|
|
81
|
-
const getGlobalResponseStructure = () => {
|
|
82
|
-
return globalResponseStructure;
|
|
83
|
-
};
|
|
84
|
-
const setGlobalResponseRootKey = (rootKey) => {
|
|
85
|
-
globalResponseStructure = {
|
|
86
|
-
...globalResponseStructure || {},
|
|
87
|
-
rootKey
|
|
88
|
-
};
|
|
89
|
-
};
|
|
90
|
-
const setGlobalResponseWrap = (wrap) => {
|
|
91
|
-
globalResponseStructure = {
|
|
92
|
-
...globalResponseStructure || {},
|
|
93
|
-
wrap
|
|
94
|
-
};
|
|
95
|
-
};
|
|
96
|
-
const getGlobalResponseWrap = () => {
|
|
97
|
-
return globalResponseStructure?.wrap;
|
|
98
|
-
};
|
|
99
|
-
const getGlobalResponseRootKey = () => {
|
|
100
|
-
return globalResponseStructure?.rootKey;
|
|
101
|
-
};
|
|
102
|
-
const setGlobalResponseFactory = (factory) => {
|
|
103
|
-
globalResponseStructure = {
|
|
104
|
-
...globalResponseStructure || {},
|
|
105
|
-
factory
|
|
106
|
-
};
|
|
107
|
-
};
|
|
108
|
-
const getGlobalResponseFactory = () => {
|
|
109
|
-
return globalResponseStructure?.factory;
|
|
110
|
-
};
|
|
111
|
-
const setGlobalPaginatedExtras = (extras) => {
|
|
112
|
-
globalPaginatedExtras = extras;
|
|
113
|
-
};
|
|
114
|
-
const getGlobalPaginatedExtras = () => {
|
|
115
|
-
return globalPaginatedExtras;
|
|
116
|
-
};
|
|
117
|
-
const setGlobalPaginatedLinks = (links) => {
|
|
118
|
-
globalPaginatedLinks = {
|
|
119
|
-
...globalPaginatedLinks,
|
|
120
|
-
...links
|
|
121
|
-
};
|
|
122
|
-
};
|
|
123
|
-
const getGlobalPaginatedLinks = () => {
|
|
124
|
-
return globalPaginatedLinks;
|
|
125
|
-
};
|
|
126
|
-
const setGlobalBaseUrl = (baseUrl) => {
|
|
127
|
-
globalBaseUrl = baseUrl;
|
|
128
|
-
};
|
|
129
|
-
const getGlobalBaseUrl = () => {
|
|
130
|
-
return globalBaseUrl;
|
|
131
|
-
};
|
|
132
|
-
const setGlobalPageName = (pageName) => {
|
|
133
|
-
globalPageName = pageName;
|
|
134
|
-
};
|
|
135
|
-
const getGlobalPageName = () => {
|
|
136
|
-
return globalPageName;
|
|
137
|
-
};
|
|
138
|
-
const setGlobalPaginatedMeta = (meta) => {
|
|
139
|
-
globalPaginatedMeta = {
|
|
140
|
-
...globalPaginatedMeta,
|
|
141
|
-
...meta
|
|
142
|
-
};
|
|
143
|
-
};
|
|
144
|
-
const getGlobalPaginatedMeta = () => {
|
|
145
|
-
return globalPaginatedMeta;
|
|
146
|
-
};
|
|
147
|
-
const setGlobalCursorMeta = (meta) => {
|
|
148
|
-
globalCursorMeta = {
|
|
149
|
-
...globalCursorMeta,
|
|
150
|
-
...meta
|
|
151
|
-
};
|
|
152
|
-
};
|
|
153
|
-
const getGlobalCursorMeta = () => {
|
|
154
|
-
return globalCursorMeta;
|
|
155
|
-
};
|
|
156
|
-
|
|
157
|
-
//#endregion
|
|
158
|
-
//#region src/utilities/pagination.ts
|
|
159
|
-
const getPaginationExtraKeys = () => {
|
|
160
|
-
const extras = getGlobalPaginatedExtras();
|
|
161
|
-
if (Array.isArray(extras)) return {
|
|
162
|
-
metaKey: extras.includes("meta") ? "meta" : void 0,
|
|
163
|
-
linksKey: extras.includes("links") ? "links" : void 0,
|
|
164
|
-
cursorKey: extras.includes("cursor") ? "cursor" : void 0
|
|
165
|
-
};
|
|
166
|
-
return {
|
|
167
|
-
metaKey: extras.meta,
|
|
168
|
-
linksKey: extras.links,
|
|
169
|
-
cursorKey: extras.cursor
|
|
170
|
-
};
|
|
171
|
-
};
|
|
172
|
-
const buildPageUrl = (page, pathName) => {
|
|
173
|
-
if (typeof page === "undefined") return;
|
|
174
|
-
const rawPath = pathName || "";
|
|
175
|
-
const base = getGlobalBaseUrl() || "";
|
|
176
|
-
const isAbsolutePath = /^https?:\/\//i.test(rawPath);
|
|
177
|
-
const normalizedBase = base.replace(/\/$/, "");
|
|
178
|
-
const normalizedPath = rawPath.replace(/^\//, "");
|
|
179
|
-
const root = isAbsolutePath ? rawPath : normalizedBase ? normalizedPath ? `${normalizedBase}/${normalizedPath}` : normalizedBase : "";
|
|
180
|
-
if (!root) return;
|
|
181
|
-
const url = new URL(root);
|
|
182
|
-
url.searchParams.set(getGlobalPageName() || "page", String(page));
|
|
183
|
-
return url.toString();
|
|
184
|
-
};
|
|
185
|
-
const buildPaginationExtras = (resource) => {
|
|
186
|
-
const { metaKey, linksKey, cursorKey } = getPaginationExtraKeys();
|
|
187
|
-
const extra = {};
|
|
188
|
-
const pagination = resource?.pagination;
|
|
189
|
-
const cursor = resource?.cursor;
|
|
190
|
-
const metaBlock = {};
|
|
191
|
-
const linksBlock = {};
|
|
192
|
-
if (pagination) {
|
|
193
|
-
const metaSource = {
|
|
194
|
-
to: pagination.to,
|
|
195
|
-
from: pagination.from,
|
|
196
|
-
links: pagination.links,
|
|
197
|
-
path: pagination.path,
|
|
198
|
-
total: pagination.total,
|
|
199
|
-
per_page: pagination.perPage,
|
|
200
|
-
last_page: pagination.lastPage,
|
|
201
|
-
current_page: pagination.currentPage
|
|
202
|
-
};
|
|
203
|
-
for (const [sourceKey, outputKey] of Object.entries(getGlobalPaginatedMeta())) {
|
|
204
|
-
if (!outputKey) continue;
|
|
205
|
-
const value = metaSource[sourceKey];
|
|
206
|
-
if (typeof value !== "undefined") metaBlock[outputKey] = value;
|
|
207
|
-
}
|
|
208
|
-
const linksSource = {
|
|
209
|
-
first: buildPageUrl(pagination.firstPage, pagination.path),
|
|
210
|
-
last: buildPageUrl(pagination.lastPage, pagination.path),
|
|
211
|
-
prev: buildPageUrl(pagination.prevPage, pagination.path),
|
|
212
|
-
next: buildPageUrl(pagination.nextPage, pagination.path)
|
|
213
|
-
};
|
|
214
|
-
for (const [sourceKey, outputKey] of Object.entries(getGlobalPaginatedLinks())) {
|
|
215
|
-
if (!outputKey) continue;
|
|
216
|
-
const value = linksSource[sourceKey];
|
|
217
|
-
if (typeof value !== "undefined") linksBlock[outputKey] = value;
|
|
218
|
-
}
|
|
219
|
-
}
|
|
220
|
-
if (cursor) {
|
|
221
|
-
const cursorBlock = {};
|
|
222
|
-
const cursorSource = {
|
|
223
|
-
previous: cursor.previous,
|
|
224
|
-
next: cursor.next
|
|
225
|
-
};
|
|
226
|
-
for (const [sourceKey, outputKey] of Object.entries(getGlobalCursorMeta())) {
|
|
227
|
-
if (!outputKey) continue;
|
|
228
|
-
const value = cursorSource[sourceKey];
|
|
229
|
-
if (typeof value !== "undefined") cursorBlock[outputKey] = value;
|
|
230
|
-
}
|
|
231
|
-
if (cursorKey && Object.keys(cursorBlock).length > 0) extra[cursorKey] = cursorBlock;
|
|
232
|
-
else if (Object.keys(cursorBlock).length > 0) metaBlock.cursor = cursorBlock;
|
|
233
|
-
}
|
|
234
|
-
if (metaKey && Object.keys(metaBlock).length > 0) extra[metaKey] = metaBlock;
|
|
235
|
-
if (linksKey && Object.keys(linksBlock).length > 0) extra[linksKey] = linksBlock;
|
|
236
|
-
return extra;
|
|
237
|
-
};
|
|
238
|
-
|
|
239
|
-
//#endregion
|
|
240
|
-
//#region src/utilities/objects.ts
|
|
241
|
-
const isPlainObject = (value) => {
|
|
242
|
-
if (typeof value !== "object" || value === null) return false;
|
|
243
|
-
if (Array.isArray(value) || value instanceof Date || value instanceof RegExp) return false;
|
|
244
|
-
const proto = Object.getPrototypeOf(value);
|
|
245
|
-
return proto === Object.prototype || proto === null;
|
|
246
|
-
};
|
|
247
|
-
const appendRootProperties = (body, extra, rootKey = "data") => {
|
|
248
|
-
if (!extra || Object.keys(extra).length === 0) return body;
|
|
249
|
-
if (Array.isArray(body)) return {
|
|
250
|
-
[rootKey]: body,
|
|
251
|
-
...extra
|
|
252
|
-
};
|
|
253
|
-
if (isPlainObject(body)) return {
|
|
254
|
-
...body,
|
|
255
|
-
...extra
|
|
256
|
-
};
|
|
257
|
-
return {
|
|
258
|
-
[rootKey]: body,
|
|
259
|
-
...extra
|
|
260
|
-
};
|
|
261
|
-
};
|
|
262
|
-
const mergeMetadata = (base, incoming) => {
|
|
263
|
-
if (!incoming) return base;
|
|
264
|
-
if (!base) return incoming;
|
|
265
|
-
const merged = { ...base };
|
|
266
|
-
for (const [key, value] of Object.entries(incoming)) {
|
|
267
|
-
const existing = merged[key];
|
|
268
|
-
if (isPlainObject(existing) && isPlainObject(value)) merged[key] = mergeMetadata(existing, value);
|
|
269
|
-
else merged[key] = value;
|
|
270
|
-
}
|
|
271
|
-
return merged;
|
|
272
|
-
};
|
|
273
|
-
|
|
274
|
-
//#endregion
|
|
275
|
-
//#region src/utilities/response.ts
|
|
276
|
-
const buildResponseEnvelope = ({ payload, meta, metaKey = "meta", wrap = true, rootKey = "data", factory, context }) => {
|
|
277
|
-
if (factory) return factory(payload, {
|
|
278
|
-
...context,
|
|
279
|
-
rootKey,
|
|
280
|
-
meta
|
|
281
|
-
});
|
|
282
|
-
if (!wrap) {
|
|
283
|
-
if (typeof meta === "undefined") return payload;
|
|
284
|
-
if (isPlainObject(payload)) return {
|
|
285
|
-
...payload,
|
|
286
|
-
[metaKey]: meta
|
|
287
|
-
};
|
|
288
|
-
return {
|
|
289
|
-
[rootKey]: payload,
|
|
290
|
-
[metaKey]: meta
|
|
291
|
-
};
|
|
292
|
-
}
|
|
293
|
-
const body = { [rootKey]: payload };
|
|
294
|
-
if (typeof meta !== "undefined") body[metaKey] = meta;
|
|
295
|
-
return body;
|
|
296
|
-
};
|
|
297
|
-
|
|
298
|
-
//#endregion
|
|
299
|
-
//#region src/utilities/metadata.ts
|
|
300
|
-
const resolveWithHookMetadata = (instance, baseWithMethod) => {
|
|
301
|
-
const candidate = instance?.with;
|
|
302
|
-
if (typeof candidate !== "function" || candidate === baseWithMethod) return;
|
|
303
|
-
if (candidate.length > 0) return;
|
|
304
|
-
const result = candidate.call(instance);
|
|
305
|
-
return isPlainObject(result) ? result : void 0;
|
|
306
|
-
};
|
|
307
|
-
|
|
308
|
-
//#endregion
|
|
309
|
-
//#region src/utilities/conditional.ts
|
|
310
|
-
const CONDITIONAL_ATTRIBUTE_MISSING = Symbol("resora.conditional.missing");
|
|
311
|
-
const resolveWhen = (condition, value) => {
|
|
312
|
-
if (!condition) return CONDITIONAL_ATTRIBUTE_MISSING;
|
|
313
|
-
return typeof value === "function" ? value() : value;
|
|
314
|
-
};
|
|
315
|
-
const resolveWhenNotNull = (value) => {
|
|
316
|
-
return value === null || typeof value === "undefined" ? CONDITIONAL_ATTRIBUTE_MISSING : value;
|
|
317
|
-
};
|
|
318
|
-
const resolveMergeWhen = (condition, value) => {
|
|
319
|
-
if (!condition) return {};
|
|
320
|
-
const resolved = typeof value === "function" ? value() : value;
|
|
321
|
-
return isPlainObject(resolved) ? resolved : {};
|
|
322
|
-
};
|
|
323
|
-
const sanitizeConditionalAttributes = (value) => {
|
|
324
|
-
if (value === CONDITIONAL_ATTRIBUTE_MISSING) return CONDITIONAL_ATTRIBUTE_MISSING;
|
|
325
|
-
if (Array.isArray(value)) return value.map((item) => sanitizeConditionalAttributes(item)).filter((item) => item !== CONDITIONAL_ATTRIBUTE_MISSING);
|
|
326
|
-
if (isPlainObject(value)) {
|
|
327
|
-
const result = {};
|
|
328
|
-
for (const [key, nestedValue] of Object.entries(value)) {
|
|
329
|
-
const sanitizedValue = sanitizeConditionalAttributes(nestedValue);
|
|
330
|
-
if (sanitizedValue === CONDITIONAL_ATTRIBUTE_MISSING) continue;
|
|
331
|
-
result[key] = sanitizedValue;
|
|
332
|
-
}
|
|
333
|
-
return result;
|
|
334
|
-
}
|
|
335
|
-
return value;
|
|
336
|
-
};
|
|
337
|
-
|
|
338
|
-
//#endregion
|
|
339
|
-
//#region src/utilities/case.ts
|
|
340
|
-
const splitWords = (str) => {
|
|
341
|
-
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);
|
|
342
|
-
};
|
|
343
|
-
const toCamelCase = (str) => {
|
|
344
|
-
return splitWords(str).map((w, i) => i === 0 ? w : w.charAt(0).toUpperCase() + w.slice(1)).join("");
|
|
345
|
-
};
|
|
346
|
-
const toSnakeCase = (str) => {
|
|
347
|
-
return splitWords(str).join("_");
|
|
348
|
-
};
|
|
349
|
-
const toPascalCase = (str) => {
|
|
350
|
-
return splitWords(str).map((w) => w.charAt(0).toUpperCase() + w.slice(1)).join("");
|
|
351
|
-
};
|
|
352
|
-
const toKebabCase = (str) => {
|
|
353
|
-
return splitWords(str).join("-");
|
|
354
|
-
};
|
|
355
|
-
const getCaseTransformer = (style) => {
|
|
356
|
-
if (typeof style === "function") return style;
|
|
357
|
-
switch (style) {
|
|
358
|
-
case "camel": return toCamelCase;
|
|
359
|
-
case "snake": return toSnakeCase;
|
|
360
|
-
case "pascal": return toPascalCase;
|
|
361
|
-
case "kebab": return toKebabCase;
|
|
362
|
-
}
|
|
363
|
-
};
|
|
364
|
-
const transformKeys = (obj, transformer) => {
|
|
365
|
-
if (obj === null || obj === void 0) return obj;
|
|
366
|
-
if (Array.isArray(obj)) return obj.map((item) => transformKeys(item, transformer));
|
|
367
|
-
if (obj instanceof Date || obj instanceof RegExp) return obj;
|
|
368
|
-
if (typeof obj === "object") return Object.fromEntries(Object.entries(obj).map(([key, value]) => [transformer(key), transformKeys(value, transformer)]));
|
|
369
|
-
return obj;
|
|
370
|
-
};
|
|
371
|
-
|
|
372
|
-
//#endregion
|
|
373
|
-
//#region src/utilities/config.ts
|
|
374
|
-
let stubsDir = path.default.resolve(process.cwd(), "node_modules/resora/stubs");
|
|
375
|
-
if (!(0, fs.existsSync)(stubsDir)) stubsDir = path.default.resolve(process.cwd(), "stubs");
|
|
376
|
-
const getDefaultConfig = () => {
|
|
377
|
-
return {
|
|
378
|
-
stubsDir,
|
|
379
|
-
preferredCase: "camel",
|
|
380
|
-
responseStructure: {
|
|
381
|
-
wrap: true,
|
|
382
|
-
rootKey: "data"
|
|
383
|
-
},
|
|
384
|
-
paginatedExtras: ["meta", "links"],
|
|
385
|
-
baseUrl: "https://localhost",
|
|
386
|
-
pageName: "page",
|
|
387
|
-
paginatedLinks: {
|
|
388
|
-
first: "first",
|
|
389
|
-
last: "last",
|
|
390
|
-
prev: "prev",
|
|
391
|
-
next: "next"
|
|
392
|
-
},
|
|
393
|
-
paginatedMeta: {
|
|
394
|
-
to: "to",
|
|
395
|
-
from: "from",
|
|
396
|
-
links: "links",
|
|
397
|
-
path: "path",
|
|
398
|
-
total: "total",
|
|
399
|
-
per_page: "per_page",
|
|
400
|
-
last_page: "last_page",
|
|
401
|
-
current_page: "current_page"
|
|
402
|
-
},
|
|
403
|
-
cursorMeta: {
|
|
404
|
-
previous: "previous",
|
|
405
|
-
next: "next"
|
|
406
|
-
},
|
|
407
|
-
resourcesDir: "src/resources",
|
|
408
|
-
stubs: {
|
|
409
|
-
config: "resora.config.stub",
|
|
410
|
-
resource: "resource.stub",
|
|
411
|
-
collection: "resource.collection.stub"
|
|
412
|
-
}
|
|
413
|
-
};
|
|
414
|
-
};
|
|
415
|
-
const defineConfig = (userConfig = {}) => {
|
|
416
|
-
const defaultConfig = getDefaultConfig();
|
|
417
|
-
return Object.assign(defaultConfig, userConfig, { stubs: Object.assign(defaultConfig.stubs, userConfig.stubs || {}) });
|
|
418
|
-
};
|
|
419
|
-
|
|
420
|
-
//#endregion
|
|
421
|
-
//#region src/cli/CliApp.ts
|
|
422
|
-
var CliApp = class {
|
|
423
|
-
command;
|
|
424
|
-
config = {};
|
|
425
|
-
constructor(config = {}) {
|
|
426
|
-
this.config = defineConfig(config);
|
|
427
|
-
}
|
|
428
|
-
async loadConfig(config = {}) {
|
|
429
|
-
this.config = defineConfig(config);
|
|
430
|
-
const possibleConfigPaths = [
|
|
431
|
-
(0, path.join)(process.cwd(), "resora.config.ts"),
|
|
432
|
-
(0, path.join)(process.cwd(), "resora.config.js"),
|
|
433
|
-
(0, path.join)(process.cwd(), "resora.config.cjs")
|
|
434
|
-
];
|
|
435
|
-
for (const configPath of possibleConfigPaths) if ((0, fs.existsSync)(configPath)) try {
|
|
436
|
-
const { default: userConfig } = await import(configPath);
|
|
437
|
-
Object.assign(this.config, userConfig);
|
|
438
|
-
break;
|
|
439
|
-
} catch (e) {
|
|
440
|
-
console.error(`Error loading config file at ${configPath}:`, e);
|
|
441
|
-
}
|
|
442
|
-
return this;
|
|
443
|
-
}
|
|
444
|
-
/**
|
|
445
|
-
* Get the current configuration object
|
|
446
|
-
* @returns
|
|
447
|
-
*/
|
|
448
|
-
getConfig() {
|
|
449
|
-
return this.config;
|
|
450
|
-
}
|
|
451
|
-
/**
|
|
452
|
-
* Initialize Resora by creating a default config file in the current directory
|
|
453
|
-
*
|
|
454
|
-
* @returns
|
|
455
|
-
*/
|
|
456
|
-
init() {
|
|
457
|
-
const outputPath = (0, path.join)(process.cwd(), "resora.config.js");
|
|
458
|
-
const stubPath = (0, path.join)(this.config.stubsDir, this.config.stubs.config);
|
|
459
|
-
if ((0, fs.existsSync)(outputPath) && !this.command.option("force")) {
|
|
460
|
-
this.command.error(`Error: ${outputPath} already exists.`);
|
|
461
|
-
process.exit(1);
|
|
462
|
-
}
|
|
463
|
-
this.ensureDirectory(outputPath);
|
|
464
|
-
if ((0, fs.existsSync)(outputPath) && this.command.option("force")) (0, fs.copyFileSync)(outputPath, outputPath.replace(/\.js$/, `.backup.${Date.now()}.js`));
|
|
465
|
-
(0, fs.writeFileSync)(outputPath, (0, fs.readFileSync)(stubPath, "utf-8"));
|
|
466
|
-
return { path: outputPath };
|
|
467
|
-
}
|
|
468
|
-
/**
|
|
469
|
-
* Utility to ensure directory exists
|
|
470
|
-
*
|
|
471
|
-
* @param filePath
|
|
472
|
-
*/
|
|
473
|
-
ensureDirectory(filePath) {
|
|
474
|
-
const dir = (0, path.dirname)(filePath);
|
|
475
|
-
if (!(0, fs.existsSync)(dir)) (0, fs.mkdirSync)(dir, { recursive: true });
|
|
476
|
-
}
|
|
477
|
-
/**
|
|
478
|
-
* Utility to generate file from stub
|
|
479
|
-
*
|
|
480
|
-
* @param stubPath
|
|
481
|
-
* @param outputPath
|
|
482
|
-
* @param replacements
|
|
483
|
-
*/
|
|
484
|
-
generateFile(stubPath, outputPath, replacements, options) {
|
|
485
|
-
if ((0, fs.existsSync)(outputPath) && !options?.force) {
|
|
486
|
-
this.command.error(`Error: ${outputPath} already exists.`);
|
|
487
|
-
process.exit(1);
|
|
488
|
-
} else if ((0, fs.existsSync)(outputPath) && options?.force) (0, fs.rmSync)(outputPath);
|
|
489
|
-
let content = (0, fs.readFileSync)(stubPath, "utf-8");
|
|
490
|
-
for (const [key, value] of Object.entries(replacements)) content = content.replace(new RegExp(`{{${key}}}`, "g"), value);
|
|
491
|
-
this.ensureDirectory(outputPath);
|
|
492
|
-
(0, fs.writeFileSync)(outputPath, content);
|
|
493
|
-
return outputPath;
|
|
494
|
-
}
|
|
495
|
-
/**
|
|
496
|
-
* Command to create a new resource or resource collection file
|
|
497
|
-
*
|
|
498
|
-
* @param name
|
|
499
|
-
* @param options
|
|
500
|
-
*/
|
|
501
|
-
makeResource(name, options) {
|
|
502
|
-
let resourceName = name;
|
|
503
|
-
if (options?.collection && !name.endsWith("Collection") && !name.endsWith("Resource")) resourceName += "Collection";
|
|
504
|
-
else if (!options?.collection && !name.endsWith("Resource") && !name.endsWith("Collection")) resourceName += "Resource";
|
|
505
|
-
const fileName = `${resourceName}.ts`;
|
|
506
|
-
const outputPath = (0, path.join)(this.config.resourcesDir, fileName);
|
|
507
|
-
const stubPath = (0, path.join)(this.config.stubsDir, options?.collection || name.endsWith("Collection") ? this.config.stubs.collection : this.config.stubs.resource);
|
|
508
|
-
if (!(0, fs.existsSync)(stubPath)) {
|
|
509
|
-
this.command.error(`Error: Stub file ${stubPath} not found.`);
|
|
510
|
-
process.exit(1);
|
|
511
|
-
}
|
|
512
|
-
const collectsName = resourceName.replace(/(Resource|Collection)$/, "") + "Resource";
|
|
513
|
-
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=`/**
|
|
514
2
|
* The resource that this collection collects.
|
|
515
3
|
*/
|
|
516
|
-
collects = ${
|
|
517
|
-
|
|
518
|
-
const collectsImport = `import ${collectsName} from './${collectsName}'\n`;
|
|
519
|
-
const hasCollects = (!!options?.collection || name.endsWith("Collection")) && (0, fs.existsSync)((0, path.join)(this.config.resourcesDir, `${collectsName}.ts`));
|
|
520
|
-
const path$2 = this.generateFile(stubPath, outputPath, {
|
|
521
|
-
ResourceName: resourceName,
|
|
522
|
-
CollectionResourceName: resourceName.replace(/(Resource|Collection)$/, "") + "Resource",
|
|
523
|
-
"collects = Resource": hasCollects ? collects : "",
|
|
524
|
-
"import = Resource": hasCollects ? collectsImport : ""
|
|
525
|
-
}, options);
|
|
526
|
-
return {
|
|
527
|
-
name: resourceName,
|
|
528
|
-
path: path$2
|
|
529
|
-
};
|
|
530
|
-
}
|
|
531
|
-
};
|
|
532
|
-
|
|
533
|
-
//#endregion
|
|
534
|
-
//#region src/cli/commands/InitCommand.ts
|
|
535
|
-
var InitCommand = class extends _h3ravel_musket.Command {
|
|
536
|
-
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
|
|
537
6
|
{--force : Force overwrite if config file already exists (existing file will be backed up) }
|
|
538
|
-
`;
|
|
539
|
-
description = "Initialize Resora";
|
|
540
|
-
async handle() {
|
|
541
|
-
this.app.command = this;
|
|
542
|
-
this.app.init();
|
|
543
|
-
this.success("Resora initialized");
|
|
544
|
-
}
|
|
545
|
-
};
|
|
546
|
-
|
|
547
|
-
//#endregion
|
|
548
|
-
//#region src/cli/commands/MakeResource.ts
|
|
549
|
-
var MakeResource = class extends _h3ravel_musket.Command {
|
|
550
|
-
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:
|
|
551
8
|
{resource : Generates a new resource file.
|
|
552
9
|
| {name : Name of the resource to create}
|
|
553
10
|
| {--c|collection : Make a resource collection}
|
|
@@ -561,966 +18,11 @@ var MakeResource = class extends _h3ravel_musket.Command {
|
|
|
561
18
|
| {prefix : prefix of the resources to create, "Admin" will create AdminResource, AdminCollection}
|
|
562
19
|
| {--force : Create the resource or collection file even if it already exists.}
|
|
563
20
|
}
|
|
564
|
-
`;
|
|
565
|
-
description = "Create a new resource or resource collection file";
|
|
566
|
-
async handle() {
|
|
567
|
-
this.app.command = this;
|
|
568
|
-
let path = "";
|
|
569
|
-
const action = this.dictionary.name || this.dictionary.baseCommand;
|
|
570
|
-
if (["resource", "collection"].includes(action) && !this.argument("name")) return void this.error("Error: Name argument is required.");
|
|
571
|
-
if (action === "all" && !this.argument("prefix")) return void this.error("Error: Prefix argument is required.");
|
|
572
|
-
switch (action) {
|
|
573
|
-
case "resource":
|
|
574
|
-
({path} = this.app.makeResource(this.argument("name"), this.options()));
|
|
575
|
-
break;
|
|
576
|
-
case "collection":
|
|
577
|
-
({path} = this.app.makeResource(this.argument("name") + "Collection", this.options()));
|
|
578
|
-
break;
|
|
579
|
-
case "all": {
|
|
580
|
-
const o1 = this.app.makeResource(this.argument("prefix"), { force: this.option("force") });
|
|
581
|
-
const o2 = this.app.makeResource(this.argument("prefix") + "Collection", {
|
|
582
|
-
collection: true,
|
|
583
|
-
force: this.option("force")
|
|
584
|
-
});
|
|
585
|
-
path = `${o1.path}, ${o2.path}`;
|
|
586
|
-
break;
|
|
587
|
-
}
|
|
588
|
-
default: this.fail(`Unknown action: ${action}`);
|
|
589
|
-
}
|
|
590
|
-
this.success(`Created: ${path}`);
|
|
591
|
-
}
|
|
592
|
-
};
|
|
593
|
-
|
|
594
|
-
//#endregion
|
|
595
|
-
//#region src/cli/logo.ts
|
|
596
|
-
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`
|
|
597
22
|
_____
|
|
598
23
|
| __ \
|
|
599
24
|
| |__) |___ ___ ___ _ __ __ _
|
|
600
25
|
| _ // _ \/ __|/ _ \| '__/ _, |
|
|
601
26
|
| | \ \ __/\__ \ (_) | | | (_| |
|
|
602
27
|
|_| \_\___||___/\___/|_| \__,_|
|
|
603
|
-
`;
|
|
604
|
-
|
|
605
|
-
//#endregion
|
|
606
|
-
//#region src/ServerResponse.ts
|
|
607
|
-
var ServerResponse = class {
|
|
608
|
-
_status = 200;
|
|
609
|
-
headers = {};
|
|
610
|
-
constructor(response, body) {
|
|
611
|
-
this.response = response;
|
|
612
|
-
this.body = body;
|
|
613
|
-
}
|
|
614
|
-
/**
|
|
615
|
-
* Set the HTTP status code for the response
|
|
616
|
-
*
|
|
617
|
-
* @param status
|
|
618
|
-
* @returns The current ServerResponse instance
|
|
619
|
-
*/
|
|
620
|
-
setStatusCode(status) {
|
|
621
|
-
this._status = status;
|
|
622
|
-
if ("status" in this.response && typeof this.response.status === "function") this.response.status(status);
|
|
623
|
-
else if ("status" in this.response) this.response.status = status;
|
|
624
|
-
return this;
|
|
625
|
-
}
|
|
626
|
-
/**
|
|
627
|
-
* Get the current HTTP status code for the response
|
|
628
|
-
*
|
|
629
|
-
* @returns
|
|
630
|
-
*/
|
|
631
|
-
status() {
|
|
632
|
-
return this._status;
|
|
633
|
-
}
|
|
634
|
-
/**
|
|
635
|
-
* Get the current HTTP status text for the response
|
|
636
|
-
*
|
|
637
|
-
* @returns
|
|
638
|
-
*/
|
|
639
|
-
statusText() {
|
|
640
|
-
if ("statusMessage" in this.response) return this.response.statusMessage;
|
|
641
|
-
else if ("statusText" in this.response) return this.response.statusText;
|
|
642
|
-
}
|
|
643
|
-
/**
|
|
644
|
-
* Set a cookie in the response header
|
|
645
|
-
*
|
|
646
|
-
* @param name The name of the cookie
|
|
647
|
-
* @param value The value of the cookie
|
|
648
|
-
* @param options Optional cookie attributes (e.g., path, domain, maxAge)
|
|
649
|
-
* @returns The current ServerResponse instance
|
|
650
|
-
*/
|
|
651
|
-
setCookie(name, value, options) {
|
|
652
|
-
this.#addHeader("Set-Cookie", `${name}=${value}; ${Object.entries(options || {}).map(([key, val]) => `${key}=${val}`).join("; ")}`);
|
|
653
|
-
return this;
|
|
654
|
-
}
|
|
655
|
-
/**
|
|
656
|
-
* Convert the resource to a JSON response body
|
|
657
|
-
*
|
|
658
|
-
* @param headers Optional headers to add to the response
|
|
659
|
-
* @returns The current ServerResponse instance
|
|
660
|
-
*/
|
|
661
|
-
setHeaders(headers) {
|
|
662
|
-
for (const [key, value] of Object.entries(headers)) this.#addHeader(key, value);
|
|
663
|
-
return this;
|
|
664
|
-
}
|
|
665
|
-
/**
|
|
666
|
-
* Add a single header to the response
|
|
667
|
-
*
|
|
668
|
-
* @param key The name of the header
|
|
669
|
-
* @param value The value of the header
|
|
670
|
-
* @returns The current ServerResponse instance
|
|
671
|
-
*/
|
|
672
|
-
header(key, value) {
|
|
673
|
-
this.#addHeader(key, value);
|
|
674
|
-
return this;
|
|
675
|
-
}
|
|
676
|
-
/**
|
|
677
|
-
* Add a single header to the response
|
|
678
|
-
*
|
|
679
|
-
* @param key The name of the header
|
|
680
|
-
* @param value The value of the header
|
|
681
|
-
*/
|
|
682
|
-
#addHeader(key, value) {
|
|
683
|
-
this.headers[key] = value;
|
|
684
|
-
if ("headers" in this.response) this.response.headers.set(key, value);
|
|
685
|
-
else if ("setHeader" in this.response) this.response.setHeader(key, value);
|
|
686
|
-
}
|
|
687
|
-
/**
|
|
688
|
-
* Promise-like then method to allow chaining with async/await or .then() syntax
|
|
689
|
-
*
|
|
690
|
-
* @param onfulfilled Callback to handle the fulfilled state of the promise, receiving the response body
|
|
691
|
-
* @param onrejected Callback to handle the rejected state of the promise, receiving the error reason
|
|
692
|
-
* @returns A promise that resolves to the result of the onfulfilled or onrejected callback
|
|
693
|
-
*/
|
|
694
|
-
then(onfulfilled, onrejected) {
|
|
695
|
-
const resolved = Promise.resolve(this.body).then(onfulfilled, onrejected);
|
|
696
|
-
if ("send" in this.response) this.response.send(this.body);
|
|
697
|
-
return resolved;
|
|
698
|
-
}
|
|
699
|
-
/**
|
|
700
|
-
* Promise-like catch method to handle rejected state of the promise
|
|
701
|
-
*
|
|
702
|
-
* @param onrejected
|
|
703
|
-
* @returns
|
|
704
|
-
*/
|
|
705
|
-
catch(onrejected) {
|
|
706
|
-
return this.then(void 0, onrejected);
|
|
707
|
-
}
|
|
708
|
-
/**
|
|
709
|
-
* Promise-like finally method to handle cleanup after promise is settled
|
|
710
|
-
*
|
|
711
|
-
* @param onfinally
|
|
712
|
-
* @returns
|
|
713
|
-
*/
|
|
714
|
-
finally(onfinally) {
|
|
715
|
-
return this.then(onfinally, onfinally);
|
|
716
|
-
}
|
|
717
|
-
};
|
|
718
|
-
|
|
719
|
-
//#endregion
|
|
720
|
-
//#region src/GenericResource.ts
|
|
721
|
-
/**
|
|
722
|
-
* GenericResource class to handle API resource transformation and response building
|
|
723
|
-
*/
|
|
724
|
-
var GenericResource = class GenericResource {
|
|
725
|
-
body = { data: {} };
|
|
726
|
-
resource;
|
|
727
|
-
collects;
|
|
728
|
-
additionalMeta;
|
|
729
|
-
withResponseContext;
|
|
730
|
-
/**
|
|
731
|
-
* Preferred case style for this resource's output keys.
|
|
732
|
-
* Set on a subclass to override the global default.
|
|
733
|
-
*/
|
|
734
|
-
static preferredCase;
|
|
735
|
-
/**
|
|
736
|
-
* Response structure override for this generic resource class.
|
|
737
|
-
*/
|
|
738
|
-
static responseStructure;
|
|
739
|
-
called = {};
|
|
740
|
-
constructor(rsc, res) {
|
|
741
|
-
this.res = res;
|
|
742
|
-
this.resource = rsc;
|
|
743
|
-
/**
|
|
744
|
-
* Copy properties from rsc to this instance for easy
|
|
745
|
-
* access, but only if data is not an array
|
|
746
|
-
*/
|
|
747
|
-
if (!Array.isArray(this.resource.data ?? this.resource)) {
|
|
748
|
-
for (const key of Object.keys(this.resource.data ?? this.resource)) if (!(key in this)) Object.defineProperty(this, key, {
|
|
749
|
-
enumerable: true,
|
|
750
|
-
configurable: true,
|
|
751
|
-
get: () => {
|
|
752
|
-
return this.resource.data?.[key] ?? this.resource[key];
|
|
753
|
-
},
|
|
754
|
-
set: (value) => {
|
|
755
|
-
if (this.resource.data && this.resource.data[key]) this.resource.data[key] = value;
|
|
756
|
-
else this.resource[key] = value;
|
|
757
|
-
}
|
|
758
|
-
});
|
|
759
|
-
}
|
|
760
|
-
}
|
|
761
|
-
/**
|
|
762
|
-
* Get the original resource data
|
|
763
|
-
*/
|
|
764
|
-
data() {
|
|
765
|
-
return this.resource;
|
|
766
|
-
}
|
|
767
|
-
/**
|
|
768
|
-
* Get the current serialized output body.
|
|
769
|
-
*/
|
|
770
|
-
getBody() {
|
|
771
|
-
this.json();
|
|
772
|
-
return this.body;
|
|
773
|
-
}
|
|
774
|
-
/**
|
|
775
|
-
* Replace the current serialized output body.
|
|
776
|
-
*/
|
|
777
|
-
setBody(body) {
|
|
778
|
-
this.body = body;
|
|
779
|
-
return this;
|
|
780
|
-
}
|
|
781
|
-
/**
|
|
782
|
-
* Conditionally include a value in serialized output.
|
|
783
|
-
*/
|
|
784
|
-
when(condition, value) {
|
|
785
|
-
return resolveWhen(condition, value);
|
|
786
|
-
}
|
|
787
|
-
/**
|
|
788
|
-
* Include a value only when it is not null/undefined.
|
|
789
|
-
*/
|
|
790
|
-
whenNotNull(value) {
|
|
791
|
-
return resolveWhenNotNull(value);
|
|
792
|
-
}
|
|
793
|
-
/**
|
|
794
|
-
* Conditionally merge object attributes into serialized output.
|
|
795
|
-
*/
|
|
796
|
-
mergeWhen(condition, value) {
|
|
797
|
-
return resolveMergeWhen(condition, value);
|
|
798
|
-
}
|
|
799
|
-
resolveResponseStructure() {
|
|
800
|
-
const local = this.constructor.responseStructure;
|
|
801
|
-
const collectsLocal = this.collects?.responseStructure;
|
|
802
|
-
const global = getGlobalResponseStructure();
|
|
803
|
-
return {
|
|
804
|
-
wrap: local?.wrap ?? collectsLocal?.wrap ?? global?.wrap ?? true,
|
|
805
|
-
rootKey: local?.rootKey ?? collectsLocal?.rootKey ?? global?.rootKey ?? "data",
|
|
806
|
-
factory: local?.factory ?? collectsLocal?.factory ?? global?.factory
|
|
807
|
-
};
|
|
808
|
-
}
|
|
809
|
-
getPayloadKey() {
|
|
810
|
-
const { wrap, rootKey, factory } = this.resolveResponseStructure();
|
|
811
|
-
return factory || !wrap ? void 0 : rootKey;
|
|
812
|
-
}
|
|
813
|
-
/**
|
|
814
|
-
* Convert resource to JSON response format
|
|
815
|
-
*
|
|
816
|
-
* @returns
|
|
817
|
-
*/
|
|
818
|
-
json() {
|
|
819
|
-
if (!this.called.json) {
|
|
820
|
-
this.called.json = true;
|
|
821
|
-
const resource = this.data();
|
|
822
|
-
let data = Array.isArray(resource) ? [...resource] : { ...resource };
|
|
823
|
-
if (Array.isArray(data) && this.collects) {
|
|
824
|
-
data = data.map((item) => new this.collects(item).data());
|
|
825
|
-
this.resource = data;
|
|
826
|
-
}
|
|
827
|
-
if (typeof data.data !== "undefined") data = data.data;
|
|
828
|
-
data = sanitizeConditionalAttributes(data);
|
|
829
|
-
const paginationExtras = buildPaginationExtras(this.resource);
|
|
830
|
-
const { metaKey } = getPaginationExtraKeys();
|
|
831
|
-
const configuredMeta = metaKey ? paginationExtras[metaKey] : void 0;
|
|
832
|
-
if (metaKey) delete paginationExtras[metaKey];
|
|
833
|
-
const caseStyle = this.constructor.preferredCase ?? getGlobalCase();
|
|
834
|
-
if (caseStyle) {
|
|
835
|
-
const transformer = getCaseTransformer(caseStyle);
|
|
836
|
-
data = transformKeys(data, transformer);
|
|
837
|
-
}
|
|
838
|
-
const customMeta = mergeMetadata(resolveWithHookMetadata(this, GenericResource.prototype.with), this.additionalMeta);
|
|
839
|
-
const { wrap, rootKey, factory } = this.resolveResponseStructure();
|
|
840
|
-
this.body = buildResponseEnvelope({
|
|
841
|
-
payload: data,
|
|
842
|
-
meta: configuredMeta,
|
|
843
|
-
metaKey,
|
|
844
|
-
wrap,
|
|
845
|
-
rootKey,
|
|
846
|
-
factory,
|
|
847
|
-
context: {
|
|
848
|
-
type: "generic",
|
|
849
|
-
resource: this.resource
|
|
850
|
-
}
|
|
851
|
-
});
|
|
852
|
-
this.body = appendRootProperties(this.body, {
|
|
853
|
-
...paginationExtras,
|
|
854
|
-
...customMeta || {}
|
|
855
|
-
}, rootKey);
|
|
856
|
-
}
|
|
857
|
-
return this;
|
|
858
|
-
}
|
|
859
|
-
/**
|
|
860
|
-
* Append structured metadata to the response body.
|
|
861
|
-
*
|
|
862
|
-
* @param meta Metadata object or metadata factory
|
|
863
|
-
* @returns
|
|
864
|
-
*/
|
|
865
|
-
with(meta) {
|
|
866
|
-
this.called.with = true;
|
|
867
|
-
if (typeof meta === "undefined") return this.additionalMeta || {};
|
|
868
|
-
const resolvedMeta = typeof meta === "function" ? meta(this.resource) : meta;
|
|
869
|
-
this.additionalMeta = mergeMetadata(this.additionalMeta, resolvedMeta);
|
|
870
|
-
if (this.called.json) {
|
|
871
|
-
const { rootKey } = this.resolveResponseStructure();
|
|
872
|
-
this.body = appendRootProperties(this.body, resolvedMeta, rootKey);
|
|
873
|
-
}
|
|
874
|
-
return this;
|
|
875
|
-
}
|
|
876
|
-
/**
|
|
877
|
-
* Typed fluent metadata helper.
|
|
878
|
-
*
|
|
879
|
-
* @param meta Metadata object or metadata factory
|
|
880
|
-
* @returns
|
|
881
|
-
*/
|
|
882
|
-
withMeta(meta) {
|
|
883
|
-
this.with(meta);
|
|
884
|
-
return this;
|
|
885
|
-
}
|
|
886
|
-
/**
|
|
887
|
-
* Convert resource to array format (for collections)
|
|
888
|
-
*
|
|
889
|
-
* @returns
|
|
890
|
-
*/
|
|
891
|
-
toArray() {
|
|
892
|
-
this.called.toArray = true;
|
|
893
|
-
this.json();
|
|
894
|
-
let data = Array.isArray(this.resource) ? [...this.resource] : { ...this.resource };
|
|
895
|
-
if (typeof data.data !== "undefined") data = data.data;
|
|
896
|
-
return data;
|
|
897
|
-
}
|
|
898
|
-
/**
|
|
899
|
-
* Add additional properties to the response body
|
|
900
|
-
*
|
|
901
|
-
* @param extra Additional properties to merge into the response body
|
|
902
|
-
* @returns
|
|
903
|
-
*/
|
|
904
|
-
additional(extra) {
|
|
905
|
-
this.called.additional = true;
|
|
906
|
-
this.json();
|
|
907
|
-
const extraData = extra.data;
|
|
908
|
-
delete extra.data;
|
|
909
|
-
delete extra.pagination;
|
|
910
|
-
const payloadKey = this.getPayloadKey();
|
|
911
|
-
if (extraData && payloadKey && typeof this.body[payloadKey] !== "undefined") this.body[payloadKey] = Array.isArray(this.body[payloadKey]) ? [...this.body[payloadKey], ...extraData] : {
|
|
912
|
-
...this.body[payloadKey],
|
|
913
|
-
...extraData
|
|
914
|
-
};
|
|
915
|
-
this.body = {
|
|
916
|
-
...this.body,
|
|
917
|
-
...extra
|
|
918
|
-
};
|
|
919
|
-
return this;
|
|
920
|
-
}
|
|
921
|
-
response(res) {
|
|
922
|
-
this.called.toResponse = true;
|
|
923
|
-
this.json();
|
|
924
|
-
const rawResponse = res ?? this.res;
|
|
925
|
-
const response = new ServerResponse(rawResponse, this.body);
|
|
926
|
-
this.withResponseContext = {
|
|
927
|
-
response,
|
|
928
|
-
raw: rawResponse
|
|
929
|
-
};
|
|
930
|
-
this.called.withResponse = true;
|
|
931
|
-
this.withResponse(response, rawResponse);
|
|
932
|
-
return response;
|
|
933
|
-
}
|
|
934
|
-
/**
|
|
935
|
-
* Customize the outgoing transport response right before dispatch.
|
|
936
|
-
*
|
|
937
|
-
* Override in custom classes to mutate headers/status/body.
|
|
938
|
-
*/
|
|
939
|
-
withResponse(_response, _rawResponse) {
|
|
940
|
-
return this;
|
|
941
|
-
}
|
|
942
|
-
/**
|
|
943
|
-
* Promise-like then method to allow chaining with async/await or .then() syntax
|
|
944
|
-
*
|
|
945
|
-
* @param onfulfilled Callback to handle the fulfilled state of the promise, receiving the response body
|
|
946
|
-
* @param onrejected Callback to handle the rejected state of the promise, receiving the error reason
|
|
947
|
-
* @returns A promise that resolves to the result of the onfulfilled or onrejected callback
|
|
948
|
-
*/
|
|
949
|
-
then(onfulfilled, onrejected) {
|
|
950
|
-
this.called.then = true;
|
|
951
|
-
this.json();
|
|
952
|
-
if (this.res) {
|
|
953
|
-
const response = new ServerResponse(this.res, this.body);
|
|
954
|
-
this.withResponseContext = {
|
|
955
|
-
response,
|
|
956
|
-
raw: this.res
|
|
957
|
-
};
|
|
958
|
-
this.called.withResponse = true;
|
|
959
|
-
this.withResponse(response, this.res);
|
|
960
|
-
} else {
|
|
961
|
-
this.called.withResponse = true;
|
|
962
|
-
this.withResponse();
|
|
963
|
-
}
|
|
964
|
-
const resolved = Promise.resolve(this.body).then(onfulfilled, onrejected);
|
|
965
|
-
if (this.res) this.res.send(this.body);
|
|
966
|
-
return resolved;
|
|
967
|
-
}
|
|
968
|
-
};
|
|
969
|
-
|
|
970
|
-
//#endregion
|
|
971
|
-
//#region src/ResourceCollection.ts
|
|
972
|
-
/**
|
|
973
|
-
* ResourceCollection class to handle API resource transformation and response building for collections
|
|
974
|
-
*/
|
|
975
|
-
var ResourceCollection = class ResourceCollection {
|
|
976
|
-
body = { data: [] };
|
|
977
|
-
resource;
|
|
978
|
-
collects;
|
|
979
|
-
additionalMeta;
|
|
980
|
-
withResponseContext;
|
|
981
|
-
/**
|
|
982
|
-
* Preferred case style for this collection's output keys.
|
|
983
|
-
* Set on a subclass to override the global default.
|
|
984
|
-
*/
|
|
985
|
-
static preferredCase;
|
|
986
|
-
/**
|
|
987
|
-
* Response structure override for this collection class.
|
|
988
|
-
*/
|
|
989
|
-
static responseStructure;
|
|
990
|
-
called = {};
|
|
991
|
-
constructor(rsc, res) {
|
|
992
|
-
this.res = res;
|
|
993
|
-
this.resource = rsc;
|
|
994
|
-
}
|
|
995
|
-
/**
|
|
996
|
-
* Get the original resource data
|
|
997
|
-
*/
|
|
998
|
-
data() {
|
|
999
|
-
return this.toArray();
|
|
1000
|
-
}
|
|
1001
|
-
/**
|
|
1002
|
-
* Get the current serialized output body.
|
|
1003
|
-
*/
|
|
1004
|
-
getBody() {
|
|
1005
|
-
this.json();
|
|
1006
|
-
return this.body;
|
|
1007
|
-
}
|
|
1008
|
-
/**
|
|
1009
|
-
* Replace the current serialized output body.
|
|
1010
|
-
*/
|
|
1011
|
-
setBody(body) {
|
|
1012
|
-
this.body = body;
|
|
1013
|
-
return this;
|
|
1014
|
-
}
|
|
1015
|
-
/**
|
|
1016
|
-
* Conditionally include a value in serialized output.
|
|
1017
|
-
*/
|
|
1018
|
-
when(condition, value) {
|
|
1019
|
-
return resolveWhen(condition, value);
|
|
1020
|
-
}
|
|
1021
|
-
/**
|
|
1022
|
-
* Include a value only when it is not null/undefined.
|
|
1023
|
-
*/
|
|
1024
|
-
whenNotNull(value) {
|
|
1025
|
-
return resolveWhenNotNull(value);
|
|
1026
|
-
}
|
|
1027
|
-
/**
|
|
1028
|
-
* Conditionally merge object attributes into serialized output.
|
|
1029
|
-
*/
|
|
1030
|
-
mergeWhen(condition, value) {
|
|
1031
|
-
return resolveMergeWhen(condition, value);
|
|
1032
|
-
}
|
|
1033
|
-
resolveResponseStructure() {
|
|
1034
|
-
const local = this.constructor.responseStructure;
|
|
1035
|
-
const collectsLocal = this.collects?.responseStructure;
|
|
1036
|
-
const global = getGlobalResponseStructure();
|
|
1037
|
-
return {
|
|
1038
|
-
wrap: local?.wrap ?? collectsLocal?.wrap ?? global?.wrap ?? true,
|
|
1039
|
-
rootKey: local?.rootKey ?? collectsLocal?.rootKey ?? global?.rootKey ?? "data",
|
|
1040
|
-
factory: local?.factory ?? collectsLocal?.factory ?? global?.factory
|
|
1041
|
-
};
|
|
1042
|
-
}
|
|
1043
|
-
getPayloadKey() {
|
|
1044
|
-
const { wrap, rootKey, factory } = this.resolveResponseStructure();
|
|
1045
|
-
return factory || !wrap ? void 0 : rootKey;
|
|
1046
|
-
}
|
|
1047
|
-
/**
|
|
1048
|
-
* Convert resource to JSON response format
|
|
1049
|
-
*
|
|
1050
|
-
* @returns
|
|
1051
|
-
*/
|
|
1052
|
-
json() {
|
|
1053
|
-
if (!this.called.json) {
|
|
1054
|
-
this.called.json = true;
|
|
1055
|
-
let data = this.data();
|
|
1056
|
-
if (this.collects) data = data.map((item) => new this.collects(item).data());
|
|
1057
|
-
data = sanitizeConditionalAttributes(data);
|
|
1058
|
-
const paginationExtras = !Array.isArray(this.resource) ? buildPaginationExtras(this.resource) : {};
|
|
1059
|
-
const { metaKey } = getPaginationExtraKeys();
|
|
1060
|
-
const configuredMeta = metaKey ? paginationExtras[metaKey] : void 0;
|
|
1061
|
-
if (metaKey) delete paginationExtras[metaKey];
|
|
1062
|
-
const caseStyle = this.constructor.preferredCase ?? this.collects?.preferredCase ?? getGlobalCase();
|
|
1063
|
-
if (caseStyle) {
|
|
1064
|
-
const transformer = getCaseTransformer(caseStyle);
|
|
1065
|
-
data = transformKeys(data, transformer);
|
|
1066
|
-
}
|
|
1067
|
-
const customMeta = mergeMetadata(resolveWithHookMetadata(this, ResourceCollection.prototype.with), this.additionalMeta);
|
|
1068
|
-
const { wrap, rootKey, factory } = this.resolveResponseStructure();
|
|
1069
|
-
this.body = buildResponseEnvelope({
|
|
1070
|
-
payload: data,
|
|
1071
|
-
meta: configuredMeta,
|
|
1072
|
-
metaKey,
|
|
1073
|
-
wrap,
|
|
1074
|
-
rootKey,
|
|
1075
|
-
factory,
|
|
1076
|
-
context: {
|
|
1077
|
-
type: "collection",
|
|
1078
|
-
resource: this.resource
|
|
1079
|
-
}
|
|
1080
|
-
});
|
|
1081
|
-
this.body = appendRootProperties(this.body, {
|
|
1082
|
-
...paginationExtras,
|
|
1083
|
-
...customMeta || {}
|
|
1084
|
-
}, rootKey);
|
|
1085
|
-
}
|
|
1086
|
-
return this;
|
|
1087
|
-
}
|
|
1088
|
-
/**
|
|
1089
|
-
* Append structured metadata to the response body.
|
|
1090
|
-
*
|
|
1091
|
-
* @param meta Metadata object or metadata factory
|
|
1092
|
-
* @returns
|
|
1093
|
-
*/
|
|
1094
|
-
with(meta) {
|
|
1095
|
-
this.called.with = true;
|
|
1096
|
-
if (typeof meta === "undefined") return this.additionalMeta || {};
|
|
1097
|
-
const resolvedMeta = typeof meta === "function" ? meta(this.resource) : meta;
|
|
1098
|
-
this.additionalMeta = mergeMetadata(this.additionalMeta, resolvedMeta);
|
|
1099
|
-
if (this.called.json) {
|
|
1100
|
-
const { rootKey } = this.resolveResponseStructure();
|
|
1101
|
-
this.body = appendRootProperties(this.body, resolvedMeta, rootKey);
|
|
1102
|
-
}
|
|
1103
|
-
return this;
|
|
1104
|
-
}
|
|
1105
|
-
/**
|
|
1106
|
-
* Typed fluent metadata helper.
|
|
1107
|
-
*
|
|
1108
|
-
* @param meta Metadata object or metadata factory
|
|
1109
|
-
* @returns
|
|
1110
|
-
*/
|
|
1111
|
-
withMeta(meta) {
|
|
1112
|
-
this.with(meta);
|
|
1113
|
-
return this;
|
|
1114
|
-
}
|
|
1115
|
-
/**
|
|
1116
|
-
* Flatten resource to return original data
|
|
1117
|
-
*
|
|
1118
|
-
* @returns
|
|
1119
|
-
*/
|
|
1120
|
-
toArray() {
|
|
1121
|
-
this.called.toArray = true;
|
|
1122
|
-
this.json();
|
|
1123
|
-
return Array.isArray(this.resource) ? [...this.resource] : [...this.resource.data];
|
|
1124
|
-
}
|
|
1125
|
-
/**
|
|
1126
|
-
* Add additional properties to the response body
|
|
1127
|
-
*
|
|
1128
|
-
* @param extra Additional properties to merge into the response body
|
|
1129
|
-
* @returns
|
|
1130
|
-
*/
|
|
1131
|
-
additional(extra) {
|
|
1132
|
-
this.called.additional = true;
|
|
1133
|
-
this.json();
|
|
1134
|
-
delete extra.cursor;
|
|
1135
|
-
delete extra.pagination;
|
|
1136
|
-
const payloadKey = this.getPayloadKey();
|
|
1137
|
-
if (extra.data && payloadKey && Array.isArray(this.body[payloadKey])) this.body[payloadKey] = [...this.body[payloadKey], ...extra.data];
|
|
1138
|
-
this.body = {
|
|
1139
|
-
...this.body,
|
|
1140
|
-
...extra
|
|
1141
|
-
};
|
|
1142
|
-
return this;
|
|
1143
|
-
}
|
|
1144
|
-
response(res) {
|
|
1145
|
-
this.called.toResponse = true;
|
|
1146
|
-
this.json();
|
|
1147
|
-
const rawResponse = res ?? this.res;
|
|
1148
|
-
const response = new ServerResponse(rawResponse, this.body);
|
|
1149
|
-
this.withResponseContext = {
|
|
1150
|
-
response,
|
|
1151
|
-
raw: rawResponse
|
|
1152
|
-
};
|
|
1153
|
-
this.called.withResponse = true;
|
|
1154
|
-
this.withResponse(response, rawResponse);
|
|
1155
|
-
return response;
|
|
1156
|
-
}
|
|
1157
|
-
/**
|
|
1158
|
-
* Customize the outgoing transport response right before dispatch.
|
|
1159
|
-
*
|
|
1160
|
-
* Override in custom classes to mutate headers/status/body.
|
|
1161
|
-
*/
|
|
1162
|
-
withResponse(_response, _rawResponse) {
|
|
1163
|
-
return this;
|
|
1164
|
-
}
|
|
1165
|
-
setCollects(collects) {
|
|
1166
|
-
this.collects = collects;
|
|
1167
|
-
return this;
|
|
1168
|
-
}
|
|
1169
|
-
/**
|
|
1170
|
-
* Promise-like then method to allow chaining with async/await or .then() syntax
|
|
1171
|
-
*
|
|
1172
|
-
* @param onfulfilled Callback to handle the fulfilled state of the promise, receiving the response body
|
|
1173
|
-
* @param onrejected Callback to handle the rejected state of the promise, receiving the error reason
|
|
1174
|
-
* @returns A promise that resolves to the result of the onfulfilled or onrejected callback
|
|
1175
|
-
*/
|
|
1176
|
-
then(onfulfilled, onrejected) {
|
|
1177
|
-
this.called.then = true;
|
|
1178
|
-
this.json();
|
|
1179
|
-
if (this.res) {
|
|
1180
|
-
const response = new ServerResponse(this.res, this.body);
|
|
1181
|
-
this.withResponseContext = {
|
|
1182
|
-
response,
|
|
1183
|
-
raw: this.res
|
|
1184
|
-
};
|
|
1185
|
-
this.called.withResponse = true;
|
|
1186
|
-
this.withResponse(response, this.res);
|
|
1187
|
-
} else {
|
|
1188
|
-
this.called.withResponse = true;
|
|
1189
|
-
this.withResponse();
|
|
1190
|
-
}
|
|
1191
|
-
const resolved = Promise.resolve(this.body).then(onfulfilled, onrejected);
|
|
1192
|
-
if (this.res) this.res.send(this.body);
|
|
1193
|
-
return resolved;
|
|
1194
|
-
}
|
|
1195
|
-
/**
|
|
1196
|
-
* Promise-like catch method to handle rejected state of the promise
|
|
1197
|
-
*
|
|
1198
|
-
* @param onrejected
|
|
1199
|
-
* @returns
|
|
1200
|
-
*/
|
|
1201
|
-
catch(onrejected) {
|
|
1202
|
-
return this.then(void 0, onrejected);
|
|
1203
|
-
}
|
|
1204
|
-
/**
|
|
1205
|
-
* Promise-like finally method to handle cleanup after promise is settled
|
|
1206
|
-
*
|
|
1207
|
-
* @param onfinally
|
|
1208
|
-
* @returns
|
|
1209
|
-
*/
|
|
1210
|
-
finally(onfinally) {
|
|
1211
|
-
return this.then(onfinally, onfinally);
|
|
1212
|
-
}
|
|
1213
|
-
};
|
|
1214
|
-
|
|
1215
|
-
//#endregion
|
|
1216
|
-
//#region src/Resource.ts
|
|
1217
|
-
/**
|
|
1218
|
-
* Resource class to handle API resource transformation and response building
|
|
1219
|
-
*/
|
|
1220
|
-
var Resource = class Resource {
|
|
1221
|
-
body = { data: {} };
|
|
1222
|
-
resource;
|
|
1223
|
-
additionalMeta;
|
|
1224
|
-
withResponseContext;
|
|
1225
|
-
/**
|
|
1226
|
-
* Preferred case style for this resource's output keys.
|
|
1227
|
-
* Set on a subclass to override the global default.
|
|
1228
|
-
*/
|
|
1229
|
-
static preferredCase;
|
|
1230
|
-
/**
|
|
1231
|
-
* Response structure override for this resource class.
|
|
1232
|
-
*/
|
|
1233
|
-
static responseStructure;
|
|
1234
|
-
called = {};
|
|
1235
|
-
constructor(rsc, res) {
|
|
1236
|
-
this.res = res;
|
|
1237
|
-
this.resource = rsc;
|
|
1238
|
-
/**
|
|
1239
|
-
* Copy properties from rsc to this instance for easy
|
|
1240
|
-
* access, but only if data is not an array
|
|
1241
|
-
*/
|
|
1242
|
-
if (!Array.isArray(this.resource.data ?? this.resource)) {
|
|
1243
|
-
for (const key of Object.keys(this.resource.data ?? this.resource)) if (!(key in this)) Object.defineProperty(this, key, {
|
|
1244
|
-
enumerable: true,
|
|
1245
|
-
configurable: true,
|
|
1246
|
-
get: () => {
|
|
1247
|
-
return this.resource.data?.[key] ?? this.resource[key];
|
|
1248
|
-
},
|
|
1249
|
-
set: (value) => {
|
|
1250
|
-
if (this.resource.data && this.resource.data[key]) this.resource.data[key] = value;
|
|
1251
|
-
else this.resource[key] = value;
|
|
1252
|
-
}
|
|
1253
|
-
});
|
|
1254
|
-
}
|
|
1255
|
-
}
|
|
1256
|
-
/**
|
|
1257
|
-
* Create a ResourceCollection from an array of resource data or a Collectible instance
|
|
1258
|
-
*
|
|
1259
|
-
* @param data
|
|
1260
|
-
* @returns
|
|
1261
|
-
*/
|
|
1262
|
-
static collection(data) {
|
|
1263
|
-
return new ResourceCollection(data).setCollects(this);
|
|
1264
|
-
}
|
|
1265
|
-
/**
|
|
1266
|
-
* Get the original resource data
|
|
1267
|
-
*/
|
|
1268
|
-
data() {
|
|
1269
|
-
return this.toArray();
|
|
1270
|
-
}
|
|
1271
|
-
/**
|
|
1272
|
-
* Get the current serialized output body.
|
|
1273
|
-
*/
|
|
1274
|
-
getBody() {
|
|
1275
|
-
this.json();
|
|
1276
|
-
return this.body;
|
|
1277
|
-
}
|
|
1278
|
-
/**
|
|
1279
|
-
* Replace the current serialized output body.
|
|
1280
|
-
*/
|
|
1281
|
-
setBody(body) {
|
|
1282
|
-
this.body = body;
|
|
1283
|
-
return this;
|
|
1284
|
-
}
|
|
1285
|
-
/**
|
|
1286
|
-
* Conditionally include a value in serialized output.
|
|
1287
|
-
*/
|
|
1288
|
-
when(condition, value) {
|
|
1289
|
-
return resolveWhen(condition, value);
|
|
1290
|
-
}
|
|
1291
|
-
/**
|
|
1292
|
-
* Include a value only when it is not null/undefined.
|
|
1293
|
-
*/
|
|
1294
|
-
whenNotNull(value) {
|
|
1295
|
-
return resolveWhenNotNull(value);
|
|
1296
|
-
}
|
|
1297
|
-
/**
|
|
1298
|
-
* Conditionally merge object attributes into serialized output.
|
|
1299
|
-
*/
|
|
1300
|
-
mergeWhen(condition, value) {
|
|
1301
|
-
return resolveMergeWhen(condition, value);
|
|
1302
|
-
}
|
|
1303
|
-
resolveResponseStructure() {
|
|
1304
|
-
const local = this.constructor.responseStructure;
|
|
1305
|
-
const global = getGlobalResponseStructure();
|
|
1306
|
-
return {
|
|
1307
|
-
wrap: local?.wrap ?? global?.wrap ?? true,
|
|
1308
|
-
rootKey: local?.rootKey ?? global?.rootKey ?? "data",
|
|
1309
|
-
factory: local?.factory ?? global?.factory
|
|
1310
|
-
};
|
|
1311
|
-
}
|
|
1312
|
-
getPayloadKey() {
|
|
1313
|
-
const { wrap, rootKey, factory } = this.resolveResponseStructure();
|
|
1314
|
-
return factory || !wrap ? void 0 : rootKey;
|
|
1315
|
-
}
|
|
1316
|
-
/**
|
|
1317
|
-
* Convert resource to JSON response format
|
|
1318
|
-
*
|
|
1319
|
-
* @returns
|
|
1320
|
-
*/
|
|
1321
|
-
json() {
|
|
1322
|
-
if (!this.called.json) {
|
|
1323
|
-
this.called.json = true;
|
|
1324
|
-
const resource = this.data();
|
|
1325
|
-
let data = Array.isArray(resource) ? [...resource] : { ...resource };
|
|
1326
|
-
if (typeof data.data !== "undefined") data = data.data;
|
|
1327
|
-
data = sanitizeConditionalAttributes(data);
|
|
1328
|
-
const caseStyle = this.constructor.preferredCase ?? getGlobalCase();
|
|
1329
|
-
if (caseStyle) {
|
|
1330
|
-
const transformer = getCaseTransformer(caseStyle);
|
|
1331
|
-
data = transformKeys(data, transformer);
|
|
1332
|
-
}
|
|
1333
|
-
const customMeta = mergeMetadata(resolveWithHookMetadata(this, Resource.prototype.with), this.additionalMeta);
|
|
1334
|
-
const { wrap, rootKey, factory } = this.resolveResponseStructure();
|
|
1335
|
-
this.body = buildResponseEnvelope({
|
|
1336
|
-
payload: data,
|
|
1337
|
-
wrap,
|
|
1338
|
-
rootKey,
|
|
1339
|
-
factory,
|
|
1340
|
-
context: {
|
|
1341
|
-
type: "resource",
|
|
1342
|
-
resource: this.resource
|
|
1343
|
-
}
|
|
1344
|
-
});
|
|
1345
|
-
this.body = appendRootProperties(this.body, customMeta, rootKey);
|
|
1346
|
-
}
|
|
1347
|
-
return this;
|
|
1348
|
-
}
|
|
1349
|
-
/**
|
|
1350
|
-
* Append structured metadata to the response body.
|
|
1351
|
-
*
|
|
1352
|
-
* @param meta Metadata object or metadata factory
|
|
1353
|
-
* @returns
|
|
1354
|
-
*/
|
|
1355
|
-
with(meta) {
|
|
1356
|
-
this.called.with = true;
|
|
1357
|
-
if (typeof meta === "undefined") return this.additionalMeta || {};
|
|
1358
|
-
const resolvedMeta = typeof meta === "function" ? meta(this.resource) : meta;
|
|
1359
|
-
this.additionalMeta = mergeMetadata(this.additionalMeta, resolvedMeta);
|
|
1360
|
-
if (this.called.json) {
|
|
1361
|
-
const { rootKey } = this.resolveResponseStructure();
|
|
1362
|
-
this.body = appendRootProperties(this.body, resolvedMeta, rootKey);
|
|
1363
|
-
}
|
|
1364
|
-
return this;
|
|
1365
|
-
}
|
|
1366
|
-
/**
|
|
1367
|
-
* Typed fluent metadata helper.
|
|
1368
|
-
*
|
|
1369
|
-
* @param meta Metadata object or metadata factory
|
|
1370
|
-
* @returns
|
|
1371
|
-
*/
|
|
1372
|
-
withMeta(meta) {
|
|
1373
|
-
this.with(meta);
|
|
1374
|
-
return this;
|
|
1375
|
-
}
|
|
1376
|
-
/**
|
|
1377
|
-
* Flatten resource to array format (for collections) or return original data for single resources
|
|
1378
|
-
*
|
|
1379
|
-
* @returns
|
|
1380
|
-
*/
|
|
1381
|
-
toArray() {
|
|
1382
|
-
this.called.toArray = true;
|
|
1383
|
-
this.json();
|
|
1384
|
-
let data = Array.isArray(this.resource) ? [...this.resource] : { ...this.resource };
|
|
1385
|
-
if (!Array.isArray(data) && typeof data.data !== "undefined") data = data.data;
|
|
1386
|
-
return data;
|
|
1387
|
-
}
|
|
1388
|
-
/**
|
|
1389
|
-
* Add additional properties to the response body
|
|
1390
|
-
*
|
|
1391
|
-
* @param extra Additional properties to merge into the response body
|
|
1392
|
-
* @returns
|
|
1393
|
-
*/
|
|
1394
|
-
additional(extra) {
|
|
1395
|
-
this.called.additional = true;
|
|
1396
|
-
this.json();
|
|
1397
|
-
const payloadKey = this.getPayloadKey();
|
|
1398
|
-
if (extra.data && payloadKey && typeof this.body[payloadKey] !== "undefined") this.body[payloadKey] = Array.isArray(this.body[payloadKey]) ? [...this.body[payloadKey], ...extra.data] : {
|
|
1399
|
-
...this.body[payloadKey],
|
|
1400
|
-
...extra.data
|
|
1401
|
-
};
|
|
1402
|
-
this.body = {
|
|
1403
|
-
...this.body,
|
|
1404
|
-
...extra
|
|
1405
|
-
};
|
|
1406
|
-
return this;
|
|
1407
|
-
}
|
|
1408
|
-
response(res) {
|
|
1409
|
-
this.called.toResponse = true;
|
|
1410
|
-
this.json();
|
|
1411
|
-
const rawResponse = res ?? this.res;
|
|
1412
|
-
const response = new ServerResponse(rawResponse, this.body);
|
|
1413
|
-
this.withResponseContext = {
|
|
1414
|
-
response,
|
|
1415
|
-
raw: rawResponse
|
|
1416
|
-
};
|
|
1417
|
-
this.called.withResponse = true;
|
|
1418
|
-
this.withResponse(response, rawResponse);
|
|
1419
|
-
return response;
|
|
1420
|
-
}
|
|
1421
|
-
/**
|
|
1422
|
-
* Customize the outgoing transport response right before dispatch.
|
|
1423
|
-
*
|
|
1424
|
-
* Override in custom classes to mutate headers/status/body.
|
|
1425
|
-
*/
|
|
1426
|
-
withResponse(_response, _rawResponse) {
|
|
1427
|
-
return this;
|
|
1428
|
-
}
|
|
1429
|
-
/**
|
|
1430
|
-
* Promise-like then method to allow chaining with async/await or .then() syntax
|
|
1431
|
-
*
|
|
1432
|
-
* @param onfulfilled Callback to handle the fulfilled state of the promise, receiving the response body
|
|
1433
|
-
* @param onrejected Callback to handle the rejected state of the promise, receiving the error reason
|
|
1434
|
-
* @returns A promise that resolves to the result of the onfulfilled or onrejected callback
|
|
1435
|
-
*/
|
|
1436
|
-
then(onfulfilled, onrejected) {
|
|
1437
|
-
this.called.then = true;
|
|
1438
|
-
this.json();
|
|
1439
|
-
if (this.res) {
|
|
1440
|
-
const response = new ServerResponse(this.res, this.body);
|
|
1441
|
-
this.withResponseContext = {
|
|
1442
|
-
response,
|
|
1443
|
-
raw: this.res
|
|
1444
|
-
};
|
|
1445
|
-
this.called.withResponse = true;
|
|
1446
|
-
this.withResponse(response, this.res);
|
|
1447
|
-
} else {
|
|
1448
|
-
this.called.withResponse = true;
|
|
1449
|
-
this.withResponse();
|
|
1450
|
-
}
|
|
1451
|
-
const resolved = Promise.resolve(this.body).then(onfulfilled, onrejected);
|
|
1452
|
-
if (this.res) this.res.send(this.body);
|
|
1453
|
-
return resolved;
|
|
1454
|
-
}
|
|
1455
|
-
/**
|
|
1456
|
-
* Promise-like catch method to handle rejected state of the promise
|
|
1457
|
-
*
|
|
1458
|
-
* @param onrejected
|
|
1459
|
-
* @returns
|
|
1460
|
-
*/
|
|
1461
|
-
catch(onrejected) {
|
|
1462
|
-
return this.then(void 0, onrejected);
|
|
1463
|
-
}
|
|
1464
|
-
/**
|
|
1465
|
-
* Promise-like finally method to handle cleanup after promise is settled
|
|
1466
|
-
*
|
|
1467
|
-
* @param onfinally
|
|
1468
|
-
* @returns
|
|
1469
|
-
*/
|
|
1470
|
-
finally(onfinally) {
|
|
1471
|
-
return this.then(onfinally, onfinally);
|
|
1472
|
-
}
|
|
1473
|
-
};
|
|
1474
|
-
|
|
1475
|
-
//#endregion
|
|
1476
|
-
exports.ApiResource = ApiResource;
|
|
1477
|
-
exports.CONDITIONAL_ATTRIBUTE_MISSING = CONDITIONAL_ATTRIBUTE_MISSING;
|
|
1478
|
-
exports.CliApp = CliApp;
|
|
1479
|
-
exports.GenericResource = GenericResource;
|
|
1480
|
-
exports.InitCommand = InitCommand;
|
|
1481
|
-
exports.MakeResource = MakeResource;
|
|
1482
|
-
exports.Resource = Resource;
|
|
1483
|
-
exports.ResourceCollection = ResourceCollection;
|
|
1484
|
-
exports.ServerResponse = ServerResponse;
|
|
1485
|
-
exports.appendRootProperties = appendRootProperties;
|
|
1486
|
-
exports.buildPaginationExtras = buildPaginationExtras;
|
|
1487
|
-
exports.buildResponseEnvelope = buildResponseEnvelope;
|
|
1488
|
-
exports.defineConfig = defineConfig;
|
|
1489
|
-
exports.getCaseTransformer = getCaseTransformer;
|
|
1490
|
-
exports.getDefaultConfig = getDefaultConfig;
|
|
1491
|
-
exports.getGlobalBaseUrl = getGlobalBaseUrl;
|
|
1492
|
-
exports.getGlobalCase = getGlobalCase;
|
|
1493
|
-
exports.getGlobalCursorMeta = getGlobalCursorMeta;
|
|
1494
|
-
exports.getGlobalPageName = getGlobalPageName;
|
|
1495
|
-
exports.getGlobalPaginatedExtras = getGlobalPaginatedExtras;
|
|
1496
|
-
exports.getGlobalPaginatedLinks = getGlobalPaginatedLinks;
|
|
1497
|
-
exports.getGlobalPaginatedMeta = getGlobalPaginatedMeta;
|
|
1498
|
-
exports.getGlobalResponseFactory = getGlobalResponseFactory;
|
|
1499
|
-
exports.getGlobalResponseRootKey = getGlobalResponseRootKey;
|
|
1500
|
-
exports.getGlobalResponseStructure = getGlobalResponseStructure;
|
|
1501
|
-
exports.getGlobalResponseWrap = getGlobalResponseWrap;
|
|
1502
|
-
exports.getPaginationExtraKeys = getPaginationExtraKeys;
|
|
1503
|
-
exports.isPlainObject = isPlainObject;
|
|
1504
|
-
exports.mergeMetadata = mergeMetadata;
|
|
1505
|
-
exports.resolveMergeWhen = resolveMergeWhen;
|
|
1506
|
-
exports.resolveWhen = resolveWhen;
|
|
1507
|
-
exports.resolveWhenNotNull = resolveWhenNotNull;
|
|
1508
|
-
exports.resolveWithHookMetadata = resolveWithHookMetadata;
|
|
1509
|
-
exports.sanitizeConditionalAttributes = sanitizeConditionalAttributes;
|
|
1510
|
-
exports.setGlobalBaseUrl = setGlobalBaseUrl;
|
|
1511
|
-
exports.setGlobalCase = setGlobalCase;
|
|
1512
|
-
exports.setGlobalCursorMeta = setGlobalCursorMeta;
|
|
1513
|
-
exports.setGlobalPageName = setGlobalPageName;
|
|
1514
|
-
exports.setGlobalPaginatedExtras = setGlobalPaginatedExtras;
|
|
1515
|
-
exports.setGlobalPaginatedLinks = setGlobalPaginatedLinks;
|
|
1516
|
-
exports.setGlobalPaginatedMeta = setGlobalPaginatedMeta;
|
|
1517
|
-
exports.setGlobalResponseFactory = setGlobalResponseFactory;
|
|
1518
|
-
exports.setGlobalResponseRootKey = setGlobalResponseRootKey;
|
|
1519
|
-
exports.setGlobalResponseStructure = setGlobalResponseStructure;
|
|
1520
|
-
exports.setGlobalResponseWrap = setGlobalResponseWrap;
|
|
1521
|
-
exports.splitWords = splitWords;
|
|
1522
|
-
exports.toCamelCase = toCamelCase;
|
|
1523
|
-
exports.toKebabCase = toKebabCase;
|
|
1524
|
-
exports.toPascalCase = toPascalCase;
|
|
1525
|
-
exports.toSnakeCase = toSnakeCase;
|
|
1526
|
-
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;
|