@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
package/dist/index.d.ts CHANGED
@@ -1,23 +1,12 @@
1
- /**
2
- * Bundled exports for Django-style domain drill-down imports, plus curated
3
- * top-level symbols for TS-native ergonomic imports.
4
- */
5
- export * as context from './context/index';
6
- export * as filters from './filters/index';
7
- export * as pagination from './pagination/index';
8
- export * as paginators from './paginators/index';
9
- export * as resource from './resource/index';
10
- export * as serializer from './serializer/index';
11
- export * as viewset from './viewset/index';
12
- export * as view from './view/index';
13
- export { RequestContext } from './context/index';
14
- export type { BaseUser } from './context/index';
15
- export { FilterSet, type AliasFilterDeclaration, type FieldFilterDeclaration, type FilterLookup, type FilterResolver, type FilterSetDefineConfig, type FilterValueParser, } from './filters/index';
16
- export type { FilterType, RangeOperator } from './filters/index';
17
- export { CursorPaginator, OffsetPaginator, CursorPaginationInput, OffsetPaginationInput, type Page, type BasePaginatedResponse, type CursorPaginatedResponse, type OffsetPaginatedResponse, type PaginatedResponse, type Paginator, } from './pagination/index';
18
- export { Serializer, ModelSerializer, relation, type SerializerClass, type AnySerializerClass, type SerializerCreateInput, type SerializerUpdateInput, type SerializerOutput, type SerializerSchema, type ModelSerializerClass, type AnyModelSerializer, type AnyModelSerializerClass, type ModelSerializerRelationFields, type ManyToManyManagerKeys, type ManyToManyRelationField, type ManyToManyReadStrategy, type ManyToManyWriteStrategy, } from './serializer/index';
19
- export { ModelViewSet } from './viewset/index';
20
- export type { ModelViewSetOpenAPIDescription, ModelViewSetConfig, ViewSetActionDescriptor, ViewSetActionMethod, ViewSetActionScope, ResolvedViewSetActionDescriptor, } from './viewset/index';
21
- export { APIView, GenericAPIView, ListModelMixin, CreateModelMixin, RetrieveModelMixin, UpdateModelMixin, DestroyModelMixin, ListAPIView, CreateAPIView, RetrieveAPIView, ListCreateAPIView, RetrieveUpdateAPIView, RetrieveDestroyAPIView, RetrieveUpdateDestroyAPIView, } from './view/index';
22
- export type { APIViewMethod, GenericAPIViewConfig, GenericAPIViewOpenAPIDescription } from './view/index';
23
- export type { ResourceModelFieldMetadata, ResourceModelLike, ResourceModelMetadata } from './resource/index';
1
+ import { $ as Page, A as ManyToManyManagerKeys, B as SerializerOutput, C as ViewSetActionMethod, D as AnyModelSerializerClass, E as AnyModelSerializer, F as relation, G as ResourceModelFieldMetadata, H as SerializerUpdateInput, I as AnySerializerClass, J as index_d_exports$2, K as ResourceModelLike, L as Serializer, M as ManyToManyRelationField, N as ManyToManyWriteStrategy, O as ModelSerializer, P as ModelSerializerRelationFields, Q as OffsetPaginator, R as SerializerClass, S as ViewSetActionDescriptor, T as index_d_exports$5, U as APIView, V as SerializerSchema, W as APIViewMethod, X as OffsetPaginationInput, Y as CursorPaginationInput, Z as CursorPaginator, _ as GenericAPIViewOpenAPIDescription, _t as RequestContext, a as ListCreateAPIView, at as index_d_exports$1, b as ModelViewSetConfig, c as ListAPIView, ct as FilterLookup, d as RetrieveModelMixin, dt as FilterSetDefineConfig, et as Paginator, f as CreateModelMixin, ft as FilterValueParser, g as index_d_exports$4, gt as BaseUser, h as GenericAPIViewConfig, ht as index_d_exports, i as RetrieveUpdateAPIView, it as PaginatedResponse, j as ManyToManyReadStrategy, k as ModelSerializerClass, l as DestroyModelMixin, lt as FilterResolver, m as GenericAPIView, mt as FilterType, n as RetrieveUpdateDestroyAPIView, nt as CursorPaginatedResponse, o as RetrieveAPIView, ot as AliasFilterDeclaration, p as ListModelMixin, pt as RangeOperator, q as ResourceModelMetadata, r as RetrieveDestroyAPIView, rt as OffsetPaginatedResponse, s as CreateAPIView, st as FieldFilterDeclaration, t as index_d_exports$6, tt as BasePaginatedResponse, u as UpdateModelMixin, ut as FilterSet, v as ModelViewSetOpenAPIDescription, w as ViewSetActionScope, x as ResolvedViewSetActionDescriptor, y as ModelViewSet, z as SerializerCreateInput } from "./index-D6sfTSEj.js";
2
+
3
+ //#region src/paginators/index.d.ts
4
+ declare namespace index_d_exports$3 {
5
+ export { CursorPaginator, OffsetPaginator };
6
+ }
7
+ declare namespace index_d_exports$7 {
8
+ export { ModelViewSet, ModelViewSetConfig, ModelViewSetOpenAPIDescription, ResolvedViewSetActionDescriptor, ViewSetActionDescriptor, ViewSetActionMethod, ViewSetActionScope };
9
+ }
10
+ //#endregion
11
+ export { APIView, type APIViewMethod, type AliasFilterDeclaration, type AnyModelSerializer, type AnyModelSerializerClass, type AnySerializerClass, type BasePaginatedResponse, type BaseUser, CreateAPIView, CreateModelMixin, type CursorPaginatedResponse, CursorPaginationInput, CursorPaginator, DestroyModelMixin, type FieldFilterDeclaration, type FilterLookup, type FilterResolver, FilterSet, type FilterSetDefineConfig, type FilterType, type FilterValueParser, GenericAPIView, type GenericAPIViewConfig, type GenericAPIViewOpenAPIDescription, ListAPIView, ListCreateAPIView, ListModelMixin, type ManyToManyManagerKeys, type ManyToManyReadStrategy, type ManyToManyRelationField, type ManyToManyWriteStrategy, ModelSerializer, type ModelSerializerClass, type ModelSerializerRelationFields, ModelViewSet, type ModelViewSetConfig, type ModelViewSetOpenAPIDescription, type OffsetPaginatedResponse, OffsetPaginationInput, OffsetPaginator, type Page, type PaginatedResponse, type Paginator, type RangeOperator, RequestContext, type ResolvedViewSetActionDescriptor, type ResourceModelFieldMetadata, type ResourceModelLike, type ResourceModelMetadata, RetrieveAPIView, RetrieveDestroyAPIView, RetrieveModelMixin, RetrieveUpdateAPIView, RetrieveUpdateDestroyAPIView, Serializer, type SerializerClass, type SerializerCreateInput, type SerializerOutput, type SerializerSchema, type SerializerUpdateInput, UpdateModelMixin, type ViewSetActionDescriptor, type ViewSetActionMethod, type ViewSetActionScope, index_d_exports as context, index_d_exports$1 as filters, index_d_exports$2 as pagination, index_d_exports$3 as paginators, relation, index_d_exports$4 as resource, index_d_exports$5 as serializer, index_d_exports$6 as view, index_d_exports$7 as viewset };
12
+ //# sourceMappingURL=index.d.ts.map
package/dist/index.js CHANGED
@@ -1,64 +1,8 @@
1
- import { APIView, BasePaginator, CreateAPIView, CreateModelMixin, DestroyModelMixin, GenericAPIView, ListAPIView, ListCreateAPIView, ListModelMixin, OffsetPaginationInput, OffsetPaginator, RetrieveAPIView, RetrieveDestroyAPIView, RetrieveModelMixin, RetrieveUpdateAPIView, RetrieveUpdateDestroyAPIView, UpdateModelMixin, __export, inferModelFieldParsers, view_exports } from "./view-DUHg6AXl.js";
2
- import { HttpErrorFactory, NotFoundError, TangoRequest, TangoResponse, getLogger } from "@danceroutine/tango-core";
1
+ import { t as __exportAll } from "./chunk-D7D4PA-g.js";
2
+ import { _ as OffsetPaginator, a as ListCreateAPIView, b as context_exports, c as ListAPIView, d as RetrieveModelMixin, f as CreateModelMixin, g as inferModelFieldParsers, h as APIView, i as RetrieveUpdateAPIView, l as DestroyModelMixin, m as GenericAPIView, n as RetrieveUpdateDestroyAPIView, o as RetrieveAPIView, p as ListModelMixin, r as RetrieveDestroyAPIView, s as CreateAPIView, t as view_exports, u as UpdateModelMixin, v as OffsetPaginationInput, x as RequestContext, y as BasePaginator } from "./view-C9B5Lln3.js";
3
+ import { HttpErrorFactory, NotFoundError, TangoResponse, getLogger } from "@danceroutine/tango-core";
3
4
  import { ManyToManyRelatedManager, Q } from "@danceroutine/tango-orm";
