dixous 0.1.0 → 0.2.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/README.md +196 -68
- package/dist/errors.d.ts +16 -0
- package/dist/errors.d.ts.map +1 -0
- package/dist/errors.js +29 -0
- package/dist/errors.js.map +1 -0
- package/dist/index.d.ts +10 -17
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +82 -113
- package/dist/index.js.map +1 -1
- package/dist/response-methods.d.ts +2 -15
- package/dist/response-methods.d.ts.map +1 -1
- package/dist/response-methods.js +22 -21
- package/dist/response-methods.js.map +1 -1
- package/dist/standard-schema.d.ts +51 -0
- package/dist/standard-schema.d.ts.map +1 -0
- package/dist/standard-schema.js +28 -0
- package/dist/standard-schema.js.map +1 -0
- package/dist/types.d.ts +88 -54
- package/dist/types.d.ts.map +1 -1
- package/package.json +2 -5
- package/src/errors.ts +26 -0
- package/src/index.ts +114 -184
- package/src/response-methods.ts +21 -32
- package/src/standard-schema.ts +51 -0
- package/src/types.ts +118 -117
package/src/types.ts
CHANGED
|
@@ -1,141 +1,142 @@
|
|
|
1
|
-
|
|
1
|
+
import type { InferOutput, StandardSchemaV1 } from "./standard-schema.ts";
|
|
2
2
|
|
|
3
|
-
export type
|
|
4
|
-
|
|
5
|
-
};
|
|
6
|
-
|
|
7
|
-
export interface Context {
|
|
8
|
-
get<T>(key: ContextKey<T>): T | undefined;
|
|
9
|
-
set<T>(key: ContextKey<T>, value: T): void;
|
|
10
|
-
}
|
|
11
|
-
|
|
12
|
-
export type RequestOptions<Extra extends object = {}> = RequestInit & Extra;
|
|
13
|
-
|
|
14
|
-
export interface BaseClientOptions {
|
|
15
|
-
baseUrl?: string | URL;
|
|
16
|
-
headers?: HeadersInit;
|
|
17
|
-
}
|
|
3
|
+
export type RequestInput = string | URL | Request;
|
|
4
|
+
export type RequestOptions<Options extends object = {}> = RequestInit & Options;
|
|
18
5
|
|
|
19
|
-
export
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
ClientExtra extends object = {},
|
|
24
|
-
> {
|
|
6
|
+
export interface RequestContext<Options extends object = {}> {
|
|
7
|
+
/** Exact original input, even when middleware replaces request. */
|
|
8
|
+
readonly input: RequestInput;
|
|
9
|
+
/** Request used by the next execution step; middleware may replace it. */
|
|
25
10
|
request: Request;
|
|
26
|
-
readonly options
|
|
27
|
-
readonly
|
|
28
|
-
readonly state: Context;
|
|
11
|
+
/** Shallow readonly snapshot of effective client and request options. */
|
|
12
|
+
readonly options: Readonly<RequestOptions<Options>>;
|
|
29
13
|
}
|
|
30
14
|
|
|
15
|
+
/**
|
|
16
|
+
* Reruns the remaining chain. Sequential calls are allowed; concurrent calls
|
|
17
|
+
* reject. Dixous does not clone or buffer bodies: callers must ensure replayability.
|
|
18
|
+
*/
|
|
31
19
|
export type Next = () => Promise<Response>;
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
RequestExtra extends object = {},
|
|
35
|
-
ClientExtra extends object = {},
|
|
36
|
-
> = (
|
|
37
|
-
context: RequestContext<RequestExtra, ClientExtra>,
|
|
38
|
-
next: Next,
|
|
20
|
+
export type RequestMiddleware<Options extends object = {}> = (
|
|
21
|
+
context: RequestContext<Options>, next: Next,
|
|
39
22
|
) => Promise<Response>;
|
|
40
23
|
|
|
41
|
-
export
|
|
24
|
+
export interface OperationContext<Options extends object = {}> extends RequestContext<Options> {
|
|
25
|
+
/** Operation methods observe the request; only middleware replaces it. */
|
|
26
|
+
readonly request: Request;
|
|
27
|
+
/** The response this operation reads: rejects non-ok unless built by api(). The body is one-shot. */
|
|
28
|
+
response(): Promise<Response>;
|
|
29
|
+
/** Memoized raw execution without a status policy. */
|
|
30
|
+
execute(): Promise<Response>;
|
|
31
|
+
/** Rebuilds every contribution over a specific response. Contributions are pure factories. */
|
|
32
|
+
api(response: Response): MatchedOperation<{}>;
|
|
33
|
+
}
|
|
42
34
|
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
35
|
+
/** Default body readers require response.ok; schema/platform errors are preserved. */
|
|
36
|
+
export interface DefaultOperationApi {
|
|
37
|
+
json<Schema extends StandardSchemaV1>(schema: Schema): Promise<InferOutput<Schema>>;
|
|
38
|
+
text(): Promise<string>;
|
|
39
|
+
blob(): Promise<Blob>;
|
|
40
|
+
arrayBuffer(): Promise<ArrayBuffer>;
|
|
41
|
+
/** Branches on the exact status; unmatched statuses reject with UnexpectedResponseError. */
|
|
42
|
+
match: Match<{}>;
|
|
43
|
+
}
|
|
46
44
|
|
|
47
|
-
export type
|
|
45
|
+
export type ReservedOperationKey = "response" | "then";
|
|
48
46
|
|
|
49
|
-
type
|
|
50
|
-
readonly [
|
|
47
|
+
export type StatusHandlers<OperationApi extends object> = {
|
|
48
|
+
readonly [status: number]: (operation: MatchedOperation<OperationApi>) => unknown;
|
|
51
49
|
};
|
|
50
|
+
export type MatchResult<Handlers extends StatusHandlers<any>> =
|
|
51
|
+
Awaited<ReturnType<Handlers[keyof Handlers & number]>>;
|
|
52
|
+
export type Match<OperationApi extends object> =
|
|
53
|
+
<Handlers extends StatusHandlers<OperationApi>>(handlers: Handlers) => Promise<MatchResult<Handlers>>;
|
|
54
|
+
|
|
55
|
+
interface CoreOperation {
|
|
56
|
+
/** Returns the native response without applying a status policy. */
|
|
57
|
+
readonly response: () => Promise<Response>;
|
|
58
|
+
readonly then?: never;
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
export type MatchedOperation<OperationApi extends object = DefaultOperationApi> =
|
|
62
|
+
Omit<OperationApi, "match"> & CoreOperation;
|
|
63
|
+
|
|
64
|
+
// The default match is declared over {} and bound here, where the final API is known.
|
|
65
|
+
export type RequestOperation<OperationApi extends object = DefaultOperationApi> = {
|
|
66
|
+
[Key in keyof OperationApi]: Key extends "match"
|
|
67
|
+
? OperationApi[Key] extends Match<{}> ? Match<OperationApi> : OperationApi[Key]
|
|
68
|
+
: OperationApi[Key];
|
|
69
|
+
} & CoreOperation;
|
|
52
70
|
|
|
53
71
|
declare const extensionType: unique symbol;
|
|
54
72
|
|
|
55
|
-
export interface
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
73
|
+
export interface ExtensionDefinition<Options extends object, OperationApi extends object> {
|
|
74
|
+
/** Runs in extension order with onion semantics. */
|
|
75
|
+
readonly request?: RequestMiddleware<Options>;
|
|
76
|
+
/**
|
|
77
|
+
* Pure API factory: may run more than once for one logical request, once per
|
|
78
|
+
* api() call. Later contributions replace earlier methods.
|
|
79
|
+
*/
|
|
80
|
+
readonly operation?: (
|
|
81
|
+
operation: OperationContext<Options>,
|
|
82
|
+
) => OperationApiContribution<OperationApi>;
|
|
61
83
|
}
|
|
62
84
|
|
|
63
|
-
export
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
request: RequestExtra;
|
|
70
|
-
client: ClientExtra;
|
|
71
|
-
methods: Methods;
|
|
85
|
+
export type Extension<Options extends object = {}, OperationApi extends object = {}> =
|
|
86
|
+
ExtensionDefinition<Options, OperationApi> & {
|
|
87
|
+
readonly [extensionType]?: {
|
|
88
|
+
readonly options: Options;
|
|
89
|
+
readonly operationApi: OperationApi;
|
|
90
|
+
};
|
|
72
91
|
};
|
|
73
|
-
|
|
74
|
-
|
|
92
|
+
|
|
93
|
+
export interface CoreOptions {
|
|
94
|
+
/** Resolves string and URL inputs using native WHATWG URL semantics. */
|
|
95
|
+
readonly baseUrl?: string | URL;
|
|
96
|
+
/** Defaults merged by header name in derived clients and requests. */
|
|
97
|
+
readonly headers?: HeadersInit;
|
|
98
|
+
/** Defaults to globalThis.fetch. */
|
|
99
|
+
readonly fetch?: typeof globalThis.fetch;
|
|
75
100
|
}
|
|
76
101
|
|
|
77
|
-
export type
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
102
|
+
export type CreateOptions<CurrentOptions extends object, Extensions extends readonly AnyExtension[]> =
|
|
103
|
+
{ readonly extensions?: Extensions & NoInfer<{
|
|
104
|
+
// Validate inline contributions too; erased APIs retain runtime validation.
|
|
105
|
+
[Index in keyof Extensions]:
|
|
106
|
+
[OperationApiContribution<ReturnType<NonNullable<Extensions[Index]["operation"]>>>] extends [never]
|
|
107
|
+
? never : Extensions[Index];
|
|
108
|
+
}> } &
|
|
109
|
+
CoreOptions & {
|
|
110
|
+
// Materialize the fold so inline extension arrays retain tuple inference.
|
|
111
|
+
[Key in keyof ApplyExtensionOptions<CurrentOptions, Extensions>]:
|
|
112
|
+
ApplyExtensionOptions<CurrentOptions, Extensions>[Key];
|
|
113
|
+
};
|
|
85
114
|
|
|
86
|
-
export
|
|
87
|
-
|
|
88
|
-
|
|
115
|
+
export interface Dixous<Options extends object = {}, OperationApi extends object = DefaultOperationApi> {
|
|
116
|
+
/** Constructs a Request immediately; execution waits until response() is called. */
|
|
117
|
+
request(input: RequestInput, options?: RequestOptions<Options>): RequestOperation<OperationApi>;
|
|
118
|
+
/** Inherits configuration, merges headers, appends extensions; never mutates the parent. */
|
|
119
|
+
create<const Extensions extends readonly AnyExtension[] = []>(
|
|
120
|
+
options?: CreateOptions<Options, Extensions>,
|
|
121
|
+
): Dixous<ApplyExtensionOptions<Options, Extensions>, ApplyExtensionApi<OperationApi, Extensions>>;
|
|
122
|
+
readonly then?: never;
|
|
123
|
+
}
|
|
89
124
|
|
|
90
|
-
export type
|
|
91
|
-
[K in keyof BaseClientOptions]?: never;
|
|
92
|
-
};
|
|
125
|
+
export type AnyExtension = Extension<any, any>;
|
|
93
126
|
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
: never;
|
|
101
|
-
|
|
102
|
-
export type ExtensionRequestOptions<
|
|
103
|
-
Extensions extends readonly ExtensionMeta[],
|
|
104
|
-
> = UnionToIntersection<
|
|
105
|
-
Extensions[number][typeof extensionType]["request"]
|
|
106
|
-
> extends infer Result extends object
|
|
107
|
-
? Result
|
|
108
|
-
: never;
|
|
109
|
-
|
|
110
|
-
export type ExtensionClientOptions<
|
|
111
|
-
Extensions extends readonly ExtensionMeta[],
|
|
112
|
-
> = UnionToIntersection<
|
|
113
|
-
Extensions[number][typeof extensionType]["client"]
|
|
114
|
-
> extends infer Result extends object
|
|
115
|
-
? Result
|
|
116
|
-
: never;
|
|
117
|
-
|
|
118
|
-
export type ExtensionMethods<Extensions extends readonly ExtensionMeta[]> =
|
|
119
|
-
UnionToIntersection<
|
|
120
|
-
Extensions[number][typeof extensionType]["methods"]
|
|
121
|
-
> extends infer Result extends ResponseMethods
|
|
122
|
-
? Result
|
|
123
|
-
: never;
|
|
124
|
-
|
|
125
|
-
export interface Fetcher<
|
|
126
|
-
RequestExtra extends object,
|
|
127
|
-
Methods extends ResponseMethods,
|
|
128
|
-
> {
|
|
129
|
-
fetch(
|
|
130
|
-
input: string | URL | Request,
|
|
131
|
-
options?: RequestOptions<RequestExtra>,
|
|
132
|
-
): InstantiateMethods<Methods>;
|
|
133
|
-
}
|
|
127
|
+
// AnyExtension erases the API for composition; concrete APIs still reserve core keys.
|
|
128
|
+
type OperationApiContribution<Api extends object> =
|
|
129
|
+
unknown extends Api ? Api : [Extract<keyof Api, ReservedOperationKey>] extends [never] ? Api : never;
|
|
130
|
+
type ExtensionOptions<E extends AnyExtension> = E extends Extension<infer Options, any> ? Options : {};
|
|
131
|
+
type ExtensionOperationApi<E extends AnyExtension> = E extends Extension<any, infer Api> ? Api : {};
|
|
132
|
+
type Merge<Left extends object, Right extends object> = Omit<Left, keyof Right> & Right;
|
|
134
133
|
|
|
135
|
-
export
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
134
|
+
export type ApplyExtensionOptions<Current extends object, Extensions extends readonly AnyExtension[]> =
|
|
135
|
+
Extensions extends readonly [infer Head extends AnyExtension, ...infer Tail extends readonly AnyExtension[]]
|
|
136
|
+
? ApplyExtensionOptions<Merge<Current, ExtensionOptions<Head>>, Tail>
|
|
137
|
+
: Current;
|
|
138
|
+
|
|
139
|
+
export type ApplyExtensionApi<Current extends object, Extensions extends readonly AnyExtension[]> =
|
|
140
|
+
Extensions extends readonly [infer Head extends AnyExtension, ...infer Tail extends readonly AnyExtension[]]
|
|
141
|
+
? ApplyExtensionApi<Merge<Current, ExtensionOperationApi<Head>>, Tail>
|
|
142
|
+
: Current;
|