@zmdb/client 1.0.0-beta.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.
Files changed (48) hide show
  1. package/LICENSE +674 -0
  2. package/README.md +57 -0
  3. package/dist/body/index.d.ts +7 -0
  4. package/dist/body/index.d.ts.map +1 -0
  5. package/dist/body/index.js +55 -0
  6. package/dist/body/index.js.map +1 -0
  7. package/dist/errors/index.d.ts +59 -0
  8. package/dist/errors/index.d.ts.map +1 -0
  9. package/dist/errors/index.js +117 -0
  10. package/dist/errors/index.js.map +1 -0
  11. package/dist/headers/index.d.ts +6 -0
  12. package/dist/headers/index.d.ts.map +1 -0
  13. package/dist/headers/index.js +52 -0
  14. package/dist/headers/index.js.map +1 -0
  15. package/dist/index.d.ts +7 -0
  16. package/dist/index.d.ts.map +1 -0
  17. package/dist/index.js +6 -0
  18. package/dist/index.js.map +1 -0
  19. package/dist/runtime.d.ts +4 -0
  20. package/dist/runtime.d.ts.map +1 -0
  21. package/dist/runtime.js +585 -0
  22. package/dist/runtime.js.map +1 -0
  23. package/dist/testing/index.d.ts +13 -0
  24. package/dist/testing/index.d.ts.map +1 -0
  25. package/dist/testing/index.js +53 -0
  26. package/dist/testing/index.js.map +1 -0
  27. package/dist/transport/index.d.ts +4 -0
  28. package/dist/transport/index.d.ts.map +1 -0
  29. package/dist/transport/index.js +47 -0
  30. package/dist/transport/index.js.map +1 -0
  31. package/dist/types.d.ts +116 -0
  32. package/dist/types.d.ts.map +1 -0
  33. package/dist/types.js +2 -0
  34. package/dist/types.js.map +1 -0
  35. package/dist/url/index.d.ts +17 -0
  36. package/dist/url/index.d.ts.map +1 -0
  37. package/dist/url/index.js +98 -0
  38. package/dist/url/index.js.map +1 -0
  39. package/package.json +68 -0
  40. package/src/body/index.ts +58 -0
  41. package/src/errors/index.ts +152 -0
  42. package/src/headers/index.ts +57 -0
  43. package/src/index.ts +42 -0
  44. package/src/runtime.ts +741 -0
  45. package/src/testing/index.ts +67 -0
  46. package/src/transport/index.ts +56 -0
  47. package/src/types.ts +128 -0
  48. package/src/url/index.ts +113 -0