4
- import { z, z as z$1 } from "zod";
5
-
6
- //#region src/context/RequestContext.ts
7
- var RequestContext = class RequestContext {
8
- static BRAND = "tango.resources.request_context";
9
- __tangoBrand = RequestContext.BRAND;
10
- state = new Map();
11
- constructor(request, user = null, params = {}) {
12
- this.request = request;
13
- this.user = user;
14
- this.params = params;
15
- }
16
- /**
17
- * Narrow an unknown value to `RequestContext`.
18
- */
19
- static isRequestContext(value) {
20
- return typeof value === "object" && value !== null && value.__tangoBrand === RequestContext.BRAND;
21
- }
22
- /**
23
- * Construct a context with optional user payload.
24
- */
25
- static create(request, user) {
26
- return new RequestContext(TangoRequest.isTangoRequest(request) ? request : new TangoRequest(request), user ?? null);
27
- }
28
- /**
29
- * Store arbitrary per-request state for downstream middleware/handlers.
30
- */
31
- setState(key, value) {
32
- this.state.set(key, value);
33
- }
34
- /**
35
- * Retrieve previously stored request state.
36
- */
37
- getState(key) {
38
- return this.state.get(key);
39
- }
40
- /**
41
- * Check whether a state key has been set.
42
- */
43
- hasState(key) {
44
- return this.state.has(key);
45
- }
46
- /**
47
- * Clone the context, including route params and request-local state.
48
- */
49
- clone() {
50
- const cloned = new RequestContext(this.request, this.user, { ...this.params });
51
- cloned.state = new Map(this.state);
52
- return cloned;
53
- }
54
- };
55
-
56
- //#endregion
57
- //#region src/context/index.ts
58
- var context_exports = {};
59
- __export(context_exports, { RequestContext: () => RequestContext });
60
-
61
- //#endregion
5
+ import { z } from "zod";
62
6
  //#region src/filters/internal/InternalFilterType.ts
