@rest-vir/define-service 0.0.2
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-CC0 +121 -0
- package/LICENSE-MIT +21 -0
- package/README.md +88 -0
- package/dist/augments/json.d.ts +8 -0
- package/dist/augments/json.js +13 -0
- package/dist/dev-port/find-dev-port.d.ts +132 -0
- package/dist/dev-port/find-dev-port.js +156 -0
- package/dist/endpoint/endpoint-path.d.ts +27 -0
- package/dist/endpoint/endpoint-path.js +14 -0
- package/dist/endpoint/endpoint.d.ts +198 -0
- package/dist/endpoint/endpoint.js +80 -0
- package/dist/frontend-connect/connect-web-socket.d.ts +95 -0
- package/dist/frontend-connect/connect-web-socket.js +64 -0
- package/dist/frontend-connect/fetch-endpoint.d.ts +199 -0
- package/dist/frontend-connect/fetch-endpoint.js +135 -0
- package/dist/frontend-connect/generate-api.d.ts +102 -0
- package/dist/frontend-connect/generate-api.js +83 -0
- package/dist/frontend-connect/mock-client-web-socket.d.ts +187 -0
- package/dist/frontend-connect/mock-client-web-socket.js +198 -0
- package/dist/frontend-connect/web-socket-protocol-parse.d.ts +10 -0
- package/dist/frontend-connect/web-socket-protocol-parse.js +111 -0
- package/dist/index.d.ts +23 -0
- package/dist/index.js +23 -0
- package/dist/service/define-service.d.ts +19 -0
- package/dist/service/define-service.js +133 -0
- package/dist/service/match-url.d.ts +30 -0
- package/dist/service/match-url.js +31 -0
- package/dist/service/minimal-service.d.ts +44 -0
- package/dist/service/minimal-service.js +1 -0
- package/dist/service/service-definition.d.ts +80 -0
- package/dist/service/service-definition.error.d.ts +35 -0
- package/dist/service/service-definition.error.js +34 -0
- package/dist/service/service-definition.js +1 -0
- package/dist/util/mock-fetch.d.ts +107 -0
- package/dist/util/mock-fetch.js +199 -0
- package/dist/util/no-param.d.ts +16 -0
- package/dist/util/no-param.js +8 -0
- package/dist/util/origin.d.ts +43 -0
- package/dist/util/origin.js +19 -0
- package/dist/util/path-to-regexp.d.ts +54 -0
- package/dist/util/path-to-regexp.js +307 -0
- package/dist/util/search-params.d.ts +9 -0
- package/dist/util/search-params.js +1 -0
- package/dist/web-socket/common-web-socket.d.ts +103 -0
- package/dist/web-socket/common-web-socket.js +28 -0
- package/dist/web-socket/overwrite-web-socket-methods.d.ts +276 -0
- package/dist/web-socket/overwrite-web-socket-methods.js +210 -0
- package/dist/web-socket/web-socket-definition.d.ts +170 -0
- package/dist/web-socket/web-socket-definition.js +78 -0
- package/package.json +68 -0
|
@@ -0,0 +1,198 @@
|
|
|
1
|
+
import { AnyObject, HttpMethod, JsonCompatibleValue, Overwrite, type SelectFrom } from '@augment-vir/common';
|
|
2
|
+
import { type ShapeDefinition, type ShapeToRuntimeType } from 'object-shape-tester';
|
|
3
|
+
import { type RequireAtLeastOne } from 'type-fest';
|
|
4
|
+
import { type MinimalService } from '../service/minimal-service.js';
|
|
5
|
+
import { type NoParam } from '../util/no-param.js';
|
|
6
|
+
import { type OriginRequirement } from '../util/origin.js';
|
|
7
|
+
import { BaseSearchParams } from '../util/search-params.js';
|
|
8
|
+
import { type EndpointPathBase } from './endpoint-path.js';
|
|
9
|
+
/**
|
|
10
|
+
* Base Endpoint request/response shape type.
|
|
11
|
+
*
|
|
12
|
+
* @category Internal
|
|
13
|
+
* @category Package : @rest-vir/define-service
|
|
14
|
+
* @package [`@rest-vir/define-service`](https://www.npmjs.com/package/@rest-vir/define-service)
|
|
15
|
+
*/
|
|
16
|
+
export type EndpointDataShapeBase = JsonCompatibleValue;
|
|
17
|
+
/**
|
|
18
|
+
* The type for setting up an individual endpoint, used in `defineService`.
|
|
19
|
+
*
|
|
20
|
+
* @category Internal
|
|
21
|
+
* @category Package : @rest-vir/define-service
|
|
22
|
+
* @package [`@rest-vir/define-service`](https://www.npmjs.com/package/@rest-vir/define-service)
|
|
23
|
+
*/
|
|
24
|
+
export type EndpointInit<AllowedMethods extends RequireAtLeastOne<Record<HttpMethod, boolean>> = RequireAtLeastOne<Record<HttpMethod, boolean>>, RequestDataShape extends EndpointDataShapeBase | NoParam = EndpointDataShapeBase | NoParam, ResponseDataShape = unknown> = {
|
|
25
|
+
/**
|
|
26
|
+
* Shape definition for request data. Set to `undefined` for no request data.
|
|
27
|
+
*
|
|
28
|
+
* See the [`object-shape-tester`](https://www.npmjs.com/package/object-shape-tester) package
|
|
29
|
+
* for extra details on defining a shape.
|
|
30
|
+
*/
|
|
31
|
+
requestDataShape: RequestDataShape;
|
|
32
|
+
/**
|
|
33
|
+
* Shape definition for response data. Set to `undefined` for no response data.
|
|
34
|
+
*
|
|
35
|
+
* See the [`object-shape-tester`](https://www.npmjs.com/package/object-shape-tester) package
|
|
36
|
+
* for extra details on defining a shape.
|
|
37
|
+
*/
|
|
38
|
+
responseDataShape: ResponseDataShape;
|
|
39
|
+
/**
|
|
40
|
+
* All allowed (or not allowed) HTTP methods for this endpoint. Set `true` for each allowed
|
|
41
|
+
* method and omit or set `false` for each blocked method.
|
|
42
|
+
*/
|
|
43
|
+
methods: AllowedMethods;
|
|
44
|
+
/**
|
|
45
|
+
* Set a required client origin for this endpoint.
|
|
46
|
+
*
|
|
47
|
+
* - If this is omitted, the service's origin requirement is used instead.
|
|
48
|
+
* - If this is explicitly set to `undefined`, this endpoint allows any origins (regardless of the
|
|
49
|
+
* service's origin requirement).
|
|
50
|
+
* - Any other set value overrides the service's origin requirement (if it has any).
|
|
51
|
+
*/
|
|
52
|
+
requiredClientOrigin?: OriginRequirement;
|
|
53
|
+
/**
|
|
54
|
+
* A shape used to verify search params. This should match the entire search params object.
|
|
55
|
+
*
|
|
56
|
+
* Note the following:
|
|
57
|
+
*
|
|
58
|
+
* - Search param values will _always_ be in an array
|
|
59
|
+
* - Elements in search param value arrays will _always_ be strings
|
|
60
|
+
*
|
|
61
|
+
* @example
|
|
62
|
+
*
|
|
63
|
+
* ```ts
|
|
64
|
+
* import {exact, enumShape} from 'object-shape-tester';
|
|
65
|
+
*
|
|
66
|
+
* const partialEndpointInit = {
|
|
67
|
+
* searchParamsShape: {
|
|
68
|
+
* // use `tupleShape` to ensure there's exactly one entry for this search param
|
|
69
|
+
* userId: tupleShape(enumShape(MyEnum)),
|
|
70
|
+
* date: tupleShape(exact('2')),
|
|
71
|
+
* // don't use `tupleShape` here so that there can be any number of entries
|
|
72
|
+
* colors: [''],
|
|
73
|
+
* },
|
|
74
|
+
* };
|
|
75
|
+
* ```
|
|
76
|
+
*/
|
|
77
|
+
searchParamsShape?: unknown;
|
|
78
|
+
/**
|
|
79
|
+
* Any additional data that you wish to attach to this endpoint. This won't be used by the
|
|
80
|
+
* service at all, it's merely a place for you to place extra data which will be passed along to
|
|
81
|
+
* your endpoint implementations.
|
|
82
|
+
*/
|
|
83
|
+
customProps?: Record<PropertyKey, unknown> | undefined;
|
|
84
|
+
};
|
|
85
|
+
/**
|
|
86
|
+
* Used to validate {@link EndpointInit} inside of `defineService`.
|
|
87
|
+
*
|
|
88
|
+
* @category Internal
|
|
89
|
+
* @category Package : @rest-vir/define-service
|
|
90
|
+
* @package [`@rest-vir/define-service`](https://www.npmjs.com/package/@rest-vir/define-service)
|
|
91
|
+
*/
|
|
92
|
+
export declare const endpointInitShape: ShapeDefinition<{
|
|
93
|
+
requestDataShape: import("object-shape-tester").ShapeUnknown<[unknown]>;
|
|
94
|
+
responseDataShape: import("object-shape-tester").ShapeUnknown<[unknown]>;
|
|
95
|
+
searchParamsShape: import("object-shape-tester").ShapeUnknown<[unknown]>;
|
|
96
|
+
/**
|
|
97
|
+
* Set a required client origin for this endpoint.
|
|
98
|
+
*
|
|
99
|
+
* - If this is omitted, the service's origin requirement is used instead.
|
|
100
|
+
* - If this is explicitly set to `undefined`, this endpoint allows any origins (regardless of the
|
|
101
|
+
* service's origin requirement).
|
|
102
|
+
* - Any other set value overrides the service's origin requirement (if it has any).
|
|
103
|
+
*/
|
|
104
|
+
requiredClientOrigin: import("object-shape-tester").ShapeOr<[undefined, string, import("object-shape-tester").ShapeClass<[RegExpConstructor]>, () => void, import("object-shape-tester").ShapeOr<[string, import("object-shape-tester").ShapeClass<[RegExpConstructor]>, () => void]>[]]>;
|
|
105
|
+
methods: import("object-shape-tester").ShapeIndexedKeys<[{
|
|
106
|
+
keys: import("object-shape-tester").ShapeEnum<readonly [typeof HttpMethod]>;
|
|
107
|
+
values: boolean;
|
|
108
|
+
required: false;
|
|
109
|
+
}]>;
|
|
110
|
+
customProps: import("object-shape-tester").ShapeOptional<import("object-shape-tester").ShapeOr<[undefined, import("object-shape-tester").ShapeIndexedKeys<[{
|
|
111
|
+
keys: import("object-shape-tester").ShapeUnknown<[unknown]>;
|
|
112
|
+
values: import("object-shape-tester").ShapeUnknown<[unknown]>;
|
|
113
|
+
required: false;
|
|
114
|
+
}]>]>>;
|
|
115
|
+
}, false>;
|
|
116
|
+
/**
|
|
117
|
+
* Adds final properties to {@link EndpointInit} so it becomes {@link EndpointDefinition}.
|
|
118
|
+
*
|
|
119
|
+
* @category Internal
|
|
120
|
+
* @category Package : @rest-vir/define-service
|
|
121
|
+
* @package [`@rest-vir/define-service`](https://www.npmjs.com/package/@rest-vir/define-service)
|
|
122
|
+
*/
|
|
123
|
+
export type WithFinalEndpointProps<Init, EndpointPath extends EndpointPathBase> = (Init extends AnyObject ? Overwrite<Init, {
|
|
124
|
+
requestDataShape: Init['requestDataShape'] extends NoParam ? ShapeDefinition<any, true> | undefined : undefined extends Init['requestDataShape'] ? undefined : ShapeDefinition<Init['requestDataShape'], true>;
|
|
125
|
+
responseDataShape: Init['responseDataShape'] extends NoParam ? ShapeDefinition<any, true> | undefined : undefined extends Init['responseDataShape'] ? undefined : ShapeDefinition<Init['responseDataShape'], true>;
|
|
126
|
+
RequestType: Init['requestDataShape'] extends NoParam ? any : undefined extends Init['requestDataShape'] ? undefined : ShapeToRuntimeType<ShapeDefinition<Init['requestDataShape'], true>, false, true>;
|
|
127
|
+
ResponseType: Init['responseDataShape'] extends NoParam ? any : undefined extends Init['responseDataShape'] ? undefined : ShapeToRuntimeType<ShapeDefinition<Init['responseDataShape'], true>, false, true>;
|
|
128
|
+
searchParamsShape: 'searchParamsShape' extends keyof Init ? undefined extends Init['searchParamsShape'] ? undefined : ShapeDefinition<Init['searchParamsShape'], true> | undefined : undefined;
|
|
129
|
+
SearchParamsType: undefined extends Init['searchParamsShape'] ? BaseSearchParams : ShapeToRuntimeType<ShapeDefinition<Init['searchParamsShape'], true>, false, true>;
|
|
130
|
+
customProps: 'customProps' extends keyof Init ? Init['customProps'] : undefined;
|
|
131
|
+
}> : never) & {
|
|
132
|
+
path: EndpointPath;
|
|
133
|
+
isWebSocket: false;
|
|
134
|
+
isEndpoint: true;
|
|
135
|
+
service: MinimalService;
|
|
136
|
+
};
|
|
137
|
+
/**
|
|
138
|
+
* A fully defined endpoint instance. This is generated from `defineService`.
|
|
139
|
+
*
|
|
140
|
+
* @category Internal
|
|
141
|
+
* @category Package : @rest-vir/define-service
|
|
142
|
+
* @package [`@rest-vir/define-service`](https://www.npmjs.com/package/@rest-vir/define-service)
|
|
143
|
+
*/
|
|
144
|
+
export type EndpointDefinition<AllowedMethods extends RequireAtLeastOne<Record<HttpMethod, boolean>> = RequireAtLeastOne<Record<HttpMethod, boolean>>, RequestDataShape extends EndpointDataShapeBase | NoParam = NoParam, ResponseDataShape extends EndpointDataShapeBase | NoParam = NoParam, EndpointPath extends EndpointPathBase = EndpointPathBase> = WithFinalEndpointProps<EndpointInit<AllowedMethods, RequestDataShape, ResponseDataShape>, EndpointPath>;
|
|
145
|
+
/**
|
|
146
|
+
* A generic version of {@link EndpointDefinition} that any endpoint can be assigned to.
|
|
147
|
+
*
|
|
148
|
+
* @category Internal
|
|
149
|
+
* @category Package : @rest-vir/define-service
|
|
150
|
+
* @package [`@rest-vir/define-service`](https://www.npmjs.com/package/@rest-vir/define-service)
|
|
151
|
+
*/
|
|
152
|
+
export type GenericEndpointDefinition = Overwrite<EndpointDefinition, {
|
|
153
|
+
searchParamsShape: any;
|
|
154
|
+
SearchParamsType: any;
|
|
155
|
+
}>;
|
|
156
|
+
/**
|
|
157
|
+
* Extracts response and request data from an endpoint definition.
|
|
158
|
+
*
|
|
159
|
+
* @category Internal
|
|
160
|
+
* @category Package : @rest-vir/define-service
|
|
161
|
+
* @package [`@rest-vir/define-service`](https://www.npmjs.com/package/@rest-vir/define-service)
|
|
162
|
+
*/
|
|
163
|
+
export type BaseEndpointForExecutorData = Pick<EndpointDefinition, 'requestDataShape' | 'responseDataShape'>;
|
|
164
|
+
/**
|
|
165
|
+
* Extracts response and request data from an endpoint definition into different properties.
|
|
166
|
+
*
|
|
167
|
+
* @category Internal
|
|
168
|
+
* @category Package : @rest-vir/define-service
|
|
169
|
+
* @package [`@rest-vir/define-service`](https://www.npmjs.com/package/@rest-vir/define-service)
|
|
170
|
+
*/
|
|
171
|
+
export type EndpointExecutorData<SpecificEndpoint extends BaseEndpointForExecutorData> = {
|
|
172
|
+
request: ShapeToRuntimeType<SpecificEndpoint['requestDataShape'], false, true>;
|
|
173
|
+
response: ShapeToRuntimeType<SpecificEndpoint['responseDataShape'], false, true>;
|
|
174
|
+
};
|
|
175
|
+
/**
|
|
176
|
+
* Attaches request and response type-only getters to an endpoint definition.
|
|
177
|
+
*
|
|
178
|
+
* @category Internal
|
|
179
|
+
* @category Package : @rest-vir/define-service
|
|
180
|
+
* @package [`@rest-vir/define-service`](https://www.npmjs.com/package/@rest-vir/define-service)
|
|
181
|
+
*/
|
|
182
|
+
export declare function attachEndpointShapeTypeGetters<const T extends AnyObject>(endpoint: T): asserts endpoint is T & Pick<EndpointDefinition, 'RequestType' | 'ResponseType'>;
|
|
183
|
+
/**
|
|
184
|
+
* Asserts that the given finalized {@link EndpointDefinition} instance is valid.
|
|
185
|
+
*
|
|
186
|
+
* @category Internal
|
|
187
|
+
* @category Package : @rest-vir/define-service
|
|
188
|
+
* @package [`@rest-vir/define-service`](https://www.npmjs.com/package/@rest-vir/define-service)
|
|
189
|
+
*/
|
|
190
|
+
export declare function assertValidEndpoint(endpoint: Readonly<SelectFrom<EndpointDefinition, {
|
|
191
|
+
path: true;
|
|
192
|
+
methods: true;
|
|
193
|
+
isWebSocket: true;
|
|
194
|
+
isEndpoint: true;
|
|
195
|
+
service: {
|
|
196
|
+
serviceName: true;
|
|
197
|
+
};
|
|
198
|
+
}>>): void;
|
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
import { HttpMethod, } from '@augment-vir/common';
|
|
2
|
+
import { defineShape, enumShape, indexedKeys, optional, or, unknownShape, } from 'object-shape-tester';
|
|
3
|
+
import { ensureServiceDefinitionError } from '../service/service-definition.error.js';
|
|
4
|
+
import { originRequirementShape } from '../util/origin.js';
|
|
5
|
+
import { assertValidEndpointPath } from './endpoint-path.js';
|
|
6
|
+
/**
|
|
7
|
+
* Used to validate {@link EndpointInit} inside of `defineService`.
|
|
8
|
+
*
|
|
9
|
+
* @category Internal
|
|
10
|
+
* @category Package : @rest-vir/define-service
|
|
11
|
+
* @package [`@rest-vir/define-service`](https://www.npmjs.com/package/@rest-vir/define-service)
|
|
12
|
+
*/
|
|
13
|
+
export const endpointInitShape = defineShape({
|
|
14
|
+
requestDataShape: unknownShape(),
|
|
15
|
+
responseDataShape: unknownShape(),
|
|
16
|
+
searchParamsShape: unknownShape(),
|
|
17
|
+
/**
|
|
18
|
+
* Set a required client origin for this endpoint.
|
|
19
|
+
*
|
|
20
|
+
* - If this is omitted, the service's origin requirement is used instead.
|
|
21
|
+
* - If this is explicitly set to `undefined`, this endpoint allows any origins (regardless of the
|
|
22
|
+
* service's origin requirement).
|
|
23
|
+
* - Any other set value overrides the service's origin requirement (if it has any).
|
|
24
|
+
*/
|
|
25
|
+
requiredClientOrigin: originRequirementShape,
|
|
26
|
+
methods: indexedKeys({
|
|
27
|
+
keys: enumShape(HttpMethod),
|
|
28
|
+
values: false,
|
|
29
|
+
required: false,
|
|
30
|
+
}),
|
|
31
|
+
customProps: optional(or(undefined, indexedKeys({
|
|
32
|
+
keys: unknownShape(),
|
|
33
|
+
values: unknownShape(),
|
|
34
|
+
required: false,
|
|
35
|
+
}))),
|
|
36
|
+
});
|
|
37
|
+
/**
|
|
38
|
+
* Attaches request and response type-only getters to an endpoint definition.
|
|
39
|
+
*
|
|
40
|
+
* @category Internal
|
|
41
|
+
* @category Package : @rest-vir/define-service
|
|
42
|
+
* @package [`@rest-vir/define-service`](https://www.npmjs.com/package/@rest-vir/define-service)
|
|
43
|
+
*/
|
|
44
|
+
export function attachEndpointShapeTypeGetters(endpoint) {
|
|
45
|
+
Object.defineProperties(endpoint, {
|
|
46
|
+
RequestType: {
|
|
47
|
+
enumerable: false,
|
|
48
|
+
get() {
|
|
49
|
+
throw new Error('.RequestType should not be used as a value.');
|
|
50
|
+
},
|
|
51
|
+
},
|
|
52
|
+
ResponseType: {
|
|
53
|
+
enumerable: false,
|
|
54
|
+
get() {
|
|
55
|
+
throw new Error('.ResponseType should not be used as a value.');
|
|
56
|
+
},
|
|
57
|
+
},
|
|
58
|
+
});
|
|
59
|
+
}
|
|
60
|
+
/**
|
|
61
|
+
* Asserts that the given finalized {@link EndpointDefinition} instance is valid.
|
|
62
|
+
*
|
|
63
|
+
* @category Internal
|
|
64
|
+
* @category Package : @rest-vir/define-service
|
|
65
|
+
* @package [`@rest-vir/define-service`](https://www.npmjs.com/package/@rest-vir/define-service)
|
|
66
|
+
*/
|
|
67
|
+
export function assertValidEndpoint(endpoint) {
|
|
68
|
+
try {
|
|
69
|
+
assertValidEndpointPath(endpoint.path);
|
|
70
|
+
if (!Object.values(endpoint.methods).some((value) => value)) {
|
|
71
|
+
throw new Error('Endpoint has no allowed HTTP methods.');
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
catch (error) {
|
|
75
|
+
throw ensureServiceDefinitionError(error, {
|
|
76
|
+
serviceName: endpoint.service.serviceName,
|
|
77
|
+
...endpoint,
|
|
78
|
+
});
|
|
79
|
+
}
|
|
80
|
+
}
|
|
@@ -0,0 +1,95 @@
|
|
|
1
|
+
import { RequiredKeysOf, SelectFrom } from '@augment-vir/common';
|
|
2
|
+
import { IsNever } from 'type-fest';
|
|
3
|
+
import { PathParams } from '../endpoint/endpoint-path.js';
|
|
4
|
+
import { type NoParam } from '../util/no-param.js';
|
|
5
|
+
import type { CommonWebSocket } from '../web-socket/common-web-socket.js';
|
|
6
|
+
import { type ClientWebSocket, type ConnectWebSocketListeners, type GenericConnectWebSocketParams } from '../web-socket/overwrite-web-socket-methods.js';
|
|
7
|
+
import { type WebSocketDefinition } from '../web-socket/web-socket-definition.js';
|
|
8
|
+
/**
|
|
9
|
+
* Verifies that no WebSocket protocols in the given list are invalid. This doesn't have anything to
|
|
10
|
+
* do with a `WebsocketDefinition` instance, it's just checking if the given protocols can even be
|
|
11
|
+
* sent.
|
|
12
|
+
*
|
|
13
|
+
* @category Internal
|
|
14
|
+
* @category Package : @rest-vir/define-service
|
|
15
|
+
* @package [`@rest-vir/define-service`](https://www.npmjs.com/package/@rest-vir/define-service)
|
|
16
|
+
*/
|
|
17
|
+
export declare function assertValidWebSocketProtocols(protocols: ReadonlyArray<unknown> | undefined): void;
|
|
18
|
+
/**
|
|
19
|
+
* Builds a WebSocket URL.
|
|
20
|
+
*
|
|
21
|
+
* @category Internal
|
|
22
|
+
* @category Package : @rest-vir/define-service
|
|
23
|
+
* @package [`@rest-vir/define-service`](https://www.npmjs.com/package/@rest-vir/define-service)
|
|
24
|
+
*/
|
|
25
|
+
export declare function buildWebSocketUrl(webSocketDefinition: Readonly<SelectFrom<WebSocketDefinition, {
|
|
26
|
+
path: true;
|
|
27
|
+
service: {
|
|
28
|
+
serviceName: true;
|
|
29
|
+
serviceOrigin: true;
|
|
30
|
+
};
|
|
31
|
+
}>>, ...[{ pathParams },]: CollapsedConnectWebSocketParams): string;
|
|
32
|
+
/**
|
|
33
|
+
* Params for {@link connectWebSocket}.
|
|
34
|
+
*
|
|
35
|
+
* @category Internal
|
|
36
|
+
* @category Package : @rest-vir/define-service
|
|
37
|
+
* @package [`@rest-vir/define-service`](https://www.npmjs.com/package/@rest-vir/define-service)
|
|
38
|
+
*/
|
|
39
|
+
export type ConnectWebSocketParams<WebSocketToConnect extends Readonly<SelectFrom<WebSocketDefinition, {
|
|
40
|
+
path: true;
|
|
41
|
+
MessageFromClientType: true;
|
|
42
|
+
MessageFromHostType: true;
|
|
43
|
+
protocolsShape: true;
|
|
44
|
+
ProtocolsType: true;
|
|
45
|
+
searchParamsShape: true;
|
|
46
|
+
SearchParamsType: true;
|
|
47
|
+
}>>, AllowWebSocketMock extends boolean = true, WebSocketClass extends CommonWebSocket = CommonWebSocket> = {
|
|
48
|
+
/**
|
|
49
|
+
* Optional listeners that can be immediately attached to the WebSocket instance instead of
|
|
50
|
+
* requiring externally adding them.
|
|
51
|
+
*/
|
|
52
|
+
listeners?: ConnectWebSocketListeners<WebSocketToConnect, WebSocketClass>;
|
|
53
|
+
} & (IsNever<PathParams<WebSocketToConnect['path']>> extends true ? {
|
|
54
|
+
/** This WebSocket has no path parameters to configure. */
|
|
55
|
+
pathParams?: undefined;
|
|
56
|
+
} : PathParams<WebSocketToConnect['path']> extends string ? {
|
|
57
|
+
/** Required path params for this WebSocket's path. */
|
|
58
|
+
pathParams: Readonly<Record<PathParams<WebSocketToConnect['path']>, string>>;
|
|
59
|
+
} : {
|
|
60
|
+
/** This WebSocket has no path parameters to configure. */
|
|
61
|
+
pathParams?: undefined;
|
|
62
|
+
}) & (AllowWebSocketMock extends true ? Pick<GenericConnectWebSocketParams<WebSocketClass>, 'webSocketConstructor'> : unknown) & (WebSocketToConnect['protocolsShape'] extends undefined ? {
|
|
63
|
+
protocols?: string[];
|
|
64
|
+
} : {
|
|
65
|
+
protocols: WebSocketToConnect['ProtocolsType'];
|
|
66
|
+
}) & (WebSocketToConnect['searchParamsShape'] extends undefined ? {
|
|
67
|
+
searchParams?: string[];
|
|
68
|
+
} : {
|
|
69
|
+
searchParams: WebSocketToConnect['SearchParamsType'];
|
|
70
|
+
});
|
|
71
|
+
/**
|
|
72
|
+
* Collapsed version of {@link ConnectWebSocketParams} for {@link connectWebSocket} that only
|
|
73
|
+
* _requires_ an object parameter if the parameters object has any required keys.
|
|
74
|
+
*
|
|
75
|
+
* @category Internal
|
|
76
|
+
* @category Package : @rest-vir/define-service
|
|
77
|
+
* @package [`@rest-vir/define-service`](https://www.npmjs.com/package/@rest-vir/define-service)
|
|
78
|
+
*/
|
|
79
|
+
export type CollapsedConnectWebSocketParams<WebSocketToConnect extends Readonly<SelectFrom<WebSocketDefinition, {
|
|
80
|
+
path: true;
|
|
81
|
+
MessageFromClientType: true;
|
|
82
|
+
MessageFromHostType: true;
|
|
83
|
+
protocolsShape: true;
|
|
84
|
+
ProtocolsType: true;
|
|
85
|
+
searchParamsShape: true;
|
|
86
|
+
SearchParamsType: true;
|
|
87
|
+
}>> | NoParam = NoParam, AllowWebSocketMock extends boolean = true, WebSocketClass extends CommonWebSocket = CommonWebSocket> = WebSocketToConnect extends NoParam ? [Readonly<GenericConnectWebSocketParams<WebSocketClass>>?] : Readonly<ConnectWebSocketParams<Exclude<WebSocketToConnect, NoParam>, AllowWebSocketMock, WebSocketClass>> extends infer RealParams ? RequiredKeysOf<RealParams> extends never ? [RealParams?] : [RealParams] : [];
|
|
88
|
+
/**
|
|
89
|
+
* Creates and connects a new client WebSocket instance with type safety.
|
|
90
|
+
*
|
|
91
|
+
* @category Client (Frontend) Connection
|
|
92
|
+
* @category Package : @rest-vir/define-service
|
|
93
|
+
* @package [`@rest-vir/define-service`](https://www.npmjs.com/package/@rest-vir/define-service)
|
|
94
|
+
*/
|
|
95
|
+
export declare function connectWebSocket<const WebSocketToConnect extends Readonly<WebSocketDefinition> | NoParam, WebSocketClass extends CommonWebSocket>(webSocketDefinition: WebSocketToConnect extends WebSocketDefinition ? WebSocketToConnect : Readonly<WebSocketDefinition>, ...params: CollapsedConnectWebSocketParams<WebSocketToConnect, true, WebSocketClass>): Promise<ClientWebSocket<WebSocketToConnect, WebSocketClass>>;
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
import { ensureErrorAndPrependMessage, HttpMethod, } from '@augment-vir/common';
|
|
2
|
+
import { buildUrl } from 'url-vir';
|
|
3
|
+
import { finalizeWebSocket, WebSocketLocation, } from '../web-socket/overwrite-web-socket-methods.js';
|
|
4
|
+
import { buildEndpointUrl } from './fetch-endpoint.js';
|
|
5
|
+
import { parseSecWebSocketProtocolHeader } from './web-socket-protocol-parse.js';
|
|
6
|
+
/**
|
|
7
|
+
* Verifies that no WebSocket protocols in the given list are invalid. This doesn't have anything to
|
|
8
|
+
* do with a `WebsocketDefinition` instance, it's just checking if the given protocols can even be
|
|
9
|
+
* sent.
|
|
10
|
+
*
|
|
11
|
+
* @category Internal
|
|
12
|
+
* @category Package : @rest-vir/define-service
|
|
13
|
+
* @package [`@rest-vir/define-service`](https://www.npmjs.com/package/@rest-vir/define-service)
|
|
14
|
+
*/
|
|
15
|
+
export function assertValidWebSocketProtocols(protocols) {
|
|
16
|
+
if (!protocols || !protocols.length) {
|
|
17
|
+
return;
|
|
18
|
+
}
|
|
19
|
+
const joinedProtocols = protocols.join(', ');
|
|
20
|
+
try {
|
|
21
|
+
parseSecWebSocketProtocolHeader(joinedProtocols);
|
|
22
|
+
}
|
|
23
|
+
catch (error) {
|
|
24
|
+
throw ensureErrorAndPrependMessage(error, `Invalid protocols given ('${joinedProtocols}')`);
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
/**
|
|
28
|
+
* Builds a WebSocket URL.
|
|
29
|
+
*
|
|
30
|
+
* @category Internal
|
|
31
|
+
* @category Package : @rest-vir/define-service
|
|
32
|
+
* @package [`@rest-vir/define-service`](https://www.npmjs.com/package/@rest-vir/define-service)
|
|
33
|
+
*/
|
|
34
|
+
export function buildWebSocketUrl(webSocketDefinition, ...[{ pathParams } = {},]) {
|
|
35
|
+
const httpUrl = buildEndpointUrl({
|
|
36
|
+
methods: {
|
|
37
|
+
[HttpMethod.Get]: true,
|
|
38
|
+
},
|
|
39
|
+
path: webSocketDefinition.path,
|
|
40
|
+
requestDataShape: undefined,
|
|
41
|
+
responseDataShape: undefined,
|
|
42
|
+
service: webSocketDefinition.service,
|
|
43
|
+
}, { pathParams });
|
|
44
|
+
return buildUrl(httpUrl, {
|
|
45
|
+
protocol: httpUrl.startsWith('https') ? 'wss' : 'ws',
|
|
46
|
+
}).href;
|
|
47
|
+
}
|
|
48
|
+
const defaultWebSocket = function (...[url, protocols,]) {
|
|
49
|
+
return new globalThis.WebSocket(url, protocols);
|
|
50
|
+
};
|
|
51
|
+
/**
|
|
52
|
+
* Creates and connects a new client WebSocket instance with type safety.
|
|
53
|
+
*
|
|
54
|
+
* @category Client (Frontend) Connection
|
|
55
|
+
* @category Package : @rest-vir/define-service
|
|
56
|
+
* @package [`@rest-vir/define-service`](https://www.npmjs.com/package/@rest-vir/define-service)
|
|
57
|
+
*/
|
|
58
|
+
export async function connectWebSocket(webSocketDefinition, ...params) {
|
|
59
|
+
const [{ webSocketConstructor = defaultWebSocket, protocols, listeners } = {},] = params;
|
|
60
|
+
assertValidWebSocketProtocols(protocols);
|
|
61
|
+
const url = buildWebSocketUrl(webSocketDefinition, ...params);
|
|
62
|
+
const clientWebSocket = await finalizeWebSocket(webSocketDefinition, new webSocketConstructor(url, protocols, webSocketDefinition), listeners, WebSocketLocation.OnClient);
|
|
63
|
+
return clientWebSocket;
|
|
64
|
+
}
|
|
@@ -0,0 +1,199 @@
|
|
|
1
|
+
import { HttpMethod, KeyCount, type ExtractKeysWithMatchingValues, type MaybePromise, type RequiredKeysOf, type SelectFrom } from '@augment-vir/common';
|
|
2
|
+
import { type IsEqual, type IsNever } from 'type-fest';
|
|
3
|
+
import { type PathParams } from '../endpoint/endpoint-path.js';
|
|
4
|
+
import { EndpointExecutorData, type EndpointDefinition, type GenericEndpointDefinition } from '../endpoint/endpoint.js';
|
|
5
|
+
import { type NoParam } from '../util/no-param.js';
|
|
6
|
+
/**
|
|
7
|
+
* A general version of {@link FetchEndpointParams} to be used when accepting _any_ endpoint (like in
|
|
8
|
+
* tests).
|
|
9
|
+
*
|
|
10
|
+
* @category Internal
|
|
11
|
+
* @category Package : @rest-vir/define-service
|
|
12
|
+
* @package [`@rest-vir/define-service`](https://www.npmjs.com/package/@rest-vir/define-service)
|
|
13
|
+
*/
|
|
14
|
+
export type GenericFetchEndpointParams = {
|
|
15
|
+
pathParams?: Record<string, string> | undefined;
|
|
16
|
+
requestData?: any;
|
|
17
|
+
method?: HttpMethod | undefined;
|
|
18
|
+
options?: Omit<RequestInit, 'body' | 'method'> | undefined;
|
|
19
|
+
/**
|
|
20
|
+
* A custom fetch implementation. Useful for debugging or unit testing. This can safely be
|
|
21
|
+
* omitted to use the default JavaScript built-in global `fetch` function.
|
|
22
|
+
*/
|
|
23
|
+
fetch?: ((url: string, requestInit: RequestInit, endpoint?: GenericEndpointDefinition | undefined) => MaybePromise<Response>) | undefined;
|
|
24
|
+
};
|
|
25
|
+
/**
|
|
26
|
+
* Type that determines which HTTP request methods can be used for the given endpoint definition.
|
|
27
|
+
*
|
|
28
|
+
* @category Internal
|
|
29
|
+
* @category Package : @rest-vir/define-service
|
|
30
|
+
* @package [`@rest-vir/define-service`](https://www.npmjs.com/package/@rest-vir/define-service)
|
|
31
|
+
*/
|
|
32
|
+
export type FetchMethod<EndpointToFetch extends Pick<EndpointDefinition, 'methods'>> = IsEqual<KeyCount<Record<ExtractKeysWithMatchingValues<EndpointToFetch['methods'], true>, boolean>>, 1> extends true ? never : Extract<HttpMethod, ExtractKeysWithMatchingValues<EndpointToFetch['methods'], true>> | `${Extract<HttpMethod, ExtractKeysWithMatchingValues<EndpointToFetch['methods'], true>>}`;
|
|
33
|
+
/**
|
|
34
|
+
* All type safe parameters for sending a request to an endpoint using {@link fetchEndpoint}.
|
|
35
|
+
*
|
|
36
|
+
* @category Internal
|
|
37
|
+
* @category Package : @rest-vir/define-service
|
|
38
|
+
* @package [`@rest-vir/define-service`](https://www.npmjs.com/package/@rest-vir/define-service)
|
|
39
|
+
*/
|
|
40
|
+
export type FetchEndpointParams<EndpointToFetch extends SelectFrom<EndpointDefinition, {
|
|
41
|
+
path: true;
|
|
42
|
+
requestDataShape: true;
|
|
43
|
+
responseDataShape: true;
|
|
44
|
+
methods: true;
|
|
45
|
+
}>, AllowFetchMock extends boolean = true> = EndpointToFetch extends EndpointDefinition ? Readonly<(IsNever<PathParams<EndpointToFetch['path']>> extends true ? {
|
|
46
|
+
/** This endpoint has no path parameters to configure. */
|
|
47
|
+
pathParams?: undefined;
|
|
48
|
+
} : PathParams<EndpointToFetch['path']> extends string ? {
|
|
49
|
+
pathParams: Readonly<Record<PathParams<EndpointToFetch['path']>, string>>;
|
|
50
|
+
} : {
|
|
51
|
+
/** This endpoint has no path parameters to configure. */
|
|
52
|
+
pathParams?: undefined;
|
|
53
|
+
}) & (EndpointExecutorData<EndpointToFetch>['request'] extends undefined ? {
|
|
54
|
+
/**
|
|
55
|
+
* This endpoint does not accept any request data, so there is none to be
|
|
56
|
+
* set.
|
|
57
|
+
*/
|
|
58
|
+
requestData?: never;
|
|
59
|
+
} : {
|
|
60
|
+
requestData: EndpointExecutorData<EndpointToFetch>['request'];
|
|
61
|
+
}) & (IsNever<FetchMethod<EndpointToFetch>> extends true ? {
|
|
62
|
+
/**
|
|
63
|
+
* This endpoint only allows one method so it does not need to be
|
|
64
|
+
* configured.
|
|
65
|
+
*/
|
|
66
|
+
method?: never;
|
|
67
|
+
} : {
|
|
68
|
+
method: FetchMethod<EndpointToFetch>;
|
|
69
|
+
}) & (AllowFetchMock extends true ? Pick<GenericFetchEndpointParams, 'options' | 'fetch'> : Pick<GenericFetchEndpointParams, 'options'>)> : GenericFetchEndpointParams;
|
|
70
|
+
/**
|
|
71
|
+
* Type safe output from sending a request to an endpoint definition. Used by {@link fetchEndpoint}.
|
|
72
|
+
*
|
|
73
|
+
* @category Internal
|
|
74
|
+
* @category Package : @rest-vir/define-service
|
|
75
|
+
* @package [`@rest-vir/define-service`](https://www.npmjs.com/package/@rest-vir/define-service)
|
|
76
|
+
*/
|
|
77
|
+
export type FetchEndpointOutput<EndpointToFetch extends Readonly<SelectFrom<EndpointDefinition, {
|
|
78
|
+
requestDataShape: true;
|
|
79
|
+
responseDataShape: true;
|
|
80
|
+
}>> | NoParam> = Readonly<{
|
|
81
|
+
data: EndpointToFetch extends SelectFrom<EndpointDefinition, {
|
|
82
|
+
requestDataShape: true;
|
|
83
|
+
responseDataShape: true;
|
|
84
|
+
}> ? EndpointExecutorData<EndpointToFetch>['response'] : any;
|
|
85
|
+
response: Readonly<Response>;
|
|
86
|
+
}>;
|
|
87
|
+
/**
|
|
88
|
+
* Extracts an array of all allowed methods for the given endpoint definition.
|
|
89
|
+
*
|
|
90
|
+
* @category Internal
|
|
91
|
+
* @category Package : @rest-vir/define-service
|
|
92
|
+
* @package [`@rest-vir/define-service`](https://www.npmjs.com/package/@rest-vir/define-service)
|
|
93
|
+
*/
|
|
94
|
+
export declare function getAllowedEndpointMethods(endpoint: Readonly<Pick<EndpointDefinition, 'methods'>>): HttpMethod[];
|
|
95
|
+
/**
|
|
96
|
+
* A wrapper for {@link FetchEndpointParams} that requires parameters based on the endpoint being
|
|
97
|
+
* fetched.
|
|
98
|
+
*
|
|
99
|
+
* @category Internal
|
|
100
|
+
* @category Package : @rest-vir/define-service
|
|
101
|
+
* @package [`@rest-vir/define-service`](https://www.npmjs.com/package/@rest-vir/define-service)
|
|
102
|
+
*/
|
|
103
|
+
export type CollapsedFetchEndpointParams<EndpointToFetch extends Readonly<SelectFrom<EndpointDefinition, {
|
|
104
|
+
path: true;
|
|
105
|
+
requestDataShape: true;
|
|
106
|
+
responseDataShape: true;
|
|
107
|
+
methods: true;
|
|
108
|
+
}>> | NoParam, AllowFetchMock extends boolean = true> = EndpointToFetch extends NoParam ? [Readonly<GenericFetchEndpointParams>?] : Readonly<FetchEndpointParams<Exclude<EndpointToFetch, NoParam>, AllowFetchMock>> extends infer RealParams ? RequiredKeysOf<RealParams> extends never ? [RealParams?] : [RealParams] : [];
|
|
109
|
+
/**
|
|
110
|
+
* Send a request to an endpoint definition with type safe parameters.
|
|
111
|
+
*
|
|
112
|
+
* This can safely be used in frontend _or_ backend code.
|
|
113
|
+
*
|
|
114
|
+
* @category Client (Frontend) Connection
|
|
115
|
+
* @category Package : @rest-vir/define-service
|
|
116
|
+
* @example
|
|
117
|
+
*
|
|
118
|
+
* ```ts
|
|
119
|
+
* import {fetchEndpoint} from '@rest-vir/define-service';
|
|
120
|
+
*
|
|
121
|
+
* const {data, response} = await fetchEndpoint(myService.endpoints['/my-endpoint']);
|
|
122
|
+
* ```
|
|
123
|
+
*
|
|
124
|
+
* @package [`@rest-vir/define-service`](https://www.npmjs.com/package/@rest-vir/define-service)
|
|
125
|
+
*/
|
|
126
|
+
export declare function fetchEndpoint<const EndpointToFetch extends Readonly<SelectFrom<EndpointDefinition, {
|
|
127
|
+
requestDataShape: true;
|
|
128
|
+
path: true;
|
|
129
|
+
responseDataShape: true;
|
|
130
|
+
methods: true;
|
|
131
|
+
service: {
|
|
132
|
+
serviceOrigin: true;
|
|
133
|
+
serviceName: true;
|
|
134
|
+
};
|
|
135
|
+
}>> | NoParam>(endpoint: EndpointToFetch extends EndpointDefinition ? EndpointToFetch : SelectFrom<EndpointDefinition, {
|
|
136
|
+
requestDataShape: true;
|
|
137
|
+
path: true;
|
|
138
|
+
responseDataShape: true;
|
|
139
|
+
methods: true;
|
|
140
|
+
service: {
|
|
141
|
+
serviceOrigin: true;
|
|
142
|
+
serviceName: true;
|
|
143
|
+
};
|
|
144
|
+
}>, ...params: CollapsedFetchEndpointParams<EndpointToFetch>): Promise<FetchEndpointOutput<EndpointToFetch>>;
|
|
145
|
+
/**
|
|
146
|
+
* Build request init and URL for fetching an endpoint. Used in {@link fetchEndpoint}.
|
|
147
|
+
*
|
|
148
|
+
* @category Internal
|
|
149
|
+
* @category Package : @rest-vir/define-service
|
|
150
|
+
* @package [`@rest-vir/define-service`](https://www.npmjs.com/package/@rest-vir/define-service)
|
|
151
|
+
*/
|
|
152
|
+
export declare function buildEndpointRequestInit<const EndpointToFetch extends Readonly<SelectFrom<EndpointDefinition, {
|
|
153
|
+
requestDataShape: true;
|
|
154
|
+
path: true;
|
|
155
|
+
responseDataShape: true;
|
|
156
|
+
methods: true;
|
|
157
|
+
service: {
|
|
158
|
+
serviceOrigin: true;
|
|
159
|
+
serviceName: true;
|
|
160
|
+
};
|
|
161
|
+
}>> | NoParam>(endpoint: EndpointToFetch extends EndpointDefinition ? EndpointToFetch : SelectFrom<EndpointDefinition, {
|
|
162
|
+
requestDataShape: true;
|
|
163
|
+
path: true;
|
|
164
|
+
responseDataShape: true;
|
|
165
|
+
methods: true;
|
|
166
|
+
service: {
|
|
167
|
+
serviceOrigin: true;
|
|
168
|
+
serviceName: true;
|
|
169
|
+
};
|
|
170
|
+
}>, ...[{ method, options, pathParams, requestData },]: CollapsedFetchEndpointParams<EndpointToFetch, false>): {
|
|
171
|
+
url: string;
|
|
172
|
+
requestInit: RequestInit;
|
|
173
|
+
};
|
|
174
|
+
/**
|
|
175
|
+
* Creates and finalizes a URL for sending fetches to the given endpoint.
|
|
176
|
+
*
|
|
177
|
+
* @category Internal
|
|
178
|
+
* @category Package : @rest-vir/define-service
|
|
179
|
+
* @package [`@rest-vir/define-service`](https://www.npmjs.com/package/@rest-vir/define-service)
|
|
180
|
+
*/
|
|
181
|
+
export declare function buildEndpointUrl<const EndpointToFetch extends Readonly<SelectFrom<EndpointDefinition, {
|
|
182
|
+
path: true;
|
|
183
|
+
service: {
|
|
184
|
+
serviceOrigin: true;
|
|
185
|
+
serviceName: true;
|
|
186
|
+
};
|
|
187
|
+
methods: true;
|
|
188
|
+
requestDataShape: true;
|
|
189
|
+
responseDataShape: true;
|
|
190
|
+
}>> | NoParam = NoParam>(endpoint: EndpointToFetch extends EndpointDefinition ? EndpointToFetch : SelectFrom<EndpointDefinition, {
|
|
191
|
+
path: true;
|
|
192
|
+
service: {
|
|
193
|
+
serviceOrigin: true;
|
|
194
|
+
serviceName: true;
|
|
195
|
+
};
|
|
196
|
+
requestDataShape: true;
|
|
197
|
+
responseDataShape: true;
|
|
198
|
+
methods: true;
|
|
199
|
+
}>, { pathParams, }: Pick<EndpointToFetch extends NoParam ? Readonly<GenericFetchEndpointParams> : Readonly<FetchEndpointParams<Exclude<EndpointToFetch, NoParam>>>, 'pathParams'>): string;
|