@@ -0,0 +1,152 @@
1
+ import type { ClientHeaders, ValidationIssue } from '../types.js';
2
+
3
+ export interface ClientErrorInit {
4
+ readonly operationId?: string;
5
+ readonly cause?: unknown;
6
+ }
7
+
8
+ function operation(operationId: string | undefined): string {
9
+ return operationId === undefined ? 'Client request' : `Operation ${operationId}`;
10
+ }
11
+
12
+ export class ClientError extends Error {
13
+ readonly operationId: string | undefined;
14
+
15
+ constructor(message: string, init: ClientErrorInit = {}) {
16
+ super(message, init.cause === undefined ? undefined : { cause: init.cause });
17
+ this.name = 'ClientError';
18
+ this.operationId = init.operationId;
19
+ }
20
+ }
21
+
22
+ export class ClientRequestError extends ClientError {
23
+ constructor(message: string, init: ClientErrorInit = {}) {
24
+ super(message, init);
25
+ this.name = 'ClientRequestError';
26
+ }
27
+ }
28
+
29
+ export class AuthenticationError extends ClientError {
30
+ constructor(operationId: string, cause?: unknown) {
31
+ super(`Authentication failed for operation ${operationId}`, { operationId, cause });
32
+ this.name = 'AuthenticationError';
33
+ }
34
+ }
35
+
36
+ export class MissingAuthenticationError extends AuthenticationError {
37
+ constructor(operationId: string) {
38
+ super(operationId);
39
+ this.name = 'MissingAuthenticationError';
40
+ this.message = `Operation ${operationId} requires an authentication provider`;
41
+ }
42
+ }
43
+
44
+ export class TransportError extends ClientError {
45
+ constructor(operationId: string | undefined, cause: unknown) {
46
+ super(
47
+ `${operation(operationId)} failed in the transport`,
48
+ operationId === undefined ? { cause } : { operationId, cause },
49
+ );
50
+ this.name = 'TransportError';
51
+ }
52
+ }
53
+
54
+ export class ClientTimeoutError extends ClientError {
55
+ readonly timeoutMs: number;
56
+
57
+ constructor(operationId: string, timeoutMs: number) {
58
+ super(`Operation ${operationId} timed out after ${String(timeoutMs)}ms`, { operationId });
59
+ this.name = 'ClientTimeoutError';
60
+ this.timeoutMs = timeoutMs;
61
+ }
62
+ }
63
+
64
+ export class ResponseTooLargeError extends ClientError {
65
+ readonly status: number;
66
+ readonly limit: number;
67
+
68
+ constructor(operationId: string, status: number, limit: number) {
69
+ super(`Operation ${operationId} response status ${String(status)} exceeds the ${String(limit)} byte limit`, {
70
+ operationId,
71
+ });
72
+ this.name = 'ResponseTooLargeError';
73
+ this.status = status;
74
+ this.limit = limit;
75
+ }
76
+ }
77
+
78
+ export class UnexpectedStatusError extends ClientError {
79
+ readonly status: number;
80
+ readonly headers: ClientHeaders;
81
+ readonly bodySnippet: string;
82
+
83
+ constructor(operationId: string, status: number, headers: ClientHeaders, bodySnippet: string) {
84
+ const detail = bodySnippet.length === 0 ? '' : `: ${bodySnippet}`;
85
+ super(`Operation ${operationId} returned undocumented status ${String(status)}${detail}`, { operationId });
86
+ this.name = 'UnexpectedStatusError';
87
+ this.status = status;
88
+ this.headers = Object.freeze({ ...headers });
89
+ this.bodySnippet = bodySnippet;
90
+ }
91
+ }
92
+
93
+ export class UnexpectedContentTypeError extends ClientError {
94
+ readonly status: number;
95
+ readonly expected: readonly string[];
96
+ readonly received: string | undefined;
97
+
98
+ constructor(operationId: string, status: number, expected: readonly string[], received: string | undefined) {
99
+ super(
100
+ `Operation ${operationId} status ${String(status)} expected content type ${expected.join(' or ')}, ` +
101
+ `received ${received ?? 'none'}`,
102
+ { operationId },
103
+ );
104
+ this.name = 'UnexpectedContentTypeError';
105
+ this.status = status;
106
+ this.expected = Object.freeze([...expected]);
107
+ this.received = received;
108
+ }
109
+ }
110
+
111
+ export class ResponseDecodeError extends ClientError {
112
+ readonly status: number;
113
+ readonly bodySnippet: string;
114
+
115
+ constructor(operationId: string, status: number, bodySnippet: string, cause?: unknown) {
116
+ const detail = bodySnippet.length === 0 ? '' : `: ${bodySnippet}`;
117
+ super(`Operation ${operationId} could not decode status ${String(status)}${detail}`, { operationId, cause });
118
+ this.name = 'ResponseDecodeError';
119
+ this.status = status;
120
+ this.bodySnippet = bodySnippet;
121
+ }
122
+ }
123
+
124
+ export class ResponseValidationError extends ClientError {
125
+ readonly status: number;
126
+ readonly issues: readonly ValidationIssue[];
127
+
128
+ constructor(operationId: string, status: number, issues: readonly ValidationIssue[]) {
129
+ super(
130
+ `Operation ${operationId} status ${String(status)} failed response validation with ` +
131
+ `${String(issues.length)} issue(s)`,
132
+ { operationId },
133
+ );
134
+ this.name = 'ResponseValidationError';
135
+ this.status = status;
136
+ this.issues = Object.freeze(issues.map(issue => Object.freeze({ ...issue })));
137
+ }
138
+ }
139
+
140
+ export class ClientResponseError<Status extends number, Body, Headers = ClientHeaders> extends ClientError {
141
+ readonly status: Status;
142
+ readonly body: Body;
143
+ readonly headers: Headers;
144
+
145
+ constructor(operationId: string, status: Status, body: Body, headers: Headers) {
146
+ super(`Operation ${operationId} returned documented error status ${String(status)}`, { operationId });
147
+ this.name = 'ClientResponseError';
148
+ this.status = status;
149
+ this.body = body;
150
+ this.headers = headers;
151
+ }
152
+ }
@@ -0,0 +1,57 @@
1
+ import { ClientRequestError } from '../errors/index.js';
2
+ import type { ClientHeaders } from '../types.js';
3
+
4
+ const HEADER_NAME = /^[!#$%&'*+\-.^_`|~0-9A-Za-z]+$/;
5
+ const INVALID_VALUE = /[\0\r\n]/;
6
+
7
+ export const TRANSPORT_OWNED_HEADERS: ReadonlySet<string> = new Set([
8
+ 'connection',
9
+ 'content-length',
10
+ 'host',
11
+ 'proxy-connection',
12
+ 'te',
13
+ 'trailer',
14
+ 'transfer-encoding',
15
+ 'upgrade',
16
+ ]);
17
+
18
+ export function normalizeClientHeaders(headers: ClientHeaders = {}): ClientHeaders {
19
+ const normalized: Record<string, string> = {};
20
+ for (const [sourceName, value] of Object.entries(headers)) {
21
+ if (!HEADER_NAME.test(sourceName)) {
22
+ throw new ClientRequestError(`Invalid HTTP header name ${JSON.stringify(sourceName)}`);
23
+ }
24
+ if (typeof value !== 'string' || INVALID_VALUE.test(value)) {
25
+ throw new ClientRequestError(`Invalid value for HTTP header ${sourceName.toLowerCase()}`);
26
+ }
27
+ const name = sourceName.toLowerCase();
28
+ const present = normalized[name];
29
+ if (present !== undefined && present !== value) {
30
+ throw new ClientRequestError(`Conflicting values for HTTP header ${name}`);
31
+ }
32
+ normalized[name] = value;
33
+ }
34
+ return Object.freeze(normalized);
35
+ }
36
+
37
+ export function mergeClientHeaders(...sources: readonly ClientHeaders[]): ClientHeaders {
38
+ const merged: Record<string, string> = {};
39
+ for (const source of sources) {
40
+ for (const [name, value] of Object.entries(normalizeClientHeaders(source))) {
41
+ const present = merged[name];
42
+ if (present !== undefined && present !== value) {
43
+ throw new ClientRequestError(`Conflicting values for HTTP header ${name}`);
44
+ }
45
+ merged[name] = value;
46
+ }
47
+ }
48
+ return Object.freeze(merged);
49
+ }
50
+
51
+ export function assertNoTransportOwnedHeaders(headers: ClientHeaders): void {
52
+ for (const name of Object.keys(headers)) {
53
+ if (TRANSPORT_OWNED_HEADERS.has(name.toLowerCase())) {
54
+ throw new ClientRequestError(`HTTP header ${name.toLowerCase()} is owned by the transport`);
55
+ }
56
+ }
57
+ }
package/src/index.ts ADDED
@@ -0,0 +1,42 @@
1
+ export { DEFAULT_MAX_ERROR_BODY_BYTES, DEFAULT_MAX_RESPONSE_BYTES, prepareClientBody } from './body/index.js';
2
+ export {
3
+ AuthenticationError,
4
+ ClientError,
5
+ ClientRequestError,
6
+ ClientResponseError,
7
+ ClientTimeoutError,
8
+ MissingAuthenticationError,
9
+ ResponseDecodeError,
10
+ ResponseTooLargeError,
11
+ ResponseValidationError,
12
+ TransportError,
13
+ UnexpectedContentTypeError,
14
+ UnexpectedStatusError,
15
+ } from './errors/index.js';
16
+ export { CLIENT_RUNTIME_ABI, createClientRuntime } from './runtime.js';
17
+ export { createFetchTransport } from './transport/index.js';
18
+ export { stringifyClientScalar, substituteClientPath } from './url/index.js';
19
+ export type {
20
+ AuthenticationContext,
21
+ AuthenticationPatch,
22
+ AuthenticationProvider,
23
+ CallOptions,
24
+ ClientBody,
25
+ ClientBytes,
26
+ ClientHeaders,
27
+ ClientOperationResponse,
28
+ ClientOptions,
29
+ ClientQueryPair,
30
+ ClientRequest,
31
+ ClientResponse,
32
+ ClientResponseBody,
33
+ ClientRuntime,
34
+ ClientSecurityRequirement,
35
+ ClientSecurityScheme,
36
+ ClientTransport,
37
+ ClientVersionPlan,
38
+ DecodeResult,
39
+ GeneratedOperation,
40
+ PreparedClientRequest,
41
+ ValidationIssue,
42
+ } from './types.js';