@danceroutine/tango-resources 1.11.1 → 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.
- package/dist/chunk-D7D4PA-g.js +13 -0
- package/dist/index-D6sfTSEj.d.ts +902 -0
- package/dist/index.d.ts +12 -23
- package/dist/index.js +96 -152
- package/dist/index.js.map +1 -1
- package/dist/view/index.d.ts +2 -8
- package/dist/view/index.js +2 -3
- package/dist/{view-DUHg6AXl.js → view-C9B5Lln3.js} +151 -59
- package/dist/view-C9B5Lln3.js.map +1 -0
- package/package.json +6 -6
- package/dist/context/RequestContext.d.ts +0 -46
- package/dist/context/index.d.ts +0 -5
- package/dist/filters/FilterSet.d.ts +0 -95
- package/dist/filters/FilterType.d.ts +0 -2
- package/dist/filters/RangeOperator.d.ts +0 -2
- package/dist/filters/index.d.ts +0 -7
- package/dist/filters/inferModelFieldParsers.d.ts +0 -9
- package/dist/filters/internal/InternalFilterLookup.d.ts +0 -15
- package/dist/filters/internal/InternalFilterType.d.ts +0 -7
- package/dist/filters/internal/InternalRangeOperator.d.ts +0 -6
- package/dist/pagination/BasePaginator.d.ts +0 -4
- package/dist/pagination/CursorPaginationInput.d.ts +0 -7
- package/dist/pagination/OffsetPaginationInput.d.ts +0 -7
- package/dist/pagination/PaginatedResponse.d.ts +0 -12
- package/dist/pagination/Paginator.d.ts +0 -21
- package/dist/pagination/index.d.ts +0 -12
- package/dist/paginators/CursorPaginator.d.ts +0 -65
- package/dist/paginators/OffsetPaginator.d.ts +0 -74
- package/dist/paginators/index.d.ts +0 -5
- package/dist/resource/OpenAPIDescription.d.ts +0 -23
- package/dist/resource/ResourceModelLike.d.ts +0 -22
- package/dist/resource/index.d.ts +0 -5
- package/dist/serializer/ModelSerializer.d.ts +0 -67
- package/dist/serializer/Serializer.d.ts +0 -75
- package/dist/serializer/index.d.ts +0 -6
- package/dist/serializer/internal/InternalSerializerRelationKind.d.ts +0 -11
- package/dist/serializer/relation.d.ts +0 -45
- package/dist/view/APIView.d.ts +0 -26
- package/dist/view/GenericAPIView.d.ts +0 -60
- package/dist/view/generics/CreateAPIView.d.ts +0 -10
- package/dist/view/generics/ListAPIView.d.ts +0 -10
- package/dist/view/generics/ListCreateAPIView.d.ts +0 -11
- package/dist/view/generics/RetrieveAPIView.d.ts +0 -10
- package/dist/view/generics/RetrieveDestroyAPIView.d.ts +0 -11
- package/dist/view/generics/RetrieveUpdateAPIView.d.ts +0 -12
- package/dist/view/generics/RetrieveUpdateDestroyAPIView.d.ts +0 -13
- package/dist/view/generics/index.d.ts +0 -10
- package/dist/view/mixins/CreateModelMixin.d.ts +0 -11
- package/dist/view/mixins/DestroyModelMixin.d.ts +0 -11
- package/dist/view/mixins/ListModelMixin.d.ts +0 -11
- package/dist/view/mixins/RetrieveModelMixin.d.ts +0 -11
- package/dist/view/mixins/UpdateModelMixin.d.ts +0 -12
- package/dist/view/mixins/index.d.ts +0 -8
- package/dist/view-DUHg6AXl.js.map +0 -1
- package/dist/viewset/ModelViewSet.d.ts +0 -112
- package/dist/viewset/index.d.ts +0 -5
|
@@ -1,16 +1,66 @@
|
|
|
1
|
-
import {
|
|
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
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
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 ===
|
|
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") ??
|
|
96
|
-
offset: params.get("offset") ??
|
|
97
|
-
page: params.get("page") ??
|
|
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 !==
|
|
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
|
|
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
|
|
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
|
|
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
|
|
269
|
+
if (values.length === 0) return;
|
|
205
270
|
const parsed = values.map(Number);
|
|
206
|
-
if (parsed.some((value) => !Number.isInteger(value))) return
|
|
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
|
|
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
|
|
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
|
-
|
|
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
|
|
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
|
-
|
|
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
|
-
|
|
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
|
|
411
|
-
const
|
|
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
|
|
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(
|
|
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 {
|
|
638
|
-
|
|
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.
|
|
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-core": "1.11.
|
|
62
|
-
"@danceroutine/tango-orm": "1.11.
|
|
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.
|
|
66
|
+
"tsdown": "^0.22.1",
|
|
67
67
|
"typescript": "^5.6.3",
|
|
68
68
|
"vitest": "^4.1.7",
|
|
69
69
|
"zod": "^4.0.0",
|
|
70
|
-
"@danceroutine/tango-schema": "1.11.
|
|
71
|
-
"@danceroutine/tango-testing": "1.11.
|
|
70
|
+
"@danceroutine/tango-schema": "1.11.2",
|
|
71
|
+
"@danceroutine/tango-testing": "1.11.2"
|
|
72
72
|
},
|
|
73
73
|
"scripts": {
|
|
74
74
|
"build": "tsdown",
|