@rest-vir/define-service 1.2.5 → 1.3.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.
@@ -1,11 +1,56 @@
1
+ import { type IsEqual, type IsNever } from 'type-fest';
2
+ import { type NoParam } from '../util/no-param.js';
1
3
  /**
2
- * Extracts all path parameters from an endpoint path.
4
+ * Extracts all named path parameters from an endpoint path.
3
5
  *
4
6
  * @category Internal
5
7
  * @category Package : @rest-vir/define-service
6
8
  * @package [`@rest-vir/define-service`](https://www.npmjs.com/package/@rest-vir/define-service)
7
9
  */
8
- export type PathParams<EndpointPath extends string> = string extends EndpointPath ? Record<string, string> : EndpointPath extends `${string}:${infer Param}/${infer Rest}` ? Param | PathParams<`/${Rest}`> : EndpointPath extends `${string}:${infer Param}` ? Param : never;
10
+ export type NamedPathParams<EndpointPath extends string> = string extends EndpointPath ? string : EndpointPath extends `${string}:${infer Param}/${infer Rest}` ? Param | NamedPathParams<`/${Rest}`> : EndpointPath extends `${string}:${infer Param}` ? Param : IsEqual<`/${string}`, EndpointPath> extends true ? string : never;
11
+ /**
12
+ * Determines if the given endpoint path has a trailing wildcard.
13
+ *
14
+ * @category Internal
15
+ * @category Package : @rest-vir/define-service
16
+ * @package [`@rest-vir/define-service`](https://www.npmjs.com/package/@rest-vir/define-service)
17
+ */
18
+ export type HasWildcardParam<EndpointPath extends string> = string extends EndpointPath ? boolean : EndpointPath extends `${string}/*` ? true : IsEqual<`/${string}`, EndpointPath> extends true ? boolean : false;
19
+ /**
20
+ * Extracts named and wildcard path params.
21
+ *
22
+ * @category Internal
23
+ * @category Package : @rest-vir/define-service
24
+ * @package [`@rest-vir/define-service`](https://www.npmjs.com/package/@rest-vir/define-service)
25
+ */
26
+ export type PathParams<EndpointPath extends string> = {
27
+ namedParams: NamedPathParams<EndpointPath>;
28
+ hasWildcard: HasWildcardParam<EndpointPath>;
29
+ };
30
+ export type GenericPathParams = {
31
+ pathParams: Readonly<Record<string, string>>;
32
+ wildcard: string | undefined;
33
+ };
34
+ /**
35
+ * Converts an endpoint path into the fetch params needed for its to operate.
36
+ *
37
+ * @category Internal
38
+ * @category Package : @rest-vir/define-service
39
+ * @package [`@rest-vir/define-service`](https://www.npmjs.com/package/@rest-vir/define-service)
40
+ */
41
+ export type ConstructPathParams<EndpointPath extends string | NoParam> = EndpointPath extends NoParam ? GenericPathParams : (IsEqual<PathParams<Exclude<EndpointPath, NoParam>>['hasWildcard'], true> extends true ? Readonly<{
42
+ wildcard: string;
43
+ }> : IsEqual<PathParams<Exclude<EndpointPath, NoParam>>['hasWildcard'], false> extends true ? Readonly<{
44
+ wildcard?: undefined;
45
+ }> : Readonly<{
46
+ wildcard?: string | undefined;
47
+ }>) & (IsNever<PathParams<Exclude<EndpointPath, NoParam>>['namedParams']> extends true ? Readonly<{
48
+ pathParams?: undefined;
49
+ }> : PathParams<Exclude<EndpointPath, NoParam>>['namedParams'] extends string ? Readonly<{
50
+ pathParams: Readonly<Record<PathParams<Exclude<EndpointPath, NoParam>>['namedParams'], string>>;
51
+ }> : Readonly<{
52
+ pathParams?: undefined;
53
+ }>);
9
54
  /**
10
55
  * Base requirement for endpoint paths.
11
56
  *
@@ -1,6 +1,5 @@
1
1
  import { type RequiredKeysOf, type SelectFrom } from '@augment-vir/common';
2
- import { type IsNever } from 'type-fest';
3
- import { type PathParams } from '../endpoint/endpoint-path.js';
2
+ import { type ConstructPathParams } from '../endpoint/endpoint-path.js';
4
3
  import { type NoParam } from '../util/no-param.js';
5
4
  import { type CommonWebSocket } from '../web-socket/common-web-socket.js';
6
5
  import { type ClientWebSocket, type ConnectWebSocketListeners, type GenericConnectWebSocketParams } from '../web-socket/overwrite-web-socket-methods.js';
@@ -51,16 +50,7 @@ export type ConnectWebSocketParams<WebSocketToConnect extends Readonly<SelectFro
51
50
  * requiring externally adding them.
52
51
  */