63
7
  const InternalFilterType = {
64
8
  SCALAR: "scalar",
@@ -67,7 +11,6 @@ const InternalFilterType = {
67
11
  IN: "in",
68
12
  CUSTOM: "custom"
69
13
  };
70
-
71
14
  //#endregion
72
15
  //#region src/filters/internal/InternalFilterLookup.ts
73
16
  const InternalFilterLookup = {
@@ -85,14 +28,21 @@ const InternalFilterLookup = {
85
28
  ENDSWITH: "endswith",
86
29
  IENDSWITH: "iendswith"
87
30
  };
88
-
89
31
  //#endregion
90
32
  //#region src/filters/FilterSet.ts
91
33
  const FILTER_LOOKUPS = Object.values(InternalFilterLookup);
92
34
  function isFilterLookup(value) {
93
35
  return FILTER_LOOKUPS.includes(value);
94
36
  }
37
+ /**
38
+ * Declarative query-param to filter translation.
39
+ *
40
+ * A `FilterSet` lets viewsets expose safe, explicit filtering behavior
41
+ * without leaking raw ORM filter syntax to request handlers.
42
+ */
95
43
  var FilterSet = class FilterSet {
44
+ spec;
45
+ allowAllParams;
96
46
  static BRAND = "tango.resources.filter_set";
97
47
  __tangoBrand = FilterSet.BRAND;
98
48
  /**
@@ -106,8 +56,7 @@ var FilterSet = class FilterSet {
106
56
  * Build a filter set from Django-style field declarations.
107
57
  */
108
58
  static define(config) {
109
- const spec = FilterSet.normalizeDefineConfig(config);
110
- return new FilterSet(spec, config.all === "__all__");
59
+ return new FilterSet(FilterSet.normalizeDefineConfig(config), config.all === "__all__");
111
60
  }
112
61
  /**
113
62
  * Narrow an unknown value to `FilterSet`.
@@ -121,7 +70,7 @@ var FilterSet = class FilterSet {
121
70
  const fieldParsers = config.parsers ?? {};
122
71
  for (const rawField of Object.keys(fieldDeclarations)) {
123
72
  const declaration = fieldDeclarations[rawField];
124
- if (declaration === undefined) continue;
73
+ if (declaration === void 0) continue;
125
74
  const parser = fieldParsers[rawField];
126
75
  FilterSet.addFieldDeclaration(spec, rawField, declaration, parser);
127
76
  }
@@ -171,7 +120,7 @@ var FilterSet = class FilterSet {
171
120
  ].includes(value.type);
172
121
  }
173
122
  static createMultiFieldResolver(fields, lookup, parser) {
174
- if (lookup === InternalFilterLookup.ICONTAINS && parser === undefined) return {
123
+ if (lookup === InternalFilterLookup.ICONTAINS && parser === void 0) return {
175
124
  type: InternalFilterType.ILIKE,
176
125
  columns: [...fields]
177
126
  };
@@ -179,23 +128,23 @@ var FilterSet = class FilterSet {
179
128
  type: InternalFilterType.CUSTOM,
180
129
  apply: (raw) => {
181
130
  const parsed = FilterSet.resolveParserValue(raw, parser);
182
- if (parsed === undefined) return undefined;
131
+ if (parsed === void 0) return void 0;
183
132
  const composed = {};
184
133
  for (const field of fields) {
185
134
  const segment = FilterSet.resolveLookupFilter(field, lookup, parsed);
186
135
  if (!segment) continue;
187
136
  Object.assign(composed, segment);
188
137
  }
189
- return Object.keys(composed).length > 0 ? composed : undefined;
138
+ return Object.keys(composed).length > 0 ? composed : void 0;
190
139
  }
191
140
  };
192
141
  }
193
142
  static createLookupResolver(field, lookup, parser) {
194
- if (parser !== undefined) return {
143
+ if (parser !== void 0) return {
195
144
  type: InternalFilterType.CUSTOM,
196
145
  apply: (raw) => {
197
146
  const parsed = FilterSet.resolveParserValue(raw, parser);
198
- if (parsed === undefined) return undefined;
147
+ if (parsed === void 0) return void 0;
199
148
  return FilterSet.resolveLookupFilter(field, lookup, parsed);
200
149
  }
201
150
  };
@@ -227,27 +176,22 @@ var FilterSet = class FilterSet {
227
176
  }
228
177
  }
229
178
  static resolveLookupFilter(field, lookup, value) {
230
- if (value === undefined) return undefined;
179
+ if (value === void 0) return void 0;
231
180
  if (lookup === "exact") return { [field]: value };
232
181
  if (lookup === "in") {
233
182
  const arr = Array.isArray(value) ? value : String(value).split(",");
234
- const lookupKey$1 = `${String(field)}__in`;
235
- return { [lookupKey$1]: arr };
183
+ return { [`${String(field)}__in`]: arr };
236
184
  }
237
- if (lookup === "icontains") {
238
- const lookupKey$1 = `${String(field)}__icontains`;
239
- return { [lookupKey$1]: `%${FilterSet.toScalarString(value)}%` };
240
- }
241
- const lookupKey = `${String(field)}__${lookup}`;
242
- return { [lookupKey]: value };
185
+ if (lookup === "icontains") return { [`${String(field)}__icontains`]: `%${FilterSet.toScalarString(value)}%` };
186
+ return { [`${String(field)}__${lookup}`]: value };
243
187
  }
244
188
  static resolveLookupParam(baseParam, lookup) {
245
189
  if (lookup === "exact") return baseParam;
246
190
  return `${baseParam}__${lookup}`;
247
191
  }
248
192
  static resolveParserValue(value, parser) {
249
- if (value === undefined) return undefined;
250
- if (parser === undefined) return value;
193
+ if (value === void 0) return;
194
+ if (parser === void 0) return value;
251
195
  return parser(value);
252
196
  }
253
197
  static toScalarString(value) {
@@ -272,14 +216,14 @@ var FilterSet = class FilterSet {
272
216
  */
273
217
  apply(params) {
274
218
  const filters = [];
275
- const keys = new Set();
219
+ const keys = /* @__PURE__ */ new Set();
276
220
  for (const [key] of params.entries()) keys.add(key);
277
221
  for (const key of keys) {
278
- const resolver = this.spec[key] ?? (this.allowAllParams ? this.buildAllResolver(key) : undefined);
222
+ const resolver = this.spec[key] ?? (this.allowAllParams ? this.buildAllResolver(key) : void 0);
279
223
  if (!resolver) continue;
280
224
  const rawValue = params.getAll(key);
281
225
  const value = rawValue.length > 1 ? rawValue : rawValue[0];
282
- if (value === undefined) continue;
226
+ if (value === void 0) continue;
283
227
  const filter = this.resolveFilter(resolver, value);
284
228
  if (filter) filters.push(filter);
285
229
  }
@@ -287,11 +231,11 @@ var FilterSet = class FilterSet {
287
231
  }
288
232
  buildAllResolver(param) {
289
233
  const segments = param.split("__").filter(Boolean);
290
- if (segments.length === 0) return undefined;
234
+ if (segments.length === 0) return;
291
235
  const lastSegment = segments.at(-1);
292
236
  const lookup = isFilterLookup(lastSegment) ? lastSegment : "exact";
293
237
  const field = (lookup === "exact" ? segments : segments.slice(0, -1)).join("__");
294
- if (!field) return undefined;
238
+ if (!field) return;
295
239
  if (lookup === "exact") return {
296
240
  type: InternalFilterType.SCALAR,
297
241
  column: field
@@ -299,7 +243,7 @@ var FilterSet = class FilterSet {
299
243
  return FilterSet.createLookupResolver(field, lookup);
300
244
  }
301
245
  resolveFilter(resolver, value) {
302
- if (value === undefined) return undefined;
246
+ if (value === void 0) return void 0;
303
247
  switch (resolver.type) {
304
248
  case InternalFilterType.SCALAR: return { [resolver.column]: value };
305
249
  case InternalFilterType.ILIKE: {
@@ -310,17 +254,13 @@ var FilterSet = class FilterSet {
310
254
  });
311
255
  return filter;
312
256
  }
313
- case InternalFilterType.RANGE: {
314
- const lookupKey = `${String(resolver.column)}__${resolver.op}`;
315
- return { [lookupKey]: value };
316
- }
257
+ case InternalFilterType.RANGE: return { [`${String(resolver.column)}__${resolver.op}`]: value };
317
258
  case InternalFilterType.IN: {
318
259
  const arr = Array.isArray(value) ? value : String(value).split(",");
319
- const lookupKey = `${String(resolver.column)}__in`;
320
- return { [lookupKey]: arr };
260
+ return { [`${String(resolver.column)}__in`]: arr };
321
261
  }
322
262
  case InternalFilterType.CUSTOM: return resolver.apply(value);
323
- default: return undefined;
263
+ default: return;
324
264
  }
325
265
  }
326
266
  applyFieldParserOverride(resolver, parsers) {
@@ -341,28 +281,31 @@ var FilterSet = class FilterSet {
341
281
  }
342
282
  }
343
283
  };
344
-
345
284
  //#endregion
346
285
  //#region src/filters/index.ts
347
- var filters_exports = {};
348
- __export(filters_exports, { FilterSet: () => FilterSet });
349
-
286
+ var filters_exports = /* @__PURE__ */ __exportAll({ FilterSet: () => FilterSet });
350
287
  //#endregion
351
288
  //#region src/pagination/CursorPaginationInput.ts
352
- const CursorPaginationInput = z$1.object({
353
- limit: z$1.preprocess((value) => {
354
- if (value === undefined || value === null || value === "") return undefined;
289
+ const CursorPaginationInput = z.object({
290
+ limit: z.preprocess((value) => {
291
+ if (value === void 0 || value === null || value === "") return;
355
292
  const parsed = Number.parseInt(String(value), 10);
356
- if (!Number.isFinite(parsed) || parsed <= 0) return undefined;
293
+ if (!Number.isFinite(parsed) || parsed <= 0) return;
357
294
  return parsed;
358
- }, z$1.number().int().min(1).transform((value) => Math.min(value, 100)).optional()),
359
- cursor: z$1.string().nullable().default(null),
360
- ordering: z$1.string().optional()
295
+ }, z.number().int().min(1).transform((value) => Math.min(value, 100)).optional()),
296
+ cursor: z.string().nullable().default(null),
297
+ ordering: z.string().optional()
361
298
  });
362
-
363
299
  //#endregion
364
300
  //#region src/paginators/CursorPaginator.ts
301
+ /**
302
+ * Represents a single cursor page of results.
303
+ * Cursor pages do not expose numeric page navigation like offset pagination.
304
+ */
365
305
  var CursorPage = class CursorPage {
306
+ results;
307
+ nextCursor;
308
+ previousCursor;
366
309
  static BRAND = "tango.resources.cursor_page";
367
310
  __tangoBrand = CursorPage.BRAND;
368
311
  constructor(results, nextCursor, previousCursor) {
@@ -394,7 +337,15 @@ var CursorPage = class CursorPage {
394
337
  return this.results.length;
395
338
  }
396
339
  };
340
+ /**
341
+ * Cursor-based paginator for stable forward navigation with opaque cursor tokens.
342
+ * It supports `limit`, `cursor`, and `ordering` query params and returns DRF-style
343
+ * paginated envelopes with cursor links.
344
+ */
397
345
  var CursorPaginator = class CursorPaginator extends BasePaginator {
346
+ queryset;
347
+ perPage;
348
+ cursorField;
398
349
  static BRAND = "tango.resources.cursor_paginator";
399
350
  __tangoBrand = CursorPaginator.BRAND;
400
351
  limit;
@@ -420,9 +371,9 @@ var CursorPaginator = class CursorPaginator extends BasePaginator {
420
371
  */
421
372
  parse(params) {
422
373
  const parsed = CursorPaginationInput.parse({
423
- limit: params.get("limit") ?? undefined,
374
+ limit: params.get("limit") ?? void 0,
424
375
  cursor: params.get("cursor"),
425
- ordering: params.get("ordering") ?? undefined
376
+ ordering: params.get("ordering") ?? void 0
426
377
  });
427
378
  this.limit = parsed.limit ?? this.perPage;
428
379
  this.cursor = parsed.cursor;
@@ -470,8 +421,7 @@ var CursorPaginator = class CursorPaginator extends BasePaginator {
470
421
  const decoded = this.decodeCursor(this.cursor);
471
422
  if (decoded.field !== String(this.cursorField)) throw new Error("Invalid cursor: field mismatch");
472
423
  const lookup = this.direction === "asc" ? "__gt" : "__lt";
473
- const fieldLookup = `${String(this.cursorField)}${lookup}`;
474
- const filterInput = { [fieldLookup]: decoded.value };
424
+ const filterInput = { [`${String(this.cursorField)}${lookup}`]: decoded.value };
475
425
  qs = qs.filter(filterInput);
476
426
  }
477
427
  const orderToken = this.direction === "asc" ? String(this.cursorField) : `-${String(this.cursorField)}`;
@@ -523,39 +473,40 @@ var CursorPaginator = class CursorPaginator extends BasePaginator {
523
473
  return parsed;
524
474
  }
525
475
  };
526
-
527
476
  //#endregion
528
477
  //#region src/pagination/index.ts
529
- var pagination_exports = {};
530
- __export(pagination_exports, {
478
+ var pagination_exports = /* @__PURE__ */ __exportAll({
531
479
  BasePaginator: () => BasePaginator,
532
480
  CursorPaginationInput: () => CursorPaginationInput,
533
481
  CursorPaginator: () => CursorPaginator,
534
482
  OffsetPaginationInput: () => OffsetPaginationInput,
535
483
  OffsetPaginator: () => OffsetPaginator
536
484
  });
537
-
538
485
  //#endregion
539
486
  //#region src/paginators/index.ts
540
- var paginators_exports = {};
541
- __export(paginators_exports, {
487
+ var paginators_exports = /* @__PURE__ */ __exportAll({
542
488
  CursorPaginator: () => CursorPaginator,
543
489
  OffsetPaginator: () => OffsetPaginator
544
490
  });
545
-
546
491
  //#endregion
547
492
  //#region src/resource/index.ts
548
- var resource_exports = {};
549
-
493
+ var resource_exports = /* @__PURE__ */ __exportAll({});
550
494
  //#endregion
551
495
  //#region src/serializer/Serializer.ts
552
496
  const logger = getLogger("tango.resources.serializer");
553
497
  let hasWarnedAboutToRepresentationDeprecation = false;
498
+ /**
499
+ * DRF-inspired base serializer backed by Zod schemas.
500
+ *
501
+ * Tango serializers keep Zod as the source of truth for validation and type
502
+ * inference while centralizing create, update, and representation workflows in
503
+ * one class-owned contract.
504
+ */
554
505
  var Serializer = class {
555
506
  static createSchema = z.unknown();
556
507
  static updateSchema = z.unknown();
557
508
  static outputSchema = z.unknown();
558
- static outputResolvers = undefined;
509
+ static outputResolvers = void 0;
559
510
  /**
560
511
  * Return the serializer class for the current instance.
561
512
  */
@@ -636,7 +587,6 @@ var Serializer = class {
636
587
  };
637
588
  }
638
589
  };
639
-
640
590
  //#endregion
641
591
  //#region src/serializer/internal/InternalSerializerRelationKind.ts
642
592
  const InternalSerializerRelationKind = { MANY_TO_MANY: "manyToMany" };
@@ -648,12 +598,14 @@ const InternalManyToManyWriteStrategyKind = {
648
598
  PK_LIST: "pkList",
649
599
  SLUG_LIST: "slugList"
650
600
  };
651
-
652
601
  //#endregion
653
602
  //#region src/serializer/ModelSerializer.ts
603
+ /**
604
+ * Zod-backed serializer with default model-manager persistence behavior.
605
+ */
654
606
  var ModelSerializer = class extends Serializer {
655
607
  static model;
656
- static relationFields = undefined;
608
+ static relationFields = void 0;
657
609
  /**
658
610
  * Return the Tango model backing this serializer.
659
611
  */
@@ -680,9 +632,8 @@ var ModelSerializer = class extends Serializer {
680
632
  getOutputResolvers() {
681
633
  const baseResolvers = super.getOutputResolvers();
682
634
  const relationFields = this.getRelationFields();
683
- const relationResolvers = Object.fromEntries(Object.entries(relationFields).map(([fieldName, field]) => [fieldName, async (record) => this.serializeRelationField(record, fieldName, field)]));
684
635
  return {
685
- ...relationResolvers,
636
+ ...Object.fromEntries(Object.entries(relationFields).map(([fieldName, field]) => [fieldName, async (record) => this.serializeRelationField(record, fieldName, field)])),
686
637
  ...baseResolvers
687
638
  };
688
639
  }
@@ -743,14 +694,12 @@ var ModelSerializer = class extends Serializer {
743
694
  async applyRelationWrites(record, writes) {
744
695
  const relationFields = this.getRelationFields();
745
696
  for (const [fieldName, value] of Object.entries(writes)) {
746
- const field = relationFields[fieldName];
747
- if (!field) continue;
697
+ if (!relationFields[fieldName]) continue;
748
698
  await this.syncManyToManyRelation(record, fieldName, value);
749
699
  }
750
700
  }
751
701
  async serializeRelationField(record, fieldName, field) {
752
- const manager = this.getManyToManyManager(record, fieldName);
753
- const rows = (await manager.all().fetch()).results;
702
+ const rows = (await this.getManyToManyManager(record, fieldName).all().fetch()).results;
754
703
  const relationMeta = this.getManyToManyRelationMeta(fieldName);
755
704
  switch (field.read.kind) {
756
705
  case InternalManyToManyReadStrategyKind.PK_LIST: return rows.map((row) => row[relationMeta.targetPrimaryKey]);
@@ -758,7 +707,7 @@ var ModelSerializer = class extends Serializer {
758
707
  }
759
708
  }
760
709
  async syncManyToManyRelation(record, fieldName, value) {
761
- if (value === undefined) return;
710
+ if (value === void 0) return;
762
711
  const manager = this.getManyToManyManager(record, fieldName);
763
712
  const field = this.getRelationFields()[fieldName];
764
713
  if (!field) return;
@@ -798,12 +747,11 @@ var ModelSerializer = class extends Serializer {
798
747
  return manager;
799
748
  }
800
749
  getManyToManyRelationMeta(fieldName) {
801
- const relation$1 = this.getManager().meta.relations?.[fieldName];
802
- if (!relation$1 || relation$1.kind !== InternalSerializerRelationKind.MANY_TO_MANY) throw new Error(`Relation field '${fieldName}' is not a persisted many-to-many edge.`);
803
- return relation$1;
750
+ const relation = this.getManager().meta.relations?.[fieldName];
751
+ if (!relation || relation.kind !== InternalSerializerRelationKind.MANY_TO_MANY) throw new Error(`Relation field '${fieldName}' is not a persisted many-to-many edge.`);
752
+ return relation;
804
753
  }
805
754
  };
806
-
807
755
  //#endregion
808
756
  //#region src/serializer/relation.ts
809
757
  function pkList() {
@@ -834,18 +782,20 @@ const relation = {
834
782
  nested,
835
783
  slugList
836
784
  };
837
-
838
785
  //#endregion
839
786
  //#region src/serializer/index.ts
840
- var serializer_exports = {};
841
- __export(serializer_exports, {
787
+ var serializer_exports = /* @__PURE__ */ __exportAll({
842
788
  ModelSerializer: () => ModelSerializer,
843
789
  Serializer: () => Serializer,
844
790
  relation: () => relation
845
791
  });
846
-
847
792
  //#endregion
848
793
  //#region src/viewset/ModelViewSet.ts
794
+ /**
795
+ * Base class for creating RESTful API viewsets with built-in CRUD operations.
796
+ * Provides list, retrieve, create, update, and delete methods with filtering,
797
+ * search, pagination, and ordering support.
798
+ */
849
799
  var ModelViewSet = class ModelViewSet {
850
800
  static BRAND = "tango.resources.model_view_set";
851
801
  static actions = [];
@@ -869,8 +819,7 @@ var ModelViewSet = class ModelViewSet {
869
819
  static getActions(viewsetOrConstructor) {
870
820
  const viewset = ModelViewSet.isModelViewSet(viewsetOrConstructor) ? viewsetOrConstructor : null;
871
821
  const constructorValue = viewset ? viewset.constructor : viewsetOrConstructor;
872
- const actions = Array.isArray(constructorValue.actions) ? constructorValue.actions : [];
873
- return actions.map((action) => ({
822
+ return (Array.isArray(constructorValue.actions) ? constructorValue.actions : []).map((action) => ({
874
823
  ...action,
875
824
  path: viewset ? viewset.resolveActionPath(action) : ModelViewSet.resolvePathFromDescriptor(action.name, action.path)
876
825
  }));
@@ -950,8 +899,7 @@ var ModelViewSet = class ModelViewSet {
950
899
  const search = params.getSearch();
951
900
  if (search && this.searchFields.length > 0) {
952
901
  const searchFilters = this.searchFields.map((field) => {
953
- const lookup = `${String(field)}__icontains`;
954
- return { [lookup]: search };
902
+ return { [`${String(field)}__icontains`]: search };
955
903
  });
956
904
  qs = qs.filter(Q.or(...searchFilters));
957
905
  }
@@ -963,12 +911,10 @@ var ModelViewSet = class ModelViewSet {
963
911
  });
964
912
  if (orderTokens.length > 0) qs = qs.orderBy(...orderTokens.map((token) => token));
965
913
  }
966
- const paginatedQueryset = paginator.apply(qs);
967
- const resultPromise = paginatedQueryset.fetch();
968
- const totalCountPromise = paginator.needsTotalCount() ? qs.count() : Promise.resolve(undefined);
914
+ const resultPromise = paginator.apply(qs).fetch();
915
+ const totalCountPromise = paginator.needsTotalCount() ? qs.count() : Promise.resolve(void 0);
969
916
  const [result, totalCount] = await Promise.all([resultPromise, totalCountPromise]);
970
- const serializer = this.getSerializer();
971
- const rows = await serializer.serializeMany(result.items);
917
+ const rows = await this.getSerializer().serializeMany(result.items);
972
918
  const response = paginator.toResponse(rows, { totalCount });
973
919
  return TangoResponse.json(response, { status: 200 });
974
920
  } catch (error) {
@@ -997,7 +943,7 @@ var ModelViewSet = class ModelViewSet {
997
943
  try {
998
944
  const body = await ctx.request.json();
999
945
  const result = await this.getSerializer().create(body);
1000
- return TangoResponse.created(undefined, result);
946
+ return TangoResponse.created(void 0, result);
1001
947
  } catch (error) {
1002
948
  return this.handleError(error);
1003
949
  }
@@ -1059,12 +1005,10 @@ var ModelViewSet = class ModelViewSet {
1059
1005
  return primaryKeyField.name;
1060
1006
  }
1061
1007
  };
1062
-
1063
1008
  //#endregion
1064
1009
  //#region src/viewset/index.ts
1065
- var viewset_exports = {};
1066
- __export(viewset_exports, { ModelViewSet: () => ModelViewSet });
1067
-
1010
+ var viewset_exports = /* @__PURE__ */ __exportAll({ ModelViewSet: () => ModelViewSet });
1068
1011
  //#endregion
1069
1012
  export { APIView, CreateAPIView, CreateModelMixin, CursorPaginationInput, CursorPaginator, DestroyModelMixin, FilterSet, GenericAPIView, ListAPIView, ListCreateAPIView, ListModelMixin, ModelSerializer, ModelViewSet, OffsetPaginationInput, OffsetPaginator, RequestContext, RetrieveAPIView, RetrieveDestroyAPIView, RetrieveModelMixin, RetrieveUpdateAPIView, RetrieveUpdateDestroyAPIView, Serializer, UpdateModelMixin, context_exports as context, filters_exports as filters, pagination_exports as pagination, paginators_exports as paginators, relation, resource_exports as resource, serializer_exports as serializer, view_exports as view, viewset_exports as viewset };
1013
+
1070
1014
  //# sourceMappingURL=index.js.map