@zeroxsolutions/response 0.14.0 → 0.14.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/CHANGELOG.md CHANGED
@@ -1,3 +1,31 @@
1
+ ## 0.14.2 (2026-09-29)
2
+
3
+ ### 🩹 Fixes
4
+
5
+ - **response:** permit the fields of every declaration a type is reached by ([2ac6992](https://github.com/zeroxsolutions/zeroxsolutions/commit/2ac6992))
6
+
7
+ ### ❤️ Thank You
8
+
9
+ - Claude Opus 5.5 (1M context)
10
+ - Lương Văn Tú
11
+
12
+ ## 0.14.1 (2026-09-28)
13
+
14
+ ### 🚀 Features
15
+
16
+ - **response:** type a serialized collection by the members it was handed ([5f1a2f5](https://github.com/zeroxsolutions/zeroxsolutions/commit/5f1a2f5))
17
+ - **response:** let a collection response declare its meta and links ([7286ca3](https://github.com/zeroxsolutions/zeroxsolutions/commit/7286ca3))
18
+ - **response:** ship the counted meta and offset links a collection sends ([44adb03](https://github.com/zeroxsolutions/zeroxsolutions/commit/44adb03))
19
+
20
+ ### 🩹 Fixes
21
+
22
+ - **response:** refuse a serialized collection that may lack a declared member ([f2e2c4f](https://github.com/zeroxsolutions/zeroxsolutions/commit/f2e2c4f))
23
+
24
+ ### ❤️ Thank You
25
+
26
+ - Claude Opus 5.5 (1M context)
27
+ - Lương Văn Tú
28
+
1
29
  ## 0.14.0 (2026-09-28)
2
30
 
3
31
  ### 🚀 Features
package/README.md CHANGED
@@ -5,21 +5,38 @@ of `@zeroxsolutions/query`, which owns the query-string half, and shaped the sam
5
5
  wire spelling.
6
6
 
7
7
  ```ts
8
- import { errorResponse, jsonApiResource, listResponse, okResponse, toOne } from '@zeroxsolutions/response';
8
+ import {
9
+ countedCollectionMetaSchema,
10
+ errorResponse,
11
+ jsonApiResource,
12
+ listResponse,
13
+ offsetPaginationLinksSchema,
14
+ okResponse,
15
+ toOne,
16
+ } from '@zeroxsolutions/response';
9
17
 
10
18
  export const subjects = jsonApiResource('subjects', subjectAttributesSchema, {
11
19
  relationships: { org: toOne(() => orgs, (row) => row.org) },
12
20
  });
13
21
 
14
22
  responses: {
15
- 200: listResponse('Subjects', subjects), // { data: [...], included?, links? }
23
+ 200: listResponse('Subjects', subjects, { // { data: [...], included?, links, meta }
24
+ meta: countedCollectionMetaSchema,
25
+ links: offsetPaginationLinksSchema,
26
+ }),
16
27
  201: okResponse('Subject created', subjects), // { data: {...}, included?, ... }
17
28
  401: errorResponse('Authentication required'), // { errors: [...] }
18
29
  }
19
30
 
20
- return c.json(await subjects.serializeMany(rows, { links }, c.req.valid('query')));
31
+ return c.json(await subjects.serializeMany(rows, { links, meta: { total } }, c.req.valid('query')));
21
32
  ```
22
33
 
34
+ A collection declares the `meta` and `links` it sends on every answer, and each one declared is published
35
+ as required and typed. `serializeMany` types its document off what it is handed, so a handler that drops
36
+ `total` does not compile. A collection that declares neither keeps the standard's optional, free-form
37
+ members. `meta.total` is this house's own member: the cursor-pagination profile's `meta.page.total`
38
+ describes cursor paging, and these collections page by offset.
39
+
23
40
  Both take the **item** on a list and wrap it, so no route restates the carrier. They take a resource
24
41
  DECLARATION rather than a bare schema, because a document carries a `type` and its relationships, which
25
42
  a schema alone has nowhere to put.
@@ -259,8 +276,9 @@ those three members and none of them means a path segment, so borrowing one woul
259
276
  One root, no subpath, and `src/index.ts` lists every name it publishes:
260
277
 
261
278
  - `okResponse` / `listResponse` / `errorResponse`, the `JsonApiResponse` they return (exported so a
262
- consumer's declaration emit can name it), and `errorDocumentSchema`, which `errorResponse` returns
263
- inside it;
279
+ consumer's declaration emit can name it), `errorDocumentSchema`, which `errorResponse` returns
280
+ inside it, and `countedCollectionMetaSchema` / `offsetPaginationLinksSchema`, which a collection route
281
+ passes to `listResponse`;
264
282
  - the resource declaration - `jsonApiResource`, `toOne` / `toMany`, `declaredDocumentShape` - and the
265
283
  types their signatures name;
266
284
  - the error document's types, `JsonApiError`, `BASE_ERROR_CODES`, `toErrorObject` / `toErrorDocument`;
@@ -268,7 +286,7 @@ One root, no subpath, and `src/index.ts` lists every name it publishes:
268
286
  the answer, entry and map types they take;
269
287
  - `JSON_API_MEDIA_TYPE`.
270
288
 
271
- The document and resource-object schemas, the link and identifier primitives and the serializer
289
+ The document and resource-object schemas, the other link and identifier primitives and the serializer
272
290
  functions are what `jsonApiResource` is built from, and the root does not publish them: a resource's own
273
291
  `document`, `collectionDocument`, `serialize` and `serializeMany` are the one way to declare and render a
274
292
  document.
@@ -1,7 +1,8 @@
1
1
  import type { output, ZodObject, ZodType } from 'zod';
2
- import type { CollectionDocument, ResourceDocument } from './document.js';
2
+ import { type CollectionDocument, type ResourceDocument } from './document.js';
3
3
  import { errorDocumentSchema } from './errors/index.js';
4
4
  import { JSON_API_MEDIA_TYPE } from './media-type.js';
5
+ import type { Meta, PaginationLinks } from './primitives.js';
5
6
  import type { JsonApiResource } from './resource.js';
6
7
  /**
7
8
  * An OpenAPI response object for this spelling - one media type, one schema, and the `Retry-After` a
@@ -45,6 +46,15 @@ export declare function errorResponse(description: string, options?: {
45
46
  }): JsonApiResponse<typeof errorDocumentSchema>;
46
47
  /** The response object for a single resource, off the resource's own declaration. */
47
48
  export declare function okResponse<A extends ZodObject, C = unknown>(description: string, resource: JsonApiResource<A, C>): JsonApiResponse<ZodType<ResourceDocument<output<A>>>>;
48
- /** The response object for a collection, off the resource's own declaration. */
49
- export declare function listResponse<A extends ZodObject, C = unknown>(description: string, resource: JsonApiResource<A, C>): JsonApiResponse<ZodType<CollectionDocument<output<A>>>>;
49
+ /**
50
+ * The response object for a collection, off the resource's own declaration.
51
+ *
52
+ * @param members - the `meta` and `links` this route sends on every answer. Each one declared is published
53
+ * as required and typed, so a client reads it without narrowing; one left out stays the standard's
54
+ * optional, free-form member.
55
+ */
56
+ export declare function listResponse<A extends ZodObject, C = unknown, M extends ZodType<Meta> = never, L extends ZodType<PaginationLinks> = never>(description: string, resource: JsonApiResource<A, C>, members?: {
57
+ meta?: M;
58
+ links?: L;
59
+ }): JsonApiResponse<ZodType<CollectionDocument<output<A>, output<M>, output<L>>>>;
50
60
  //# sourceMappingURL=api-response.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"api-response.d.ts","sourceRoot":"","sources":["../src/api-response.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,MAAM,EAAE,SAAS,EAAE,OAAO,EAAE,MAAM,KAAK,CAAC;AACtD,OAAO,KAAK,EAAE,kBAAkB,EAAE,gBAAgB,EAAE,MAAM,eAAe,CAAC;AAC1E,OAAO,EAAE,mBAAmB,EAAE,MAAM,mBAAmB,CAAC;AACxD,OAAO,EAAE,mBAAmB,EAAE,MAAM,iBAAiB,CAAC;AACtD,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,eAAe,CAAC;AAErD;;;;;;GAMG;AACH,MAAM,WAAW,eAAe,CAAC,CAAC,SAAS,OAAO;IAChD,WAAW,EAAE,MAAM,CAAC;IACpB,OAAO,EAAE,MAAM,CAAC,OAAO,mBAAmB,EAAE;QAAE,MAAM,EAAE,CAAC,CAAA;KAAE,CAAC,CAAC;IAC3D,OAAO,CAAC,EAAE;QAAE,aAAa,EAAE,gBAAgB,CAAA;KAAE,CAAC;CAC/C;AAED;;;;;GAKG;AACH,MAAM,MAAM,gBAAgB,GAAG;IAC7B,WAAW,EAAE,MAAM,CAAC;IACpB,QAAQ,EAAE,IAAI,CAAC;IACf,MAAM,EAAE;QAAE,IAAI,EAAE,SAAS,CAAC;QAAC,OAAO,EAAE,CAAC,CAAA;KAAE,CAAC;CACzC,CAAC;AAOF;;;;;;GAMG;AACH,wBAAgB,aAAa,CAC3B,WAAW,EAAE,MAAM,EACnB,OAAO,CAAC,EAAE;IAAE,iBAAiB,EAAE,MAAM,CAAA;CAAE,GACtC,eAAe,CAAC,OAAO,mBAAmB,CAAC,CAqB7C;AAED,qFAAqF;AACrF,wBAAgB,UAAU,CAAC,CAAC,SAAS,SAAS,EAAE,CAAC,GAAG,OAAO,EACzD,WAAW,EAAE,MAAM,EACnB,QAAQ,EAAE,eAAe,CAAC,CAAC,EAAE,CAAC,CAAC,GAC9B,eAAe,CAAC,OAAO,CAAC,gBAAgB,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAEvD;AAED,gFAAgF;AAChF,wBAAgB,YAAY,CAAC,CAAC,SAAS,SAAS,EAAE,CAAC,GAAG,OAAO,EAC3D,WAAW,EAAE,MAAM,EACnB,QAAQ,EAAE,eAAe,CAAC,CAAC,EAAE,CAAC,CAAC,GAC9B,eAAe,CAAC,OAAO,CAAC,kBAAkB,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAEzD"}
1
+ {"version":3,"file":"api-response.d.ts","sourceRoot":"","sources":["../src/api-response.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,MAAM,EAAE,SAAS,EAAE,OAAO,EAAE,MAAM,KAAK,CAAC;AACtD,OAAO,EAA4B,KAAK,kBAAkB,EAAE,KAAK,gBAAgB,EAAE,MAAM,eAAe,CAAC;AACzG,OAAO,EAAE,mBAAmB,EAAE,MAAM,mBAAmB,CAAC;AACxD,OAAO,EAAE,mBAAmB,EAAE,MAAM,iBAAiB,CAAC;AACtD,OAAO,KAAK,EAAE,IAAI,EAAE,eAAe,EAAE,MAAM,iBAAiB,CAAC;AAC7D,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,eAAe,CAAC;AAErD;;;;;;GAMG;AACH,MAAM,WAAW,eAAe,CAAC,CAAC,SAAS,OAAO;IAChD,WAAW,EAAE,MAAM,CAAC;IACpB,OAAO,EAAE,MAAM,CAAC,OAAO,mBAAmB,EAAE;QAAE,MAAM,EAAE,CAAC,CAAA;KAAE,CAAC,CAAC;IAC3D,OAAO,CAAC,EAAE;QAAE,aAAa,EAAE,gBAAgB,CAAA;KAAE,CAAC;CAC/C;AAED;;;;;GAKG;AACH,MAAM,MAAM,gBAAgB,GAAG;IAC7B,WAAW,EAAE,MAAM,CAAC;IACpB,QAAQ,EAAE,IAAI,CAAC;IACf,MAAM,EAAE;QAAE,IAAI,EAAE,SAAS,CAAC;QAAC,OAAO,EAAE,CAAC,CAAA;KAAE,CAAC;CACzC,CAAC;AAOF;;;;;;GAMG;AACH,wBAAgB,aAAa,CAC3B,WAAW,EAAE,MAAM,EACnB,OAAO,CAAC,EAAE;IAAE,iBAAiB,EAAE,MAAM,CAAA;CAAE,GACtC,eAAe,CAAC,OAAO,mBAAmB,CAAC,CAqB7C;AAED,qFAAqF;AACrF,wBAAgB,UAAU,CAAC,CAAC,SAAS,SAAS,EAAE,CAAC,GAAG,OAAO,EACzD,WAAW,EAAE,MAAM,EACnB,QAAQ,EAAE,eAAe,CAAC,CAAC,EAAE,CAAC,CAAC,GAC9B,eAAe,CAAC,OAAO,CAAC,gBAAgB,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAEvD;AAED;;;;;;GAMG;AACH,wBAAgB,YAAY,CAC1B,CAAC,SAAS,SAAS,EACnB,CAAC,GAAG,OAAO,EACX,CAAC,SAAS,OAAO,CAAC,IAAI,CAAC,GAAG,KAAK,EAC/B,CAAC,SAAS,OAAO,CAAC,eAAe,CAAC,GAAG,KAAK,EAE1C,WAAW,EAAE,MAAM,EACnB,QAAQ,EAAE,eAAe,CAAC,CAAC,EAAE,CAAC,CAAC,EAC/B,OAAO,GAAE;IAAE,IAAI,CAAC,EAAE,CAAC,CAAC;IAAC,KAAK,CAAC,EAAE,CAAC,CAAA;CAAO,GACpC,eAAe,CAAC,OAAO,CAAC,kBAAkB,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,MAAM,CAAC,CAAC,CAAC,EAAE,MAAM,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAQ/E"}
@@ -1,3 +1,4 @@
1
+ import { collectionDocumentSchema } from './document.js';
1
2
  import { errorDocumentSchema } from './errors/index.js';
2
3
  import { JSON_API_MEDIA_TYPE } from './media-type.js';
3
4
  const answers = (description, schema) => ({
@@ -35,7 +36,16 @@ export function errorResponse(description, options) {
35
36
  export function okResponse(description, resource) {
36
37
  return answers(description, resource.document);
37
38
  }
38
- /** The response object for a collection, off the resource's own declaration. */
39
- export function listResponse(description, resource) {
40
- return answers(description, resource.collectionDocument);
39
+ /**
40
+ * The response object for a collection, off the resource's own declaration.
41
+ *
42
+ * @param members - the `meta` and `links` this route sends on every answer. Each one declared is published
43
+ * as required and typed, so a client reads it without narrowing; one left out stays the standard's
44
+ * optional, free-form member.
45
+ */
46
+ export function listResponse(description, resource, members = {}) {
47
+ const declared = members.meta !== undefined || members.links !== undefined;
48
+ return answers(description, declared
49
+ ? collectionDocumentSchema(resource, members)
50
+ : resource.collectionDocument);
41
51
  }
@@ -1 +1 @@
1
- {"version":3,"file":"document-shape.d.ts","sourceRoot":"","sources":["../src/document-shape.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,kBAAkB,EAAE,MAAM,qBAAqB,CAAC;AAGzE;;;;;;;;;;;;;GAaG;AACH,wBAAgB,qBAAqB,CAAC,CAAC,SAAS,SAAS,EAAE,KAAK,CAAC,CAAC,SAAS,MAAM,GAAG,CAAC,EACnF,IAAI,EAAE,CAAC,EACP,OAAO,GAAE;IAAE,eAAe,CAAC,EAAE,CAAC,CAAA;CAAO,GACpC,kBAAkB,CAAC,CAAC,EAAE,CAAC,CAAC,CAyB1B"}
1
+ {"version":3,"file":"document-shape.d.ts","sourceRoot":"","sources":["../src/document-shape.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,kBAAkB,EAAE,MAAM,qBAAqB,CAAC;AAGzE;;;;;;;;;;;;;GAaG;AACH,wBAAgB,qBAAqB,CAAC,CAAC,SAAS,SAAS,EAAE,KAAK,CAAC,CAAC,SAAS,MAAM,GAAG,CAAC,EACnF,IAAI,EAAE,CAAC,EACP,OAAO,GAAE;IAAE,eAAe,CAAC,EAAE,CAAC,CAAA;CAAO,GACpC,kBAAkB,CAAC,CAAC,EAAE,CAAC,CAAC,CA+B1B"}
@@ -14,7 +14,16 @@
14
14
  */
15
15
  export function declaredDocumentShape(root, options = {}) {
16
16
  const maxDepth = options.maxIncludeDepth ?? 3;
17
- const reached = new Map([[root.type, root]]);
17
+ // A type two declarations reach (a whole resource under one relationship, its identifier alone under another) is
18
+ // one type to a fieldset, so it permits the fields of every declaration that reaches it.
19
+ const fields = new Map();
20
+ const reach = (resource) => {
21
+ const names = fields.get(resource.type) ?? new Set();
22
+ for (const name of [...Object.keys(resource.attributes.shape), ...Object.keys(resource.relationships)])
23
+ names.add(name);
24
+ fields.set(resource.type, names);
25
+ };
26
+ reach(root);
18
27
  const include = [];
19
28
  // A path never revisits a type: two resources naming each other make `class.subject.class...` infinite, and the
20
29
  // second lap addresses the same rows the first one already compounded.
@@ -27,14 +36,13 @@ export function declaredDocumentShape(root, options = {}) {
27
36
  continue;
28
37
  const path = prefix === '' ? name : `${prefix}.${name}`;
29
38
  include.push(path);
30
- reached.set(related.type, related);
39
+ reach(related);
31
40
  walk(related, path, new Set([...seen, related.type]), depth + 1);
32
41
  }
33
42
  };
34
43
  walk(root, '', new Set([root.type]), 0);
35
- const fields = Object.fromEntries([...reached.values()].map((resource) => [
36
- resource.type,
37
- [...Object.keys(resource.attributes.shape), ...Object.keys(resource.relationships)],
38
- ]));
39
- return { fields, include };
44
+ return {
45
+ fields: Object.fromEntries([...fields].map(([type, names]) => [type, [...names]])),
46
+ include,
47
+ };
40
48
  }
@@ -24,14 +24,41 @@ export interface ResourceDocument<A> {
24
24
  meta?: Meta;
25
25
  jsonapi?: JsonApiObject;
26
26
  }
27
- /** A collection document - the same members with `data` an array and paginated `links`. */
28
- export interface CollectionDocument<A> {
27
+ /**
28
+ * One top-level member of a collection document. A route that declared it sends it every time, so it is
29
+ * required and typed; one that did not keeps the standard's optional, free-form member.
30
+ */
31
+ type CollectionMember<K extends 'links' | 'meta', Declared, Loose> = [Declared] extends [never] ? {
32
+ [P in K]?: Loose;
33
+ } : {
34
+ [P in K]: Declared;
35
+ };
36
+ /**
37
+ * A collection document - the same members with `data` an array and paginated `links`.
38
+ *
39
+ * `M` and `L` are the `meta` and `links` a route declared, `never` where it declared none.
40
+ */
41
+ export type CollectionDocument<A, M = never, L = never> = {
29
42
  data: ResourceObject<A>[];
30
43
  included?: ResourceObject<unknown>[];
31
- links?: PaginationLinks;
32
- meta?: Meta;
33
44
  jsonapi?: JsonApiObject;
34
- }
45
+ } & CollectionMember<'links', L, PaginationLinks> & CollectionMember<'meta', M, Meta>;
46
+ /**
47
+ * The collection document `serializeMany` answers for `extras`: each member it was handed is present and
48
+ * typed as handed, which is what a route's declared members are checked against.
49
+ *
50
+ * Tuple-wrapped so it does not distribute: extras that carry `meta` on one branch of a union and not the
51
+ * other may send none, and must not type it as present.
52
+ */
53
+ export type CollectionDocumentFor<A, X> = CollectionDocument<A, [
54
+ X
55
+ ] extends [{
56
+ meta: infer M;
57
+ }] ? M : never, [
58
+ X
59
+ ] extends [{
60
+ links: infer L;
61
+ }] ? L : never>;
35
62
  /**
36
63
  * Wraps `attributes` into a resource object - `{ type, id, attributes?, relationships?, links?, meta? }`.
37
64
  *
@@ -53,7 +80,12 @@ export declare function resourceDocumentSchema<A = unknown, C = unknown>(root: R
53
80
  /**
54
81
  * The document for a COLLECTION - the same members, with `data` an array and paginated `links`.
55
82
  *
56
- * Strict at the top level for the same reason as {@link resourceDocumentSchema}.
83
+ * A member in `members` replaces the standard's optional one, so the route that declared it publishes it
84
+ * as required. Strict at the top level for the same reason as {@link resourceDocumentSchema}.
57
85
  */
58
- export declare function collectionDocumentSchema<A = unknown, C = unknown>(root: RelatedResource<C>): ZodType<CollectionDocument<A>>;
86
+ export declare function collectionDocumentSchema<A = unknown, C = unknown, M = never, L = never>(root: RelatedResource<C>, members?: {
87
+ meta?: ZodType;
88
+ links?: ZodType;
89
+ }): ZodType<CollectionDocument<A, M, L>>;
90
+ export {};
59
91
  //# sourceMappingURL=document.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"document.d.ts","sourceRoot":"","sources":["../src/document.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,SAAS,EAAE,OAAO,EAAE,MAAM,KAAK,CAAC;AAC9C,OAAO,EAML,KAAK,aAAa,EAClB,KAAK,IAAI,EACT,KAAK,eAAe,EACpB,KAAK,iBAAiB,EACtB,KAAK,kBAAkB,EACvB,KAAK,aAAa,EAClB,KAAK,aAAa,EACnB,MAAM,iBAAiB,CAAC;AACzB,OAAO,EAA2C,KAAK,eAAe,EAAE,KAAK,eAAe,EAAE,MAAM,mBAAmB,CAAC;AAExH,4FAA4F;AAC5F,MAAM,WAAW,YAAY;IAC3B,IAAI,CAAC,EAAE,kBAAkB,GAAG,kBAAkB,EAAE,GAAG,IAAI,CAAC;IACxD,KAAK,CAAC,EAAE,iBAAiB,CAAC;IAC1B,IAAI,CAAC,EAAE,IAAI,CAAC;CACb;AAED,iFAAiF;AACjF,MAAM,WAAW,cAAc,CAAC,CAAC;IAC/B,IAAI,EAAE,MAAM,CAAC;IACb,EAAE,EAAE,MAAM,CAAC;IAGX,UAAU,EAAE,CAAC,CAAC;IACd,aAAa,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,YAAY,CAAC,CAAC;IAC7C,KAAK,CAAC,EAAE,aAAa,CAAC;IACtB,IAAI,CAAC,EAAE,IAAI,CAAC;CACb;AAED,yGAAyG;AACzG,MAAM,WAAW,gBAAgB,CAAC,CAAC;IACjC,IAAI,EAAE,cAAc,CAAC,CAAC,CAAC,CAAC;IACxB,QAAQ,CAAC,EAAE,cAAc,CAAC,OAAO,CAAC,EAAE,CAAC;IACrC,KAAK,CAAC,EAAE,aAAa,CAAC;IACtB,IAAI,CAAC,EAAE,IAAI,CAAC;IACZ,OAAO,CAAC,EAAE,aAAa,CAAC;CACzB;AAED,2FAA2F;AAC3F,MAAM,WAAW,kBAAkB,CAAC,CAAC;IACnC,IAAI,EAAE,cAAc,CAAC,CAAC,CAAC,EAAE,CAAC;IAC1B,QAAQ,CAAC,EAAE,cAAc,CAAC,OAAO,CAAC,EAAE,CAAC;IACrC,KAAK,CAAC,EAAE,eAAe,CAAC;IACxB,IAAI,CAAC,EAAE,IAAI,CAAC;IACZ,OAAO,CAAC,EAAE,aAAa,CAAC;CACzB;AAED;;;;;GAKG;AACH,wBAAgB,oBAAoB,CAAC,CAAC,EACpC,IAAI,EAAE,MAAM,EACZ,UAAU,EAAE,SAAS,EACrB,aAAa,GAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,eAAe,CAAC,CAAC,CAAC,CAAC,CAAM,GAC/D,OAAO,CAUT;AAgBD;;;;;;;;;GASG;AACH,wBAAgB,sBAAsB,CAAC,CAAC,GAAG,OAAO,EAAE,CAAC,GAAG,OAAO,EAC7D,IAAI,EAAE,eAAe,CAAC,CAAC,CAAC,GACvB,OAAO,CAAC,gBAAgB,CAAC,CAAC,CAAC,CAAC,CAQ9B;AAED;;;;GAIG;AACH,wBAAgB,wBAAwB,CAAC,CAAC,GAAG,OAAO,EAAE,CAAC,GAAG,OAAO,EAC/D,IAAI,EAAE,eAAe,CAAC,CAAC,CAAC,GACvB,OAAO,CAAC,kBAAkB,CAAC,CAAC,CAAC,CAAC,CAQhC"}
1
+ {"version":3,"file":"document.d.ts","sourceRoot":"","sources":["../src/document.ts"],"names":[],"mappings":"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"}
package/dist/document.js CHANGED
@@ -53,14 +53,15 @@ export function resourceDocumentSchema(root) {
53
53
  /**
54
54
  * The document for a COLLECTION - the same members, with `data` an array and paginated `links`.
55
55
  *
56
- * Strict at the top level for the same reason as {@link resourceDocumentSchema}.
56
+ * A member in `members` replaces the standard's optional one, so the route that declared it publishes it
57
+ * as required. Strict at the top level for the same reason as {@link resourceDocumentSchema}.
57
58
  */
58
- export function collectionDocumentSchema(root) {
59
+ export function collectionDocumentSchema(root, members = {}) {
59
60
  return z.strictObject({
60
61
  data: z.array(root.resourceObject),
61
62
  included: includedSchema(root).optional(),
62
- links: paginationLinksSchema.optional(),
63
- meta: metaSchema.optional(),
63
+ links: members.links ?? paginationLinksSchema.optional(),
64
+ meta: members.meta ?? metaSchema.optional(),
64
65
  jsonapi: jsonApiObjectSchema.optional(),
65
66
  });
66
67
  }
package/dist/index.d.ts CHANGED
@@ -3,13 +3,14 @@
3
3
  */
4
4
  export { errorResponse, listResponse, okResponse, type JsonApiResponse, type RetryAfterHeader, } from './api-response.js';
5
5
  export { createdResponse } from './created-response.js';
6
- export type { CollectionDocument, Relationship, ResourceDocument, ResourceObject } from './document.js';
6
+ export type { CollectionDocument, CollectionDocumentFor, Relationship, ResourceDocument, ResourceObject, } from './document.js';
7
7
  export * from './document-graph.js';
8
8
  export { declaredDocumentShape } from './document-shape.js';
9
9
  export { createDomainErrorResolver, defineErrorMap, errorEntry, type DomainErrorAnswer, type DomainErrorEntry, } from './domain-error-resolver.js';
10
10
  export type { DomainErrorMap, ErrorClass, UpstreamErrorMap } from './error-map.js';
11
11
  export { BASE_ERROR_CODES, errorDocumentSchema, JsonApiError, toErrorDocument, toErrorObject, type ErrorDocument, type ErrorLinks, type ErrorObject, type ErrorSource, type ErrorStatus, type JsonApiErrorInit, type ResolvedError, } from './errors/index.js';
12
12
  export { JSON_API_MEDIA_TYPE } from './media-type.js';
13
+ export { countedCollectionMetaSchema, offsetPaginationLinksSchema } from './primitives.js';
13
14
  export type { JsonApiObject, Link, Meta, PaginationLinks, RelationshipLinks, ResourceIdentifier, ResourceLinks, TopLevelLinks, } from './primitives.js';
14
15
  export { toMany, toOne, type LoadContextArguments, type RelatedResource, type RelationshipDef, type RelationshipLoader, type RelationshipOptions, } from './relationship.js';
15
16
  export { jsonApiResource, type JsonApiResource, type ResourceOptions } from './resource.js';
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;GAEG;AACH,OAAO,EACL,aAAa,EACb,YAAY,EACZ,UAAU,EACV,KAAK,eAAe,EACpB,KAAK,gBAAgB,GACtB,MAAM,mBAAmB,CAAC;AAC3B,OAAO,EAAE,eAAe,EAAE,MAAM,uBAAuB,CAAC;AACxD,YAAY,EAAE,kBAAkB,EAAE,YAAY,EAAE,gBAAgB,EAAE,cAAc,EAAE,MAAM,eAAe,CAAC;AACxG,cAAc,qBAAqB,CAAC;AACpC,OAAO,EAAE,qBAAqB,EAAE,MAAM,qBAAqB,CAAC;AAC5D,OAAO,EACL,yBAAyB,EACzB,cAAc,EACd,UAAU,EACV,KAAK,iBAAiB,EACtB,KAAK,gBAAgB,GACtB,MAAM,4BAA4B,CAAC;AACpC,YAAY,EAAE,cAAc,EAAE,UAAU,EAAE,gBAAgB,EAAE,MAAM,gBAAgB,CAAC;AACnF,OAAO,EACL,gBAAgB,EAChB,mBAAmB,EACnB,YAAY,EACZ,eAAe,EACf,aAAa,EACb,KAAK,aAAa,EAClB,KAAK,UAAU,EACf,KAAK,WAAW,EAChB,KAAK,WAAW,EAChB,KAAK,WAAW,EAChB,KAAK,gBAAgB,EACrB,KAAK,aAAa,GACnB,MAAM,mBAAmB,CAAC;AAC3B,OAAO,EAAE,mBAAmB,EAAE,MAAM,iBAAiB,CAAC;AACtD,YAAY,EACV,aAAa,EACb,IAAI,EACJ,IAAI,EACJ,eAAe,EACf,iBAAiB,EACjB,kBAAkB,EAClB,aAAa,EACb,aAAa,GACd,MAAM,iBAAiB,CAAC;AACzB,OAAO,EACL,MAAM,EACN,KAAK,EACL,KAAK,oBAAoB,EACzB,KAAK,eAAe,EACpB,KAAK,eAAe,EACpB,KAAK,kBAAkB,EACvB,KAAK,mBAAmB,GACzB,MAAM,mBAAmB,CAAC;AAC3B,OAAO,EAAE,eAAe,EAAE,KAAK,eAAe,EAAE,KAAK,eAAe,EAAE,MAAM,eAAe,CAAC;AAC5F,YAAY,EAAE,gBAAgB,EAAE,UAAU,EAAE,sBAAsB,EAAE,kBAAkB,EAAE,GAAG,EAAE,MAAM,gBAAgB,CAAC;AACpH,OAAO,EAAE,6BAA6B,EAAE,KAAK,mBAAmB,EAAE,MAAM,qBAAqB,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;GAEG;AACH,OAAO,EACL,aAAa,EACb,YAAY,EACZ,UAAU,EACV,KAAK,eAAe,EACpB,KAAK,gBAAgB,GACtB,MAAM,mBAAmB,CAAC;AAC3B,OAAO,EAAE,eAAe,EAAE,MAAM,uBAAuB,CAAC;AACxD,YAAY,EACV,kBAAkB,EAClB,qBAAqB,EACrB,YAAY,EACZ,gBAAgB,EAChB,cAAc,GACf,MAAM,eAAe,CAAC;AACvB,cAAc,qBAAqB,CAAC;AACpC,OAAO,EAAE,qBAAqB,EAAE,MAAM,qBAAqB,CAAC;AAC5D,OAAO,EACL,yBAAyB,EACzB,cAAc,EACd,UAAU,EACV,KAAK,iBAAiB,EACtB,KAAK,gBAAgB,GACtB,MAAM,4BAA4B,CAAC;AACpC,YAAY,EAAE,cAAc,EAAE,UAAU,EAAE,gBAAgB,EAAE,MAAM,gBAAgB,CAAC;AACnF,OAAO,EACL,gBAAgB,EAChB,mBAAmB,EACnB,YAAY,EACZ,eAAe,EACf,aAAa,EACb,KAAK,aAAa,EAClB,KAAK,UAAU,EACf,KAAK,WAAW,EAChB,KAAK,WAAW,EAChB,KAAK,WAAW,EAChB,KAAK,gBAAgB,EACrB,KAAK,aAAa,GACnB,MAAM,mBAAmB,CAAC;AAC3B,OAAO,EAAE,mBAAmB,EAAE,MAAM,iBAAiB,CAAC;AACtD,OAAO,EAAE,2BAA2B,EAAE,2BAA2B,EAAE,MAAM,iBAAiB,CAAC;AAC3F,YAAY,EACV,aAAa,EACb,IAAI,EACJ,IAAI,EACJ,eAAe,EACf,iBAAiB,EACjB,kBAAkB,EAClB,aAAa,EACb,aAAa,GACd,MAAM,iBAAiB,CAAC;AACzB,OAAO,EACL,MAAM,EACN,KAAK,EACL,KAAK,oBAAoB,EACzB,KAAK,eAAe,EACpB,KAAK,eAAe,EACpB,KAAK,kBAAkB,EACvB,KAAK,mBAAmB,GACzB,MAAM,mBAAmB,CAAC;AAC3B,OAAO,EAAE,eAAe,EAAE,KAAK,eAAe,EAAE,KAAK,eAAe,EAAE,MAAM,eAAe,CAAC;AAC5F,YAAY,EAAE,gBAAgB,EAAE,UAAU,EAAE,sBAAsB,EAAE,kBAAkB,EAAE,GAAG,EAAE,MAAM,gBAAgB,CAAC;AACpH,OAAO,EAAE,6BAA6B,EAAE,KAAK,mBAAmB,EAAE,MAAM,qBAAqB,CAAC"}
package/dist/index.js CHANGED
@@ -8,6 +8,7 @@ export { declaredDocumentShape } from './document-shape.js';
8
8
  export { createDomainErrorResolver, defineErrorMap, errorEntry, } from './domain-error-resolver.js';
9
9
  export { BASE_ERROR_CODES, errorDocumentSchema, JsonApiError, toErrorDocument, toErrorObject, } from './errors/index.js';
10
10
  export { JSON_API_MEDIA_TYPE } from './media-type.js';
11
+ export { countedCollectionMetaSchema, offsetPaginationLinksSchema } from './primitives.js';
11
12
  export { toMany, toOne, } from './relationship.js';
12
13
  export { jsonApiResource } from './resource.js';
13
14
  export { createUpstreamErrorTranslator } from './upstream-error.js';
@@ -213,6 +213,26 @@ export declare const paginationLinksSchema: z.ZodObject<{
213
213
  }, z.core.$strip>;
214
214
  /** Collection pagination links - inferred from {@link paginationLinksSchema}. */
215
215
  export type PaginationLinks = z.infer<typeof paginationLinksSchema>;
216
+ /**
217
+ * The `meta` of a collection that counts its rows: `total` is how many match the read, across every page.
218
+ *
219
+ * This is house vocabulary, not the cursor-pagination profile's `meta.page.total`. That profile
220
+ * describes cursor paging, and this house pages by `page[offset]` and `page[limit]`.
221
+ */
222
+ export declare const countedCollectionMetaSchema: z.ZodObject<{
223
+ total: z.ZodNumber;
224
+ }, z.core.$strip>;
225
+ /**
226
+ * The links an offset-paged collection always carries. `first` and `last` always exist under offset
227
+ * paging, so they are strings; `prev` and `next` are null where there is no such page.
228
+ */
229
+ export declare const offsetPaginationLinksSchema: z.ZodObject<{
230
+ self: z.ZodString;
231
+ first: z.ZodString;
232
+ last: z.ZodString;
233
+ prev: z.ZodNullable<z.ZodString>;
234
+ next: z.ZodNullable<z.ZodString>;
235
+ }, z.core.$strip>;
216
236
  /** A relationship's links: its own `self`/`related`, and the pages of a to-many linkage. */
217
237
  export declare const relationshipLinksSchema: z.ZodObject<{
218
238
  self: z.ZodOptional<z.ZodUnion<readonly [z.ZodString, z.ZodObject<{
@@ -1 +1 @@
1
- {"version":3,"file":"primitives.d.ts","sourceRoot":"","sources":["../src/primitives.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAExB,8DAA8D;AAC9D,eAAO,MAAM,UAAU,wCAAoC,CAAC;AAC5D,oEAAoE;AACpE,MAAM,MAAM,IAAI,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,UAAU,CAAC,CAAC;AAsB9C,iGAAiG;AACjG,eAAO,MAAM,UAAU;;;;;;;;;;;;;;;8BAIrB,CAAC;AACH,gEAAgE;AAChE,MAAM,MAAM,IAAI,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,UAAU,CAAC,CAAC;AAE9C,iFAAiF;AACjF,eAAO,MAAM,mBAAmB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;iBAG9B,CAAC;AACH,6EAA6E;AAC7E,MAAM,MAAM,aAAa,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,mBAAmB,CAAC,CAAC;AAEhE,gGAAgG;AAChG,eAAO,MAAM,mBAAmB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;iBAAqE,CAAC;AACtG,gGAAgG;AAChG,MAAM,MAAM,aAAa,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,mBAAmB,CAAC,CAAC;AAEhE,gFAAgF;AAChF,eAAO,MAAM,qBAAqB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;iBAOhC,CAAC;AACH,iFAAiF;AACjF,MAAM,MAAM,eAAe,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,qBAAqB,CAAC,CAAC;AAEpE,4FAA4F;AAC5F,eAAO,MAAM,uBAAuB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;iBAKlC,CAAC;AACH,8EAA8E;AAC9E,MAAM,MAAM,iBAAiB,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,uBAAuB,CAAC,CAAC;AAExE,iGAAiG;AACjG,eAAO,MAAM,wBAAwB;;;iBAAiD,CAAC;AACvF,0FAA0F;AAC1F,MAAM,MAAM,kBAAkB,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,wBAAwB,CAAC,CAAC;AAE1E,uHAAuH;AACvH,eAAO,MAAM,mBAAmB;;;;;iBAK9B,CAAC;AACH,kFAAkF;AAClF,MAAM,MAAM,aAAa,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,mBAAmB,CAAC,CAAC"}
1
+ {"version":3,"file":"primitives.d.ts","sourceRoot":"","sources":["../src/primitives.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAExB,8DAA8D;AAC9D,eAAO,MAAM,UAAU,wCAAoC,CAAC;AAC5D,oEAAoE;AACpE,MAAM,MAAM,IAAI,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,UAAU,CAAC,CAAC;AAsB9C,iGAAiG;AACjG,eAAO,MAAM,UAAU;;;;;;;;;;;;;;;8BAIrB,CAAC;AACH,gEAAgE;AAChE,MAAM,MAAM,IAAI,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,UAAU,CAAC,CAAC;AAE9C,iFAAiF;AACjF,eAAO,MAAM,mBAAmB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;iBAG9B,CAAC;AACH,6EAA6E;AAC7E,MAAM,MAAM,aAAa,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,mBAAmB,CAAC,CAAC;AAEhE,gGAAgG;AAChG,eAAO,MAAM,mBAAmB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;iBAAqE,CAAC;AACtG,gGAAgG;AAChG,MAAM,MAAM,aAAa,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,mBAAmB,CAAC,CAAC;AAEhE,gFAAgF;AAChF,eAAO,MAAM,qBAAqB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;iBAOhC,CAAC;AACH,iFAAiF;AACjF,MAAM,MAAM,eAAe,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,qBAAqB,CAAC,CAAC;AAEpE;;;;;GAKG;AACH,eAAO,MAAM,2BAA2B;;iBAAsD,CAAC;AAE/F;;;GAGG;AACH,eAAO,MAAM,2BAA2B;;;;;;iBAMtC,CAAC;AAEH,4FAA4F;AAC5F,eAAO,MAAM,uBAAuB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;iBAKlC,CAAC;AACH,8EAA8E;AAC9E,MAAM,MAAM,iBAAiB,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,uBAAuB,CAAC,CAAC;AAExE,iGAAiG;AACjG,eAAO,MAAM,wBAAwB;;;iBAAiD,CAAC;AACvF,0FAA0F;AAC1F,MAAM,MAAM,kBAAkB,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,wBAAwB,CAAC,CAAC;AAE1E,uHAAuH;AACvH,eAAO,MAAM,mBAAmB;;;;;iBAK9B,CAAC;AACH,kFAAkF;AAClF,MAAM,MAAM,aAAa,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,mBAAmB,CAAC,CAAC"}
@@ -41,6 +41,24 @@ export const paginationLinksSchema = z.object({
41
41
  last: linkSchema.optional(),
42
42
  describedby: linkSchema.optional(),
43
43
  });
44
+ /**
45
+ * The `meta` of a collection that counts its rows: `total` is how many match the read, across every page.
46
+ *
47
+ * This is house vocabulary, not the cursor-pagination profile's `meta.page.total`. That profile
48
+ * describes cursor paging, and this house pages by `page[offset]` and `page[limit]`.
49
+ */
50
+ export const countedCollectionMetaSchema = z.object({ total: z.number().int().nonnegative() });
51
+ /**
52
+ * The links an offset-paged collection always carries. `first` and `last` always exist under offset
53
+ * paging, so they are strings; `prev` and `next` are null where there is no such page.
54
+ */
55
+ export const offsetPaginationLinksSchema = z.object({
56
+ self: z.string(),
57
+ first: z.string(),
58
+ last: z.string(),
59
+ prev: z.string().nullable(),
60
+ next: z.string().nullable(),
61
+ });
44
62
  /** A relationship's links: its own `self`/`related`, and the pages of a to-many linkage. */
45
63
  export const relationshipLinksSchema = resourceLinksSchema.extend({
46
64
  first: linkSchema.optional(),
@@ -1,5 +1,5 @@
1
1
  import type { output, ZodObject, ZodType } from 'zod';
2
- import { type CollectionDocument, type ResourceDocument } from './document.js';
2
+ import { type CollectionDocument, type CollectionDocumentFor, type ResourceDocument } from './document.js';
3
3
  import { type LoadContextArguments, type RelatedResource, type RelationshipDef } from './relationship.js';
4
4
  import { type CollectionExtras, type RequestedDocumentShape, type Row } from './serialize.js';
5
5
  /** The context every loader in a relationship map runs under. */
@@ -36,8 +36,14 @@ export interface JsonApiResource<A extends ZodObject, C = unknown, T extends str
36
36
  * @param context - what the loaders are given; the call takes it only where a loader is reachable
37
37
  */
38
38
  serialize(row: Row, requested?: RequestedDocumentShape, ...context: LoadContextArguments<C>): Promise<ResourceDocument<output<A>>>;
39
- /** Serializes rows into a collection document, merging in pagination `links` and `meta`. */
40
- serializeMany(rows: Row[], extras?: CollectionExtras, requested?: RequestedDocumentShape, ...context: LoadContextArguments<C>): Promise<CollectionDocument<output<A>>>;
39
+ /**
40
+ * Serializes rows into a collection document, merging in pagination `links` and `meta`.
41
+ *
42
+ * Typed off `extras`: a member passed is a member present, so the result checks against what the route
43
+ * declared with `listResponse`. A key that is neither `links` nor `meta` is refused, since the
44
+ * serializer drops it unsent.
45
+ */
46
+ serializeMany<X extends CollectionExtras = Record<never, never>>(rows: Row[], extras?: X & Record<Exclude<keyof X, keyof CollectionExtras>, never>, requested?: RequestedDocumentShape, ...context: LoadContextArguments<C>): Promise<CollectionDocumentFor<output<A>, X>>;
41
47
  }
42
48
  /** A relationship map's own names, and none where the map is the empty default. */
43
49
  type RelationshipName<R> = string extends keyof R ? never : keyof R & string;
@@ -1 +1 @@
1
- {"version":3,"file":"resource.d.ts","sourceRoot":"","sources":["../src/resource.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"resource.d.ts","sourceRoot":"","sources":["../src/resource.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,MAAM,EAAE,SAAS,EAAE,OAAO,EAAE,MAAM,KAAK,CAAC;AACtD,OAAO,EAIL,KAAK,kBAAkB,EACvB,KAAK,qBAAqB,EAC1B,KAAK,gBAAgB,EACtB,MAAM,eAAe,CAAC;AAGvB,OAAO,EAAe,KAAK,oBAAoB,EAAE,KAAK,eAAe,EAAE,KAAK,eAAe,EAAE,MAAM,mBAAmB,CAAC;AACvH,OAAO,EAKL,KAAK,gBAAgB,EAErB,KAAK,sBAAsB,EAE3B,KAAK,GAAG,EACT,MAAM,gBAAgB,CAAC;AAExB,iEAAiE;AAEjE,KAAK,sBAAsB,CAAC,CAAC,IAAI,CAAC,CAAC,MAAM,CAAC,CAAC,SAAS,eAAe,CAAC,MAAM,CAAC,EAAE,GAAG,CAAC,GAAG,CAAC,GAAG,OAAO,CAAC;AAEhG,uDAAuD;AACvD,MAAM,WAAW,eAAe,CAAC,CAAC;IAChC,mGAAmG;IACnG,aAAa,CAAC,EAAE,CAAC,CAAC;CACnB;AAED;;;;;;;;;;GAUG;AACH,MAAM,WAAW,eAAe,CAC9B,CAAC,SAAS,SAAS,EACnB,CAAC,GAAG,OAAO,EACX,CAAC,SAAS,MAAM,GAAG,MAAM,EAEzB,CAAC,SAAS,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,eAAe,CAAC,GAAG,EAAE,GAAG,CAAC,CAAC,CAAC,GAAG,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,eAAe,CAAC,CAAC,CAAC,CAAC,CAAC,CAC5G,SAAQ,eAAe,CAAC,CAAC,CAAC;IAC1B,QAAQ,CAAC,IAAI,EAAE,CAAC,CAAC;IACjB,QAAQ,CAAC,UAAU,EAAE,CAAC,CAAC;IACvB,QAAQ,CAAC,aAAa,EAAE,CAAC,CAAC;IAC1B,4FAA4F;IAC5F,QAAQ,CAAC,QAAQ,EAAE,OAAO,CAAC,gBAAgB,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;IACxD,8EAA8E;IAC9E,QAAQ,CAAC,kBAAkB,EAAE,OAAO,CAAC,kBAAkB,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;IACpE;;;;;;OAMG;IACH,SAAS,CACP,GAAG,EAAE,GAAG,EACR,SAAS,CAAC,EAAE,sBAAsB,EAClC,GAAG,OAAO,EAAE,oBAAoB,CAAC,CAAC,CAAC,GAClC,OAAO,CAAC,gBAAgB,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;IACxC;;;;;;OAMG;IACH,aAAa,CAAC,CAAC,SAAS,gBAAgB,GAAG,MAAM,CAAC,KAAK,EAAE,KAAK,CAAC,EAC7D,IAAI,EAAE,GAAG,EAAE,EACX,MAAM,CAAC,EAAE,CAAC,GAAG,MAAM,CAAC,OAAO,CAAC,MAAM,CAAC,EAAE,MAAM,gBAAgB,CAAC,EAAE,KAAK,CAAC,EACpE,SAAS,CAAC,EAAE,sBAAsB,EAClC,GAAG,OAAO,EAAE,oBAAoB,CAAC,CAAC,CAAC,GAClC,OAAO,CAAC,qBAAqB,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC;CACjD;AAgBD,mFAAmF;AACnF,KAAK,gBAAgB,CAAC,CAAC,IAAI,MAAM,SAAS,MAAM,CAAC,GAAG,KAAK,GAAG,MAAM,CAAC,GAAG,MAAM,CAAC;AAE7E;;;GAGG;AACH,KAAK,cAAc,CAAC,CAAC,SAAS,SAAS,EAAE,CAAC,IAAI;IAC5C,CAAC,MAAM,CAAC,CAAC,OAAO,CAAC,GAAG,MAAM,GAAG,CAAC,MAAM,GAAG,IAAI,GAAG,gBAAgB,CAAC,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,gBAAgB,CAAC,CAAC,CAAC,GAAG,CAAC,MAAM,GAAG,IAAI,CAAC,CAAC;CAC9G,SAAS,CAAC,KAAK,CAAC,GACb,OAAO,GACP,KAAK,CAAC;AAwDV,6GAA6G;AAC7G,wBAAgB,eAAe,CAC7B,KAAK,CAAC,CAAC,SAAS,MAAM,EACtB,CAAC,SAAS,SAAS,EAEnB,KAAK,CAAC,CAAC,SAAS,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,eAAe,CAAC,GAAG,EAAE,GAAG,CAAC,CAAC,CAAC,GAAG,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,KAAK,CAAC,CAAC,EACrG,CAAC,GAAG,sBAAsB,CAAC,CAAC,CAAC,EAC7B,IAAI,EAAE,CAAC,EAAE,UAAU,EAAE,CAAC,GAAG,cAAc,CAAC,CAAC,EAAE,CAAC,CAAC,EAAE,OAAO,GAAE,eAAe,CAAC,CAAC,CAAM,GAAG,eAAe,CAAC,CAAC,EAAE,CAAC,EAAE,CAAC,EAAE,CAAC,CAAC,CAkF9G"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@zeroxsolutions/response",
3
- "version": "0.14.0",
3
+ "version": "0.14.2",
4
4
  "private": false,
5
5
  "type": "module",
6
6
  "sideEffects": false,