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.
Files changed (3) hide show
  1. package/dist/index.cjs +6 -1861
  2. package/dist/index.mjs +6 -1779
  3. package/package.json +1 -1
package/dist/index.cjs CHANGED
@@ -1,907 +1,10 @@
1
- Object.defineProperty(exports, Symbol.toStringTag, { value: 'Module' });
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 = ${collectsName}
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;