@jsonapi-serde/server 0.0.0
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/LICENSE +22 -0
- package/README.md +22 -0
- package/dist/common/error.d.ts +53 -0
- package/dist/common/error.js +114 -0
- package/dist/common/index.d.ts +3 -0
- package/dist/common/index.js +3 -0
- package/dist/common/json-api.d.ts +94 -0
- package/dist/common/json-api.js +1 -0
- package/dist/common/response.d.ts +43 -0
- package/dist/common/response.js +78 -0
- package/dist/common/utils.d.ts +5 -0
- package/dist/common/utils.js +1 -0
- package/dist/http/accept.d.ts +53 -0
- package/dist/http/accept.js +114 -0
- package/dist/http/content-type.d.ts +18 -0
- package/dist/http/content-type.js +36 -0
- package/dist/http/index.d.ts +4 -0
- package/dist/http/index.js +4 -0
- package/dist/http/media-type-parser.d.ts +34 -0
- package/dist/http/media-type-parser.js +118 -0
- package/dist/http/search-params.d.ts +31 -0
- package/dist/http/search-params.js +99 -0
- package/dist/request/body.d.ts +165 -0
- package/dist/request/body.js +273 -0
- package/dist/request/index.d.ts +2 -0
- package/dist/request/index.js +2 -0
- package/dist/request/query.d.ts +116 -0
- package/dist/request/query.js +142 -0
- package/dist/response/index.d.ts +1 -0
- package/dist/response/index.js +1 -0
- package/dist/response/internal.d.ts +6 -0
- package/dist/response/internal.js +156 -0
- package/dist/response/serializer.d.ts +154 -0
- package/dist/response/serializer.js +38 -0
- package/package.json +69 -0
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Thrown when parsing the header fails due to invalid syntax or unexpected characters
|
|
3
|
+
*/
|
|
4
|
+
export declare class MediaTypeParserError extends Error {
|
|
5
|
+
}
|
|
6
|
+
/**
|
|
7
|
+
* Parsed representation of a single media type
|
|
8
|
+
*/
|
|
9
|
+
export type MediaType = {
|
|
10
|
+
type: string;
|
|
11
|
+
subType: string;
|
|
12
|
+
parameters: Record<string, string>;
|
|
13
|
+
weight: number;
|
|
14
|
+
acceptExt: Record<string, string>;
|
|
15
|
+
};
|
|
16
|
+
/**
|
|
17
|
+
* List of all parsed media types sorted by descending quality (`q`) weight
|
|
18
|
+
*/
|
|
19
|
+
export type Accept = MediaType[];
|
|
20
|
+
type RawParameter = [string, string];
|
|
21
|
+
export declare abstract class MediaTypeParser {
|
|
22
|
+
protected readonly header: string;
|
|
23
|
+
protected readonly length: number;
|
|
24
|
+
protected index: number;
|
|
25
|
+
protected constructor(header: string);
|
|
26
|
+
protected readParameters(allowMediaTypeSeparator: boolean): RawParameter[];
|
|
27
|
+
protected readParameter(): RawParameter;
|
|
28
|
+
protected readParameterValue(): string;
|
|
29
|
+
protected readQuotedString(): string;
|
|
30
|
+
protected skipWhitespace(): void;
|
|
31
|
+
protected consumeChar(char: string): void;
|
|
32
|
+
protected readToken(): string;
|
|
33
|
+
}
|
|
34
|
+
export {};
|
|
@@ -0,0 +1,118 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Thrown when parsing the header fails due to invalid syntax or unexpected characters
|
|
3
|
+
*/
|
|
4
|
+
export class MediaTypeParserError extends Error {
|
|
5
|
+
}
|
|
6
|
+
/**
|
|
7
|
+
* Returns a string of ASCII characters in a given range
|
|
8
|
+
*/
|
|
9
|
+
const asciiRange = (start, end) => {
|
|
10
|
+
let chars = "";
|
|
11
|
+
for (let ascii = start; ascii <= end; ++ascii) {
|
|
12
|
+
chars += String.fromCharCode(ascii);
|
|
13
|
+
}
|
|
14
|
+
return chars;
|
|
15
|
+
};
|
|
16
|
+
// Definitions from RFCs used in parsing tokens and quoted strings.
|
|
17
|
+
const DIGIT = asciiRange(0x30, 0x39);
|
|
18
|
+
const ALPHA = asciiRange(0x41, 0x5a) + asciiRange(0x61, 0x7a);
|
|
19
|
+
const VCHAR = asciiRange(0x21, 0x7e);
|
|
20
|
+
const TCHAR = `!#$%&'*+-.^_\`|~${DIGIT}${ALPHA}`;
|
|
21
|
+
const OBS_TEXT = asciiRange(0x80, 0xff);
|
|
22
|
+
const QD_TEXT = `\t !${asciiRange(0x23, 0x5b)}${asciiRange(0x50, 0x7e)}${OBS_TEXT}`;
|
|
23
|
+
const QCHAR = `\t ${VCHAR} ${OBS_TEXT}`;
|
|
24
|
+
export class MediaTypeParser {
|
|
25
|
+
header;
|
|
26
|
+
length;
|
|
27
|
+
index = 0;
|
|
28
|
+
constructor(header) {
|
|
29
|
+
this.header = header;
|
|
30
|
+
this.length = header.length;
|
|
31
|
+
}
|
|
32
|
+
readParameters(allowMediaTypeSeparator) {
|
|
33
|
+
const parameters = [];
|
|
34
|
+
while (this.index < this.length) {
|
|
35
|
+
this.skipWhitespace();
|
|
36
|
+
parameters.push(this.readParameter());
|
|
37
|
+
this.skipWhitespace();
|
|
38
|
+
if (this.index === this.length ||
|
|
39
|
+
(allowMediaTypeSeparator && this.header[this.index] === ",")) {
|
|
40
|
+
break;
|
|
41
|
+
}
|
|
42
|
+
this.consumeChar(";");
|
|
43
|
+
}
|
|
44
|
+
return parameters;
|
|
45
|
+
}
|
|
46
|
+
readParameter() {
|
|
47
|
+
this.skipWhitespace();
|
|
48
|
+
const parameterName = this.readToken();
|
|
49
|
+
this.consumeChar("=");
|
|
50
|
+
const parameterValue = this.readParameterValue();
|
|
51
|
+
return [parameterName, parameterValue];
|
|
52
|
+
}
|
|
53
|
+
readParameterValue() {
|
|
54
|
+
if (this.header[this.index] === '"') {
|
|
55
|
+
return this.readQuotedString();
|
|
56
|
+
}
|
|
57
|
+
return this.readToken();
|
|
58
|
+
}
|
|
59
|
+
readQuotedString() {
|
|
60
|
+
this.consumeChar('"');
|
|
61
|
+
let result = "";
|
|
62
|
+
let endFound = false;
|
|
63
|
+
while (this.index < this.length) {
|
|
64
|
+
const char = this.header[this.index];
|
|
65
|
+
this.index += 1;
|
|
66
|
+
if (char === '"') {
|
|
67
|
+
endFound = true;
|
|
68
|
+
break;
|
|
69
|
+
}
|
|
70
|
+
if (char !== "\\") {
|
|
71
|
+
if (!QD_TEXT.includes(char)) {
|
|
72
|
+
throw new MediaTypeParserError(`Unexpected character at pos ${this.index - 1}`);
|
|
73
|
+
}
|
|
74
|
+
result += char;
|
|
75
|
+
continue;
|
|
76
|
+
}
|
|
77
|
+
if (this.index === this.length) {
|
|
78
|
+
throw new MediaTypeParserError("Unexpected end of header");
|
|
79
|
+
}
|
|
80
|
+
const quotedChar = this.header[this.index];
|
|
81
|
+
this.index += 1;
|
|
82
|
+
if (!QCHAR.includes(quotedChar)) {
|
|
83
|
+
throw new MediaTypeParserError(`Unexpected character at pos ${this.index - 1}`);
|
|
84
|
+
}
|
|
85
|
+
result += quotedChar;
|
|
86
|
+
}
|
|
87
|
+
if (!endFound) {
|
|
88
|
+
throw new MediaTypeParserError("Unclosed quoted string");
|
|
89
|
+
}
|
|
90
|
+
return result;
|
|
91
|
+
}
|
|
92
|
+
skipWhitespace() {
|
|
93
|
+
while (this.index < this.length &&
|
|
94
|
+
(this.header[this.index] === " " || this.header[this.index] === "\t")) {
|
|
95
|
+
this.index += 1;
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
consumeChar(char) {
|
|
99
|
+
if (this.index === this.length) {
|
|
100
|
+
throw new MediaTypeParserError("Unexpected end of header");
|
|
101
|
+
}
|
|
102
|
+
if (this.header[this.index] !== char) {
|
|
103
|
+
throw new MediaTypeParserError(`Unexpected character "${this.header[this.index]}" at pos ${this.index}, expected "${char}"`);
|
|
104
|
+
}
|
|
105
|
+
this.index += 1;
|
|
106
|
+
}
|
|
107
|
+
readToken() {
|
|
108
|
+
let token = "";
|
|
109
|
+
while (this.index < this.length && TCHAR.includes(this.header[this.index])) {
|
|
110
|
+
token += this.header[this.index];
|
|
111
|
+
this.index += 1;
|
|
112
|
+
}
|
|
113
|
+
if (token.length === 0) {
|
|
114
|
+
throw new MediaTypeParserError(`Could not find a token at pos ${this.index}`);
|
|
115
|
+
}
|
|
116
|
+
return token;
|
|
117
|
+
}
|
|
118
|
+
}
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Represents a nested object in parsed search params
|
|
3
|
+
*/
|
|
4
|
+
type ParsedSearchParamsObject = {
|
|
5
|
+
[k: string]: ParsedSearchParamsObject | ParsedSearchParamsArray | string;
|
|
6
|
+
};
|
|
7
|
+
/**
|
|
8
|
+
* Represents an array in parsed search params
|
|
9
|
+
*/
|
|
10
|
+
type ParsedSearchParamsArray = (ParsedSearchParamsObject | ParsedSearchParamsArray | string)[];
|
|
11
|
+
/**
|
|
12
|
+
* Root type representing the fully parsed search parameters
|
|
13
|
+
*/
|
|
14
|
+
export type ParsedSearchParams = ParsedSearchParamsObject;
|
|
15
|
+
/**
|
|
16
|
+
* Input type accepted by `URLSearchParams`, such as string, object, or iterable
|
|
17
|
+
*/
|
|
18
|
+
export type SearchParamsInput = ConstructorParameters<typeof URLSearchParams>[0];
|
|
19
|
+
/**
|
|
20
|
+
* Parses a URL query string or `URLSearchParams` input into a deeply nested object
|
|
21
|
+
*
|
|
22
|
+
* Supports bracket and array notation like `user[address][city]=NY`.
|
|
23
|
+
*
|
|
24
|
+
* @example
|
|
25
|
+
* ```ts
|
|
26
|
+
* parseSearchParams("foo[0]=bar&foo[1]=baz");
|
|
27
|
+
* // => { foo: ["bar", "baz"] }
|
|
28
|
+
* ```
|
|
29
|
+
*/
|
|
30
|
+
export declare const parseSearchParams: (input: SearchParamsInput) => ParsedSearchParams;
|
|
31
|
+
export {};
|
|
@@ -0,0 +1,99 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Matches the full key with optional bracketed subkeys (e.g., "user[address][city]")
|
|
3
|
+
*/
|
|
4
|
+
const fullKeyRegex = /^([^\[\]]+)((?:\[[^\[\]]*])*)$/;
|
|
5
|
+
/**
|
|
6
|
+
* Matches each bracketed section inside a key (e.g., "[address]", "[city]")
|
|
7
|
+
*/
|
|
8
|
+
const bracketsRegex = /\[([^\[\]]*)]/g;
|
|
9
|
+
/**
|
|
10
|
+
* Matches numeric array indices (e.g., "0", "1")
|
|
11
|
+
*/
|
|
12
|
+
const indexRegex = /^\d+$/;
|
|
13
|
+
/**
|
|
14
|
+
* Parses a query string key into a path array
|
|
15
|
+
*/
|
|
16
|
+
const parseKey = (key) => {
|
|
17
|
+
const match = fullKeyRegex.exec(key);
|
|
18
|
+
if (!match) {
|
|
19
|
+
return null;
|
|
20
|
+
}
|
|
21
|
+
const path = [match[1]];
|
|
22
|
+
const brackets = match[2];
|
|
23
|
+
path.push(...Array.from(brackets.matchAll(bracketsRegex), (match) => match[1]));
|
|
24
|
+
return path;
|
|
25
|
+
};
|
|
26
|
+
/**
|
|
27
|
+
* Recursively removes undefined entries and empty holes from arrays
|
|
28
|
+
*/
|
|
29
|
+
const filterEmptyItems = (current) => {
|
|
30
|
+
if (Array.isArray(current)) {
|
|
31
|
+
return current
|
|
32
|
+
.filter((value) => value !== undefined)
|
|
33
|
+
.map((value) => {
|
|
34
|
+
if (typeof value === "string") {
|
|
35
|
+
return value;
|
|
36
|
+
}
|
|
37
|
+
return filterEmptyItems(value);
|
|
38
|
+
});
|
|
39
|
+
}
|
|
40
|
+
return Object.fromEntries(Object.entries(current).map(([key, value]) => {
|
|
41
|
+
if (typeof value === "string") {
|
|
42
|
+
return [key, value];
|
|
43
|
+
}
|
|
44
|
+
return [key, filterEmptyItems(value)];
|
|
45
|
+
}));
|
|
46
|
+
};
|
|
47
|
+
/**
|
|
48
|
+
* Parses a URL query string or `URLSearchParams` input into a deeply nested object
|
|
49
|
+
*
|
|
50
|
+
* Supports bracket and array notation like `user[address][city]=NY`.
|
|
51
|
+
*
|
|
52
|
+
* @example
|
|
53
|
+
* ```ts
|
|
54
|
+
* parseSearchParams("foo[0]=bar&foo[1]=baz");
|
|
55
|
+
* // => { foo: ["bar", "baz"] }
|
|
56
|
+
* ```
|
|
57
|
+
*/
|
|
58
|
+
// biome-ignore lint/complexity/noExcessiveCognitiveComplexity: Kept within one function for performance reasons
|
|
59
|
+
export const parseSearchParams = (input) => {
|
|
60
|
+
const searchParams = new URLSearchParams(input);
|
|
61
|
+
const result = {};
|
|
62
|
+
for (const [key, value] of searchParams) {
|
|
63
|
+
const path = parseKey(key);
|
|
64
|
+
if (!path) {
|
|
65
|
+
continue;
|
|
66
|
+
}
|
|
67
|
+
let previous = null;
|
|
68
|
+
let keyInPrevious = null;
|
|
69
|
+
let current = result;
|
|
70
|
+
for (let i = 0; i < path.length; ++i) {
|
|
71
|
+
const part = path[i];
|
|
72
|
+
const isLast = i === path.length - 1;
|
|
73
|
+
const isIndex = indexRegex.test(part);
|
|
74
|
+
if (Array.isArray(current) && !isIndex && part !== "") {
|
|
75
|
+
current = Object.fromEntries([...current.entries()].filter(([_, value]) => value !== undefined));
|
|
76
|
+
if (previous && keyInPrevious !== null) {
|
|
77
|
+
previous[keyInPrevious] = current;
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
const key = Array.isArray(current) && isIndex
|
|
81
|
+
? Number.parseInt(part, 10)
|
|
82
|
+
: Array.isArray(current) && part === ""
|
|
83
|
+
? current.length
|
|
84
|
+
: part;
|
|
85
|
+
if (isLast) {
|
|
86
|
+
current[key] = value;
|
|
87
|
+
continue;
|
|
88
|
+
}
|
|
89
|
+
previous = current;
|
|
90
|
+
keyInPrevious = key;
|
|
91
|
+
if (!current[key] || typeof current[key] === "string") {
|
|
92
|
+
const next = path[i + 1];
|
|
93
|
+
current[key] = next === "" || indexRegex.test(next) ? [] : {};
|
|
94
|
+
}
|
|
95
|
+
current = current[key];
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
return filterEmptyItems(result);
|
|
99
|
+
};
|
|
@@ -0,0 +1,165 @@
|
|
|
1
|
+
import { z } from "zod/v4";
|
|
2
|
+
import type { $ZodType } from "zod/v4/core";
|
|
3
|
+
/**
|
|
4
|
+
* Represents the structure of the request body and its content type
|
|
5
|
+
*/
|
|
6
|
+
export type BodyContext = {
|
|
7
|
+
/** The request body, either as a raw JSON string or already-parsed object */
|
|
8
|
+
body: Record<string, unknown> | string;
|
|
9
|
+
/** The request's Content-Type header value */
|
|
10
|
+
contentType: string | undefined;
|
|
11
|
+
};
|
|
12
|
+
/**
|
|
13
|
+
* Zod schema for a resource identifier with a required `type` and `id`
|
|
14
|
+
*/
|
|
15
|
+
export type ResourceIdentifierSchema<TType extends string> = $ZodType<{
|
|
16
|
+
type: TType;
|
|
17
|
+
id: string;
|
|
18
|
+
}>;
|
|
19
|
+
/**
|
|
20
|
+
* Constructs a Zod schema for a JSON:API resource identifier
|
|
21
|
+
*/
|
|
22
|
+
export declare const resourceIdentifierSchema: <TType extends string>(type: TType, idSchema?: $ZodType<string>) => ResourceIdentifierSchema<TType>;
|
|
23
|
+
/**
|
|
24
|
+
* Zod schema for a client-generated resource identifier with a local ID (`lid`)
|
|
25
|
+
*/
|
|
26
|
+
export type ClientResourceIdentifierSchema<TType extends string> = $ZodType<{
|
|
27
|
+
type: TType;
|
|
28
|
+
lid: string;
|
|
29
|
+
}>;
|
|
30
|
+
/**
|
|
31
|
+
* Constructs a Zod schema for a client resource identifier using `lid`
|
|
32
|
+
*/
|
|
33
|
+
export declare const clientResourceIdentifierSchema: <TType extends string>(type: TType) => ClientResourceIdentifierSchema<TType>;
|
|
34
|
+
type AnyResourceIdentifier = z.output<ResourceIdentifierSchema<string>> | z.output<ClientResourceIdentifierSchema<string>>;
|
|
35
|
+
/**
|
|
36
|
+
* Union type representing one or more resource identifiers or `null`
|
|
37
|
+
*/
|
|
38
|
+
export type RelationshipDataSchema = $ZodType<AnyResourceIdentifier | AnyResourceIdentifier[] | null>;
|
|
39
|
+
/**
|
|
40
|
+
* Schema for a JSON:API relationship object with a `data` field
|
|
41
|
+
*/
|
|
42
|
+
export type RelationshipSchema<TDataSchema extends RelationshipDataSchema> = $ZodType<{
|
|
43
|
+
data: z.output<TDataSchema>;
|
|
44
|
+
}>;
|
|
45
|
+
/**
|
|
46
|
+
* Constructs a relationship object schema that wraps a given relationship data schema
|
|
47
|
+
*/
|
|
48
|
+
export declare const relationshipSchema: <TDataSchema extends RelationshipDataSchema>(schema: TDataSchema) => RelationshipSchema<TDataSchema>;
|
|
49
|
+
/**
|
|
50
|
+
* Zod schema for a map of named relationships
|
|
51
|
+
*/
|
|
52
|
+
export type RelationshipsSchema = $ZodType<Record<string, z.output<RelationshipSchema<RelationshipDataSchema>>>>;
|
|
53
|
+
/**
|
|
54
|
+
* Zod schema for a generic JSON:API attributes object
|
|
55
|
+
*/
|
|
56
|
+
export type AttributesSchema = $ZodType<Record<string, unknown>>;
|
|
57
|
+
/**
|
|
58
|
+
* Configuration object for parsing a resource request
|
|
59
|
+
*
|
|
60
|
+
* Includes optional `idSchema`, `attributesSchema`, and `relationshipsSchema`, and an optional map of
|
|
61
|
+
* `includedTypeSchemas` keyed by type name.
|
|
62
|
+
*/
|
|
63
|
+
export type ParseResourceRequestOptions<TIdSchema extends $ZodType<string> | undefined, TType extends string, TAttributesSchema extends AttributesSchema | undefined, TRelationshipsSchema extends RelationshipsSchema | undefined, TIncludedTypeSchemas extends IncludedTypeSchemas | undefined> = {
|
|
64
|
+
/** Optional Zod schema to validate the `id` field in the primary resource */
|
|
65
|
+
idSchema?: TIdSchema;
|
|
66
|
+
/** The expected resource `type` value for the primary data object */
|
|
67
|
+
type: TType;
|
|
68
|
+
/** Optional Zod schema for validating the `attributes` object in the request */
|
|
69
|
+
attributesSchema?: TAttributesSchema;
|
|
70
|
+
/** Optional Zod schema for validating the `relationships` object in the request */
|
|
71
|
+
relationshipsSchema?: TRelationshipsSchema;
|
|
72
|
+
/**
|
|
73
|
+
* Optional map of included resource `type` names to their validation schemas
|
|
74
|
+
*
|
|
75
|
+
* Used to validate and organize entries in the top-level `included` array.
|
|
76
|
+
*/
|
|
77
|
+
includedTypeSchemas?: TIncludedTypeSchemas;
|
|
78
|
+
};
|
|
79
|
+
/**
|
|
80
|
+
* A parsed included resource entry
|
|
81
|
+
*/
|
|
82
|
+
export type IncludedResource<TOptions extends AnyIncludedResourceSchemas> = {
|
|
83
|
+
/** The local identifier (lid) of the included resource */
|
|
84
|
+
lid: string;
|
|
85
|
+
/** The `type` of the included resource */
|
|
86
|
+
type: string;
|
|
87
|
+
/** The `attributes` of the resource, if a schema was provided */
|
|
88
|
+
attributes: TOptions["attributesSchema"] extends AttributesSchema ? z.output<TOptions["attributesSchema"]> : undefined;
|
|
89
|
+
/** The `relationships` of the resource, if a schema was provided. */
|
|
90
|
+
relationships: TOptions["relationshipsSchema"] extends RelationshipsSchema ? z.output<TOptions["relationshipsSchema"]> : undefined;
|
|
91
|
+
};
|
|
92
|
+
/**
|
|
93
|
+
* A container class for looking up included resources of a given type by `lid`
|
|
94
|
+
*/
|
|
95
|
+
export declare class IncludedResourceMap<TResourceOptions extends AnyIncludedResourceSchemas> {
|
|
96
|
+
private readonly type;
|
|
97
|
+
private resources;
|
|
98
|
+
constructor(type: string, resources: Map<string, IncludedResource<TResourceOptions>>);
|
|
99
|
+
/**
|
|
100
|
+
* Returns the resource with the given `lid` or null if not found
|
|
101
|
+
*/
|
|
102
|
+
safeGet(lid: string): IncludedResource<TResourceOptions> | null;
|
|
103
|
+
/**
|
|
104
|
+
* Returns the resource with the given `lid`, or throws if not included
|
|
105
|
+
*
|
|
106
|
+
* @throws {JsonApiError} if the resource is missing
|
|
107
|
+
*/
|
|
108
|
+
get(lid: string): IncludedResource<TResourceOptions>;
|
|
109
|
+
}
|
|
110
|
+
/**
|
|
111
|
+
* Map of all included resource types and their corresponding resource maps
|
|
112
|
+
*/
|
|
113
|
+
export type IncludedTypesContainer<T extends IncludedTypeSchemas> = {
|
|
114
|
+
[K in keyof T]: IncludedResourceMap<T[K]>;
|
|
115
|
+
};
|
|
116
|
+
/**
|
|
117
|
+
* Parsed result from a resource request
|
|
118
|
+
*/
|
|
119
|
+
export type ParseResourceRequestResult<TIdSchema extends $ZodType<string> | undefined, TType extends string, TAttributesSchema extends AttributesSchema | undefined, TRelationshipsSchema extends RelationshipsSchema | undefined, TIncludedTypeSchemas extends IncludedTypeSchemas | undefined> = {
|
|
120
|
+
id: TIdSchema extends $ZodType<string> ? z.output<TIdSchema> : undefined;
|
|
121
|
+
type: TType;
|
|
122
|
+
attributes: TAttributesSchema extends AttributesSchema ? z.output<TAttributesSchema> : undefined;
|
|
123
|
+
relationships: TRelationshipsSchema extends RelationshipsSchema ? z.output<TRelationshipsSchema> : undefined;
|
|
124
|
+
includedTypes: TIncludedTypeSchemas extends IncludedTypeSchemas ? IncludedTypesContainer<TIncludedTypeSchemas> : undefined;
|
|
125
|
+
};
|
|
126
|
+
/**
|
|
127
|
+
* Describes the schema of an included resource, including optional attributes and relationships
|
|
128
|
+
*/
|
|
129
|
+
export type IncludedResourceSchemas<TAttributesSchema extends AttributesSchema | undefined, TRelationshipsSchema extends RelationshipsSchema | undefined> = {
|
|
130
|
+
/** Optional Zod schema for validating the `attributes` object */
|
|
131
|
+
attributesSchema?: TAttributesSchema;
|
|
132
|
+
/** Optional Zod schema for validating the `relationships` object */
|
|
133
|
+
relationshipsSchema?: TRelationshipsSchema;
|
|
134
|
+
};
|
|
135
|
+
type AnyIncludedResourceSchemas = IncludedResourceSchemas<any, any>;
|
|
136
|
+
/**
|
|
137
|
+
* Map of included resource types to their schemas
|
|
138
|
+
*/
|
|
139
|
+
export type IncludedTypeSchemas = {
|
|
140
|
+
[key: string]: IncludedResourceSchemas<AttributesSchema | undefined, RelationshipsSchema | undefined>;
|
|
141
|
+
};
|
|
142
|
+
/**
|
|
143
|
+
* Parses a JSON:API resource request
|
|
144
|
+
*
|
|
145
|
+
* Validates content type, parses the document, and extracts resource and included data according to the provided
|
|
146
|
+
* options.
|
|
147
|
+
*
|
|
148
|
+
* @throws {JsonApiError} for invalid content type or malformed document
|
|
149
|
+
*/
|
|
150
|
+
export declare const parseResourceRequest: <TIdSchema extends $ZodType<string> | undefined, TType extends string, TAttributesSchema extends AttributesSchema | undefined, TRelationshipsSchema extends RelationshipsSchema | undefined, TIncludedTypeSchemas extends IncludedTypeSchemas | undefined>(context: BodyContext, options: ParseResourceRequestOptions<TIdSchema, TType, TAttributesSchema, TRelationshipsSchema, TIncludedTypeSchemas>) => ParseResourceRequestResult<NoInfer<TIdSchema>, NoInfer<TType>, NoInfer<TAttributesSchema>, NoInfer<TRelationshipsSchema>, NoInfer<TIncludedTypeSchemas>>;
|
|
151
|
+
/**
|
|
152
|
+
* Parses a JSON:API relationship request and returns an ID
|
|
153
|
+
*
|
|
154
|
+
* The `idSchema` can be made `.nullable`.
|
|
155
|
+
*
|
|
156
|
+
* @throws {JsonApiError} for invalid content type or schema errors
|
|
157
|
+
*/
|
|
158
|
+
export declare const parseRelationshipRequest: <TIdSchema extends $ZodType<string | null> | undefined>(context: BodyContext, type: string, idSchema?: TIdSchema) => TIdSchema extends $ZodType<string | null> ? z.output<NoInfer<TIdSchema>> : string;
|
|
159
|
+
/**
|
|
160
|
+
* Parses a JSON:API relationships request and returns a list of string IDs
|
|
161
|
+
*
|
|
162
|
+
* @throws {JsonApiError} for invalid content type or schema errors
|
|
163
|
+
*/
|
|
164
|
+
export declare const parseRelationshipsRequest: (context: BodyContext, type: string, idSchema?: $ZodType<string>) => string[];
|
|
165
|
+
export {};
|