53
52
  listeners?: ConnectWebSocketListeners<WebSocketToConnect, WebSocketClass>;
54
- } & (IsNever<PathParams<WebSocketToConnect['path']>> extends true ? {
55
- /** This WebSocket has no path parameters to configure. */
56
- pathParams?: undefined;
57
- } : PathParams<WebSocketToConnect['path']> extends string ? {
58
- /** Required path params for this WebSocket's path. */
59
- pathParams: Readonly<Record<PathParams<WebSocketToConnect['path']>, string>>;
60
- } : {
61
- /** This WebSocket has no path parameters to configure. */
62
- pathParams?: undefined;
63
- }) & (AllowWebSocketMock extends true ? Pick<GenericConnectWebSocketParams<WebSocketClass>, 'webSocketConstructor'> : unknown) & (WebSocketToConnect['protocolsShape'] extends undefined ? {
53
+ } & ConstructPathParams<WebSocketToConnect['path']> & (AllowWebSocketMock extends true ? Pick<GenericConnectWebSocketParams<WebSocketClass>, 'webSocketConstructor'> : unknown) & (WebSocketToConnect['protocolsShape'] extends undefined ? {
64
54
  protocols?: string[];
65
55
  } : {
66
56
  protocols: WebSocketToConnect['ProtocolsType'];
@@ -1,6 +1,6 @@
1
- import { HttpMethod, type ExtractKeysWithMatchingValues, type KeyCount, type MaybePromise, type RequiredKeysOf, type SelectFrom } from '@augment-vir/common';
1
+ import { HttpMethod, type ExtractKeysWithMatchingValues, type KeyCount, type MaybePromise, type PartialWithUndefined, type RequiredKeysOf, type SelectFrom } from '@augment-vir/common';
2
2
  import { type IsEqual, type IsNever } from 'type-fest';
3
- import { type PathParams } from '../endpoint/endpoint-path.js';
3
+ import { type ConstructPathParams, type GenericPathParams } from '../endpoint/endpoint-path.js';
4
4
  import { type EndpointDefinition, type EndpointExecutorData, type GenericEndpointDefinition } from '../endpoint/endpoint.js';
5
5
  import { type NoParam } from '../util/no-param.js';
6
6
  import { type BaseSearchParams } from '../util/search-params.js';
@@ -12,8 +12,7 @@ import { type BaseSearchParams } from '../util/search-params.js';
12
12
  * @category Package : @rest-vir/define-service
13
13
  * @package [`@rest-vir/define-service`](https://www.npmjs.com/package/@rest-vir/define-service)
14
14
  */
15
- export type GenericFetchEndpointParams = {
16
- pathParams?: Record<string, string> | undefined;
15
+ export type GenericFetchEndpointParams = PartialWithUndefined<GenericPathParams> & {
17
16
  requestData?: any;
18
17
  searchParams?: BaseSearchParams | undefined;
19
18
  bypassResponseValidation?: undefined | boolean;
@@ -45,15 +44,7 @@ export type FetchEndpointParams<EndpointToFetch extends SelectFrom<EndpointDefin
45
44
  requestDataShape: true;
46
45
  responseDataShape: true;
47
46
  methods: true;
48
- }>, AllowFetchMock extends boolean = true> = EndpointToFetch extends EndpointDefinition ? Readonly<(IsNever<PathParams<EndpointToFetch['path']>> extends true ? {
49
- /** This endpoint has no path parameters to configure. */
50
- pathParams?: undefined;
51
- } : PathParams<EndpointToFetch['path']> extends string ? {
52
- pathParams: Readonly<Record<PathParams<EndpointToFetch['path']>, string>>;
53
- } : {
54
- /** This endpoint has no path parameters to configure. */
55
- pathParams?: undefined;
56
- }) & (EndpointToFetch['SearchParamsType'] extends undefined ? {
47
+ }>, AllowFetchMock extends boolean = true> = EndpointToFetch extends EndpointDefinition ? Readonly<ConstructPathParams<EndpointToFetch['path']> & (EndpointToFetch['SearchParamsType'] extends undefined ? {
57
48
  searchParams?: never;
58
49
  } : {
59
50
  searchParams: EndpointToFetch['SearchParamsType'];
@@ -181,7 +172,7 @@ export declare function buildEndpointRequestInit<const EndpointToFetch extends R
181
172
  serviceOrigin: true;
182
173
  serviceName: true;
183
174
  };
184
- }>, ...[{ method, options, pathParams, requestData, searchParams },]: CollapsedFetchEndpointParams<EndpointToFetch, false>): {
175
+ }>, ...[{ method, options, pathParams, requestData, searchParams, wildcard },]: CollapsedFetchEndpointParams<EndpointToFetch, false>): {
185
176
  url: string;
186
177
  requestInit: RequestInit;
187
178
  };
@@ -212,4 +203,4 @@ export declare function buildEndpointUrl<const EndpointToFetch extends Readonly<
212
203
  searchParamsShape: true;
213
204
  responseDataShape: true;
214
205
  methods: true;
215
- }>, { pathParams, searchParams, }: Pick<EndpointToFetch extends NoParam ? Readonly<GenericFetchEndpointParams> : Readonly<FetchEndpointParams<Exclude<EndpointToFetch, NoParam>>>, 'pathParams' | 'searchParams'>): string;
206
+ }>, { pathParams, searchParams, wildcard, }: Pick<EndpointToFetch extends NoParam ? Readonly<GenericFetchEndpointParams> : Readonly<FetchEndpointParams<Exclude<EndpointToFetch, NoParam>>>, 'pathParams' | 'searchParams' | 'wildcard'>): string;
@@ -93,7 +93,7 @@ export async function fetchEndpoint(endpoint, ...params) {
93
93
  * @category Package : @rest-vir/define-service
94
94
  * @package [`@rest-vir/define-service`](https://www.npmjs.com/package/@rest-vir/define-service)
95
95
  */
96
- export function buildEndpointRequestInit(endpoint, ...[{ method, options = {}, pathParams, requestData, searchParams } = {},]) {
96
+ export function buildEndpointRequestInit(endpoint, ...[{ method, options = {}, pathParams, requestData, searchParams, wildcard } = {},]) {
97
97
  const headers = options.headers instanceof Headers
98
98
  ? Object.fromEntries(options.headers.entries())
99
99
  : check.isArray(options.headers)
@@ -115,6 +115,7 @@ export function buildEndpointRequestInit(endpoint, ...[{ method, options = {}, p
115
115
  const url = buildEndpointUrl(endpoint, {
116
116
  pathParams,
117
117
  searchParams,
118
+ wildcard,
118
119
  });
119
120
  const requestInit = {
120
121
  ...options,
@@ -142,7 +143,7 @@ export function buildEndpointRequestInit(endpoint, ...[{ method, options = {}, p
142
143
  * @category Package : @rest-vir/define-service
143
144
  * @package [`@rest-vir/define-service`](https://www.npmjs.com/package/@rest-vir/define-service)
144
145
  */
145
- export function buildEndpointUrl(endpoint, { pathParams, searchParams, }) {
146
+ export function buildEndpointUrl(endpoint, { pathParams, searchParams, wildcard, }) {
146
147
  let pathParamsCount = 0;
147
148
  if (endpoint.searchParamsShape) {
148
149
  assertValidShape(searchParams, endpoint.searchParamsShape, {
@@ -150,20 +151,26 @@ export function buildEndpointUrl(endpoint, { pathParams, searchParams, }) {
150
151
  allowExtraKeys: true,
151
152
  }, `Invalid search params given to '${endpoint.path}' in service '${endpoint.service.serviceName}'`);
152
153
  }
154
+ if (endpoint.path.endsWith('/*') && wildcard == undefined) {
155
+ throw new Error('Missing value for wildcard param.');
156
+ }
157
+ const pathname = endpoint.path
158
+ .replaceAll(/\/:([^/]+)/g, (wholeMatch, paramName) => {
159
+ pathParamsCount++;
160
+ if (pathParams && check.hasKey(pathParams, paramName) && pathParams[paramName]) {
161
+ return addPrefix({
162
+ value: pathParams[paramName],
163
+ prefix: '/',
164
+ });
165
+ }
166
+ else {
167
+ throw new Error(`Missing value for path param '${paramName}'.`);
168
+ }
169
+ })
170
+ .replace(/\/\*$/, addPrefix({ value: wildcard || '', prefix: '/' }));
153
171
  const builtUrl = buildUrl(endpoint.service.serviceOrigin, {
154
172
  search: searchParams,
155
- pathname: endpoint.path.replaceAll(/\/:([^/]+)/g, (wholeMatch, paramName) => {
156
- pathParamsCount++;
157
- if (pathParams && check.hasKey(pathParams, paramName) && pathParams[paramName]) {
158
- return addPrefix({
159
- value: pathParams[paramName],
160
- prefix: '/',
161
- });
162
- }
163
- else {
164
- throw new Error(`Missing value for path param '${paramName}'.`);
165
- }
166
- }),
173
+ pathname,
167
174
  }).href;
168
175
  if (!pathParamsCount && pathParams) {
169
176
  throw new Error(`'${endpoint.path}' in service '${endpoint.service.serviceName}' does not allow any path params but some where set.`);
@@ -1,7 +1,7 @@
1
1
  import { type MaybePromise } from '@augment-vir/common';
2
2
  /**
3
- * Explicity denotes that any origin is allowed. Use {@link isAnyOrigin} to check if something is
4
- * equal to this.
3
+ * Explicity denotes that any origin (`*`) is allowed. Use {@link isAnyOrigin} to check if something
4
+ * is equal to this.
5
5
  *
6
6
  * @category Internal
7
7
  * @category Package : @rest-vir/define-service
@@ -26,6 +26,34 @@ export declare function isAnyOrigin(input: unknown): input is AnyOrigin;
26
26
  * @package [`@rest-vir/define-service`](https://www.npmjs.com/package/@rest-vir/define-service)
27
27
  */
28
28
  export type AnyOrigin = typeof AnyOrigin;
29
+ /**
30
+ * Different from {@link AnyOrigin} in that it accepts _all origins_ as the accepted origin. In
31
+ * practice this is similar to {@link AnyOrigin} but allows credentials to be included in requests
32
+ * when the browser normally blocks them for {@link AnyOrigin} (`*`).
33
+ *
34
+ * @category Internal
35
+ * @category Package : @rest-vir/define-service
36
+ * @package [`@rest-vir/define-service`](https://www.npmjs.com/package/@rest-vir/define-service)
37
+ */
38
+ export declare const AllOrigins: {
39
+ allOrigins: boolean;
40
+ };
41
+ /**
42
+ * Checks if the input is equal to {@link AllOrigins}.
43
+ *
44
+ * @category Internal
45
+ * @category Package : @rest-vir/define-service
46
+ * @package [`@rest-vir/define-service`](https://www.npmjs.com/package/@rest-vir/define-service)
47
+ */
48
+ export declare function isAllOrigins(input: unknown): input is AllOrigins;
49
+ /**
50
+ * Type for {@link AllOrigins}.
51
+ *
52
+ * @category Internal
53
+ * @category Package : @rest-vir/define-service
54
+ * @package [`@rest-vir/define-service`](https://www.npmjs.com/package/@rest-vir/define-service)
55
+ */
56
+ export type AllOrigins = typeof AllOrigins;
29
57
  /**
30
58
  * Options explained:
31
59
  *
@@ -33,7 +61,9 @@ export type AnyOrigin = typeof AnyOrigin;
33
61
  * parent service's origin requirement. When on the service, `undefined` is not allowed.
34
62
  * - `string`: require all request origins to exactly match the given string.
35
63
  * - `RegExp`: all request origins must match the RegExp.
36
- * - {@link AnyOrigin}: allow any origin.
64
+ * - {@link AnyOrigin}: allow any origin with a `*`.
65
+ * - {@link AllOrigins}: accept whatever origin is received as the required origin (See
66
+ * {@link AllOrigins} for details on how this is different from {@link AnyOrigin}).
37
67
  * - A function: allow custom checking. If this function returns something truthy, the origin is
38
68
  * allowed.
39
69
  * - An array: a combination of `string` values, `RegExp` values, or functions to compare against. If
@@ -43,7 +73,7 @@ export type AnyOrigin = typeof AnyOrigin;
43
73
  * @category Package : @rest-vir/define-service
44
74
  * @package [`@rest-vir/define-service`](https://www.npmjs.com/package/@rest-vir/define-service)
45
75
  */
46
- export type OriginRequirement = undefined | string | RegExp | AnyOrigin | (((origin: string | undefined) => MaybePromise<boolean>) | string | RegExp)[] | ((origin: string | undefined) => MaybePromise<boolean>);
76
+ export type OriginRequirement = undefined | string | RegExp | AnyOrigin | AllOrigins | (((origin: string | undefined) => MaybePromise<boolean>) | string | RegExp)[] | ((origin: string | undefined) => MaybePromise<boolean>);
47
77
  /**
48
78
  * Shape definition for {@link OriginRequirement}.
49
79
  *
@@ -1,8 +1,8 @@
1
1
  import { check } from '@augment-vir/assert';
2
2
  import { classShape, defineShape, exactShape, unionShape } from 'object-shape-tester';
3
3
  /**
4
- * Explicity denotes that any origin is allowed. Use {@link isAnyOrigin} to check if something is
5
- * equal to this.
4
+ * Explicity denotes that any origin (`*`) is allowed. Use {@link isAnyOrigin} to check if something
5
+ * is equal to this.
6
6
  *
7
7
  * @category Internal
8
8
  * @category Package : @rest-vir/define-service
@@ -21,6 +21,28 @@ export const AnyOrigin = {
21
21
  export function isAnyOrigin(input) {
22
22
  return check.jsonEquals(input, AnyOrigin);
23
23
  }
24
+ /**
25
+ * Different from {@link AnyOrigin} in that it accepts _all origins_ as the accepted origin. In
26
+ * practice this is similar to {@link AnyOrigin} but allows credentials to be included in requests
27
+ * when the browser normally blocks them for {@link AnyOrigin} (`*`).
28
+ *
29
+ * @category Internal
30
+ * @category Package : @rest-vir/define-service
31
+ * @package [`@rest-vir/define-service`](https://www.npmjs.com/package/@rest-vir/define-service)
32
+ */
33
+ export const AllOrigins = {
34
+ allOrigins: true,
35
+ };
36
+ /**
37
+ * Checks if the input is equal to {@link AllOrigins}.
38
+ *
39
+ * @category Internal
40
+ * @category Package : @rest-vir/define-service
41
+ * @package [`@rest-vir/define-service`](https://www.npmjs.com/package/@rest-vir/define-service)
42
+ */
43
+ export function isAllOrigins(input) {
44
+ return check.jsonEquals(input, AllOrigins);
45
+ }
24
46
  /**
25
47
  * Shape definition for {@link OriginRequirement}.
26
48
  *
@@ -44,6 +66,9 @@ export async function checkOriginRequirement(origin, originRequirement) {
44
66
  /** Any origin has been explicitly allowed. */
45
67
  return AnyOrigin;
46
68
  }
69
+ else if (isAllOrigins(originRequirement)) {
70
+ return true;
71
+ }
47
72
  else if (originRequirement == undefined) {
48
73
  /** No checking occurred. */
49
74
  return undefined;
@@ -283,7 +283,7 @@ function toRegExp(tokens, keys) {
283
283
  result += `(${negate(DEFAULT_DELIMITER, isSafeSegmentParam ? '' : backtrack)}+)`;
284
284
  }
285
285
  else {
286
- result += `([\\s\\S]+)`;
286
+ result += String.raw `([\s\S]+)`;
287
287
  }
288
288
  keys.push(token);
289
289
  backtrack = '';
@@ -303,5 +303,5 @@ function negate(delimiter, backtrack) {
303
303
  if (delimiter.length < 2) {
304
304
  return `(?:(?!${escape(backtrack)})[^${escape(delimiter)}])`;
305
305
  }
306
- return `(?:(?!${escape(backtrack)}|${escape(delimiter)})[\\s\\S])`;
306
+ return String.raw `(?:(?!${escape(backtrack)}|${escape(delimiter)})[\s\S])`;
307
307
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@rest-vir/define-service",
3
- "version": "1.2.5",
3
+ "version": "1.3.0",
4
4
  "description": "Define an connect to a declarative and type safe REST and WebSocket service.",
5
5
  "keywords": [
6
6
  "rest",
@@ -40,14 +40,14 @@
40
40
  "test:update": "npm test update"
41
41
  },
42
42
  "dependencies": {
43
- "@augment-vir/assert": "^31.40.0",
44
- "@augment-vir/common": "^31.40.0",
43
+ "@augment-vir/assert": "^31.48.0",
44
+ "@augment-vir/common": "^31.48.0",
45
45
  "date-vir": "^8.0.0",
46
- "type-fest": "^5.1.0",
46
+ "type-fest": "^5.2.0",
47
47
  "url-vir": "^2.1.6"
48
48
  },
49
49
  "devDependencies": {
50
- "@augment-vir/test": "^31.40.0",
50
+ "@augment-vir/test": "^31.48.0",
51
51
  "@web/dev-server-esbuild": "^1.0.4",
52
52
  "@web/test-runner": "^0.20.2",
53
53
  "@web/test-runner-commands": "^0.9.0",
@@ -55,7 +55,7 @@
55
55
  "@web/test-runner-visual-regression": "^0.10.0",
56
56
  "istanbul-smart-text-reporter": "^1.1.5",
57
57
  "markdown-code-example-inserter": "^3.0.3",
58
- "object-shape-tester": "^6.9.2",
58
+ "object-shape-tester": "^6.9.3",
59
59
  "typedoc": "^0.28.14",
60
60
  "ws": "^8.18.3"
61
61
  },