@danceroutine/tango-resources 1.11.0 → 1.11.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (56) hide show
  1. package/dist/chunk-D7D4PA-g.js +13 -0
  2. package/dist/index-D6sfTSEj.d.ts +902 -0
  3. package/dist/index.d.ts +12 -23
  4. package/dist/index.js +96 -152
  5. package/dist/index.js.map +1 -1
  6. package/dist/view/index.d.ts +2 -8
  7. package/dist/view/index.js +2 -3
  8. package/dist/{view-DUHg6AXl.js → view-C9B5Lln3.js} +151 -59
  9. package/dist/view-C9B5Lln3.js.map +1 -0
  10. package/package.json +7 -7
  11. package/dist/context/RequestContext.d.ts +0 -46
  12. package/dist/context/index.d.ts +0 -5
  13. package/dist/filters/FilterSet.d.ts +0 -95
  14. package/dist/filters/FilterType.d.ts +0 -2
  15. package/dist/filters/RangeOperator.d.ts +0 -2
  16. package/dist/filters/index.d.ts +0 -7
  17. package/dist/filters/inferModelFieldParsers.d.ts +0 -9
  18. package/dist/filters/internal/InternalFilterLookup.d.ts +0 -15
  19. package/dist/filters/internal/InternalFilterType.d.ts +0 -7
  20. package/dist/filters/internal/InternalRangeOperator.d.ts +0 -6
  21. package/dist/pagination/BasePaginator.d.ts +0 -4
  22. package/dist/pagination/CursorPaginationInput.d.ts +0 -7
  23. package/dist/pagination/OffsetPaginationInput.d.ts +0 -7
  24. package/dist/pagination/PaginatedResponse.d.ts +0 -12
  25. package/dist/pagination/Paginator.d.ts +0 -21
  26. package/dist/pagination/index.d.ts +0 -12
  27. package/dist/paginators/CursorPaginator.d.ts +0 -65
  28. package/dist/paginators/OffsetPaginator.d.ts +0 -74
  29. package/dist/paginators/index.d.ts +0 -5
  30. package/dist/resource/OpenAPIDescription.d.ts +0 -23
  31. package/dist/resource/ResourceModelLike.d.ts +0 -22
  32. package/dist/resource/index.d.ts +0 -5
  33. package/dist/serializer/ModelSerializer.d.ts +0 -67
  34. package/dist/serializer/Serializer.d.ts +0 -75
  35. package/dist/serializer/index.d.ts +0 -6
  36. package/dist/serializer/internal/InternalSerializerRelationKind.d.ts +0 -11
  37. package/dist/serializer/relation.d.ts +0 -45
  38. package/dist/view/APIView.d.ts +0 -26
  39. package/dist/view/GenericAPIView.d.ts +0 -60
  40. package/dist/view/generics/CreateAPIView.d.ts +0 -10
  41. package/dist/view/generics/ListAPIView.d.ts +0 -10
  42. package/dist/view/generics/ListCreateAPIView.d.ts +0 -11
  43. package/dist/view/generics/RetrieveAPIView.d.ts +0 -10
  44. package/dist/view/generics/RetrieveDestroyAPIView.d.ts +0 -11
  45. package/dist/view/generics/RetrieveUpdateAPIView.d.ts +0 -12
  46. package/dist/view/generics/RetrieveUpdateDestroyAPIView.d.ts +0 -13
  47. package/dist/view/generics/index.d.ts +0 -10
  48. package/dist/view/mixins/CreateModelMixin.d.ts +0 -11
  49. package/dist/view/mixins/DestroyModelMixin.d.ts +0 -11
  50. package/dist/view/mixins/ListModelMixin.d.ts +0 -11
  51. package/dist/view/mixins/RetrieveModelMixin.d.ts +0 -11
  52. package/dist/view/mixins/UpdateModelMixin.d.ts +0 -12
  53. package/dist/view/mixins/index.d.ts +0 -8
  54. package/dist/view-DUHg6AXl.js.map +0 -1
  55. package/dist/viewset/ModelViewSet.d.ts +0 -112
  56. package/dist/viewset/index.d.ts +0 -5
@@ -1,16 +1,66 @@
1
- import { HttpErrorFactory, NotFoundError, TangoResponse, TangoResponse as TangoResponse$1 } from "@danceroutine/tango-core";
1
+ import { t as __exportAll } from "./chunk-D7D4PA-g.js";
2
+ import { HttpErrorFactory, NotFoundError, TangoRequest, TangoResponse } from "@danceroutine/tango-core";
2
3
  import { Q, QueryResult } from "@danceroutine/tango-orm";
3
4
  import { z } from "zod";
