@procedures/client 0.47.1 → 0.48.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/CHANGELOG.md +92 -68
- package/README.md +3 -3
- package/dist/.tsbuildinfo +1 -1
- package/dist/adapters/http/client/batch.d.ts.map +1 -1
- package/dist/adapters/http/client/batch.js.map +1 -1
- package/dist/adapters/http/server/body.d.ts.map +1 -1
- package/dist/adapters/http/server/body.js.map +1 -1
- package/dist/adapters/http/shared/constants.d.ts +82 -78
- package/dist/adapters/http/shared/constants.d.ts.map +1 -1
- package/dist/adapters/http/shared/constants.js +78 -80
- package/dist/adapters/http/shared/constants.js.map +1 -1
- package/dist/adapters/http/shared/errors.d.ts +15 -13
- package/dist/adapters/http/shared/errors.d.ts.map +1 -1
- package/dist/adapters/http/shared/errors.js +13 -14
- package/dist/adapters/http/shared/errors.js.map +1 -1
- package/dist/adapters/port/transport.d.ts.map +1 -1
- package/dist/adapters/port/transport.js.map +1 -1
- package/dist/adapters/sequence.d.ts.map +1 -1
- package/dist/adapters/sequence.js.map +1 -1
- package/dist/adapters/websocket/client/types.d.ts +16 -6
- package/dist/adapters/websocket/client/types.d.ts.map +1 -1
- package/dist/adapters/websocket/client/types.js +7 -8
- package/dist/adapters/websocket/client/types.js.map +1 -1
- package/dist/adapters/websocket/shared/rpc.d.ts.map +1 -1
- package/dist/adapters/websocket/shared/rpc.js.map +1 -1
- package/dist/browser.d.ts +1 -1
- package/dist/browser.d.ts.map +1 -1
- package/dist/browser.js.map +1 -1
- package/dist/client/batch-executor.d.ts.map +1 -1
- package/dist/client/batch-executor.js.map +1 -1
- package/dist/client/client.d.ts.map +1 -1
- package/dist/client/client.js +3 -4
- package/dist/client/client.js.map +1 -1
- package/dist/client/errors/codes.d.ts +66 -64
- package/dist/client/errors/codes.d.ts.map +1 -1
- package/dist/client/errors/codes.js +64 -65
- package/dist/client/errors/codes.js.map +1 -1
- package/dist/client/errors/http-status.d.ts +69 -67
- package/dist/client/errors/http-status.d.ts.map +1 -1
- package/dist/client/errors/http-status.js +67 -68
- package/dist/client/errors/http-status.js.map +1 -1
- package/dist/client/errors/types.d.ts +38 -15
- package/dist/client/errors/types.d.ts.map +1 -1
- package/dist/client/errors/types.js +17 -19
- package/dist/client/errors/types.js.map +1 -1
- package/dist/client/middleware/circuit-breaker.d.ts.map +1 -1
- package/dist/client/middleware/circuit-breaker.js.map +1 -1
- package/dist/client/middleware/lru.d.ts +1 -1
- package/dist/client/middleware/lru.d.ts.map +1 -1
- package/dist/client/middleware/lru.js.map +1 -1
- package/dist/client/middleware/rate-limit.d.ts.map +1 -1
- package/dist/client/middleware/rate-limit.js.map +1 -1
- package/dist/client/middleware/resilience.d.ts.map +1 -1
- package/dist/client/middleware/resilience.js +2 -1
- package/dist/client/middleware/resilience.js.map +1 -1
- package/dist/client/route-resolver.d.ts.map +1 -1
- package/dist/client/route-resolver.js.map +1 -1
- package/dist/client/types.d.ts.map +1 -1
- package/dist/client/types.js.map +1 -1
- package/dist/client/validation/types.d.ts +1 -7
- package/dist/client/validation/types.d.ts.map +1 -1
- package/dist/client/validation/types.js +4 -7
- package/dist/client/validation/types.js.map +1 -1
- package/dist/events/bus.d.ts +35 -0
- package/dist/events/bus.d.ts.map +1 -1
- package/dist/events/bus.js +135 -22
- package/dist/events/bus.js.map +1 -1
- package/dist/events/index.d.ts +1 -1
- package/dist/events/index.d.ts.map +1 -1
- package/dist/events/index.js.map +1 -1
- package/dist/events/publisher.d.ts.map +1 -1
- package/dist/events/publisher.js +4 -7
- package/dist/events/publisher.js.map +1 -1
- package/dist/events/recheck.d.ts.map +1 -1
- package/dist/events/recheck.js.map +1 -1
- package/dist/events/remote.d.ts +1 -7
- package/dist/events/remote.d.ts.map +1 -1
- package/dist/events/remote.js +4 -7
- package/dist/events/remote.js.map +1 -1
- package/dist/events/types.d.ts +30 -0
- package/dist/events/types.d.ts.map +1 -1
- package/dist/middleware/compose.d.ts.map +1 -1
- package/dist/middleware/compose.js +6 -10
- package/dist/middleware/compose.js.map +1 -1
- package/dist/procedures/caller.d.ts +1 -1
- package/dist/procedures/caller.js +1 -1
- package/dist/procedures/collection/admission.d.ts +1 -1
- package/dist/procedures/collection/admission.js +1 -1
- package/dist/procedures/collection/hint-filter.d.ts +1 -7
- package/dist/procedures/collection/hint-filter.d.ts.map +1 -1
- package/dist/procedures/collection/hint-filter.js +4 -7
- package/dist/procedures/collection/hint-filter.js.map +1 -1
- package/dist/server/face-rules.d.ts +1 -1
- package/dist/server/face-rules.js +1 -1
- package/dist/server/procedure-server.d.ts +7 -5
- package/dist/server/procedure-server.d.ts.map +1 -1
- package/dist/server/procedure-server.js +10 -8
- package/dist/server/procedure-server.js.map +1 -1
- package/dist/server/types.d.ts.map +1 -1
- package/dist/server/types.js.map +1 -1
- package/dist/testing/transport-contract.d.ts +1 -1
- package/dist/testing/transport-contract.js +1 -1
- package/package.json +8 -4
- package/src/adapters/http/client/batch.ts +4 -1
- package/src/adapters/http/server/body.ts +4 -1
- package/src/adapters/http/shared/constants.ts +84 -78
- package/src/adapters/http/shared/errors.ts +16 -13
- package/src/adapters/port/transport.ts +4 -1
- package/src/adapters/sequence.ts +5 -1
- package/src/adapters/websocket/client/types.ts +19 -6
- package/src/adapters/websocket/shared/rpc.ts +15 -3
- package/src/browser.ts +1 -0
- package/src/client/batch-executor.ts +5 -1
- package/src/client/client.ts +3 -4
- package/src/client/errors/README.md +12 -12
- package/src/client/errors/codes.ts +67 -64
- package/src/client/errors/http-status.ts +70 -67
- package/src/client/errors/types.ts +44 -15
- package/src/client/middleware/circuit-breaker.ts +7 -2
- package/src/client/middleware/lru.ts +7 -2
- package/src/client/middleware/rate-limit.ts +4 -1
- package/src/client/middleware/resilience.ts +2 -1
- package/src/client/route-resolver.ts +5 -1
- package/src/client/types.ts +7 -2
- package/src/client/validation/types.ts +13 -6
- package/src/events/bus.ts +152 -23
- package/src/events/index.ts +1 -0
- package/src/events/publisher.ts +22 -9
- package/src/events/recheck.ts +4 -1
- package/src/events/remote.ts +33 -12
- package/src/events/types.ts +34 -0
- package/src/middleware/compose.ts +6 -22
- package/src/procedures/caller.ts +1 -1
- package/src/procedures/collection/admission.ts +1 -1
- package/src/procedures/collection/hint-filter.ts +13 -6
- package/src/server/face-rules.ts +1 -1
- package/src/server/procedure-server.ts +11 -8
- package/src/server/types.ts +7 -2
- package/src/testing/transport-contract.ts +1 -1
|
@@ -110,7 +110,11 @@ function cancelledResult(path: ProcedurePath, message: string): ProcedureCallRes
|
|
|
110
110
|
* ```
|
|
111
111
|
*/
|
|
112
112
|
export class BatchExecutor {
|
|
113
|
-
|
|
113
|
+
private readonly execute: ProcedureExecutor;
|
|
114
|
+
|
|
115
|
+
constructor(execute: ProcedureExecutor) {
|
|
116
|
+
this.execute = execute;
|
|
117
|
+
}
|
|
114
118
|
|
|
115
119
|
/**
|
|
116
120
|
* Execute resolved routes with the specified strategy.
|
package/src/client/client.ts
CHANGED
|
@@ -38,7 +38,7 @@ import type {
|
|
|
38
38
|
TypedClientMiddleware,
|
|
39
39
|
} from "./types.js";
|
|
40
40
|
import { ClientError } from "./types.js";
|
|
41
|
-
import { compose } from "
|
|
41
|
+
import { compose } from "@mark1russell7/collections/layers";
|
|
42
42
|
import { pathToMethod } from "../server/method.js";
|
|
43
43
|
import type { CallOptions, ClientContextInput } from "./context.js";
|
|
44
44
|
import { mergeContext, normalizeCallOptions } from "./context.js";
|
|
@@ -920,9 +920,8 @@ export class Client<TContext = {}> {
|
|
|
920
920
|
yield* self.transport.send<TReq, TRes>(context.message);
|
|
921
921
|
};
|
|
922
922
|
|
|
923
|
-
//
|
|
924
|
-
|
|
925
|
-
return compose<false>(...(this.middleware as any[]), transportRunner) as ClientRunner<TReq, TRes>;
|
|
923
|
+
// The first middleware is the outermost one (onion model)
|
|
924
|
+
return compose(...(this.middleware as ClientMiddleware<TReq, TRes>[]))(transportRunner);
|
|
926
925
|
}
|
|
927
926
|
|
|
928
927
|
/**
|
|
@@ -4,8 +4,8 @@ Protocol-agnostic error handling with rich metadata and bidirectional HTTP statu
|
|
|
4
4
|
|
|
5
5
|
## Zero Magic Strings & Numbers
|
|
6
6
|
|
|
7
|
-
- **ErrorCode
|
|
8
|
-
- **HTTPStatus
|
|
7
|
+
- **ErrorCode** - All error codes use the values of this object (no string literals)
|
|
8
|
+
- **HTTPStatus** - All HTTP status codes use the values of this object (no magic numbers)
|
|
9
9
|
- **Single source of truth** - `HTTP_STATUS_ERROR_MAP` defines bidirectional mapping
|
|
10
10
|
|
|
11
11
|
## Architecture
|
|
@@ -67,7 +67,7 @@ import { errorCodeToHTTPStatus, ErrorCode, HTTPStatus } from "@common/client/err
|
|
|
67
67
|
// Map error code to HTTP status
|
|
68
68
|
function handleError(code: ErrorCode): number {
|
|
69
69
|
const status = errorCodeToHTTPStatus(code);
|
|
70
|
-
return status; // Returns
|
|
70
|
+
return status; // Returns a value of HTTPStatus (404, 500, etc.)
|
|
71
71
|
}
|
|
72
72
|
|
|
73
73
|
// Example
|
|
@@ -100,8 +100,8 @@ try {
|
|
|
100
100
|
common/src/client/errors/
|
|
101
101
|
├── README.md ← You are here
|
|
102
102
|
├── index.ts ← Public API exports
|
|
103
|
-
├── codes.ts ← ErrorCode
|
|
104
|
-
├── http-status.ts ← HTTPStatus
|
|
103
|
+
├── codes.ts ← ErrorCode (70+ codes)
|
|
104
|
+
├── http-status.ts ← HTTPStatus (all HTTP status codes)
|
|
105
105
|
├── types.ts ← TypeScript interfaces
|
|
106
106
|
├── registry.ts ← ERROR_REGISTRY with full metadata
|
|
107
107
|
├── http-errors.ts ← HTTP-specific errors + bidirectional mapping
|
|
@@ -135,7 +135,7 @@ Works across all transports:
|
|
|
135
135
|
|
|
136
136
|
Full TypeScript support:
|
|
137
137
|
```typescript
|
|
138
|
-
// All
|
|
138
|
+
// All values are type-safe
|
|
139
139
|
const code: ErrorCode = ErrorCode.TIMEOUT;
|
|
140
140
|
const status: HTTPStatus = HTTPStatus.NOT_FOUND;
|
|
141
141
|
|
|
@@ -201,7 +201,7 @@ console.error(formatError(error, true)); // include stack trace
|
|
|
201
201
|
|
|
202
202
|
## Best Practices
|
|
203
203
|
|
|
204
|
-
1. **Always use
|
|
204
|
+
1. **Always use the named values** - Never use string literals or magic numbers
|
|
205
205
|
2. **Use the registry** - Don't create error metadata manually
|
|
206
206
|
3. **Provide context** - Include requestId, transport, method when creating errors
|
|
207
207
|
4. **Check retryable flag** - Respect the metadata when implementing retry logic
|
|
@@ -209,7 +209,7 @@ console.error(formatError(error, true)); // include stack trace
|
|
|
209
209
|
|
|
210
210
|
## Adding New Errors
|
|
211
211
|
|
|
212
|
-
1. Add to `ErrorCode`
|
|
212
|
+
1. Add to `ErrorCode` in `codes.ts`
|
|
213
213
|
2. Add metadata to `ERROR_REGISTRY` in `registry.ts` or `HTTP_ERROR_REGISTRY` in `http-errors.ts`
|
|
214
214
|
3. If HTTP-related, add to `HTTP_STATUS_ERROR_MAP` in `http-errors.ts`
|
|
215
215
|
|
|
@@ -217,11 +217,11 @@ console.error(formatError(error, true)); // include stack trace
|
|
|
217
217
|
|
|
218
218
|
Example:
|
|
219
219
|
```typescript
|
|
220
|
-
// 1. Add to ErrorCode
|
|
221
|
-
export
|
|
220
|
+
// 1. Add to ErrorCode
|
|
221
|
+
export const ErrorCode = {
|
|
222
222
|
// ... existing codes
|
|
223
|
-
MY_NEW_ERROR
|
|
224
|
-
}
|
|
223
|
+
MY_NEW_ERROR: "MY_NEW_ERROR",
|
|
224
|
+
} as const;
|
|
225
225
|
|
|
226
226
|
// 2. Add to registry
|
|
227
227
|
export const ERROR_REGISTRY: ErrorRegistry = {
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* Error
|
|
2
|
+
* Error Codes
|
|
3
3
|
*
|
|
4
|
-
*
|
|
5
|
-
* NO MAGIC STRINGS - use these
|
|
4
|
+
* ALL error codes.
|
|
5
|
+
* NO MAGIC STRINGS - use these values everywhere!
|
|
6
6
|
*/
|
|
7
7
|
|
|
8
8
|
/**
|
|
@@ -10,81 +10,84 @@
|
|
|
10
10
|
*
|
|
11
11
|
* Protocol-agnostic error codes that work across all transports.
|
|
12
12
|
*/
|
|
13
|
-
export
|
|
13
|
+
export const ErrorCode = {
|
|
14
14
|
// Network errors
|
|
15
|
-
NETWORK_ERROR
|
|
16
|
-
CONNECTION_REFUSED
|
|
17
|
-
DNS_ERROR
|
|
18
|
-
STREAM_ENDED
|
|
15
|
+
NETWORK_ERROR: "NETWORK_ERROR",
|
|
16
|
+
CONNECTION_REFUSED: "CONNECTION_REFUSED",
|
|
17
|
+
DNS_ERROR: "DNS_ERROR",
|
|
18
|
+
STREAM_ENDED: "STREAM_ENDED",
|
|
19
19
|
|
|
20
20
|
// Timeout errors
|
|
21
|
-
TIMEOUT
|
|
22
|
-
CONNECTION_TIMEOUT
|
|
23
|
-
READ_TIMEOUT
|
|
24
|
-
DEADLINE_EXCEEDED
|
|
21
|
+
TIMEOUT: "TIMEOUT",
|
|
22
|
+
CONNECTION_TIMEOUT: "CONNECTION_TIMEOUT",
|
|
23
|
+
READ_TIMEOUT: "READ_TIMEOUT",
|
|
24
|
+
DEADLINE_EXCEEDED: "DEADLINE_EXCEEDED",
|
|
25
25
|
|
|
26
26
|
// Cancellation
|
|
27
|
-
ABORTED
|
|
27
|
+
ABORTED: "ABORTED",
|
|
28
28
|
|
|
29
29
|
// Protocol/parsing errors
|
|
30
|
-
PARSE_ERROR
|
|
31
|
-
SERIALIZE_ERROR
|
|
32
|
-
INVALID_RESPONSE
|
|
33
|
-
PROTOCOL_ERROR
|
|
30
|
+
PARSE_ERROR: "PARSE_ERROR",
|
|
31
|
+
SERIALIZE_ERROR: "SERIALIZE_ERROR",
|
|
32
|
+
INVALID_RESPONSE: "INVALID_RESPONSE",
|
|
33
|
+
PROTOCOL_ERROR: "PROTOCOL_ERROR",
|
|
34
34
|
|
|
35
35
|
// Client errors (4xx equivalent)
|
|
36
|
-
BAD_REQUEST
|
|
37
|
-
UNAUTHORIZED
|
|
38
|
-
FORBIDDEN
|
|
39
|
-
NOT_FOUND
|
|
40
|
-
METHOD_NOT_ALLOWED
|
|
41
|
-
NOT_ACCEPTABLE
|
|
42
|
-
PROXY_AUTHENTICATION_REQUIRED
|
|
43
|
-
CONFLICT
|
|
44
|
-
GONE
|
|
45
|
-
LENGTH_REQUIRED
|
|
46
|
-
PRECONDITION_FAILED
|
|
47
|
-
PAYLOAD_TOO_LARGE
|
|
48
|
-
URI_TOO_LONG
|
|
49
|
-
UNSUPPORTED_MEDIA_TYPE
|
|
50
|
-
RANGE_NOT_SATISFIABLE
|
|
51
|
-
EXPECTATION_FAILED
|
|
52
|
-
IM_A_TEAPOT
|
|
53
|
-
MISDIRECTED_REQUEST
|
|
54
|
-
UNPROCESSABLE_ENTITY
|
|
55
|
-
LOCKED
|
|
56
|
-
FAILED_DEPENDENCY
|
|
57
|
-
TOO_EARLY
|
|
58
|
-
UPGRADE_REQUIRED
|
|
59
|
-
PRECONDITION_REQUIRED
|
|
60
|
-
TOO_MANY_REQUESTS
|
|
61
|
-
REQUEST_HEADER_FIELDS_TOO_LARGE
|
|
62
|
-
UNAVAILABLE_FOR_LEGAL_REASONS
|
|
63
|
-
CLIENT_CLOSED_REQUEST
|
|
64
|
-
METADATA_TOO_LARGE
|
|
36
|
+
BAD_REQUEST: "BAD_REQUEST",
|
|
37
|
+
UNAUTHORIZED: "UNAUTHORIZED",
|
|
38
|
+
FORBIDDEN: "FORBIDDEN",
|
|
39
|
+
NOT_FOUND: "NOT_FOUND",
|
|
40
|
+
METHOD_NOT_ALLOWED: "METHOD_NOT_ALLOWED",
|
|
41
|
+
NOT_ACCEPTABLE: "NOT_ACCEPTABLE",
|
|
42
|
+
PROXY_AUTHENTICATION_REQUIRED: "PROXY_AUTHENTICATION_REQUIRED",
|
|
43
|
+
CONFLICT: "CONFLICT",
|
|
44
|
+
GONE: "GONE",
|
|
45
|
+
LENGTH_REQUIRED: "LENGTH_REQUIRED",
|
|
46
|
+
PRECONDITION_FAILED: "PRECONDITION_FAILED",
|
|
47
|
+
PAYLOAD_TOO_LARGE: "PAYLOAD_TOO_LARGE",
|
|
48
|
+
URI_TOO_LONG: "URI_TOO_LONG",
|
|
49
|
+
UNSUPPORTED_MEDIA_TYPE: "UNSUPPORTED_MEDIA_TYPE",
|
|
50
|
+
RANGE_NOT_SATISFIABLE: "RANGE_NOT_SATISFIABLE",
|
|
51
|
+
EXPECTATION_FAILED: "EXPECTATION_FAILED",
|
|
52
|
+
IM_A_TEAPOT: "IM_A_TEAPOT",
|
|
53
|
+
MISDIRECTED_REQUEST: "MISDIRECTED_REQUEST",
|
|
54
|
+
UNPROCESSABLE_ENTITY: "UNPROCESSABLE_ENTITY",
|
|
55
|
+
LOCKED: "LOCKED",
|
|
56
|
+
FAILED_DEPENDENCY: "FAILED_DEPENDENCY",
|
|
57
|
+
TOO_EARLY: "TOO_EARLY",
|
|
58
|
+
UPGRADE_REQUIRED: "UPGRADE_REQUIRED",
|
|
59
|
+
PRECONDITION_REQUIRED: "PRECONDITION_REQUIRED",
|
|
60
|
+
TOO_MANY_REQUESTS: "TOO_MANY_REQUESTS",
|
|
61
|
+
REQUEST_HEADER_FIELDS_TOO_LARGE: "REQUEST_HEADER_FIELDS_TOO_LARGE",
|
|
62
|
+
UNAVAILABLE_FOR_LEGAL_REASONS: "UNAVAILABLE_FOR_LEGAL_REASONS",
|
|
63
|
+
CLIENT_CLOSED_REQUEST: "CLIENT_CLOSED_REQUEST",
|
|
64
|
+
METADATA_TOO_LARGE: "METADATA_TOO_LARGE",
|
|
65
65
|
|
|
66
66
|
// Server errors (5xx equivalent)
|
|
67
|
-
INTERNAL_ERROR
|
|
68
|
-
NOT_IMPLEMENTED
|
|
69
|
-
BAD_GATEWAY
|
|
70
|
-
SERVICE_UNAVAILABLE
|
|
71
|
-
GATEWAY_TIMEOUT
|
|
72
|
-
HTTP_VERSION_NOT_SUPPORTED
|
|
73
|
-
VARIANT_ALSO_NEGOTIATES
|
|
74
|
-
INSUFFICIENT_STORAGE
|
|
75
|
-
LOOP_DETECTED
|
|
76
|
-
NOT_EXTENDED
|
|
77
|
-
NETWORK_AUTHENTICATION_REQUIRED
|
|
78
|
-
HTTP_NETWORK_CONNECT_TIMEOUT
|
|
67
|
+
INTERNAL_ERROR: "INTERNAL_ERROR",
|
|
68
|
+
NOT_IMPLEMENTED: "NOT_IMPLEMENTED",
|
|
69
|
+
BAD_GATEWAY: "BAD_GATEWAY",
|
|
70
|
+
SERVICE_UNAVAILABLE: "SERVICE_UNAVAILABLE",
|
|
71
|
+
GATEWAY_TIMEOUT: "GATEWAY_TIMEOUT",
|
|
72
|
+
HTTP_VERSION_NOT_SUPPORTED: "HTTP_VERSION_NOT_SUPPORTED",
|
|
73
|
+
VARIANT_ALSO_NEGOTIATES: "VARIANT_ALSO_NEGOTIATES",
|
|
74
|
+
INSUFFICIENT_STORAGE: "INSUFFICIENT_STORAGE",
|
|
75
|
+
LOOP_DETECTED: "LOOP_DETECTED",
|
|
76
|
+
NOT_EXTENDED: "NOT_EXTENDED",
|
|
77
|
+
NETWORK_AUTHENTICATION_REQUIRED: "NETWORK_AUTHENTICATION_REQUIRED",
|
|
78
|
+
HTTP_NETWORK_CONNECT_TIMEOUT: "HTTP_NETWORK_CONNECT_TIMEOUT",
|
|
79
79
|
|
|
80
80
|
// Configuration errors
|
|
81
|
-
INVALID_CONFIG
|
|
82
|
-
INVALID_URL
|
|
83
|
-
INVALID_METHOD
|
|
81
|
+
INVALID_CONFIG: "INVALID_CONFIG",
|
|
82
|
+
INVALID_URL: "INVALID_URL",
|
|
83
|
+
INVALID_METHOD: "INVALID_METHOD",
|
|
84
84
|
|
|
85
85
|
// HTTP-specific
|
|
86
|
-
HTTP_ERROR
|
|
86
|
+
HTTP_ERROR: "HTTP_ERROR",
|
|
87
87
|
|
|
88
88
|
// Unknown
|
|
89
|
-
UNKNOWN
|
|
90
|
-
}
|
|
89
|
+
UNKNOWN: "UNKNOWN",
|
|
90
|
+
} as const;
|
|
91
|
+
|
|
92
|
+
/** This type is the union of the values of `ErrorCode`. */
|
|
93
|
+
export type ErrorCode = (typeof ErrorCode)[keyof typeof ErrorCode];
|
|
@@ -1,82 +1,85 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* HTTP Status Code Constants
|
|
3
3
|
*
|
|
4
|
-
*
|
|
4
|
+
* All HTTP status codes.
|
|
5
5
|
* NO MAGIC NUMBERS - use these constants everywhere!
|
|
6
6
|
*/
|
|
7
7
|
|
|
8
|
-
export
|
|
8
|
+
export const HTTPStatus = {
|
|
9
9
|
// 1xx Informational
|
|
10
|
-
CONTINUE
|
|
11
|
-
SWITCHING_PROTOCOLS
|
|
12
|
-
PROCESSING
|
|
13
|
-
EARLY_HINTS
|
|
10
|
+
CONTINUE: 100,
|
|
11
|
+
SWITCHING_PROTOCOLS: 101,
|
|
12
|
+
PROCESSING: 102,
|
|
13
|
+
EARLY_HINTS: 103,
|
|
14
14
|
|
|
15
15
|
// 2xx Success
|
|
16
|
-
OK
|
|
17
|
-
CREATED
|
|
18
|
-
ACCEPTED
|
|
19
|
-
NON_AUTHORITATIVE_INFORMATION
|
|
20
|
-
NO_CONTENT
|
|
21
|
-
RESET_CONTENT
|
|
22
|
-
PARTIAL_CONTENT
|
|
23
|
-
MULTI_STATUS
|
|
24
|
-
ALREADY_REPORTED
|
|
25
|
-
IM_USED
|
|
16
|
+
OK: 200,
|
|
17
|
+
CREATED: 201,
|
|
18
|
+
ACCEPTED: 202,
|
|
19
|
+
NON_AUTHORITATIVE_INFORMATION: 203,
|
|
20
|
+
NO_CONTENT: 204,
|
|
21
|
+
RESET_CONTENT: 205,
|
|
22
|
+
PARTIAL_CONTENT: 206,
|
|
23
|
+
MULTI_STATUS: 207,
|
|
24
|
+
ALREADY_REPORTED: 208,
|
|
25
|
+
IM_USED: 226,
|
|
26
26
|
|
|
27
27
|
// 3xx Redirection
|
|
28
|
-
MULTIPLE_CHOICES
|
|
29
|
-
MOVED_PERMANENTLY
|
|
30
|
-
FOUND
|
|
31
|
-
SEE_OTHER
|
|
32
|
-
NOT_MODIFIED
|
|
33
|
-
USE_PROXY
|
|
34
|
-
TEMPORARY_REDIRECT
|
|
35
|
-
PERMANENT_REDIRECT
|
|
28
|
+
MULTIPLE_CHOICES: 300,
|
|
29
|
+
MOVED_PERMANENTLY: 301,
|
|
30
|
+
FOUND: 302,
|
|
31
|
+
SEE_OTHER: 303,
|
|
32
|
+
NOT_MODIFIED: 304,
|
|
33
|
+
USE_PROXY: 305,
|
|
34
|
+
TEMPORARY_REDIRECT: 307,
|
|
35
|
+
PERMANENT_REDIRECT: 308,
|
|
36
36
|
|
|
37
37
|
// 4xx Client Errors
|
|
38
|
-
BAD_REQUEST
|
|
39
|
-
UNAUTHORIZED
|
|
40
|
-
PAYMENT_REQUIRED
|
|
41
|
-
FORBIDDEN
|
|
42
|
-
NOT_FOUND
|
|
43
|
-
METHOD_NOT_ALLOWED
|
|
44
|
-
NOT_ACCEPTABLE
|
|
45
|
-
PROXY_AUTHENTICATION_REQUIRED
|
|
46
|
-
REQUEST_TIMEOUT
|
|
47
|
-
CONFLICT
|
|
48
|
-
GONE
|
|
49
|
-
LENGTH_REQUIRED
|
|
50
|
-
PRECONDITION_FAILED
|
|
51
|
-
PAYLOAD_TOO_LARGE
|
|
52
|
-
URI_TOO_LONG
|
|
53
|
-
UNSUPPORTED_MEDIA_TYPE
|
|
54
|
-
RANGE_NOT_SATISFIABLE
|
|
55
|
-
EXPECTATION_FAILED
|
|
56
|
-
IM_A_TEAPOT
|
|
57
|
-
MISDIRECTED_REQUEST
|
|
58
|
-
UNPROCESSABLE_ENTITY
|
|
59
|
-
LOCKED
|
|
60
|
-
FAILED_DEPENDENCY
|
|
61
|
-
TOO_EARLY
|
|
62
|
-
UPGRADE_REQUIRED
|
|
63
|
-
PRECONDITION_REQUIRED
|
|
64
|
-
TOO_MANY_REQUESTS
|
|
65
|
-
REQUEST_HEADER_FIELDS_TOO_LARGE
|
|
66
|
-
UNAVAILABLE_FOR_LEGAL_REASONS
|
|
67
|
-
CLIENT_CLOSED_REQUEST
|
|
38
|
+
BAD_REQUEST: 400,
|
|
39
|
+
UNAUTHORIZED: 401,
|
|
40
|
+
PAYMENT_REQUIRED: 402,
|
|
41
|
+
FORBIDDEN: 403,
|
|
42
|
+
NOT_FOUND: 404,
|
|
43
|
+
METHOD_NOT_ALLOWED: 405,
|
|
44
|
+
NOT_ACCEPTABLE: 406,
|
|
45
|
+
PROXY_AUTHENTICATION_REQUIRED: 407,
|
|
46
|
+
REQUEST_TIMEOUT: 408,
|
|
47
|
+
CONFLICT: 409,
|
|
48
|
+
GONE: 410,
|
|
49
|
+
LENGTH_REQUIRED: 411,
|
|
50
|
+
PRECONDITION_FAILED: 412,
|
|
51
|
+
PAYLOAD_TOO_LARGE: 413,
|
|
52
|
+
URI_TOO_LONG: 414,
|
|
53
|
+
UNSUPPORTED_MEDIA_TYPE: 415,
|
|
54
|
+
RANGE_NOT_SATISFIABLE: 416,
|
|
55
|
+
EXPECTATION_FAILED: 417,
|
|
56
|
+
IM_A_TEAPOT: 418,
|
|
57
|
+
MISDIRECTED_REQUEST: 421,
|
|
58
|
+
UNPROCESSABLE_ENTITY: 422,
|
|
59
|
+
LOCKED: 423,
|
|
60
|
+
FAILED_DEPENDENCY: 424,
|
|
61
|
+
TOO_EARLY: 425,
|
|
62
|
+
UPGRADE_REQUIRED: 426,
|
|
63
|
+
PRECONDITION_REQUIRED: 428,
|
|
64
|
+
TOO_MANY_REQUESTS: 429,
|
|
65
|
+
REQUEST_HEADER_FIELDS_TOO_LARGE: 431,
|
|
66
|
+
UNAVAILABLE_FOR_LEGAL_REASONS: 451,
|
|
67
|
+
CLIENT_CLOSED_REQUEST: 499, // nginx-specific
|
|
68
68
|
|
|
69
69
|
// 5xx Server Errors
|
|
70
|
-
INTERNAL_SERVER_ERROR
|
|
71
|
-
NOT_IMPLEMENTED
|
|
72
|
-
BAD_GATEWAY
|
|
73
|
-
SERVICE_UNAVAILABLE
|
|
74
|
-
GATEWAY_TIMEOUT
|
|
75
|
-
HTTP_VERSION_NOT_SUPPORTED
|
|
76
|
-
VARIANT_ALSO_NEGOTIATES
|
|
77
|
-
INSUFFICIENT_STORAGE
|
|
78
|
-
LOOP_DETECTED
|
|
79
|
-
NOT_EXTENDED
|
|
80
|
-
NETWORK_AUTHENTICATION_REQUIRED
|
|
81
|
-
NETWORK_CONNECT_TIMEOUT
|
|
82
|
-
}
|
|
70
|
+
INTERNAL_SERVER_ERROR: 500,
|
|
71
|
+
NOT_IMPLEMENTED: 501,
|
|
72
|
+
BAD_GATEWAY: 502,
|
|
73
|
+
SERVICE_UNAVAILABLE: 503,
|
|
74
|
+
GATEWAY_TIMEOUT: 504,
|
|
75
|
+
HTTP_VERSION_NOT_SUPPORTED: 505,
|
|
76
|
+
VARIANT_ALSO_NEGOTIATES: 506,
|
|
77
|
+
INSUFFICIENT_STORAGE: 507,
|
|
78
|
+
LOOP_DETECTED: 508,
|
|
79
|
+
NOT_EXTENDED: 510,
|
|
80
|
+
NETWORK_AUTHENTICATION_REQUIRED: 511,
|
|
81
|
+
NETWORK_CONNECT_TIMEOUT: 599, // non-standard
|
|
82
|
+
} as const;
|
|
83
|
+
|
|
84
|
+
/** This type is the union of the values of `HTTPStatus`. */
|
|
85
|
+
export type HTTPStatus = (typeof HTTPStatus)[keyof typeof HTTPStatus];
|
|
@@ -8,39 +8,68 @@
|
|
|
8
8
|
/**
|
|
9
9
|
* Error severity levels.
|
|
10
10
|
*/
|
|
11
|
-
export
|
|
11
|
+
export const ErrorSeverity = {
|
|
12
12
|
/** Informational - not an actual error */
|
|
13
|
-
INFO
|
|
13
|
+
INFO: "info",
|
|
14
14
|
/** Warning - degraded but functional */
|
|
15
|
-
WARNING
|
|
15
|
+
WARNING: "warning",
|
|
16
16
|
/** Error - operation failed */
|
|
17
|
-
ERROR
|
|
17
|
+
ERROR: "error",
|
|
18
18
|
/** Critical - system-level failure */
|
|
19
|
-
CRITICAL
|
|
19
|
+
CRITICAL: "critical",
|
|
20
|
+
} as const;
|
|
21
|
+
|
|
22
|
+
/** This type is the union of the values of `ErrorSeverity`. */
|
|
23
|
+
export type ErrorSeverity = (typeof ErrorSeverity)[keyof typeof ErrorSeverity];
|
|
24
|
+
|
|
25
|
+
/** These types keep each member of `ErrorSeverity` usable as a type. */
|
|
26
|
+
// eslint-disable-next-line @typescript-eslint/no-namespace
|
|
27
|
+
export declare namespace ErrorSeverity {
|
|
28
|
+
export type INFO = typeof ErrorSeverity.INFO;
|
|
29
|
+
export type WARNING = typeof ErrorSeverity.WARNING;
|
|
30
|
+
export type ERROR = typeof ErrorSeverity.ERROR;
|
|
31
|
+
export type CRITICAL = typeof ErrorSeverity.CRITICAL;
|
|
20
32
|
}
|
|
21
33
|
|
|
22
34
|
/**
|
|
23
35
|
* Error categories for classification.
|
|
24
36
|
*/
|
|
25
|
-
export
|
|
37
|
+
export const ErrorCategory = {
|
|
26
38
|
/** Network-level errors (connectivity, DNS, etc.) */
|
|
27
|
-
NETWORK
|
|
39
|
+
NETWORK: "network",
|
|
28
40
|
/** Client-side errors (validation, configuration) */
|
|
29
|
-
CLIENT
|
|
41
|
+
CLIENT: "client",
|
|
30
42
|
/** Server-side errors (5xx, internal errors) */
|
|
31
|
-
SERVER
|
|
43
|
+
SERVER: "server",
|
|
32
44
|
/** Protocol-level errors (parsing, encoding) */
|
|
33
|
-
PROTOCOL
|
|
45
|
+
PROTOCOL: "protocol",
|
|
34
46
|
/** Authentication/authorization errors */
|
|
35
|
-
AUTH
|
|
47
|
+
AUTH: "auth",
|
|
36
48
|
/** Rate limiting and throttling */
|
|
37
|
-
RATE_LIMIT
|
|
49
|
+
RATE_LIMIT: "rate_limit",
|
|
38
50
|
/** Timeout errors */
|
|
39
|
-
TIMEOUT
|
|
51
|
+
TIMEOUT: "timeout",
|
|
40
52
|
/** Cancellation errors */
|
|
41
|
-
CANCELLED
|
|
53
|
+
CANCELLED: "cancelled",
|
|
42
54
|
/** Unknown/uncategorized errors */
|
|
43
|
-
UNKNOWN
|
|
55
|
+
UNKNOWN: "unknown",
|
|
56
|
+
} as const;
|
|
57
|
+
|
|
58
|
+
/** This type is the union of the values of `ErrorCategory`. */
|
|
59
|
+
export type ErrorCategory = (typeof ErrorCategory)[keyof typeof ErrorCategory];
|
|
60
|
+
|
|
61
|
+
/** These types keep each member of `ErrorCategory` usable as a type. */
|
|
62
|
+
// eslint-disable-next-line @typescript-eslint/no-namespace
|
|
63
|
+
export declare namespace ErrorCategory {
|
|
64
|
+
export type NETWORK = typeof ErrorCategory.NETWORK;
|
|
65
|
+
export type CLIENT = typeof ErrorCategory.CLIENT;
|
|
66
|
+
export type SERVER = typeof ErrorCategory.SERVER;
|
|
67
|
+
export type PROTOCOL = typeof ErrorCategory.PROTOCOL;
|
|
68
|
+
export type AUTH = typeof ErrorCategory.AUTH;
|
|
69
|
+
export type RATE_LIMIT = typeof ErrorCategory.RATE_LIMIT;
|
|
70
|
+
export type TIMEOUT = typeof ErrorCategory.TIMEOUT;
|
|
71
|
+
export type CANCELLED = typeof ErrorCategory.CANCELLED;
|
|
72
|
+
export type UNKNOWN = typeof ErrorCategory.UNKNOWN;
|
|
44
73
|
}
|
|
45
74
|
|
|
46
75
|
/**
|
|
@@ -116,13 +116,18 @@ export interface CircuitBreakerStats {
|
|
|
116
116
|
* Circuit breaker error thrown when circuit is open.
|
|
117
117
|
*/
|
|
118
118
|
export class CircuitBreakerError extends Error {
|
|
119
|
+
public readonly state: CircuitState;
|
|
120
|
+
public readonly lastError: Error | null;
|
|
121
|
+
|
|
119
122
|
constructor(
|
|
120
|
-
|
|
121
|
-
|
|
123
|
+
state: CircuitState,
|
|
124
|
+
lastError: Error | null
|
|
122
125
|
) {
|
|
123
126
|
super(
|
|
124
127
|
`Circuit breaker is ${state}${lastError ? `: ${lastError.message}` : ""}`
|
|
125
128
|
);
|
|
129
|
+
this.state = state;
|
|
130
|
+
this.lastError = lastError;
|
|
126
131
|
this.name = "CircuitBreakerError";
|
|
127
132
|
}
|
|
128
133
|
}
|
|
@@ -8,6 +8,9 @@
|
|
|
8
8
|
* `hashMap` of that package).
|
|
9
9
|
*/
|
|
10
10
|
export class LruMap<K, V> {
|
|
11
|
+
private readonly capacity: number;
|
|
12
|
+
private readonly onEvict?: ((key: K, value: V) => void) | undefined;
|
|
13
|
+
|
|
11
14
|
private readonly entries = new Map<K, V>();
|
|
12
15
|
|
|
13
16
|
/**
|
|
@@ -15,9 +18,11 @@ export class LruMap<K, V> {
|
|
|
15
18
|
* @param onEvict - Called for each key that a write removes to keep the capacity.
|
|
16
19
|
*/
|
|
17
20
|
constructor(
|
|
18
|
-
|
|
19
|
-
|
|
21
|
+
capacity: number,
|
|
22
|
+
onEvict?: (key: K, value: V) => void
|
|
20
23
|
) {
|
|
24
|
+
this.capacity = capacity;
|
|
25
|
+
this.onEvict = onEvict;
|
|
21
26
|
if (!Number.isInteger(capacity) || capacity < 1) {
|
|
22
27
|
throw new RangeError(`The capacity of an LRU map must be a positive whole number, not ${capacity}`);
|
|
23
28
|
}
|
|
@@ -89,11 +89,14 @@ export interface RateLimitStats {
|
|
|
89
89
|
* Rate limit error thrown when limit exceeded.
|
|
90
90
|
*/
|
|
91
91
|
export class RateLimitError extends Error {
|
|
92
|
+
public readonly stats: RateLimitStats;
|
|
93
|
+
|
|
92
94
|
constructor(
|
|
93
95
|
message: string,
|
|
94
|
-
|
|
96
|
+
stats: RateLimitStats
|
|
95
97
|
) {
|
|
96
98
|
super(message);
|
|
99
|
+
this.stats = stats;
|
|
97
100
|
this.name = "RateLimitError";
|
|
98
101
|
}
|
|
99
102
|
}
|
|
@@ -16,6 +16,7 @@
|
|
|
16
16
|
* call goes to the server as `procedure-timeout` with each try (`client/deadline.ts`).
|
|
17
17
|
*/
|
|
18
18
|
|
|
19
|
+
import { compose } from "@mark1russell7/collections/layers";
|
|
19
20
|
import type { ClientMiddleware, ClientRunner, TypedClientMiddleware } from "../types.js";
|
|
20
21
|
import type { CacheContext, CircuitBreakerContext, RateLimitContext, RetryContext, TimeoutContext } from "./contexts.js";
|
|
21
22
|
import { createCacheMiddleware, type CacheOptions } from "./cache.js";
|
|
@@ -76,6 +77,6 @@ export function resilience(options: ResilienceOptions): TypedClientMiddleware<Re
|
|
|
76
77
|
layers.push(createTimeoutMiddleware({ perAttempt: options.perAttempt, ...(message ? { message: `${message} (per-attempt)` } : {}) }) as ClientMiddleware);
|
|
77
78
|
}
|
|
78
79
|
const composed = <TReq, TRes>(next: ClientRunner<TReq, TRes>): ClientRunner<TReq, TRes> =>
|
|
79
|
-
|
|
80
|
+
compose(...(layers as ClientMiddleware<TReq, TRes>[]))(next);
|
|
80
81
|
return composed as TypedClientMiddleware<ResilienceContext, {}>;
|
|
81
82
|
}
|
|
@@ -99,7 +99,11 @@ export interface RouteResolutionResult {
|
|
|
99
99
|
* ```
|
|
100
100
|
*/
|
|
101
101
|
export class RouteResolver {
|
|
102
|
-
|
|
102
|
+
private readonly registry: ProcedureRegistry;
|
|
103
|
+
|
|
104
|
+
constructor(registry: ProcedureRegistry) {
|
|
105
|
+
this.registry = registry;
|
|
106
|
+
}
|
|
103
107
|
|
|
104
108
|
/**
|
|
105
109
|
* Resolve a route structure to procedures.
|
package/src/client/types.ts
CHANGED
|
@@ -326,6 +326,9 @@ export type AccumulateContext<
|
|
|
326
326
|
* Client error thrown when response has error status.
|
|
327
327
|
*/
|
|
328
328
|
export class ClientError extends Error {
|
|
329
|
+
public readonly status: Extract<Status, { type: "error" }>;
|
|
330
|
+
public readonly responseId: string;
|
|
331
|
+
|
|
329
332
|
/**
|
|
330
333
|
* The metadata of the error response, for example `reason` of a `FORBIDDEN` error, or
|
|
331
334
|
* `conflicts` of a `VERSION_CONFLICT` error (roadmap CLI-16). Empty when the response has none.
|
|
@@ -333,11 +336,13 @@ export class ClientError extends Error {
|
|
|
333
336
|
public readonly metadata: Metadata;
|
|
334
337
|
|
|
335
338
|
constructor(
|
|
336
|
-
|
|
337
|
-
|
|
339
|
+
status: Extract<Status, { type: "error" }>,
|
|
340
|
+
responseId: string,
|
|
338
341
|
metadata: Metadata = {},
|
|
339
342
|
) {
|
|
340
343
|
super(status.message);
|
|
344
|
+
this.status = status;
|
|
345
|
+
this.responseId = responseId;
|
|
341
346
|
this.name = "ClientError";
|
|
342
347
|
this.metadata = metadata;
|
|
343
348
|
}
|
|
@@ -114,18 +114,25 @@ export type ValidationPhase = "input" | "output";
|
|
|
114
114
|
* Error thrown when validation fails.
|
|
115
115
|
*/
|
|
116
116
|
export class ValidationError extends Error {
|
|
117
|
+
/** Which phase of validation failed */
|
|
118
|
+
public readonly phase: ValidationPhase;
|
|
119
|
+
/** The underlying Zod error */
|
|
120
|
+
public readonly zodError: ZodErrorLike;
|
|
121
|
+
/** The method being called */
|
|
122
|
+
public readonly method: Method;
|
|
123
|
+
|
|
117
124
|
override readonly name = "ValidationError";
|
|
118
125
|
|
|
119
126
|
constructor(
|
|
120
127
|
message: string,
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
public readonly zodError: ZodErrorLike,
|
|
125
|
-
/** The method being called */
|
|
126
|
-
public readonly method: Method,
|
|
128
|
+
phase: ValidationPhase,
|
|
129
|
+
zodError: ZodErrorLike,
|
|
130
|
+
method: Method,
|
|
127
131
|
) {
|
|
128
132
|
super(message);
|
|
133
|
+
this.phase = phase;
|
|
134
|
+
this.zodError = zodError;
|
|
135
|
+
this.method = method;
|
|
129
136
|
|
|
130
137
|
// Maintain proper stack trace in V8 environments
|
|
131
138
|
if (Error.captureStackTrace) {
|