@zeroxsolutions/response 0.13.0 → 0.14.1
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 +89 -0
- package/README.md +25 -6
- package/dist/api-response.d.ts +13 -3
- package/dist/api-response.d.ts.map +1 -1
- package/dist/api-response.js +13 -3
- package/dist/created-response.d.ts +23 -0
- package/dist/created-response.d.ts.map +1 -0
- package/dist/created-response.js +19 -0
- package/dist/document-load.d.ts +1 -1
- package/dist/document-load.d.ts.map +1 -1
- package/dist/document-load.js +20 -8
- package/dist/document.d.ts +55 -11
- package/dist/document.d.ts.map +1 -1
- package/dist/document.js +23 -10
- package/dist/errors/error-document.d.ts +7 -2
- package/dist/errors/error-document.d.ts.map +1 -1
- package/dist/errors/error-document.js +7 -3
- package/dist/index.d.ts +4 -2
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +2 -0
- package/dist/member-name.d.ts +9 -0
- package/dist/member-name.d.ts.map +1 -0
- package/dist/member-name.js +15 -0
- package/dist/primitives.d.ts +194 -2
- package/dist/primitives.d.ts.map +1 -1
- package/dist/primitives.js +32 -2
- package/dist/relationship.d.ts.map +1 -1
- package/dist/relationship.js +7 -3
- package/dist/resource.d.ts +19 -4
- package/dist/resource.d.ts.map +1 -1
- package/dist/resource.js +70 -2
- package/dist/serialize.d.ts +14 -4
- package/dist/serialize.d.ts.map +1 -1
- package/dist/serialize.js +32 -3
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -1,3 +1,92 @@
|
|
|
1
|
+
## 0.14.1 (2026-09-28)
|
|
2
|
+
|
|
3
|
+
### 🚀 Features
|
|
4
|
+
|
|
5
|
+
- **response:** type a serialized collection by the members it was handed ([5f1a2f5](https://github.com/zeroxsolutions/zeroxsolutions/commit/5f1a2f5))
|
|
6
|
+
- **response:** let a collection response declare its meta and links ([7286ca3](https://github.com/zeroxsolutions/zeroxsolutions/commit/7286ca3))
|
|
7
|
+
- **response:** ship the counted meta and offset links a collection sends ([44adb03](https://github.com/zeroxsolutions/zeroxsolutions/commit/44adb03))
|
|
8
|
+
|
|
9
|
+
### 🩹 Fixes
|
|
10
|
+
|
|
11
|
+
- **response:** refuse a serialized collection that may lack a declared member ([f2e2c4f](https://github.com/zeroxsolutions/zeroxsolutions/commit/f2e2c4f))
|
|
12
|
+
|
|
13
|
+
### ❤️ Thank You
|
|
14
|
+
|
|
15
|
+
- Claude Opus 5.5 (1M context)
|
|
16
|
+
- Lương Văn Tú
|
|
17
|
+
|
|
18
|
+
## 0.14.0 (2026-09-28)
|
|
19
|
+
|
|
20
|
+
### 🚀 Features
|
|
21
|
+
|
|
22
|
+
- **response:** answer a created resource with its location ([e9c0086](https://github.com/zeroxsolutions/zeroxsolutions/commit/e9c0086))
|
|
23
|
+
- ⚠️ **response:** refuse a declaration the standard's field rules forbid ([30e6b77](https://github.com/zeroxsolutions/zeroxsolutions/commit/30e6b77))
|
|
24
|
+
|
|
25
|
+
### 🩹 Fixes
|
|
26
|
+
|
|
27
|
+
- ⚠️ **response:** walk a pipe's input when checking member names ([0949d22](https://github.com/zeroxsolutions/zeroxsolutions/commit/0949d22))
|
|
28
|
+
- **response:** match link types to their schemas ([0f7c79b](https://github.com/zeroxsolutions/zeroxsolutions/commit/0f7c79b))
|
|
29
|
+
- ⚠️ **response:** refuse a resource id that is not a string ([57f60ab](https://github.com/zeroxsolutions/zeroxsolutions/commit/57f60ab))
|
|
30
|
+
- **response:** keep a primary resource out of included ([c38859f](https://github.com/zeroxsolutions/zeroxsolutions/commit/c38859f))
|
|
31
|
+
- ⚠️ **response:** read only the document members the standard defines ([fc5c51a](https://github.com/zeroxsolutions/zeroxsolutions/commit/fc5c51a))
|
|
32
|
+
- ⚠️ **response:** refuse an error object that carries no member ([7d888ed](https://github.com/zeroxsolutions/zeroxsolutions/commit/7d888ed))
|
|
33
|
+
|
|
34
|
+
### ⚠️ Breaking Changes
|
|
35
|
+
|
|
36
|
+
- **response:** walk a pipe's input when checking member names ([0949d22](https://github.com/zeroxsolutions/zeroxsolutions/commit/0949d22))
|
|
37
|
+
a nested attribute member name that breaks the
|
|
38
|
+
standard's member-name rule, and sits behind a .transform() or
|
|
39
|
+
.pipe(), now throws at jsonApiResource's own declaration where it
|
|
40
|
+
previously passed unchecked.
|
|
41
|
+
- **response:** refuse a declaration the standard's field rules forbid ([30e6b77](https://github.com/zeroxsolutions/zeroxsolutions/commit/30e6b77))
|
|
42
|
+
jsonApiResource throws, and fails to compile, on a field named type or id, a name shared by an attribute and a relationship, an illegal member name, or a foreign-key attribute beside its relationship.
|
|
43
|
+
- **response:** refuse a resource id that is not a string ([57f60ab](https://github.com/zeroxsolutions/zeroxsolutions/commit/57f60ab))
|
|
44
|
+
Row['id'] is typed string, and serializing a row, embedded row or foreign key whose id is not a string throws TypeError.
|
|
45
|
+
- **response:** read only the document members the standard defines ([fc5c51a](https://github.com/zeroxsolutions/zeroxsolutions/commit/fc5c51a))
|
|
46
|
+
a data document carrying errors, an error document carrying data, and an empty relationship object are refused.
|
|
47
|
+
- **response:** refuse an error object that carries no member ([7d888ed](https://github.com/zeroxsolutions/zeroxsolutions/commit/7d888ed))
|
|
48
|
+
toErrorDocument throws on an empty error object.
|
|
49
|
+
|
|
50
|
+
### ❤️ Thank You
|
|
51
|
+
|
|
52
|
+
- Claude Opus 5.5 (1M context)
|
|
53
|
+
- Lương Văn Tú
|
|
54
|
+
|
|
55
|
+
## 0.13.0 (2026-09-27)
|
|
56
|
+
|
|
57
|
+
### 🚀 Features
|
|
58
|
+
|
|
59
|
+
- ⚠️ **response:** type the fieldsets and include paths a document answers ([98bbb58](https://github.com/zeroxsolutions/zeroxsolutions/commit/98bbb58))
|
|
60
|
+
- ⚠️ **response:** keep a resource's type, attributes and relationships as literals ([738889e](https://github.com/zeroxsolutions/zeroxsolutions/commit/738889e))
|
|
61
|
+
|
|
62
|
+
### 🩹 Fixes
|
|
63
|
+
|
|
64
|
+
- ⚠️ **response:** pin an include path stopping at a wide edge ([515a3e9](https://github.com/zeroxsolutions/zeroxsolutions/commit/515a3e9))
|
|
65
|
+
|
|
66
|
+
### ⚠️ Breaking Changes
|
|
67
|
+
|
|
68
|
+
- **response:** pin an include path stopping at a wide edge ([515a3e9](https://github.com/zeroxsolutions/zeroxsolutions/commit/515a3e9))
|
|
69
|
+
jsonApiResource's type parameters moved from <A, C> to
|
|
70
|
+
<T, A, R, C>, so an explicit `jsonApiResource<typeof attrs, Ctx>` no
|
|
71
|
+
longer compiles; remove the type arguments and let inference type it.
|
|
72
|
+
The exported ResourceOptions<C> is now ResourceOptions<R>.
|
|
73
|
+
BREAKING CHANGE: an explicit toOne<Ctx> or toMany<Ctx> no longer
|
|
74
|
+
compiles; remove the type argument.
|
|
75
|
+
- **response:** type the fieldsets and include paths a document answers ([98bbb58](https://github.com/zeroxsolutions/zeroxsolutions/commit/98bbb58))
|
|
76
|
+
declaredDocumentShape answers typed fieldsets and include
|
|
77
|
+
paths, walks at most three relationships by default, and answers a fieldset
|
|
78
|
+
only for a type a kept path reaches. DeclaredDocumentShape is removed;
|
|
79
|
+
TypedDocumentShape replaces it.
|
|
80
|
+
- **response:** keep a resource's type, attributes and relationships as literals ([738889e](https://github.com/zeroxsolutions/zeroxsolutions/commit/738889e))
|
|
81
|
+
toOne and toMany take the related resource as their first type
|
|
82
|
+
parameter and infer the loader context; a call passing the context explicitly,
|
|
83
|
+
toOne<Ctx>(...), no longer compiles and drops the explicit argument.
|
|
84
|
+
|
|
85
|
+
### ❤️ Thank You
|
|
86
|
+
|
|
87
|
+
- Claude Opus 5.5 (1M context)
|
|
88
|
+
- Lương Văn Tú
|
|
89
|
+
|
|
1
90
|
## 0.12.2 (2026-09-27)
|
|
2
91
|
|
|
3
92
|
### 🚀 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 {
|
|
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
|
|
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.
|
|
@@ -134,6 +151,7 @@ app.onError(
|
|
|
134
151
|
namespace: 'academics', // every code THESE handlers mint carries it, so one surface has one vocabulary
|
|
135
152
|
category: ['academics', 'http'],
|
|
136
153
|
resolve: createDomainErrorResolver(academicsErrors),
|
|
154
|
+
authenticationChallenge: 'Bearer',
|
|
137
155
|
}),
|
|
138
156
|
);
|
|
139
157
|
```
|
|
@@ -258,8 +276,9 @@ those three members and none of them means a path segment, so borrowing one woul
|
|
|
258
276
|
One root, no subpath, and `src/index.ts` lists every name it publishes:
|
|
259
277
|
|
|
260
278
|
- `okResponse` / `listResponse` / `errorResponse`, the `JsonApiResponse` they return (exported so a
|
|
261
|
-
consumer's declaration emit can name it),
|
|
262
|
-
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`;
|
|
263
282
|
- the resource declaration - `jsonApiResource`, `toOne` / `toMany`, `declaredDocumentShape` - and the
|
|
264
283
|
types their signatures name;
|
|
265
284
|
- the error document's types, `JsonApiError`, `BASE_ERROR_CODES`, `toErrorObject` / `toErrorDocument`;
|
|
@@ -267,7 +286,7 @@ One root, no subpath, and `src/index.ts` lists every name it publishes:
|
|
|
267
286
|
the answer, entry and map types they take;
|
|
268
287
|
- `JSON_API_MEDIA_TYPE`.
|
|
269
288
|
|
|
270
|
-
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
|
|
271
290
|
functions are what `jsonApiResource` is built from, and the root does not publish them: a resource's own
|
|
272
291
|
`document`, `collectionDocument`, `serialize` and `serializeMany` are the one way to declare and render a
|
|
273
292
|
document.
|
package/dist/api-response.d.ts
CHANGED
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
import type { output, ZodObject, ZodType } from 'zod';
|
|
2
|
-
import type
|
|
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
|
-
/**
|
|
49
|
-
|
|
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,
|
|
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"}
|
package/dist/api-response.js
CHANGED
|
@@ -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
|
-
/**
|
|
39
|
-
|
|
40
|
-
|
|
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
|
}
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import type { Link } from './primitives.js';
|
|
2
|
+
/**
|
|
3
|
+
* The arguments of a 201 that created `document`'s resource: the document, the status, and the `Location` the standard
|
|
4
|
+
* asks a 201 to carry. Spread into `c.json(...)`.
|
|
5
|
+
*
|
|
6
|
+
* `type` and `id` sit in the bound alongside `links`, both of which every `ResourceObject` carries: a document
|
|
7
|
+
* whose resource has no `links` at all would otherwise share no property with a bound of `links` alone, which
|
|
8
|
+
* is a "weak type" TypeScript refuses to accept a value for - and a resource with no self link is the common case.
|
|
9
|
+
*
|
|
10
|
+
* @throws Error when the resource's `self` link names another location, since the standard requires the two to match
|
|
11
|
+
*/
|
|
12
|
+
export declare function createdResponse<D extends {
|
|
13
|
+
data: {
|
|
14
|
+
type: string;
|
|
15
|
+
id: string;
|
|
16
|
+
links?: {
|
|
17
|
+
self?: Link;
|
|
18
|
+
};
|
|
19
|
+
};
|
|
20
|
+
}>(document: D, location: string): readonly [D, 201, {
|
|
21
|
+
Location: string;
|
|
22
|
+
}];
|
|
23
|
+
//# sourceMappingURL=created-response.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"created-response.d.ts","sourceRoot":"","sources":["../src/created-response.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,IAAI,EAAE,MAAM,iBAAiB,CAAC;AAM5C;;;;;;;;;GASG;AACH,wBAAgB,eAAe,CAAC,CAAC,SAAS;IAAE,IAAI,EAAE;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,EAAE,EAAE,MAAM,CAAC;QAAC,KAAK,CAAC,EAAE;YAAE,IAAI,CAAC,EAAE,IAAI,CAAA;SAAE,CAAA;KAAE,CAAA;CAAE,EACvG,QAAQ,EAAE,CAAC,EACX,QAAQ,EAAE,MAAM,GACf,SAAS,CAAC,CAAC,EAAE,GAAG,EAAE;IAAE,QAAQ,EAAE,MAAM,CAAA;CAAE,CAAC,CAMzC"}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
/** Where a link points, whichever of the standard's forms it takes. */
|
|
2
|
+
const hrefOf = (link) => link === null || link === undefined ? undefined : typeof link === 'string' ? link : link.href;
|
|
3
|
+
/**
|
|
4
|
+
* The arguments of a 201 that created `document`'s resource: the document, the status, and the `Location` the standard
|
|
5
|
+
* asks a 201 to carry. Spread into `c.json(...)`.
|
|
6
|
+
*
|
|
7
|
+
* `type` and `id` sit in the bound alongside `links`, both of which every `ResourceObject` carries: a document
|
|
8
|
+
* whose resource has no `links` at all would otherwise share no property with a bound of `links` alone, which
|
|
9
|
+
* is a "weak type" TypeScript refuses to accept a value for - and a resource with no self link is the common case.
|
|
10
|
+
*
|
|
11
|
+
* @throws Error when the resource's `self` link names another location, since the standard requires the two to match
|
|
12
|
+
*/
|
|
13
|
+
export function createdResponse(document, location) {
|
|
14
|
+
const self = hrefOf(document.data.links?.self);
|
|
15
|
+
if (self !== undefined && self !== location) {
|
|
16
|
+
throw new Error(`The created resource's self link "${self}" differs from its Location "${location}".`);
|
|
17
|
+
}
|
|
18
|
+
return [document, 201, { Location: location }];
|
|
19
|
+
}
|
package/dist/document-load.d.ts
CHANGED
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
* relationship declares belongs to `relationship.ts`.
|
|
5
5
|
*/
|
|
6
6
|
import type { RelatedResource, RelationshipDef } from './relationship.js';
|
|
7
|
-
import type
|
|
7
|
+
import { type LoadedRows, type RequestedDocumentShape, type Row } from './serialize.js';
|
|
8
8
|
/**
|
|
9
9
|
* What one loaded relationship hands the serializer for a row: the row its loader answered, or the
|
|
10
10
|
* identifier alone where it answered none.
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"document-load.d.ts","sourceRoot":"","sources":["../src/document-load.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AACH,OAAO,KAAK,EAAE,eAAe,EAAE,eAAe,EAAE,MAAM,mBAAmB,CAAC;AAC1E,OAAO,KAAK,
|
|
1
|
+
{"version":3,"file":"document-load.d.ts","sourceRoot":"","sources":["../src/document-load.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AACH,OAAO,KAAK,EAAE,eAAe,EAAE,eAAe,EAAE,MAAM,mBAAmB,CAAC;AAC1E,OAAO,EAAe,KAAK,UAAU,EAAE,KAAK,sBAAsB,EAAE,KAAK,GAAG,EAAE,MAAM,gBAAgB,CAAC;AAmCrG;;;;;;GAMG;AACH,wBAAgB,aAAa,CAAC,CAAC,EAC7B,GAAG,EAAE,eAAe,CAAC,CAAC,CAAC,EACvB,GAAG,EAAE,GAAG,EACR,MAAM,EAAE,UAAU,GAAG,SAAS,GAC7B,GAAG,GAAG,GAAG,EAAE,GAAG,IAAI,CAKpB;AA2BD;;;;;;;;;GASG;AACH,wBAAsB,gBAAgB,CAAC,CAAC,EACtC,IAAI,EAAE,eAAe,CAAC,CAAC,CAAC,EACxB,IAAI,EAAE,SAAS,GAAG,EAAE,EACpB,SAAS,EAAE,sBAAsB,GAAG,SAAS,EAC7C,OAAO,EAAE,CAAC,GACT,OAAO,CAAC,UAAU,GAAG,SAAS,CAAC,CA8BjC"}
|
package/dist/document-load.js
CHANGED
|
@@ -1,7 +1,18 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
1
|
+
import { assertRowId } from './serialize.js';
|
|
2
|
+
/**
|
|
3
|
+
* The ids one row carries for a loaded relationship. A null or absent key asks for nothing; any other
|
|
4
|
+
* non-string is refused rather than silently dropped, naming the relationship's own related `type`.
|
|
5
|
+
*/
|
|
6
|
+
function readIds(value, type) {
|
|
3
7
|
const values = Array.isArray(value) ? value : [value];
|
|
4
|
-
|
|
8
|
+
const ids = [];
|
|
9
|
+
for (const one of values) {
|
|
10
|
+
if (one === null || one === undefined)
|
|
11
|
+
continue;
|
|
12
|
+
assertRowId(type, { id: one });
|
|
13
|
+
ids.push(one);
|
|
14
|
+
}
|
|
15
|
+
return ids;
|
|
5
16
|
}
|
|
6
17
|
/** The rows one row already carries for a relationship that declares no loader. */
|
|
7
18
|
function readRows(value) {
|
|
@@ -16,8 +27,9 @@ function readRows(value) {
|
|
|
16
27
|
* linkage a client reads - only what lands under `included`.
|
|
17
28
|
*/
|
|
18
29
|
export function loadedRelated(def, row, loaded) {
|
|
19
|
-
const
|
|
20
|
-
const
|
|
30
|
+
const type = def.related().type;
|
|
31
|
+
const answered = loaded?.get(type);
|
|
32
|
+
const rows = readIds(def.read(row), type).map((id) => answered?.get(id) ?? { id });
|
|
21
33
|
return def.cardinality === 'many' ? rows : (rows.at(0) ?? null);
|
|
22
34
|
}
|
|
23
35
|
/** Runs one relationship's loader, and answers the rows the next depth reads its ids off. */
|
|
@@ -29,13 +41,13 @@ async function loadRelated(def, rows, loaded, context) {
|
|
|
29
41
|
const type = def.related().type;
|
|
30
42
|
const answered = loaded.get(type) ?? new Map();
|
|
31
43
|
loaded.set(type, answered);
|
|
32
|
-
const ids = [...new Set(rows.flatMap((row) => readIds(def.read(row))))];
|
|
44
|
+
const ids = [...new Set(rows.flatMap((row) => readIds(def.read(row), type)))];
|
|
33
45
|
if (ids.length === 0)
|
|
34
46
|
return [];
|
|
35
47
|
const related = await load(ids, context);
|
|
36
48
|
for (const row of related) {
|
|
37
|
-
|
|
38
|
-
|
|
49
|
+
assertRowId(type, row);
|
|
50
|
+
answered.set(row.id, row);
|
|
39
51
|
}
|
|
40
52
|
return related;
|
|
41
53
|
}
|
package/dist/document.d.ts
CHANGED
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
import type { ZodObject, ZodType } from 'zod';
|
|
2
|
-
import { type JsonApiObject, type Meta, type PaginationLinks, type ResourceIdentifier, type ResourceLinks } from './primitives.js';
|
|
2
|
+
import { type JsonApiObject, type Meta, type PaginationLinks, type RelationshipLinks, type ResourceIdentifier, type ResourceLinks, type TopLevelLinks } from './primitives.js';
|
|
3
3
|
import { type RelatedResource, type RelationshipDef } from './relationship.js';
|
|
4
4
|
/** One relationship as a document carries it - the linkage, plus its own links and meta. */
|
|
5
5
|
export interface Relationship {
|
|
6
6
|
data?: ResourceIdentifier | ResourceIdentifier[] | null;
|
|
7
|
-
links?:
|
|
7
|
+
links?: RelationshipLinks;
|
|
8
8
|
meta?: Meta;
|
|
9
9
|
}
|
|
10
10
|
/** A resource object, with `attributes` carrying the declared attribute type. */
|
|
@@ -20,19 +20,51 @@ export interface ResourceObject<A> {
|
|
|
20
20
|
export interface ResourceDocument<A> {
|
|
21
21
|
data: ResourceObject<A>;
|
|
22
22
|
included?: ResourceObject<unknown>[];
|
|
23
|
-
links?:
|
|
23
|
+
links?: TopLevelLinks;
|
|
24
24
|
meta?: Meta;
|
|
25
25
|
jsonapi?: JsonApiObject;
|
|
26
26
|
}
|
|
27
|
-
/**
|
|
28
|
-
|
|
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
|
-
}
|
|
35
|
-
/**
|
|
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>;
|
|
62
|
+
/**
|
|
63
|
+
* Wraps `attributes` into a resource object - `{ type, id, attributes?, relationships?, links?, meta? }`.
|
|
64
|
+
*
|
|
65
|
+
* `attributes` parses as absent here, unlike {@link ResourceObject}'s own required member: this schema
|
|
66
|
+
* also validates a document read off another server, which may publish a resource with none.
|
|
67
|
+
*/
|
|
36
68
|
export declare function resourceObjectSchema<C>(type: string, attributes: ZodObject, relationships?: Readonly<Record<string, RelationshipDef<C>>>): ZodType;
|
|
37
69
|
/**
|
|
38
70
|
* The document for a SINGLE resource - `{ data, included?, links?, meta?, jsonapi? }`.
|
|
@@ -40,8 +72,20 @@ export declare function resourceObjectSchema<C>(type: string, attributes: ZodObj
|
|
|
40
72
|
* `A` is asserted rather than inferred: the schema is assembled from a relationship graph reached
|
|
41
73
|
* through thunks, so nothing static carries the attribute type, and a client typing a route off this
|
|
42
74
|
* schema would otherwise read every member as `unknown`.
|
|
75
|
+
*
|
|
76
|
+
* Strict at the top level: the standard forbids any top-level member beyond these (extension members
|
|
77
|
+
* aside), which is what refuses a `data` document that also carries `errors`.
|
|
43
78
|
*/
|
|
44
79
|
export declare function resourceDocumentSchema<A = unknown, C = unknown>(root: RelatedResource<C>): ZodType<ResourceDocument<A>>;
|
|
45
|
-
/**
|
|
46
|
-
|
|
80
|
+
/**
|
|
81
|
+
* The document for a COLLECTION - the same members, with `data` an array and paginated `links`.
|
|
82
|
+
*
|
|
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}.
|
|
85
|
+
*/
|
|
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 {};
|
|
47
91
|
//# sourceMappingURL=document.d.ts.map
|
package/dist/document.d.ts.map
CHANGED
|
@@ -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,
|
|
1
|
+
{"version":3,"file":"document.d.ts","sourceRoot":"","sources":["../src/document.ts"],"names":[],"mappings":"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"}
|
package/dist/document.js
CHANGED
|
@@ -1,13 +1,18 @@
|
|
|
1
1
|
import { z } from 'zod';
|
|
2
|
-
import { jsonApiObjectSchema, metaSchema, paginationLinksSchema, resourceLinksSchema, } from './primitives.js';
|
|
2
|
+
import { jsonApiObjectSchema, metaSchema, paginationLinksSchema, resourceLinksSchema, topLevelLinksSchema, } from './primitives.js';
|
|
3
3
|
import { relationshipsSchema, reachableResources } from './relationship.js';
|
|
4
|
-
/**
|
|
4
|
+
/**
|
|
5
|
+
* Wraps `attributes` into a resource object - `{ type, id, attributes?, relationships?, links?, meta? }`.
|
|
6
|
+
*
|
|
7
|
+
* `attributes` parses as absent here, unlike {@link ResourceObject}'s own required member: this schema
|
|
8
|
+
* also validates a document read off another server, which may publish a resource with none.
|
|
9
|
+
*/
|
|
5
10
|
export function resourceObjectSchema(type, attributes, relationships = {}) {
|
|
6
11
|
const linkage = relationshipsSchema(relationships);
|
|
7
12
|
return z.object({
|
|
8
13
|
type: z.literal(type),
|
|
9
14
|
id: z.string(),
|
|
10
|
-
attributes,
|
|
15
|
+
attributes: attributes.optional(),
|
|
11
16
|
...(linkage ? { relationships: linkage.optional() } : {}),
|
|
12
17
|
links: resourceLinksSchema.optional(),
|
|
13
18
|
meta: metaSchema.optional(),
|
|
@@ -32,23 +37,31 @@ function includedSchema(root) {
|
|
|
32
37
|
* `A` is asserted rather than inferred: the schema is assembled from a relationship graph reached
|
|
33
38
|
* through thunks, so nothing static carries the attribute type, and a client typing a route off this
|
|
34
39
|
* schema would otherwise read every member as `unknown`.
|
|
40
|
+
*
|
|
41
|
+
* Strict at the top level: the standard forbids any top-level member beyond these (extension members
|
|
42
|
+
* aside), which is what refuses a `data` document that also carries `errors`.
|
|
35
43
|
*/
|
|
36
44
|
export function resourceDocumentSchema(root) {
|
|
37
|
-
return z.
|
|
45
|
+
return z.strictObject({
|
|
38
46
|
data: root.resourceObject,
|
|
39
47
|
included: includedSchema(root).optional(),
|
|
40
|
-
links:
|
|
48
|
+
links: topLevelLinksSchema.optional(),
|
|
41
49
|
meta: metaSchema.optional(),
|
|
42
50
|
jsonapi: jsonApiObjectSchema.optional(),
|
|
43
51
|
});
|
|
44
52
|
}
|
|
45
|
-
/**
|
|
46
|
-
|
|
47
|
-
|
|
53
|
+
/**
|
|
54
|
+
* The document for a COLLECTION - the same members, with `data` an array and paginated `links`.
|
|
55
|
+
*
|
|
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}.
|
|
58
|
+
*/
|
|
59
|
+
export function collectionDocumentSchema(root, members = {}) {
|
|
60
|
+
return z.strictObject({
|
|
48
61
|
data: z.array(root.resourceObject),
|
|
49
62
|
included: includedSchema(root).optional(),
|
|
50
|
-
links: paginationLinksSchema.optional(),
|
|
51
|
-
meta: metaSchema.optional(),
|
|
63
|
+
links: members.links ?? paginationLinksSchema.optional(),
|
|
64
|
+
meta: members.meta ?? metaSchema.optional(),
|
|
52
65
|
jsonapi: jsonApiObjectSchema.optional(),
|
|
53
66
|
});
|
|
54
67
|
}
|
|
@@ -42,7 +42,7 @@ export declare const errorLinksSchema: z.ZodObject<{
|
|
|
42
42
|
}, z.core.$strip>, z.ZodNull]>>;
|
|
43
43
|
}, z.core.$strip>;
|
|
44
44
|
export type ErrorLinks = z.infer<typeof errorLinksSchema>;
|
|
45
|
-
/** A single JSON:API error object
|
|
45
|
+
/** A single JSON:API error object: every member is optional, and at least one is present. */
|
|
46
46
|
export declare const errorObjectSchema: z.ZodObject<{
|
|
47
47
|
id: z.ZodOptional<z.ZodString>;
|
|
48
48
|
links: z.ZodOptional<z.ZodObject<{
|
|
@@ -94,6 +94,9 @@ export type ErrorObject = z.infer<typeof errorObjectSchema>;
|
|
|
94
94
|
/**
|
|
95
95
|
* A JSON:API error document - a top-level `errors` array of at least one member, which never carries `data`.
|
|
96
96
|
*
|
|
97
|
+
* Strict at the top level: the standard forbids any top-level member beyond these (extension members
|
|
98
|
+
* aside), which is what refuses a document that carries both `errors` and `data`.
|
|
99
|
+
*
|
|
97
100
|
* Nothing here renders the top-level `meta` or `links`. They are declared so the schema `errorResponse` publishes is
|
|
98
101
|
* the whole document, and a client parsing another server's document keeps them.
|
|
99
102
|
*/
|
|
@@ -182,8 +185,10 @@ export declare const errorDocumentSchema: z.ZodObject<{
|
|
|
182
185
|
}, z.core.$strip>>;
|
|
183
186
|
jsonapi: z.ZodOptional<z.ZodObject<{
|
|
184
187
|
version: z.ZodOptional<z.ZodString>;
|
|
188
|
+
ext: z.ZodOptional<z.ZodArray<z.ZodString>>;
|
|
189
|
+
profile: z.ZodOptional<z.ZodArray<z.ZodString>>;
|
|
185
190
|
meta: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodUnknown>>;
|
|
186
191
|
}, z.core.$strip>>;
|
|
187
|
-
}, z.core.$
|
|
192
|
+
}, z.core.$strict>;
|
|
188
193
|
export type ErrorDocument = z.infer<typeof errorDocumentSchema>;
|
|
189
194
|
//# sourceMappingURL=error-document.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"error-document.d.ts","sourceRoot":"","sources":["../../src/errors/error-document.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAGxB,sFAAsF;AACtF,eAAO,MAAM,iBAAiB;;;;iBAMlB,CAAC;AACb,MAAM,MAAM,WAAW,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,iBAAiB,CAAC,CAAC;AAE5D,qGAAqG;AACrG,eAAO,MAAM,gBAAgB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;iBAA8D,CAAC;AAC5F,MAAM,MAAM,UAAU,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,gBAAgB,CAAC,CAAC;AAE1D,
|
|
1
|
+
{"version":3,"file":"error-document.d.ts","sourceRoot":"","sources":["../../src/errors/error-document.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAGxB,sFAAsF;AACtF,eAAO,MAAM,iBAAiB;;;;iBAMlB,CAAC;AACb,MAAM,MAAM,WAAW,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,iBAAiB,CAAC,CAAC;AAE5D,qGAAqG;AACrG,eAAO,MAAM,gBAAgB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;iBAA8D,CAAC;AAC5F,MAAM,MAAM,UAAU,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,gBAAgB,CAAC,CAAC;AAE1D,6FAA6F;AAC7F,eAAO,MAAM,iBAAiB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;iBAYoF,CAAC;AACnH,MAAM,MAAM,WAAW,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,iBAAiB,CAAC,CAAC;AAE5D;;;;;;;;GAQG;AACH,eAAO,MAAM,mBAAmB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;kBAK9B,CAAC;AACH,MAAM,MAAM,aAAa,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,mBAAmB,CAAC,CAAC"}
|
|
@@ -10,7 +10,7 @@ export const errorSourceSchema = z
|
|
|
10
10
|
.partial();
|
|
11
11
|
/** An error object's `links`: a page about this occurrence, and a page about its type of problem. */
|
|
12
12
|
export const errorLinksSchema = z.object({ about: linkSchema, type: linkSchema }).partial();
|
|
13
|
-
/** A single JSON:API error object
|
|
13
|
+
/** A single JSON:API error object: every member is optional, and at least one is present. */
|
|
14
14
|
export const errorObjectSchema = z
|
|
15
15
|
.object({
|
|
16
16
|
id: z.string(),
|
|
@@ -22,14 +22,18 @@ export const errorObjectSchema = z
|
|
|
22
22
|
source: errorSourceSchema,
|
|
23
23
|
meta: metaSchema,
|
|
24
24
|
})
|
|
25
|
-
.partial()
|
|
25
|
+
.partial()
|
|
26
|
+
.refine((object) => Object.keys(object).length > 0, { message: 'An error object carries at least one member.' });
|
|
26
27
|
/**
|
|
27
28
|
* A JSON:API error document - a top-level `errors` array of at least one member, which never carries `data`.
|
|
28
29
|
*
|
|
30
|
+
* Strict at the top level: the standard forbids any top-level member beyond these (extension members
|
|
31
|
+
* aside), which is what refuses a document that carries both `errors` and `data`.
|
|
32
|
+
*
|
|
29
33
|
* Nothing here renders the top-level `meta` or `links`. They are declared so the schema `errorResponse` publishes is
|
|
30
34
|
* the whole document, and a client parsing another server's document keeps them.
|
|
31
35
|
*/
|
|
32
|
-
export const errorDocumentSchema = z.
|
|
36
|
+
export const errorDocumentSchema = z.strictObject({
|
|
33
37
|
errors: z.array(errorObjectSchema).min(1),
|
|
34
38
|
meta: metaSchema.optional(),
|
|
35
39
|
links: z.object({ self: linkSchema, describedby: linkSchema }).partial().optional(),
|
package/dist/index.d.ts
CHANGED
|
@@ -2,14 +2,16 @@
|
|
|
2
2
|
* The response half of the HTTP contract, in the one wire spelling this house answers: a JSON:API document.
|
|
3
3
|
*/
|
|
4
4
|
export { errorResponse, listResponse, okResponse, type JsonApiResponse, type RetryAfterHeader, } from './api-response.js';
|
|
5
|
-
export
|
|
5
|
+
export { createdResponse } from './created-response.js';
|
|
6
|
+
export type { CollectionDocument, CollectionDocumentFor, Relationship, ResourceDocument, ResourceObject, } from './document.js';
|
|
6
7
|
export * from './document-graph.js';
|
|
7
8
|
export { declaredDocumentShape } from './document-shape.js';
|
|
8
9
|
export { createDomainErrorResolver, defineErrorMap, errorEntry, type DomainErrorAnswer, type DomainErrorEntry, } from './domain-error-resolver.js';
|
|
9
10
|
export type { DomainErrorMap, ErrorClass, UpstreamErrorMap } from './error-map.js';
|
|
10
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';
|
|
11
12
|
export { JSON_API_MEDIA_TYPE } from './media-type.js';
|
|
12
|
-
export
|
|
13
|
+
export { countedCollectionMetaSchema, offsetPaginationLinksSchema } from './primitives.js';
|
|
14
|
+
export type { JsonApiObject, Link, Meta, PaginationLinks, RelationshipLinks, ResourceIdentifier, ResourceLinks, TopLevelLinks, } from './primitives.js';
|
|
13
15
|
export { toMany, toOne, type LoadContextArguments, type RelatedResource, type RelationshipDef, type RelationshipLoader, type RelationshipOptions, } from './relationship.js';
|
|
14
16
|
export { jsonApiResource, type JsonApiResource, type ResourceOptions } from './resource.js';
|
|
15
17
|
export type { CollectionExtras, LoadedRows, RequestedDocumentShape, ResourceSerializer, Row } from './serialize.js';
|
package/dist/index.d.ts.map
CHANGED
|
@@ -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,
|
|
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
|
@@ -2,11 +2,13 @@
|
|
|
2
2
|
* The response half of the HTTP contract, in the one wire spelling this house answers: a JSON:API document.
|
|
3
3
|
*/
|
|
4
4
|
export { errorResponse, listResponse, okResponse, } from './api-response.js';
|
|
5
|
+
export { createdResponse } from './created-response.js';
|
|
5
6
|
export * from './document-graph.js';
|
|
6
7
|
export { declaredDocumentShape } from './document-shape.js';
|
|
7
8
|
export { createDomainErrorResolver, defineErrorMap, errorEntry, } from './domain-error-resolver.js';
|
|
8
9
|
export { BASE_ERROR_CODES, errorDocumentSchema, JsonApiError, toErrorDocument, toErrorObject, } from './errors/index.js';
|
|
9
10
|
export { JSON_API_MEDIA_TYPE } from './media-type.js';
|
|
11
|
+
export { countedCollectionMetaSchema, offsetPaginationLinksSchema } from './primitives.js';
|
|
10
12
|
export { toMany, toOne, } from './relationship.js';
|
|
11
13
|
export { jsonApiResource } from './resource.js';
|
|
12
14
|
export { createUpstreamErrorTranslator } from './upstream-error.js';
|