4
-
5
- //#region rolldown:runtime
6
- var __defProp = Object.defineProperty;
7
- var __export = (target, all) => {
8
- for (var name in all) __defProp(target, name, {
9
- get: all[name],
10
- enumerable: true
11
- });
5
+ //#region src/context/RequestContext.ts
6
+ /**
7
+ * Normalized request context passed through the framework adapter into viewset methods.
8
+ * Generic over the user type so consumers can plug in their own auth infrastructure.
9
+ */
10
+ var RequestContext = class RequestContext {
11
+ request;
12
+ user;
13
+ params;
14
+ static BRAND = "tango.resources.request_context";
15
+ __tangoBrand = RequestContext.BRAND;
16
+ state = /* @__PURE__ */ new Map();
17
+ constructor(request, user = null, params = {}) {
18
+ this.request = request;
19
+ this.user = user;
20
+ this.params = params;
21
+ }
22
+ /**
23
+ * Narrow an unknown value to `RequestContext`.
24
+ */
25
+ static isRequestContext(value) {
26
+ return typeof value === "object" && value !== null && value.__tangoBrand === RequestContext.BRAND;
27
+ }
28
+ /**
29
+ * Construct a context with optional user payload.
30
+ */
31
+ static create(request, user) {
32
+ return new RequestContext(TangoRequest.isTangoRequest(request) ? request : new TangoRequest(request), user ?? null);
33
+ }
34
+ /**
35
+ * Store arbitrary per-request state for downstream middleware/handlers.
36
+ */
37
+ setState(key, value) {
38
+ this.state.set(key, value);
39
+ }
40
+ /**
41
+ * Retrieve previously stored request state.
42
+ */
43
+ getState(key) {
44
+ return this.state.get(key);
45
+ }
46
+ /**
47
+ * Check whether a state key has been set.
48
+ */
49
+ hasState(key) {
50
+ return this.state.has(key);
51
+ }
52
+ /**
53
+ * Clone the context, including route params and request-local state.
54
+ */
55
+ clone() {
56
+ const cloned = new RequestContext(this.request, this.user, { ...this.params });
57
+ cloned.state = new Map(this.state);
58
+ return cloned;
59
+ }
12
60
  };
13
-
61
+ //#endregion
62
+ //#region src/context/index.ts
63
+ var context_exports = /* @__PURE__ */ __exportAll({ RequestContext: () => RequestContext });
14
64
  //#endregion
15
65
  //#region src/pagination/BasePaginator.ts
16
66
  var BasePaginator = class {
@@ -19,7 +69,6 @@ var BasePaginator = class {
19
69
  return [...rows];
20
70
  }
21
71
  };
22
-
23
72
  //#endregion
24
73
  //#region src/pagination/OffsetPaginationInput.ts
25
74
  const OffsetPaginationInput = z.object({
@@ -27,10 +76,13 @@ const OffsetPaginationInput = z.object({
27
76
  offset: z.coerce.number().int().min(0).default(0),
28
77
  page: z.coerce.number().int().min(1).optional()
29
78
  });
30
-
31
79
  //#endregion
32
80
  //#region src/paginators/OffsetPaginator.ts
33
81
  var OffsetPage = class OffsetPage {
82
+ results;
83
+ pageNumber;
84
+ perPage;
85
+ totalCount;
34
86
  static BRAND = "tango.resources.offset_page";
35
87
  __tangoBrand = OffsetPage.BRAND;
36
88
  constructor(results, pageNumber, perPage, totalCount) {
@@ -44,7 +96,7 @@ var OffsetPage = class OffsetPage {
44
96
  }
45
97
  /** Whether a next page exists based on known total count. */
46
98
  hasNext() {
47
- if (this.totalCount === undefined) return false;
99
+ if (this.totalCount === void 0) return false;
48
100
  return this.endIndex() < this.totalCount;
49
101
  }
50
102
  /** Whether a previous page exists. */
@@ -68,7 +120,22 @@ var OffsetPage = class OffsetPage {
68
120
  return this.startIndex() + this.results.length;
69
121
  }
70
122
  };
123
+ /**
124
+ * Offset/limit paginator modelled after DRF's LimitOffsetPagination.
125
+ * Handles parsing limit/offset/page from URL query params and building
126
+ * the paginated response envelope with next/previous links.
127
+ *
128
+ * @example
129
+ * ```typescript
130
+ * const paginator = new OffsetPaginator(queryset);
131
+ * const { limit, offset } = paginator.parseParams(searchParams);
132
+ * const results = await queryset.limit(limit).offset(offset).fetchAll();
133
+ * const response = paginator.getPaginatedResponse(results, totalCount);
134
+ * ```
135
+ */
71
136
  var OffsetPaginator = class OffsetPaginator extends BasePaginator {
137
+ queryset;
138
+ perPage;
72
139
  static BRAND = "tango.resources.offset_paginator";
73
140
  __tangoBrand = OffsetPaginator.BRAND;
74
141
  limit = 25;
@@ -92,9 +159,9 @@ var OffsetPaginator = class OffsetPaginator extends BasePaginator {
92
159
  */
93
160
  parse(params) {
94
161
  const input = {
95
- limit: params.get("limit") ?? undefined,
96
- offset: params.get("offset") ?? undefined,
97
- page: params.get("page") ?? undefined
162
+ limit: params.get("limit") ?? void 0,
163
+ offset: params.get("offset") ?? void 0,
164
+ page: params.get("page") ?? void 0
98
165
  };
99
166
  const parsed = OffsetPaginationInput.parse(input);
100
167
  if (parsed.page) parsed.offset = (parsed.page - 1) * parsed.limit;
@@ -121,7 +188,7 @@ var OffsetPaginator = class OffsetPaginator extends BasePaginator {
121
188
  toResponse(results, context) {
122
189
  const totalCount = context?.totalCount;
123
190
  const response = { results: this.resolveQueryResultRows(results) };
124
- if (totalCount !== undefined) {
191
+ if (totalCount !== void 0) {
125
192
  response.count = totalCount;
126
193
  if (this.offset + this.limit < totalCount) response.next = this.buildPageLink(this.offset + this.limit, context?.params);
127
194
  if (this.offset > 0) {
@@ -176,49 +243,53 @@ var OffsetPaginator = class OffsetPaginator extends BasePaginator {
176
243
  }).toRelativeURL();
177
244
  }
178
245
  };
179
-
180
246
  //#endregion
181
247
  //#region src/filters/inferModelFieldParsers.ts
182
248
  function normalizeParserTokens(raw) {
183
- const tokens = Array.isArray(raw) ? raw : String(raw).split(",");
184
- const normalized = tokens.map((value) => value.trim());
249
+ const normalized = (Array.isArray(raw) ? raw : String(raw).split(",")).map((value) => value.trim());
185
250
  return normalized.every((value) => value.length > 0) ? normalized : [];
186
251
  }
187
252
  function createBooleanParser() {
188
253
  return (raw) => {
189
254
  const values = normalizeParserTokens(raw);
190
- if (values.length === 0) return undefined;
255
+ if (values.length === 0) return;
191
256
  const parsed = values.map((value) => {
192
257
  const normalized = value.toLowerCase();
193
258
  if (normalized === "true" || normalized === "1") return true;
194
259
  if (normalized === "false" || normalized === "0") return false;
195
260
  return null;
196
261
  });
197
- if (parsed.some((value) => value === null)) return undefined;
262
+ if (parsed.some((value) => value === null)) return;
198
263
  return parsed.length === 1 ? parsed[0] : parsed;
199
264
  };
200
265
  }
201
266
  function createIntegerParser() {
202
267
  return (raw) => {
203
268
  const values = normalizeParserTokens(raw);
204
- if (values.length === 0) return undefined;
269
+ if (values.length === 0) return;
205
270
  const parsed = values.map(Number);
206
- if (parsed.some((value) => !Number.isInteger(value))) return undefined;
271
+ if (parsed.some((value) => !Number.isInteger(value))) return;
207
272
  return parsed.length === 1 ? parsed[0] : parsed;
208
273
  };
209
274
  }
210
275
  function createTimestampParser() {
211
276
  return (raw) => {
212
277
  const values = normalizeParserTokens(raw);
213
- if (values.length === 0) return undefined;
278
+ if (values.length === 0) return;
214
279
  const parsed = values.map((value) => {
215
280
  const date = new Date(value);
216
281
  return Number.isNaN(date.getTime()) ? null : date;
217
282
  });
218
- if (parsed.some((value) => value === null)) return undefined;
283
+ if (parsed.some((value) => value === null)) return;
219
284
  return parsed.length === 1 ? parsed[0] : parsed;
220
285
  };
221
286
  }
287
+ /**
288
+ * Infer resource-level query-value parsers from Tango model metadata.
289
+ *
290
+ * Parsers are inferred conservatively from field metadata so HTTP query filters
291
+ * can be coerced into typed ORM inputs without framework-specific glue.
292
+ */
222
293
  function inferModelFieldParsers(model) {
223
294
  const metadata = model.metadata;
224
295
  if (!metadata) return {};
@@ -239,9 +310,11 @@ function inferModelFieldParsers(model) {
239
310
  }
240
311
  return parsers;
241
312
  }
242
-
243
313
  //#endregion
244
314
  //#region src/view/APIView.ts
315
+ /**
316
+ * Lightweight class-based request dispatcher for non-model API endpoints.
317
+ */
245
318
  var APIView = class APIView {
246
319
  static BRAND = "tango.resources.api_view";
247
320
  __tangoBrand = APIView.BRAND;
@@ -257,8 +330,7 @@ var APIView = class APIView {
257
330
  async dispatch(ctx) {
258
331
  const method = normalizeMethod(ctx.request.method);
259
332
  if (!method) return this.httpMethodNotAllowed();
260
- const handler = this.getMethodHandler(method);
261
- return handler(ctx);
333
+ return this.getMethodHandler(method)(ctx);
262
334
  }
263
335
  getAllowedMethods() {
264
336
  const allowed = [];
@@ -285,7 +357,7 @@ var APIView = class APIView {
285
357
  return Promise.resolve(this.httpMethodNotAllowed());
286
358
  }
287
359
  httpMethodNotAllowed() {
288
- return TangoResponse$1.methodNotAllowed(this.getAllowedMethods());
360
+ return TangoResponse.methodNotAllowed(this.getAllowedMethods());
289
361
  }
290
362
  getMethodHandler(method) {
291
363
  if (method === "GET") return (ctx) => this.get(ctx);
@@ -304,9 +376,11 @@ function normalizeMethod(method) {
304
376
  if (upper === "DELETE") return "DELETE";
305
377
  return null;
306
378
  }
307
-
308
379
  //#endregion
309
380
  //#region src/view/GenericAPIView.ts
381
+ /**
382
+ * Generic API base class that centralizes query/build/validation helpers.
383
+ */
310
384
  var GenericAPIView = class extends APIView {
311
385
  serializerClass;
312
386
  filters;
@@ -373,8 +447,7 @@ var GenericAPIView = class extends APIView {
373
447
  return this.lookupField ?? this.getManager().meta.pk;
374
448
  }
375
449
  getLookupValue(ctx) {
376
- const value = ctx.params[this.lookupParam]?.trim();
377
- return value || null;
450
+ return ctx.params[this.lookupParam]?.trim() || null;
378
451
  }
379
452
  getPaginator(queryset) {
380
453
  if (this.paginatorFactory) return this.paginatorFactory(queryset);
@@ -394,8 +467,7 @@ var GenericAPIView = class extends APIView {
394
467
  const search = params.getSearch();
395
468
  if (search && this.searchFields.length > 0) {
396
469
  const searchFilters = this.searchFields.map((field) => {
397
- const lookup = `${String(field)}__icontains`;
398
- return { [lookup]: search };
470
+ return { [`${String(field)}__icontains`]: search };
399
471
  });
400
472
  qs = qs.filter(Q.or(...searchFilters));
401
473
  }
@@ -407,12 +479,10 @@ var GenericAPIView = class extends APIView {
407
479
  });
408
480
  if (orderTokens.length > 0) qs = qs.orderBy(...orderTokens.map((token) => token));
409
481
  }
410
- const paginatedQueryset = paginator.apply(qs);
411
- const resultPromise = paginatedQueryset.fetch();
412
- const totalCountPromise = paginator.needsTotalCount() ? qs.count() : Promise.resolve(undefined);
482
+ const resultPromise = paginator.apply(qs).fetch();
483
+ const totalCountPromise = paginator.needsTotalCount() ? qs.count() : Promise.resolve(void 0);
413
484
  const [result, totalCount] = await Promise.all([resultPromise, totalCountPromise]);
414
- const serializer = this.getSerializer();
415
- const rows = await serializer.serializeMany(result.items);
485
+ const rows = await this.getSerializer().serializeMany(result.items);
416
486
  const response = paginator.toResponse(rows, { totalCount });
417
487
  return TangoResponse.json(response, { status: 200 });
418
488
  } catch (error) {
@@ -423,7 +493,7 @@ var GenericAPIView = class extends APIView {
423
493
  try {
424
494
  const body = await ctx.request.json();
425
495
  const result = await this.getSerializer().create(body);
426
- return TangoResponse.created(undefined, result);
496
+ return TangoResponse.created(void 0, result);
427
497
  } catch (error) {
428
498
  return this.handleError(error);
429
499
  }
@@ -477,9 +547,11 @@ var GenericAPIView = class extends APIView {
477
547
  return primaryKeyField.name;
478
548
  }
479
549
  };
480
-
481
550
  //#endregion
482
551
  //#region src/view/mixins/ListModelMixin.ts
552
+ /**
553
+ * Mixin that wires `GET` requests to the generic list implementation.
554
+ */
483
555
  var ListModelMixin = class extends GenericAPIView {
484
556
  list(ctx) {
485
557
  return this.performList(ctx);
@@ -488,9 +560,11 @@ var ListModelMixin = class extends GenericAPIView {
488
560
  return this.list(ctx);
489
561
  }
490
562
  };
491
-
492
563
  //#endregion
493
564
  //#region src/view/mixins/CreateModelMixin.ts
565
+ /**
566
+ * Mixin that wires `POST` requests to the generic create implementation.
567
+ */
494
568
  var CreateModelMixin = class extends GenericAPIView {
495
569
  create(ctx) {
496
570
  return this.performCreate(ctx);
@@ -499,9 +573,11 @@ var CreateModelMixin = class extends GenericAPIView {
499
573
  return this.create(ctx);
500
574
  }
501
575
  };
502
-
503
576
  //#endregion
504
577
  //#region src/view/mixins/RetrieveModelMixin.ts
578
+ /**
579
+ * Mixin that wires `GET` requests to the generic retrieve implementation.
580
+ */
505
581
  var RetrieveModelMixin = class extends GenericAPIView {
506
582
  retrieve(ctx) {
507
583
  return this.performRetrieve(ctx);
@@ -510,9 +586,11 @@ var RetrieveModelMixin = class extends GenericAPIView {
510
586
  return this.retrieve(ctx);
511
587
  }
512
588
  };
513
-
514
589
  //#endregion
515
590
  //#region src/view/mixins/UpdateModelMixin.ts
591
+ /**
592
+ * Mixin that wires `PUT` and `PATCH` requests to the generic update implementation.
593
+ */
516
594
  var UpdateModelMixin = class extends GenericAPIView {
517
595
  update(ctx) {
518
596
  return this.performUpdate(ctx);
@@ -524,9 +602,11 @@ var UpdateModelMixin = class extends GenericAPIView {
524
602
  return this.update(ctx);
525
603
  }
526
604
  };
527
-
528
605
  //#endregion
529
606
  //#region src/view/mixins/DestroyModelMixin.ts
607
+ /**
608
+ * Mixin that wires `DELETE` requests to the generic destroy implementation.
609
+ */
530
610
  var DestroyModelMixin = class extends GenericAPIView {
531
611
  destroy(ctx) {
532
612
  return this.performDestroy(ctx);
@@ -535,33 +615,41 @@ var DestroyModelMixin = class extends GenericAPIView {
535
615
  return this.destroy(ctx);
536
616
  }
537
617
  };
538
-
539
618
  //#endregion
540
619
  //#region src/view/generics/ListAPIView.ts
620
+ /**
621
+ * Generic API view for endpoints that only expose a list operation.
622
+ */
541
623
  var ListAPIView = class extends ListModelMixin {
542
624
  get(ctx) {
543
625
  return super.get(ctx);
544
626
  }
545
627
  };
546
-
547
628
  //#endregion
548
629
  //#region src/view/generics/CreateAPIView.ts
630
+ /**
631
+ * Generic API view for endpoints that only support resource creation.
632
+ */
549
633
  var CreateAPIView = class extends CreateModelMixin {
550
634
  post(ctx) {
551
635
  return super.post(ctx);
552
636
  }
553
637
  };
554
-
555
638
  //#endregion
556
639
  //#region src/view/generics/RetrieveAPIView.ts
640
+ /**
641
+ * Generic API view for endpoints that retrieve a single resource by lookup.
642
+ */
557
643
  var RetrieveAPIView = class extends RetrieveModelMixin {
558
644
  get(ctx) {
559
645
  return super.get(ctx);
560
646
  }
561
647
  };
562
-
563
648
  //#endregion
564
649
  //#region src/view/generics/ListCreateAPIView.ts
650
+ /**
651
+ * Generic API view for collection endpoints that list and create resources.
652
+ */
565
653
  var ListCreateAPIView = class extends GenericAPIView {
566
654
  get(ctx) {
567
655
  return this.performList(ctx);
@@ -570,9 +658,11 @@ var ListCreateAPIView = class extends GenericAPIView {
570
658
  return this.performCreate(ctx);
571
659
  }
572
660
  };
573
-
574
661
  //#endregion
575
662
  //#region src/view/generics/RetrieveUpdateAPIView.ts
663
+ /**
664
+ * Generic API view for endpoints that retrieve and update a single resource.
665
+ */
576
666
  var RetrieveUpdateAPIView = class extends GenericAPIView {
577
667
  get(ctx) {
578
668
  return this.performRetrieve(ctx);
@@ -584,9 +674,11 @@ var RetrieveUpdateAPIView = class extends GenericAPIView {
584
674
  return this.performUpdate(ctx);
585
675
  }
586
676
  };
587
-
588
677
  //#endregion
589
678
  //#region src/view/generics/RetrieveDestroyAPIView.ts
679
+ /**
680
+ * Generic API view for endpoints that retrieve and delete a single resource.
681
+ */
590
682
  var RetrieveDestroyAPIView = class extends GenericAPIView {
591
683
  get(ctx) {
592
684
  return this.performRetrieve(ctx);
@@ -595,9 +687,11 @@ var RetrieveDestroyAPIView = class extends GenericAPIView {
595
687
  return this.performDestroy(ctx);
596
688
  }
597
689
  };
598
-
599
690
  //#endregion
600
691
  //#region src/view/generics/RetrieveUpdateDestroyAPIView.ts
692
+ /**
693
+ * Generic API view for full detail endpoints that retrieve, update, and delete.
694
+ */
601
695
  var RetrieveUpdateDestroyAPIView = class extends GenericAPIView {
602
696
  get(ctx) {
603
697
  return this.performRetrieve(ctx);
@@ -612,11 +706,9 @@ var RetrieveUpdateDestroyAPIView = class extends GenericAPIView {
612
706
  return this.performDestroy(ctx);
613
707
  }
614
708
  };
615
-
616
709
  //#endregion
617
710
  //#region src/view/index.ts
618
- var view_exports = {};
619
- __export(view_exports, {
711
+ var view_exports = /* @__PURE__ */ __exportAll({
620
712
  APIView: () => APIView,
621
713
  CreateAPIView: () => CreateAPIView,
622
714
  CreateModelMixin: () => CreateModelMixin,
@@ -632,7 +724,7 @@ __export(view_exports, {
632
724
  RetrieveUpdateDestroyAPIView: () => RetrieveUpdateDestroyAPIView,
633
725
  UpdateModelMixin: () => UpdateModelMixin
634
726
  });
635
-
636
727
  //#endregion
637
- export { APIView, BasePaginator, CreateAPIView, CreateModelMixin, DestroyModelMixin, GenericAPIView, ListAPIView, ListCreateAPIView, ListModelMixin, OffsetPaginationInput, OffsetPaginator, RetrieveAPIView, RetrieveDestroyAPIView, RetrieveModelMixin, RetrieveUpdateAPIView, RetrieveUpdateDestroyAPIView, UpdateModelMixin, __export, inferModelFieldParsers, view_exports };
638
- //# sourceMappingURL=view-DUHg6AXl.js.map
728
+ export { OffsetPaginator as _, ListCreateAPIView as a, context_exports as b, ListAPIView as c, RetrieveModelMixin as d, CreateModelMixin as f, inferModelFieldParsers as g, APIView as h, RetrieveUpdateAPIView as i, DestroyModelMixin as l, GenericAPIView as m, RetrieveUpdateDestroyAPIView as n, RetrieveAPIView as o, ListModelMixin as p, RetrieveDestroyAPIView as r, CreateAPIView as s, view_exports as t, UpdateModelMixin as u, OffsetPaginationInput as v, RequestContext as x, BasePaginator as y };
729
+
730
+ //# sourceMappingURL=view-C9B5Lln3.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"view-C9B5Lln3.js","names":[],"sources":["../src/context/RequestContext.ts","../src/context/index.ts","../src/pagination/BasePaginator.ts","../src/pagination/OffsetPaginationInput.ts","../src/paginators/OffsetPaginator.ts","../src/filters/inferModelFieldParsers.ts","../src/view/APIView.ts","../src/view/GenericAPIView.ts","../src/view/mixins/ListModelMixin.ts","../src/view/mixins/CreateModelMixin.ts","../src/view/mixins/RetrieveModelMixin.ts","../src/view/mixins/UpdateModelMixin.ts","../src/view/mixins/DestroyModelMixin.ts","../src/view/generics/ListAPIView.ts","../src/view/generics/CreateAPIView.ts","../src/view/generics/RetrieveAPIView.ts","../src/view/generics/ListCreateAPIView.ts","../src/view/generics/RetrieveUpdateAPIView.ts","../src/view/generics/RetrieveDestroyAPIView.ts","../src/view/generics/RetrieveUpdateDestroyAPIView.ts","../src/view/index.ts"],"sourcesContent":["import { TangoRequest } from '@danceroutine/tango-core';\n\n/**\n * Default user shape for RequestContext.\n * Consumers can provide their own user type via the TUser generic parameter.\n */\nexport interface BaseUser {\n id: string | number;\n roles?: string[];\n}\n\n/**\n * Normalized request context passed through the framework adapter into viewset methods.\n * Generic over the user type so consumers can plug in their own auth infrastructure.\n */\nexport class RequestContext<TUser = BaseUser> {\n static readonly BRAND = 'tango.resources.request_context' as const;\n readonly __tangoBrand: typeof RequestContext.BRAND = RequestContext.BRAND;\n private state: Map<string | symbol, unknown> = new Map();\n\n constructor(\n public readonly request: TangoRequest,\n public user: TUser | null = null,\n public params: Record<string, string> = {}\n ) {}\n\n /**\n * Narrow an unknown value to `RequestContext`.\n */\n static isRequestContext<TUser = BaseUser>(value: unknown): value is RequestContext<TUser> {\n return (\n typeof value === 'object' &&\n value !== null &&\n (value as { __tangoBrand?: unknown }).__tangoBrand === RequestContext.BRAND\n );\n }\n\n /**\n * Construct a context with optional user payload.\n */\n static create<TUser = BaseUser>(request: Request, user?: TUser | null): RequestContext<TUser> {\n return new RequestContext<TUser>(\n TangoRequest.isTangoRequest(request) ? request : new TangoRequest(request),\n user ?? null\n );\n }\n\n /**\n * Store arbitrary per-request state for downstream middleware/handlers.\n */\n setState<T>(key: string | symbol, value: T): void {\n this.state.set(key, value);\n }\n\n /**\n * Retrieve previously stored request state.\n */\n getState<T>(key: string | symbol): T | undefined {\n return this.state.get(key) as T | undefined;\n }\n\n /**\n * Check whether a state key has been set.\n */\n hasState(key: string | symbol): boolean {\n return this.state.has(key);\n }\n\n /**\n * Clone the context, including route params and request-local state.\n */\n clone(): RequestContext<TUser> {\n const cloned = new RequestContext<TUser>(this.request, this.user, { ...this.params });\n cloned.state = new Map(this.state);\n return cloned;\n }\n}\n","/**\n * Domain boundary barrel: centralizes this subdomain's public contract.\n */\n\nexport type { BaseUser } from './RequestContext';\nexport { RequestContext } from './RequestContext';\n","import { QueryResult } from '@danceroutine/tango-orm';\n\nexport abstract class BasePaginator {\n protected resolveQueryResultRows<T>(rows: readonly T[] | QueryResult<T>): T[] {\n if (QueryResult.isQueryResult<T>(rows)) {\n return rows.toArray();\n }\n return [...rows];\n }\n}\n","import { z } from 'zod';\n\nexport type OffsetPaginationInputValue = {\n limit: number;\n offset: number;\n page?: number;\n};\n\nexport const OffsetPaginationInput: z.ZodType<OffsetPaginationInputValue> = z.object({\n limit: z.coerce\n .number()\n .int()\n .min(1)\n .default(25)\n .transform((value) => Math.min(value, 100)),\n offset: z.coerce.number().int().min(0).default(0),\n page: z.coerce.number().int().min(1).optional(),\n});\n","import { TangoQueryParams } from '@danceroutine/tango-core';\nimport type { QueryResult, QuerySet } from '@danceroutine/tango-orm';\nimport { BasePaginator } from '../pagination/BasePaginator';\nimport type { Paginator, Page } from '../pagination/Paginator';\nimport type { OffsetPaginatedResponse } from '../pagination/PaginatedResponse';\nimport { OffsetPaginationInput } from '../pagination/OffsetPaginationInput';\n\nclass OffsetPage<T> implements Page<T> {\n static readonly BRAND = 'tango.resources.offset_page' as const;\n readonly __tangoBrand: typeof OffsetPage.BRAND = OffsetPage.BRAND;\n\n constructor(\n public readonly results: T[],\n private readonly pageNumber: number,\n private readonly perPage: number,\n private readonly totalCount?: number\n ) {}\n\n static isOffsetPage<T>(value: unknown): value is OffsetPage<T> {\n return (\n typeof value === 'object' &&\n value !== null &&\n (value as { __tangoBrand?: unknown }).__tangoBrand === OffsetPage.BRAND\n );\n }\n\n /** Whether a next page exists based on known total count. */\n hasNext(): boolean {\n if (this.totalCount === undefined) {\n return false;\n }\n return this.endIndex() < this.totalCount;\n }\n\n /** Whether a previous page exists. */\n hasPrevious(): boolean {\n return this.pageNumber > 1;\n }\n\n /** The next page number, if available. */\n nextPageNumber(): number | null {\n return this.hasNext() ? this.pageNumber + 1 : null;\n }\n\n /** The previous page number, if available. */\n previousPageNumber(): number | null {\n return this.hasPrevious() ? this.pageNumber - 1 : null;\n }\n\n /** Zero-based start index of this page in the full result set. */\n startIndex(): number {\n return (this.pageNumber - 1) * this.perPage;\n }\n\n /** Exclusive end index of this page in the full result set. */\n endIndex(): number {\n return this.startIndex() + this.results.length;\n }\n}\n\n/**\n * Offset/limit paginator modelled after DRF's LimitOffsetPagination.\n * Handles parsing limit/offset/page from URL query params and building\n * the paginated response envelope with next/previous links.\n *\n * @example\n * ```typescript\n * const paginator = new OffsetPaginator(queryset);\n * const { limit, offset } = paginator.parseParams(searchParams);\n * const results = await queryset.limit(limit).offset(offset).fetchAll();\n * const response = paginator.getPaginatedResponse(results, totalCount);\n * ```\n */\nexport class OffsetPaginator<T extends Record<string, unknown>>\n extends BasePaginator\n implements Paginator<T, T, OffsetPaginatedResponse<T>>\n{\n static readonly BRAND = 'tango.resources.offset_paginator' as const;\n readonly __tangoBrand: typeof OffsetPaginator.BRAND = OffsetPaginator.BRAND;\n private limit = 25;\n private offset = 0;\n\n constructor(\n private queryset: QuerySet<T>,\n private perPage: number = 25\n ) {\n super();\n this.limit = perPage;\n }\n\n /**\n * Narrow an unknown value to `OffsetPaginator`.\n */\n static isOffsetPaginator<T extends Record<string, unknown>>(value: unknown): value is OffsetPaginator<T> {\n return (\n typeof value === 'object' &&\n value !== null &&\n (value as { __tangoBrand?: unknown }).__tangoBrand === OffsetPaginator.BRAND\n );\n }\n\n /**\n * Parse limit, offset, and page from Tango query params.\n * If `page` is provided, it's converted to an offset.\n * Stores parsed values for use by getPaginatedResponse.\n */\n parse(params: TangoQueryParams): void {\n const input = {\n limit: params.get('limit') ?? undefined,\n offset: params.get('offset') ?? undefined,\n page: params.get('page') ?? undefined,\n };\n\n const parsed = OffsetPaginationInput.parse(input);\n\n if (parsed.page) {\n parsed.offset = (parsed.page - 1) * parsed.limit;\n }\n\n this.limit = parsed.limit;\n this.offset = parsed.offset;\n }\n\n /**\n * Parse params and return `{ limit, offset }` for compatibility callers.\n */\n parseParams(params: TangoQueryParams): { limit: number; offset: number } {\n this.parse(params);\n return { limit: this.limit, offset: this.offset };\n }\n\n /**\n * Build a DRF-style paginated response with count, next, and previous links.\n * Uses the limit/offset stored from the most recent parseParams call.\n */\n needsTotalCount(): boolean {\n return true;\n }\n\n toResponse<TResult>(\n results: readonly TResult[] | QueryResult<TResult>,\n context?: { totalCount?: number; params?: TangoQueryParams }\n ): OffsetPaginatedResponse<TResult> {\n const totalCount = context?.totalCount;\n const response: OffsetPaginatedResponse<TResult> = { results: this.resolveQueryResultRows(results) };\n\n if (totalCount !== undefined) {\n response.count = totalCount;\n\n if (this.offset + this.limit < totalCount) {\n response.next = this.buildPageLink(this.offset + this.limit, context?.params);\n }\n\n if (this.offset > 0) {\n const prevOffset = Math.max(0, this.offset - this.limit);\n response.previous = this.buildPageLink(prevOffset, context?.params);\n }\n }\n\n return response;\n }\n\n /**\n * Backward-compatible alias for `toResponse`.\n */\n getPaginatedResponse<TResult>(\n results: readonly TResult[] | QueryResult<TResult>,\n totalCount?: number,\n params?: TangoQueryParams\n ): OffsetPaginatedResponse<TResult> {\n return this.toResponse(results, { totalCount, params });\n }\n\n /**\n * Apply current limit/offset to a queryset.\n */\n apply<TBaseResult extends Record<string, unknown>, TSourceModel, THydrated extends Record<string, unknown>>(\n queryset: QuerySet<T, TBaseResult, TSourceModel, THydrated>\n ): QuerySet<T, TBaseResult, TSourceModel, THydrated> {\n return queryset.limit(this.limit).offset(this.offset);\n }\n\n /**\n * Fetch a 1-based page number from the bound queryset.\n */\n async paginate(page: number): Promise<Page<T>> {\n return this.getPage(page);\n }\n\n /**\n * Fetch a 1-based page and return page metadata.\n */\n async getPage(page: number): Promise<Page<T>> {\n const offset = (page - 1) * this.perPage;\n const results = await this.queryset.offset(offset).limit(this.perPage).fetch();\n\n const totalCount = await this.count();\n\n return new OffsetPage(this.resolveQueryResultRows(results), page, this.perPage, totalCount);\n }\n\n /**\n * Count total rows for the current queryset state.\n */\n async count(): Promise<number> {\n return this.queryset.count();\n }\n\n private buildPageLink(offset: number, params?: TangoQueryParams): string {\n if (!params) {\n return `?limit=${this.limit}&offset=${offset}`;\n }\n\n return params\n .withValues({\n limit: this.limit,\n offset,\n page: null,\n })\n .toRelativeURL();\n }\n}\n","import type { ResourceModelLike } from '../resource/index';\nimport type { FilterValueParser } from './FilterSet';\n\nfunction normalizeParserTokens(raw: string | string[]): string[] {\n const tokens = Array.isArray(raw) ? raw : String(raw).split(',');\n const normalized = tokens.map((value) => value.trim());\n return normalized.every((value) => value.length > 0) ? normalized : [];\n}\n\nfunction createBooleanParser(): FilterValueParser {\n return (raw) => {\n const values = normalizeParserTokens(raw);\n if (values.length === 0) {\n return undefined;\n }\n\n const parsed = values.map((value) => {\n const normalized = value.toLowerCase();\n\n if (normalized === 'true' || normalized === '1') {\n return true;\n }\n\n if (normalized === 'false' || normalized === '0') {\n return false;\n }\n\n return null;\n });\n\n if (parsed.some((value) => value === null)) {\n return undefined;\n }\n\n return parsed.length === 1 ? parsed[0]! : (parsed as boolean[]);\n };\n}\n\nfunction createIntegerParser(): FilterValueParser {\n return (raw) => {\n const values = normalizeParserTokens(raw);\n if (values.length === 0) {\n return undefined;\n }\n\n const parsed = values.map(Number);\n\n if (parsed.some((value) => !Number.isInteger(value))) {\n return undefined;\n }\n\n return parsed.length === 1 ? parsed[0] : parsed;\n };\n}\n\nfunction createTimestampParser(): FilterValueParser {\n return (raw) => {\n const values = normalizeParserTokens(raw);\n if (values.length === 0) {\n return undefined;\n }\n\n const parsed = values.map((value) => {\n const date = new Date(value);\n return Number.isNaN(date.getTime()) ? null : date;\n });\n\n if (parsed.some((value) => value === null)) {\n return undefined;\n }\n\n return parsed.length === 1 ? parsed[0]! : (parsed as Date[]);\n };\n}\n\n/**\n * Infer resource-level query-value parsers from Tango model metadata.\n *\n * Parsers are inferred conservatively from field metadata so HTTP query filters\n * can be coerced into typed ORM inputs without framework-specific glue.\n */\nexport function inferModelFieldParsers<T extends Record<string, unknown>>(\n model: ResourceModelLike<T>\n): Partial<Record<keyof T, FilterValueParser>> {\n const metadata = model.metadata;\n if (!metadata) {\n return {};\n }\n\n const parsers: Partial<Record<keyof T, FilterValueParser>> = {};\n\n for (const field of metadata.fields) {\n switch (field.type) {\n case 'bool':\n parsers[field.name as keyof T] = createBooleanParser();\n break;\n case 'serial':\n case 'int':\n case 'bigint':\n parsers[field.name as keyof T] = createIntegerParser();\n break;\n case 'timestamptz':\n parsers[field.name as keyof T] = createTimestampParser();\n break;\n default:\n break;\n }\n }\n\n return parsers;\n}\n","import { TangoResponse } from '@danceroutine/tango-core';\nimport { RequestContext } from '../context/index';\n\nexport type APIViewMethod = 'GET' | 'POST' | 'PUT' | 'PATCH' | 'DELETE';\n\ntype APIViewMethodHandler = (ctx: RequestContext) => Promise<TangoResponse>;\n\n/**\n * Lightweight class-based request dispatcher for non-model API endpoints.\n */\nexport abstract class APIView {\n static readonly BRAND = 'tango.resources.api_view' as const;\n readonly __tangoBrand: typeof APIView.BRAND = APIView.BRAND;\n\n /**\n * Narrow an unknown value to `APIView`.\n */\n static isAPIView(value: unknown): value is APIView {\n return (\n typeof value === 'object' &&\n value !== null &&\n (value as { __tangoBrand?: unknown }).__tangoBrand === APIView.BRAND\n );\n }\n\n /**\n * Dispatch the request to the handler for the current HTTP method.\n */\n async dispatch(ctx: RequestContext): Promise<TangoResponse> {\n const method = normalizeMethod(ctx.request.method);\n if (!method) {\n return this.httpMethodNotAllowed();\n }\n\n const handler = this.getMethodHandler(method);\n return handler(ctx);\n }\n\n getAllowedMethods(): readonly APIViewMethod[] {\n const allowed: APIViewMethod[] = [];\n if (this.get !== APIView.prototype.get) {\n allowed.push('GET');\n }\n if (this.post !== APIView.prototype.post) {\n allowed.push('POST');\n }\n if (this.put !== APIView.prototype.put) {\n allowed.push('PUT');\n }\n if (this.patch !== APIView.prototype.patch) {\n allowed.push('PATCH');\n }\n if (this.delete !== APIView.prototype.delete) {\n allowed.push('DELETE');\n }\n return allowed;\n }\n\n protected get(_ctx: RequestContext): Promise<TangoResponse> {\n return Promise.resolve(this.httpMethodNotAllowed());\n }\n\n protected post(_ctx: RequestContext): Promise<TangoResponse> {\n return Promise.resolve(this.httpMethodNotAllowed());\n }\n\n protected put(_ctx: RequestContext): Promise<TangoResponse> {\n return Promise.resolve(this.httpMethodNotAllowed());\n }\n\n protected patch(_ctx: RequestContext): Promise<TangoResponse> {\n return Promise.resolve(this.httpMethodNotAllowed());\n }\n\n protected delete(_ctx: RequestContext): Promise<TangoResponse> {\n return Promise.resolve(this.httpMethodNotAllowed());\n }\n\n protected httpMethodNotAllowed(): TangoResponse {\n return TangoResponse.methodNotAllowed(this.getAllowedMethods());\n }\n\n private getMethodHandler(method: APIViewMethod): APIViewMethodHandler {\n if (method === 'GET') {\n return (ctx) => this.get(ctx);\n }\n if (method === 'POST') {\n return (ctx) => this.post(ctx);\n }\n if (method === 'PUT') {\n return (ctx) => this.put(ctx);\n }\n if (method === 'PATCH') {\n return (ctx) => this.patch(ctx);\n }\n return (ctx) => this.delete(ctx);\n }\n}\n\nfunction normalizeMethod(method: string): APIViewMethod | null {\n const upper = method.toUpperCase();\n if (upper === 'GET') {\n return 'GET';\n }\n if (upper === 'POST') {\n return 'POST';\n }\n if (upper === 'PUT') {\n return 'PUT';\n }\n if (upper === 'PATCH') {\n return 'PATCH';\n }\n if (upper === 'DELETE') {\n return 'DELETE';\n }\n return null;\n}\n","import { HttpErrorFactory, TangoResponse, type JsonValue, NotFoundError } from '@danceroutine/tango-core';\nimport { Q, type FilterInput, type ManagerLike, type QuerySet } from '@danceroutine/tango-orm';\nimport type { OffsetPaginatedResponse, Paginator } from '../pagination/index';\nimport { OffsetPaginator } from '../paginators/OffsetPaginator';\nimport { APIView } from './APIView';\nimport { RequestContext } from '../context/index';\nimport type { FilterSet } from '../filters/index';\nimport { inferModelFieldParsers } from '../filters/inferModelFieldParsers';\nimport type { GenericAPIViewOpenAPIDescription } from '../resource/index';\nimport type { AnyModelSerializer, SerializerOutput } from '../serializer/index';\nimport type { ResourceModelLike } from '../resource/index';\n\ntype SearchFieldRef<TModel extends Record<string, unknown>> = Extract<keyof TModel, string> | string;\n\nexport interface GenericAPIViewConfig<\n TModel extends Record<string, unknown>,\n TSerializer extends AnyModelSerializer<TModel>,\n> {\n serializer: TSerializer;\n filters?: FilterSet<TModel>;\n orderingFields?: (keyof TModel)[];\n searchFields?: SearchFieldRef<TModel>[];\n lookupField?: keyof TModel;\n lookupParam?: string;\n paginatorFactory?: (queryset: QuerySet<TModel>) => Paginator<TModel, SerializerOutput<TSerializer>>;\n}\n\n/**\n * Generic API base class that centralizes query/build/validation helpers.\n */\nexport abstract class GenericAPIView<\n TModel extends Record<string, unknown>,\n TSerializer extends AnyModelSerializer<TModel>,\n> extends APIView {\n protected readonly serializerClass: TSerializer;\n protected readonly filters?: FilterSet<TModel>;\n protected readonly orderingFields: readonly (keyof TModel)[];\n protected readonly searchFields: readonly SearchFieldRef<TModel>[];\n protected readonly lookupField?: keyof TModel;\n protected readonly lookupParam: string;\n protected readonly paginatorFactory?: (\n queryset: QuerySet<TModel>\n ) => Paginator<TModel, SerializerOutput<TSerializer>>;\n private serializer?: InstanceType<TSerializer>;\n\n constructor(config: GenericAPIViewConfig<TModel, TSerializer>) {\n super();\n this.serializerClass = config.serializer;\n this.filters = config.filters;\n this.orderingFields = config.orderingFields ?? [];\n this.searchFields = config.searchFields ?? [];\n this.lookupField = config.lookupField;\n this.lookupParam = config.lookupParam ?? 'id';\n this.paginatorFactory = config.paginatorFactory;\n }\n\n /**\n * Return the serializer class that owns this resource contract.\n */\n getSerializerClass(): TSerializer {\n return this.serializerClass;\n }\n\n /**\n * Return the serializer instance for the current resource.\n */\n getSerializer(): InstanceType<TSerializer> {\n if (!this.serializer) {\n this.serializer = new this.serializerClass() as InstanceType<TSerializer>;\n }\n\n return this.serializer;\n }\n\n /**\n * Describe the public HTTP contract that this resource contributes to OpenAPI generation.\n */\n describeOpenAPI(): GenericAPIViewOpenAPIDescription<TModel, TSerializer> {\n const model = this.requireModelMetadata();\n return {\n model,\n outputSchema: this.getOutputSchema(),\n createSchema: this.getCreateSchema(),\n updateSchema: this.getUpdateSchema(),\n searchFields: this.searchFields,\n orderingFields: this.orderingFields,\n lookupField: this.lookupField ?? this.getLookupFieldFromMetadata(model),\n lookupParam: this.lookupParam,\n allowedMethods: this.getAllowedMethods(),\n usesDefaultOffsetPagination: !this.paginatorFactory,\n };\n }\n\n protected getManager(): ManagerLike<TModel> {\n return this.getSerializer().getManager();\n }\n\n protected getOutputSchema(): TSerializer['outputSchema'] {\n return this.getSerializer().getOutputSchema() as TSerializer['outputSchema'];\n }\n\n protected getCreateSchema(): TSerializer['createSchema'] {\n return this.getSerializer().getCreateSchema() as TSerializer['createSchema'];\n }\n\n protected getUpdateSchema(): TSerializer['updateSchema'] {\n return this.getSerializer().getUpdateSchema() as TSerializer['updateSchema'];\n }\n\n protected getLookupField(): keyof TModel {\n return this.lookupField ?? (this.getManager().meta.pk as keyof TModel);\n }\n\n protected getLookupValue(ctx: RequestContext): string | null {\n const value = ctx.params[this.lookupParam]?.trim();\n return value || null;\n }\n\n protected getPaginator(queryset: QuerySet<TModel>): Paginator<TModel, SerializerOutput<TSerializer>> {\n if (this.paginatorFactory) {\n return this.paginatorFactory(queryset);\n }\n return new OffsetPaginator<TModel>(queryset) as Paginator<TModel, SerializerOutput<TSerializer>>;\n }\n\n protected async performList(ctx: RequestContext): Promise<TangoResponse> {\n try {\n const params = ctx.request.queryParams;\n const baseQueryset = this.getManager().query();\n const paginator = this.getPaginator(baseQueryset);\n paginator.parse(params);\n\n let qs = baseQueryset;\n\n if (this.filters) {\n const filterInputs = this.filters\n .withFieldParsers(inferModelFieldParsers(this.getSerializer().getModel()))\n .apply(params);\n if (filterInputs.length > 0) {\n qs = qs.filter(Q.and(...filterInputs));\n }\n }\n\n const search = params.getSearch();\n if (search && this.searchFields.length > 0) {\n const searchFilters: FilterInput<TModel>[] = this.searchFields.map((field) => {\n const lookup = `${String(field)}__icontains`;\n return { [lookup]: search } as FilterInput<TModel>;\n });\n qs = qs.filter(Q.or(...searchFilters));\n }\n\n const ordering = params.getOrdering();\n if (ordering.length > 0) {\n const orderTokens = ordering.filter((field) => {\n const cleanField = field.startsWith('-') ? field.slice(1) : field;\n return this.orderingFields.includes(cleanField as keyof TModel);\n });\n if (orderTokens.length > 0) {\n qs = qs.orderBy(...orderTokens.map((token) => token as keyof TModel | `-${string & keyof TModel}`));\n }\n }\n\n const paginatedQueryset = paginator.apply(qs);\n const resultPromise = paginatedQueryset.fetch();\n const totalCountPromise = paginator.needsTotalCount()\n ? qs.count()\n : Promise.resolve<number | undefined>(undefined);\n const [result, totalCount] = await Promise.all([resultPromise, totalCountPromise]);\n const serializer = this.getSerializer();\n const rows = await serializer.serializeMany(result.items);\n const response = paginator.toResponse(rows as SerializerOutput<TSerializer>[], {\n totalCount,\n }) as OffsetPaginatedResponse<SerializerOutput<TSerializer>>;\n\n return TangoResponse.json(response as unknown as JsonValue, { status: 200 });\n } catch (error) {\n return this.handleError(error);\n }\n }\n\n protected async performCreate(ctx: RequestContext): Promise<TangoResponse> {\n try {\n const body = await ctx.request.json();\n const result = await this.getSerializer().create(body);\n\n return TangoResponse.created(undefined, result as JsonValue);\n } catch (error) {\n return this.handleError(error);\n }\n }\n\n protected async performRetrieve(ctx: RequestContext): Promise<TangoResponse> {\n try {\n const value = this.getLookupValue(ctx);\n if (!value) {\n throw new NotFoundError('Lookup parameter was not provided.');\n }\n\n const lookupField = this.getLookupField();\n const filterByLookup = { [lookupField]: value } as FilterInput<TModel>;\n const result = await this.getManager().query().filter(filterByLookup).fetchOne();\n if (!result) {\n throw new NotFoundError(\n `No ${this.getManager().meta.table} record found for ${String(lookupField)}=${value}.`\n );\n }\n\n return TangoResponse.json((await this.getSerializer().serialize(result)) as JsonValue, { status: 200 });\n } catch (error) {\n return this.handleError(error);\n }\n }\n\n protected async performUpdate(ctx: RequestContext): Promise<TangoResponse> {\n try {\n const value = this.getLookupValue(ctx);\n if (!value) {\n throw new NotFoundError('Lookup parameter was not provided.');\n }\n\n const body = await ctx.request.json();\n const result = await this.getSerializer().update(value as TModel[keyof TModel], body);\n\n return TangoResponse.json(result as JsonValue, { status: 200 });\n } catch (error) {\n return this.handleError(error);\n }\n }\n\n protected async performDestroy(ctx: RequestContext): Promise<TangoResponse> {\n try {\n const value = this.getLookupValue(ctx);\n if (!value) {\n throw new NotFoundError('Lookup parameter was not provided.');\n }\n\n await this.getManager().delete(value as TModel[keyof TModel]);\n return TangoResponse.noContent();\n } catch (error) {\n return this.handleError(error);\n }\n }\n\n protected handleError(error: unknown): TangoResponse {\n const httpError = HttpErrorFactory.toHttpError(error);\n return TangoResponse.json(httpError.body as JsonValue, { status: httpError.status });\n }\n\n private requireModelMetadata(): ResourceModelLike<TModel> & {\n metadata: NonNullable<ResourceModelLike<TModel>['metadata']>;\n } {\n const model = this.getSerializer().getModel();\n\n if (!model.metadata) {\n throw new Error('OpenAPI generation requires Tango model metadata on GenericAPIView models.');\n }\n\n return model as ResourceModelLike<TModel> & {\n metadata: NonNullable<ResourceModelLike<TModel>['metadata']>;\n };\n }\n\n private getLookupFieldFromMetadata(\n model: ResourceModelLike<TModel> & {\n metadata: NonNullable<ResourceModelLike<TModel>['metadata']>;\n }\n ): keyof TModel {\n const primaryKeyField = model.metadata.fields.find((field) => field.primaryKey);\n\n if (!primaryKeyField) {\n throw new Error('OpenAPI generation requires a primary key field in Tango model metadata.');\n }\n\n return primaryKeyField.name as keyof TModel;\n }\n}\n","import { GenericAPIView } from '../GenericAPIView';\nimport type { TangoResponse } from '@danceroutine/tango-core';\nimport { RequestContext } from '../../context/index';\nimport type { AnyModelSerializer } from '../../serializer/index';\n\n/**\n * Mixin that wires `GET` requests to the generic list implementation.\n */\nexport abstract class ListModelMixin<\n TModel extends Record<string, unknown>,\n TSerializer extends AnyModelSerializer<TModel>,\n> extends GenericAPIView<TModel, TSerializer> {\n protected list(ctx: RequestContext): Promise<TangoResponse> {\n return this.performList(ctx);\n }\n\n protected override get(ctx: RequestContext): Promise<TangoResponse> {\n return this.list(ctx);\n }\n}\n","import { GenericAPIView } from '../GenericAPIView';\nimport type { TangoResponse } from '@danceroutine/tango-core';\nimport { RequestContext } from '../../context/index';\nimport type { AnyModelSerializer } from '../../serializer/index';\n\n/**\n * Mixin that wires `POST` requests to the generic create implementation.\n */\nexport abstract class CreateModelMixin<\n TModel extends Record<string, unknown>,\n TSerializer extends AnyModelSerializer<TModel>,\n> extends GenericAPIView<TModel, TSerializer> {\n protected create(ctx: RequestContext): Promise<TangoResponse> {\n return this.performCreate(ctx);\n }\n\n protected override post(ctx: RequestContext): Promise<TangoResponse> {\n return this.create(ctx);\n }\n}\n","import { GenericAPIView } from '../GenericAPIView';\nimport type { TangoResponse } from '@danceroutine/tango-core';\nimport { RequestContext } from '../../context/index';\nimport type { AnyModelSerializer } from '../../serializer/index';\n\n/**\n * Mixin that wires `GET` requests to the generic retrieve implementation.\n */\nexport abstract class RetrieveModelMixin<\n TModel extends Record<string, unknown>,\n TSerializer extends AnyModelSerializer<TModel>,\n> extends GenericAPIView<TModel, TSerializer> {\n protected retrieve(ctx: RequestContext): Promise<TangoResponse> {\n return this.performRetrieve(ctx);\n }\n\n protected override get(ctx: RequestContext): Promise<TangoResponse> {\n return this.retrieve(ctx);\n }\n}\n","import { GenericAPIView } from '../GenericAPIView';\nimport type { TangoResponse } from '@danceroutine/tango-core';\nimport { RequestContext } from '../../context/index';\nimport type { AnyModelSerializer } from '../../serializer/index';\n\n/**\n * Mixin that wires `PUT` and `PATCH` requests to the generic update implementation.\n */\nexport abstract class UpdateModelMixin<\n TModel extends Record<string, unknown>,\n TSerializer extends AnyModelSerializer<TModel>,\n> extends GenericAPIView<TModel, TSerializer> {\n protected update(ctx: RequestContext): Promise<TangoResponse> {\n return this.performUpdate(ctx);\n }\n\n protected override put(ctx: RequestContext): Promise<TangoResponse> {\n return this.update(ctx);\n }\n\n protected override patch(ctx: RequestContext): Promise<TangoResponse> {\n return this.update(ctx);\n }\n}\n","import { GenericAPIView } from '../GenericAPIView';\nimport type { TangoResponse } from '@danceroutine/tango-core';\nimport { RequestContext } from '../../context/index';\nimport type { AnyModelSerializer } from '../../serializer/index';\n\n/**\n * Mixin that wires `DELETE` requests to the generic destroy implementation.\n */\nexport abstract class DestroyModelMixin<\n TModel extends Record<string, unknown>,\n TSerializer extends AnyModelSerializer<TModel>,\n> extends GenericAPIView<TModel, TSerializer> {\n protected destroy(ctx: RequestContext): Promise<TangoResponse> {\n return this.performDestroy(ctx);\n }\n\n protected override delete(ctx: RequestContext): Promise<TangoResponse> {\n return this.destroy(ctx);\n }\n}\n","import { ListModelMixin } from '../mixins/ListModelMixin';\nimport type { TangoResponse } from '@danceroutine/tango-core';\nimport { RequestContext } from '../../context/index';\nimport type { AnyModelSerializer } from '../../serializer/index';\n\n/**\n * Generic API view for endpoints that only expose a list operation.\n */\nexport abstract class ListAPIView<\n TModel extends Record<string, unknown>,\n TSerializer extends AnyModelSerializer<TModel>,\n> extends ListModelMixin<TModel, TSerializer> {\n protected override get(ctx: RequestContext): Promise<TangoResponse> {\n return super.get(ctx);\n }\n}\n","import { CreateModelMixin } from '../mixins/CreateModelMixin';\nimport type { TangoResponse } from '@danceroutine/tango-core';\nimport { RequestContext } from '../../context/index';\nimport type { AnyModelSerializer } from '../../serializer/index';\n\n/**\n * Generic API view for endpoints that only support resource creation.\n */\nexport abstract class CreateAPIView<\n TModel extends Record<string, unknown>,\n TSerializer extends AnyModelSerializer<TModel>,\n> extends CreateModelMixin<TModel, TSerializer> {\n protected override post(ctx: RequestContext): Promise<TangoResponse> {\n return super.post(ctx);\n }\n}\n","import { RetrieveModelMixin } from '../mixins/RetrieveModelMixin';\nimport type { TangoResponse } from '@danceroutine/tango-core';\nimport { RequestContext } from '../../context/index';\nimport type { AnyModelSerializer } from '../../serializer/index';\n\n/**\n * Generic API view for endpoints that retrieve a single resource by lookup.\n */\nexport abstract class RetrieveAPIView<\n TModel extends Record<string, unknown>,\n TSerializer extends AnyModelSerializer<TModel>,\n> extends RetrieveModelMixin<TModel, TSerializer> {\n protected override get(ctx: RequestContext): Promise<TangoResponse> {\n return super.get(ctx);\n }\n}\n","import { GenericAPIView } from '../GenericAPIView';\nimport type { TangoResponse } from '@danceroutine/tango-core';\nimport { RequestContext } from '../../context/index';\nimport type { AnyModelSerializer } from '../../serializer/index';\n\n/**\n * Generic API view for collection endpoints that list and create resources.\n */\nexport abstract class ListCreateAPIView<\n TModel extends Record<string, unknown>,\n TSerializer extends AnyModelSerializer<TModel>,\n> extends GenericAPIView<TModel, TSerializer> {\n protected override get(ctx: RequestContext): Promise<TangoResponse> {\n return this.performList(ctx);\n }\n\n protected override post(ctx: RequestContext): Promise<TangoResponse> {\n return this.performCreate(ctx);\n }\n}\n","import { GenericAPIView } from '../GenericAPIView';\nimport type { TangoResponse } from '@danceroutine/tango-core';\nimport { RequestContext } from '../../context/index';\nimport type { AnyModelSerializer } from '../../serializer/index';\n\n/**\n * Generic API view for endpoints that retrieve and update a single resource.\n */\nexport abstract class RetrieveUpdateAPIView<\n TModel extends Record<string, unknown>,\n TSerializer extends AnyModelSerializer<TModel>,\n> extends GenericAPIView<TModel, TSerializer> {\n protected override get(ctx: RequestContext): Promise<TangoResponse> {\n return this.performRetrieve(ctx);\n }\n\n protected override put(ctx: RequestContext): Promise<TangoResponse> {\n return this.performUpdate(ctx);\n }\n\n protected override patch(ctx: RequestContext): Promise<TangoResponse> {\n return this.performUpdate(ctx);\n }\n}\n","import { GenericAPIView } from '../GenericAPIView';\nimport type { TangoResponse } from '@danceroutine/tango-core';\nimport { RequestContext } from '../../context/index';\nimport type { AnyModelSerializer } from '../../serializer/index';\n\n/**\n * Generic API view for endpoints that retrieve and delete a single resource.\n */\nexport abstract class RetrieveDestroyAPIView<\n TModel extends Record<string, unknown>,\n TSerializer extends AnyModelSerializer<TModel>,\n> extends GenericAPIView<TModel, TSerializer> {\n protected override get(ctx: RequestContext): Promise<TangoResponse> {\n return this.performRetrieve(ctx);\n }\n\n protected override delete(ctx: RequestContext): Promise<TangoResponse> {\n return this.performDestroy(ctx);\n }\n}\n","import { GenericAPIView } from '../GenericAPIView';\nimport type { TangoResponse } from '@danceroutine/tango-core';\nimport { RequestContext } from '../../context/index';\nimport type { AnyModelSerializer } from '../../serializer/index';\n\n/**\n * Generic API view for full detail endpoints that retrieve, update, and delete.\n */\nexport abstract class RetrieveUpdateDestroyAPIView<\n TModel extends Record<string, unknown>,\n TSerializer extends AnyModelSerializer<TModel>,\n> extends GenericAPIView<TModel, TSerializer> {\n protected override get(ctx: RequestContext): Promise<TangoResponse> {\n return this.performRetrieve(ctx);\n }\n\n protected override put(ctx: RequestContext): Promise<TangoResponse> {\n return this.performUpdate(ctx);\n }\n\n protected override patch(ctx: RequestContext): Promise<TangoResponse> {\n return this.performUpdate(ctx);\n }\n\n protected override delete(ctx: RequestContext): Promise<TangoResponse> {\n return this.performDestroy(ctx);\n }\n}\n","/**\n * Domain boundary barrel: centralizes this subdomain's public contract.\n */\n\nexport { APIView, type APIViewMethod } from './APIView';\nexport { GenericAPIView, type GenericAPIViewConfig } from './GenericAPIView';\nexport type { GenericAPIViewOpenAPIDescription } from '../resource/index';\nexport {\n ListModelMixin,\n CreateModelMixin,\n RetrieveModelMixin,\n UpdateModelMixin,\n DestroyModelMixin,\n} from './mixins/index';\nexport {\n ListAPIView,\n CreateAPIView,\n RetrieveAPIView,\n ListCreateAPIView,\n RetrieveUpdateAPIView,\n RetrieveDestroyAPIView,\n RetrieveUpdateDestroyAPIView,\n} from './generics/index';\n"],"mappings":"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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@danceroutine/tango-resources",
3
- "version": "1.11.0",
3
+ "version": "1.11.2",
4
4
  "description": "ModelViewSet, serializers, filters, and pagination for Tango",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",
@@ -58,17 +58,17 @@
58
58
  },
59
59
  "dependencies": {
60
60
  "zod": "^4.0.0",
61
- "@danceroutine/tango-orm": "1.11.0",
62
- "@danceroutine/tango-core": "1.11.0"
61
+ "@danceroutine/tango-core": "1.11.2",
62
+ "@danceroutine/tango-orm": "1.11.2"
63
63
  },
64
64
  "devDependencies": {
65
65
  "@types/node": "^22.9.0",
66
- "tsdown": "^0.4.0",
66
+ "tsdown": "^0.22.1",
67
67
  "typescript": "^5.6.3",
68
- "vitest": "^4.0.6",
68
+ "vitest": "^4.1.7",
69
69
  "zod": "^4.0.0",
70
- "@danceroutine/tango-schema": "1.11.0",
71
- "@danceroutine/tango-testing": "1.11.0"
70
+ "@danceroutine/tango-schema": "1.11.2",
71
+ "@danceroutine/tango-testing": "1.11.2"
72
72
  },
73
73
  "scripts": {
74
74
  "build": "tsdown",