shelving 1.168.2 → 1.170.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/README.md +1 -1
- package/endpoint/Endpoint.d.ts +11 -7
- package/endpoint/Endpoint.js +41 -14
- package/endpoint/util.js +1 -1
- package/error/RequestError.d.ts +16 -15
- package/error/RequestError.js +12 -15
- package/error/ResponseError.d.ts +9 -2
- package/error/ResponseError.js +4 -1
- package/package.json +5 -2
- package/store/URLStore.d.ts +8 -3
- package/store/URLStore.js +18 -6
- package/util/uri.d.ts +13 -3
- package/util/uri.js +19 -2
- package/util/validate.d.ts +4 -4
- package/util/validate.js +16 -7
package/README.md
CHANGED
|
@@ -37,7 +37,7 @@ Shelving does not include code for CommonJS `require()` imports, so using it in
|
|
|
37
37
|
|
|
38
38
|
## Modules
|
|
39
39
|
|
|
40
|
-
Shelving is created from small individual modules which can be imported individually (using e.g. `import { addProp } from "shelving/object`). Modules marked with `✅` are also re-exported from the main `"shelving"` module.
|
|
40
|
+
Shelving is created from small individual modules which can be imported individually (using e.g. `import { addProp } from "shelving/util/object`). Modules marked with `✅` are also re-exported from the main `"shelving"` module.
|
|
41
41
|
|
|
42
42
|
@todo Write these docs!
|
|
43
43
|
|
package/endpoint/Endpoint.d.ts
CHANGED
|
@@ -33,8 +33,11 @@ export declare class Endpoint<P, R> {
|
|
|
33
33
|
* @param callback The endpoint callback function that implements the logic for this endpoint by receiving the payload and returning the response.
|
|
34
34
|
* @param unsafePayload The payload to pass into the callback (will be validated against this endpoint's payload schema).
|
|
35
35
|
* @param request The entire HTTP request that is being handled (payload was possibly extracted from this somehow).
|
|
36
|
+
*
|
|
37
|
+
* @throws `Feedback` if the payload is invalid.
|
|
38
|
+
* @throws `ValueError` if `callback()` returns an invalid result.
|
|
36
39
|
*/
|
|
37
|
-
handle(callback: EndpointCallback<P, R>, unsafePayload: unknown, request: Request): Promise<Response>;
|
|
40
|
+
handle(callback: EndpointCallback<P, R>, unsafePayload: unknown, request: Request, caller?: AnyCaller): Promise<Response>;
|
|
38
41
|
/**
|
|
39
42
|
* Render the URL for this endpoint with the given payload.
|
|
40
43
|
* - URL mioght contain `{placeholder}` values that are replaced with values from the payload.
|
|
@@ -45,13 +48,14 @@ export declare class Endpoint<P, R> {
|
|
|
45
48
|
* - Validates a payload against this endpoints payload schema
|
|
46
49
|
* - Return an HTTP `Request` that will send it the valid payload to this endpoint.
|
|
47
50
|
*
|
|
48
|
-
* @throws Feedback if the payload is invalid.
|
|
51
|
+
* @throws `Feedback` if the payload is invalid.
|
|
49
52
|
*/
|
|
50
53
|
request(payload: P, options?: RequestOptions, caller?: AnyCaller): Request;
|
|
51
54
|
/**
|
|
52
55
|
* Validate an HTTP `Response` against this endpoint.
|
|
53
|
-
*
|
|
54
|
-
* @throws ResponseError if the response
|
|
56
|
+
*
|
|
57
|
+
* @throws `ResponseError` if the response status is not ok (200-299)
|
|
58
|
+
* @throws `ResponseError` if the response content is invalid.
|
|
55
59
|
*/
|
|
56
60
|
response(response: Response, caller?: AnyCaller): Promise<R>;
|
|
57
61
|
/**
|
|
@@ -59,9 +63,9 @@ export declare class Endpoint<P, R> {
|
|
|
59
63
|
* - Validate the `payload` against this endpoint's payload schema.
|
|
60
64
|
* - Validate the returned response against this endpoint's result schema.
|
|
61
65
|
*
|
|
62
|
-
* @throws Feedback if the payload is invalid.
|
|
63
|
-
* @throws ResponseError if the response status is not ok (200-299)
|
|
64
|
-
* @throws ResponseError if the response content is invalid.
|
|
66
|
+
* @throws `Feedback` if the payload is invalid.
|
|
67
|
+
* @throws `ResponseError` if the response status is not ok (200-299)
|
|
68
|
+
* @throws `ResponseError` if the response content is invalid.
|
|
65
69
|
*/
|
|
66
70
|
fetch(payload: P, options?: RequestOptions, caller?: AnyCaller): Promise<R>;
|
|
67
71
|
/** Convert to string, e.g. `GET https://a.com/user/{id}` */
|
package/endpoint/Endpoint.js
CHANGED
|
@@ -1,10 +1,11 @@
|
|
|
1
1
|
import { ResponseError } from "../error/ResponseError.js";
|
|
2
|
+
import { ValueError } from "../error/ValueError.js";
|
|
3
|
+
import { Feedback } from "../feedback/Feedback.js";
|
|
2
4
|
import { UNDEFINED } from "../schema/Schema.js";
|
|
3
5
|
import { assertDictionary } from "../util/dictionary.js";
|
|
4
6
|
import { getMessage } from "../util/error.js";
|
|
5
7
|
import { getRequest, getResponse, getResponseContent } from "../util/http.js";
|
|
6
8
|
import { getPlaceholders, renderTemplate } from "../util/template.js";
|
|
7
|
-
import { getValid } from "../util/validate.js";
|
|
8
9
|
/**
|
|
9
10
|
* An abstract API resource definition, used to specify types for e.g. serverless functions.
|
|
10
11
|
*
|
|
@@ -42,16 +43,29 @@ export class Endpoint {
|
|
|
42
43
|
* @param callback The endpoint callback function that implements the logic for this endpoint by receiving the payload and returning the response.
|
|
43
44
|
* @param unsafePayload The payload to pass into the callback (will be validated against this endpoint's payload schema).
|
|
44
45
|
* @param request The entire HTTP request that is being handled (payload was possibly extracted from this somehow).
|
|
46
|
+
*
|
|
47
|
+
* @throws `Feedback` if the payload is invalid.
|
|
48
|
+
* @throws `ValueError` if `callback()` returns an invalid result.
|
|
45
49
|
*/
|
|
46
|
-
async handle(callback, unsafePayload, request) {
|
|
50
|
+
async handle(callback, unsafePayload, request, caller = this.handle) {
|
|
47
51
|
// Validate the payload against this endpoint's payload type.
|
|
48
52
|
const payload = this.payload.validate(unsafePayload);
|
|
49
53
|
// Call the callback with the validated payload to get the result.
|
|
50
54
|
const unsafeResult = await callback(payload, request);
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
+
try {
|
|
56
|
+
// Convert the result to a `Response` object.
|
|
57
|
+
return getResponse(this.result.validate(unsafeResult));
|
|
58
|
+
}
|
|
59
|
+
catch (thrown) {
|
|
60
|
+
if (thrown instanceof Feedback)
|
|
61
|
+
throw new ValueError(`Invalid result for ${this.toString()}:\n${thrown.message}`, {
|
|
62
|
+
endpoint: this,
|
|
63
|
+
callback,
|
|
64
|
+
cause: thrown,
|
|
65
|
+
caller,
|
|
66
|
+
});
|
|
67
|
+
throw thrown;
|
|
68
|
+
}
|
|
55
69
|
}
|
|
56
70
|
/**
|
|
57
71
|
* Render the URL for this endpoint with the given payload.
|
|
@@ -73,15 +87,16 @@ export class Endpoint {
|
|
|
73
87
|
* - Validates a payload against this endpoints payload schema
|
|
74
88
|
* - Return an HTTP `Request` that will send it the valid payload to this endpoint.
|
|
75
89
|
*
|
|
76
|
-
* @throws Feedback if the payload is invalid.
|
|
90
|
+
* @throws `Feedback` if the payload is invalid.
|
|
77
91
|
*/
|
|
78
92
|
request(payload, options = {}, caller = this.request) {
|
|
79
93
|
return getRequest(this.method, this.url, this.payload.validate(payload), options, caller);
|
|
80
94
|
}
|
|
81
95
|
/**
|
|
82
96
|
* Validate an HTTP `Response` against this endpoint.
|
|
83
|
-
*
|
|
84
|
-
* @throws ResponseError if the response
|
|
97
|
+
*
|
|
98
|
+
* @throws `ResponseError` if the response status is not ok (200-299)
|
|
99
|
+
* @throws `ResponseError` if the response content is invalid.
|
|
85
100
|
*/
|
|
86
101
|
async response(response, caller = this.response) {
|
|
87
102
|
// Get the response.
|
|
@@ -89,18 +104,30 @@ export class Endpoint {
|
|
|
89
104
|
const content = await getResponseContent(response, caller);
|
|
90
105
|
// Throw `ResponseError` if the API returns status outside the 200-299 range.
|
|
91
106
|
if (!ok)
|
|
92
|
-
throw new ResponseError(getMessage(content) ?? `Error ${status}`, { cause:
|
|
107
|
+
throw new ResponseError(getMessage(content) ?? `Error ${status}`, { code: status, cause: response, caller });
|
|
93
108
|
// Validate the success response.
|
|
94
|
-
|
|
109
|
+
try {
|
|
110
|
+
return this.result.validate(content);
|
|
111
|
+
}
|
|
112
|
+
catch (thrown) {
|
|
113
|
+
if (thrown instanceof Feedback)
|
|
114
|
+
throw new ResponseError(`Invalid result for ${this.toString()}:\n${thrown.message}`, {
|
|
115
|
+
endpoint: this,
|
|
116
|
+
code: 422,
|
|
117
|
+
cause: thrown,
|
|
118
|
+
caller,
|
|
119
|
+
});
|
|
120
|
+
throw thrown;
|
|
121
|
+
}
|
|
95
122
|
}
|
|
96
123
|
/**
|
|
97
124
|
* Perform a fetch to this endpoint.
|
|
98
125
|
* - Validate the `payload` against this endpoint's payload schema.
|
|
99
126
|
* - Validate the returned response against this endpoint's result schema.
|
|
100
127
|
*
|
|
101
|
-
* @throws Feedback if the payload is invalid.
|
|
102
|
-
* @throws ResponseError if the response status is not ok (200-299)
|
|
103
|
-
* @throws ResponseError if the response content is invalid.
|
|
128
|
+
* @throws `Feedback` if the payload is invalid.
|
|
129
|
+
* @throws `ResponseError` if the response status is not ok (200-299)
|
|
130
|
+
* @throws `ResponseError` if the response content is invalid.
|
|
104
131
|
*/
|
|
105
132
|
async fetch(payload, options = {}, caller = this.fetch) {
|
|
106
133
|
const response = await fetch(this.request(payload, options, caller));
|
package/endpoint/util.js
CHANGED
|
@@ -46,5 +46,5 @@ async function handleEndpoint(endpoint, callback, params, request, caller = hand
|
|
|
46
46
|
// - If the content is anything else (e.g. string, number, array), return it directly (but you'll have no way to access the other params).
|
|
47
47
|
const payload = content === undefined ? params : isPlainObject(content) ? { ...content, ...params } : content;
|
|
48
48
|
// Call `endpoint.handle()` with the payload and request.
|
|
49
|
-
return endpoint.handle(callback, payload, request);
|
|
49
|
+
return endpoint.handle(callback, payload, request, caller);
|
|
50
50
|
}
|
package/error/RequestError.d.ts
CHANGED
|
@@ -1,27 +1,28 @@
|
|
|
1
1
|
import { BaseError, type BaseErrorOptions } from "./BaseError.js";
|
|
2
|
-
/**
|
|
2
|
+
/** Options for `RequestError`. */
|
|
3
|
+
interface RequestErrorOptions extends BaseErrorOptions {
|
|
4
|
+
readonly code?: number;
|
|
5
|
+
}
|
|
6
|
+
/** Throw when a request isn't well-formed or is unacceptable in some way. */
|
|
3
7
|
export declare class RequestError extends BaseError {
|
|
4
|
-
/**
|
|
8
|
+
/** HTTP status code for this error, in the range `400-499` */
|
|
5
9
|
readonly code: number;
|
|
6
|
-
constructor(message?: string, options?:
|
|
10
|
+
constructor(message?: string, options?: RequestErrorOptions);
|
|
7
11
|
}
|
|
8
|
-
/**
|
|
12
|
+
/** Throw if an operation failed because the user is not logged in, or the login information is not well-formed. */
|
|
9
13
|
export declare class UnauthorizedError extends RequestError {
|
|
10
|
-
|
|
11
|
-
constructor(message?: string, options?: BaseErrorOptions);
|
|
14
|
+
constructor(message?: string, options?: RequestErrorOptions);
|
|
12
15
|
}
|
|
13
|
-
/**
|
|
16
|
+
/** Throw if the requested content is not found. */
|
|
14
17
|
export declare class NotFoundError extends RequestError {
|
|
15
|
-
|
|
16
|
-
constructor(message?: string, options?: BaseErrorOptions);
|
|
18
|
+
constructor(message?: string, options?: RequestErrorOptions);
|
|
17
19
|
}
|
|
18
|
-
/**
|
|
20
|
+
/** Throw when a request is valid and well-formed, but its actual data is not. */
|
|
19
21
|
export declare class UnprocessableError extends RequestError {
|
|
20
|
-
|
|
21
|
-
constructor(message?: string, options?: BaseErrorOptions);
|
|
22
|
+
constructor(message?: string, options?: RequestErrorOptions);
|
|
22
23
|
}
|
|
23
|
-
/**
|
|
24
|
+
/** Throw if an operation failed because the user is logged in, but does not have sufficient privileges to access this content. */
|
|
24
25
|
export declare class ForbiddenError extends RequestError {
|
|
25
|
-
|
|
26
|
-
constructor(message?: string, options?: BaseErrorOptions);
|
|
26
|
+
constructor(message?: string, options?: RequestErrorOptions);
|
|
27
27
|
}
|
|
28
|
+
export {};
|
package/error/RequestError.js
CHANGED
|
@@ -1,42 +1,39 @@
|
|
|
1
1
|
import { BaseError } from "./BaseError.js";
|
|
2
|
-
/**
|
|
2
|
+
/** Throw when a request isn't well-formed or is unacceptable in some way. */
|
|
3
3
|
export class RequestError extends BaseError {
|
|
4
|
-
/**
|
|
5
|
-
code
|
|
4
|
+
/** HTTP status code for this error, in the range `400-499` */
|
|
5
|
+
code;
|
|
6
6
|
constructor(message, options) {
|
|
7
7
|
super(message, { caller: RequestError, ...options });
|
|
8
|
+
this.code = options?.code ?? 400;
|
|
8
9
|
}
|
|
9
10
|
}
|
|
10
11
|
RequestError.prototype.name = "RequestError";
|
|
11
|
-
/**
|
|
12
|
+
/** Throw if an operation failed because the user is not logged in, or the login information is not well-formed. */
|
|
12
13
|
export class UnauthorizedError extends RequestError {
|
|
13
|
-
code = 401;
|
|
14
14
|
constructor(message, options) {
|
|
15
|
-
super(message, { caller: UnauthorizedError, ...options });
|
|
15
|
+
super(message, { caller: UnauthorizedError, code: 401, ...options });
|
|
16
16
|
}
|
|
17
17
|
}
|
|
18
18
|
UnauthorizedError.prototype.name = "UnauthorizedError";
|
|
19
|
-
/**
|
|
19
|
+
/** Throw if the requested content is not found. */
|
|
20
20
|
export class NotFoundError extends RequestError {
|
|
21
|
-
code = 404;
|
|
22
21
|
constructor(message, options) {
|
|
23
|
-
super(message, { caller: NotFoundError, ...options });
|
|
22
|
+
super(message, { caller: NotFoundError, code: 404, ...options });
|
|
24
23
|
}
|
|
25
24
|
}
|
|
26
25
|
NotFoundError.prototype.name = "NotFoundError";
|
|
27
|
-
/**
|
|
26
|
+
/** Throw when a request is valid and well-formed, but its actual data is not. */
|
|
28
27
|
export class UnprocessableError extends RequestError {
|
|
29
|
-
code = 422;
|
|
30
28
|
constructor(message, options) {
|
|
31
|
-
super(message, { caller: UnprocessableError, ...options });
|
|
29
|
+
super(message, { caller: UnprocessableError, code: 422, ...options });
|
|
32
30
|
}
|
|
33
31
|
}
|
|
34
32
|
UnprocessableError.prototype.name = "UnprocessableError";
|
|
35
|
-
/**
|
|
33
|
+
/** Throw if an operation failed because the user is logged in, but does not have sufficient privileges to access this content. */
|
|
36
34
|
export class ForbiddenError extends RequestError {
|
|
37
|
-
code = 403;
|
|
38
35
|
constructor(message, options) {
|
|
39
|
-
super(message, { caller: ForbiddenError, ...options });
|
|
36
|
+
super(message, { caller: ForbiddenError, code: 403, ...options });
|
|
40
37
|
}
|
|
41
38
|
}
|
|
42
39
|
ForbiddenError.prototype.name = "ForbiddenError";
|
package/error/ResponseError.d.ts
CHANGED
|
@@ -1,5 +1,12 @@
|
|
|
1
1
|
import { BaseError, type BaseErrorOptions } from "./BaseError.js";
|
|
2
|
-
/**
|
|
2
|
+
/** Options for `ResponseError`. */
|
|
3
|
+
interface ResponseErrorOptions extends BaseErrorOptions {
|
|
4
|
+
readonly code?: number;
|
|
5
|
+
}
|
|
6
|
+
/** Error thrown when a received HTTP response isn't OK. */
|
|
3
7
|
export declare class ResponseError extends BaseError {
|
|
4
|
-
|
|
8
|
+
/** HTTP status code for the response. */
|
|
9
|
+
readonly code: number;
|
|
10
|
+
constructor(message?: string, options?: ResponseErrorOptions);
|
|
5
11
|
}
|
|
12
|
+
export {};
|
package/error/ResponseError.js
CHANGED
|
@@ -1,8 +1,11 @@
|
|
|
1
1
|
import { BaseError } from "./BaseError.js";
|
|
2
|
-
/** Error thrown when
|
|
2
|
+
/** Error thrown when a received HTTP response isn't OK. */
|
|
3
3
|
export class ResponseError extends BaseError {
|
|
4
|
+
/** HTTP status code for the response. */
|
|
5
|
+
code;
|
|
4
6
|
constructor(message = ResponseError.prototype.message, options) {
|
|
5
7
|
super(message, { caller: ResponseError, ...options });
|
|
8
|
+
this.code = options?.code ?? 400;
|
|
6
9
|
}
|
|
7
10
|
}
|
|
8
11
|
ResponseError.prototype.name = "ResponseError";
|
package/package.json
CHANGED
|
@@ -11,8 +11,11 @@
|
|
|
11
11
|
"state-management",
|
|
12
12
|
"query-builder"
|
|
13
13
|
],
|
|
14
|
-
"version": "1.
|
|
15
|
-
"repository":
|
|
14
|
+
"version": "1.170.2",
|
|
15
|
+
"repository": {
|
|
16
|
+
"type": "git",
|
|
17
|
+
"url": "git+https://github.com/dhoulb/shelving.git"
|
|
18
|
+
},
|
|
16
19
|
"author": "Dave Houlbrooke <dave@shax.com>",
|
|
17
20
|
"license": "0BSD",
|
|
18
21
|
"type": "module",
|
package/store/URLStore.d.ts
CHANGED
|
@@ -25,14 +25,18 @@ export declare class URLStore extends Store<URL> {
|
|
|
25
25
|
getParam(key: string): string | undefined;
|
|
26
26
|
/** Require a single param in this URL, or throw `RequiredError` if it could not be found. */
|
|
27
27
|
requireParam(key: string): string | undefined;
|
|
28
|
-
/**
|
|
29
|
-
setParam(key: string, value: unknown): void;
|
|
30
|
-
/** Update several params in this URL. */
|
|
28
|
+
/** Set all params in this URL (all current params are cleared). */
|
|
31
29
|
setParams(params: PossibleURIParams): void;
|
|
30
|
+
/** Set a single named param in this URL. */
|
|
31
|
+
setParam(key: string, value: unknown): void;
|
|
32
|
+
/** Update several params in this URL (merged with current params). */
|
|
33
|
+
updateParams(params: PossibleURIParams): void;
|
|
32
34
|
/** Delete one or more params in this URL. */
|
|
33
35
|
deleteParam(key: string, ...keys: string[]): void;
|
|
34
36
|
/** Delete one or more params in this URL. */
|
|
35
37
|
deleteParams(key: string, ...keys: string[]): void;
|
|
38
|
+
/** Clear all params from this URL. */
|
|
39
|
+
clearParams(): void;
|
|
36
40
|
/** Return the current URL with an additional param. */
|
|
37
41
|
withParam(key: string, value: unknown): URL;
|
|
38
42
|
/** Return the current URL with an additional param. */
|
|
@@ -41,5 +45,6 @@ export declare class URLStore extends Store<URL> {
|
|
|
41
45
|
omitParams(...keys: string[]): URL;
|
|
42
46
|
/** Return the current URL with an additional param. */
|
|
43
47
|
omitParam(key: string): URL;
|
|
48
|
+
equal(a: URL, b: URL): boolean;
|
|
44
49
|
toString(): string;
|
|
45
50
|
}
|
package/store/URLStore.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { getGetter, getSetter } from "../util/class.js";
|
|
2
|
-
import { getURIParam, getURIParams, omitURIParams, requireURIParam, withURIParam, withURIParams, } from "../util/uri.js";
|
|
2
|
+
import { clearURIParams, getURIParam, getURIParams, omitURIParams, requireURIParam, withURIParam, withURIParams, } from "../util/uri.js";
|
|
3
3
|
import { getURL, requireURL } from "../util/url.js";
|
|
4
4
|
import { Store } from "./Store.js";
|
|
5
5
|
/** Store a URL, e.g. `https://top.com/a/b/c` */
|
|
@@ -62,13 +62,17 @@ export class URLStore extends Store {
|
|
|
62
62
|
requireParam(key) {
|
|
63
63
|
return requireURIParam(this.value.searchParams, key, getSetter(this, "requireParam"));
|
|
64
64
|
}
|
|
65
|
-
/**
|
|
65
|
+
/** Set all params in this URL (all current params are cleared). */
|
|
66
|
+
setParams(params) {
|
|
67
|
+
this.value = withURIParams(clearURIParams(this.value, this.setParams), params, this.setParams);
|
|
68
|
+
}
|
|
69
|
+
/** Set a single named param in this URL. */
|
|
66
70
|
setParam(key, value) {
|
|
67
|
-
this.value = withURIParam(this.value, key, value, this.
|
|
71
|
+
this.value = withURIParam(this.value, key, value, this.setParam);
|
|
68
72
|
}
|
|
69
|
-
/** Update several params in this URL. */
|
|
70
|
-
|
|
71
|
-
this.value = withURIParams(this.value, params, this.
|
|
73
|
+
/** Update several params in this URL (merged with current params). */
|
|
74
|
+
updateParams(params) {
|
|
75
|
+
this.value = withURIParams(this.value, params, this.updateParams);
|
|
72
76
|
}
|
|
73
77
|
/** Delete one or more params in this URL. */
|
|
74
78
|
deleteParam(key, ...keys) {
|
|
@@ -78,6 +82,10 @@ export class URLStore extends Store {
|
|
|
78
82
|
deleteParams(key, ...keys) {
|
|
79
83
|
this.value = omitURIParams(this.value, key, ...keys);
|
|
80
84
|
}
|
|
85
|
+
/** Clear all params from this URL. */
|
|
86
|
+
clearParams() {
|
|
87
|
+
this.value = clearURIParams(this.value, this.clearParams);
|
|
88
|
+
}
|
|
81
89
|
/** Return the current URL with an additional param. */
|
|
82
90
|
withParam(key, value) {
|
|
83
91
|
return withURIParam(this.value, key, value, this.withParam);
|
|
@@ -94,6 +102,10 @@ export class URLStore extends Store {
|
|
|
94
102
|
omitParam(key) {
|
|
95
103
|
return omitURIParams(this.value, key);
|
|
96
104
|
}
|
|
105
|
+
// Override `equal()` to just compare the hrefs.
|
|
106
|
+
equal(a, b) {
|
|
107
|
+
return a.href === b.href;
|
|
108
|
+
}
|
|
97
109
|
toString() {
|
|
98
110
|
return this.href;
|
|
99
111
|
}
|
package/util/uri.d.ts
CHANGED
|
@@ -63,7 +63,10 @@ export declare function requireURIString(possible: PossibleURI, caller?: AnyCall
|
|
|
63
63
|
export type URIParams = ImmutableDictionary<string>;
|
|
64
64
|
/** Type for things that can be converted to named URI parameters. */
|
|
65
65
|
export type PossibleURIParams = PossibleURI | URLSearchParams | ImmutableDictionary<unknown>;
|
|
66
|
-
/**
|
|
66
|
+
/**
|
|
67
|
+
* Get a set of params for a URI as a dictionary.
|
|
68
|
+
* - Any params with `undefined` value will be ignored.
|
|
69
|
+
*/
|
|
67
70
|
export declare function getURIParams(input: PossibleURIParams, caller?: AnyCaller): URIParams;
|
|
68
71
|
/** Get a single named param from a URI. */
|
|
69
72
|
export declare function getURIParam(input: PossibleURIParams, key: string): string | undefined;
|
|
@@ -71,13 +74,17 @@ export declare function getURIParam(input: PossibleURIParams, key: string): stri
|
|
|
71
74
|
export declare function requireURIParam(input: PossibleURIParams, key: string, caller?: AnyCaller): string;
|
|
72
75
|
/**
|
|
73
76
|
* Return a URI with a new param set (or same URI if no changes were made).
|
|
74
|
-
* -
|
|
77
|
+
* - Any params with `undefined` value will be ignored.
|
|
78
|
+
*
|
|
79
|
+
* @throws `ValueError` if the value could not be converted to a string.
|
|
75
80
|
*/
|
|
76
81
|
export declare function withURIParam(url: URL | URLString, key: string, value: unknown, caller?: AnyCaller): URL;
|
|
77
82
|
export declare function withURIParam(url: PossibleURI, key: string, value: unknown, caller?: AnyCaller): URI;
|
|
78
83
|
/**
|
|
79
84
|
* Return a URI with several new params set (or same URI if no changes were made).
|
|
80
|
-
* -
|
|
85
|
+
* - Param with `undefined` value will be ignored.
|
|
86
|
+
*
|
|
87
|
+
* @throws `ValueError` if any of the values could not be converted to strings.
|
|
81
88
|
*/
|
|
82
89
|
export declare function withURIParams(url: URL | URLString, params: PossibleURIParams, caller?: AnyCaller): URL;
|
|
83
90
|
export declare function withURIParams(url: PossibleURI, params: PossibleURIParams, caller?: AnyCaller): URI;
|
|
@@ -86,6 +93,9 @@ export declare function omitURIParams(url: URL | URLString, ...keys: string[]):
|
|
|
86
93
|
export declare function omitURIParams(url: PossibleURI, ...keys: string[]): URI;
|
|
87
94
|
/** Return a URI without a param (or same URI if no changes were made). */
|
|
88
95
|
export declare const omitURIParam: (url: PossibleURI, key: string) => URI;
|
|
96
|
+
/** Return a URI with no search params (or same URI if no changes were made). */
|
|
97
|
+
export declare function clearURIParams(url: URL | URLString, caller?: AnyCaller): URL;
|
|
98
|
+
export declare function clearURIParams(url: PossibleURI, caller?: AnyCaller): URI;
|
|
89
99
|
/** A single schema for a URL. */
|
|
90
100
|
export type URIScheme = `${string}:`;
|
|
91
101
|
/** List of allowed URI schemes. */
|
package/util/uri.js
CHANGED
|
@@ -19,13 +19,14 @@ export function getURI(possible) {
|
|
|
19
19
|
if (isURI(possible))
|
|
20
20
|
return possible;
|
|
21
21
|
try {
|
|
22
|
-
return new globalThis.URL(possible);
|
|
22
|
+
return new globalThis.URL(possible, _BASE);
|
|
23
23
|
}
|
|
24
24
|
catch {
|
|
25
25
|
return undefined;
|
|
26
26
|
}
|
|
27
27
|
}
|
|
28
28
|
}
|
|
29
|
+
const _BASE = typeof document === "object" ? document.baseURI : undefined;
|
|
29
30
|
/** Convert a possible URI to a URI, or throw `RequiredError` if conversion fails. */
|
|
30
31
|
export function requireURI(possible, caller = requireURI) {
|
|
31
32
|
const url = getURI(possible);
|
|
@@ -42,6 +43,7 @@ export function requireURIString(possible, caller = requireURIString) {
|
|
|
42
43
|
}
|
|
43
44
|
/**
|
|
44
45
|
* Get a set of entries for a set of possible URI params.
|
|
46
|
+
*- Any params with `undefined` value will be ignored.
|
|
45
47
|
*
|
|
46
48
|
* Note: Not as simple as just converting with `Object.fromEntries()`:
|
|
47
49
|
* 1. When `URLSearchParams` contains multiple values for the same key, calling `params.get()` will return the _first_ value.
|
|
@@ -58,6 +60,8 @@ function* getURIEntries(input, caller = getURIParams) {
|
|
|
58
60
|
else {
|
|
59
61
|
const done = [];
|
|
60
62
|
for (const [key, value] of getDictionaryItems(input)) {
|
|
63
|
+
if (value === undefined)
|
|
64
|
+
continue; // Skip undefined.
|
|
61
65
|
if (done.includes(key))
|
|
62
66
|
continue;
|
|
63
67
|
done.push(key);
|
|
@@ -68,7 +72,10 @@ function* getURIEntries(input, caller = getURIParams) {
|
|
|
68
72
|
}
|
|
69
73
|
}
|
|
70
74
|
}
|
|
71
|
-
/**
|
|
75
|
+
/**
|
|
76
|
+
* Get a set of params for a URI as a dictionary.
|
|
77
|
+
* - Any params with `undefined` value will be ignored.
|
|
78
|
+
*/
|
|
72
79
|
export function getURIParams(input, caller = getURIParams) {
|
|
73
80
|
const output = {};
|
|
74
81
|
for (const [key, str] of getURIEntries(input, caller))
|
|
@@ -92,6 +99,8 @@ export function requireURIParam(input, key, caller = requireURIParam) {
|
|
|
92
99
|
}
|
|
93
100
|
export function withURIParam(url, key, value, caller = withURIParam) {
|
|
94
101
|
const input = requireURI(url, caller);
|
|
102
|
+
if (value === undefined)
|
|
103
|
+
return input; // Ignore undefined.
|
|
95
104
|
const output = new URI(input);
|
|
96
105
|
const str = getString(value);
|
|
97
106
|
if (str === undefined)
|
|
@@ -115,5 +124,13 @@ export function omitURIParams(url, ...keys) {
|
|
|
115
124
|
}
|
|
116
125
|
/** Return a URI without a param (or same URI if no changes were made). */
|
|
117
126
|
export const omitURIParam = omitURIParams;
|
|
127
|
+
export function clearURIParams(url, caller = clearURIParams) {
|
|
128
|
+
const input = requireURI(url, caller);
|
|
129
|
+
if (!input.search.length)
|
|
130
|
+
return input;
|
|
131
|
+
const output = new URI(input);
|
|
132
|
+
output.search = "";
|
|
133
|
+
return output;
|
|
134
|
+
}
|
|
118
135
|
/** Valid HTTP schemes for a URI. */
|
|
119
136
|
export const HTTP_SCHEMES = ["http:", "https:"];
|
package/util/validate.d.ts
CHANGED
|
@@ -1,6 +1,4 @@
|
|
|
1
|
-
import type { BaseError, BaseErrorOptions } from "../error/BaseError.js";
|
|
2
1
|
import type { ImmutableArray, PossibleArray } from "./array.js";
|
|
3
|
-
import type { Constructor } from "./class.js";
|
|
4
2
|
import type { Data } from "./data.js";
|
|
5
3
|
import type { ImmutableDictionary } from "./dictionary.js";
|
|
6
4
|
import type { AnyCaller } from "./function.js";
|
|
@@ -28,8 +26,10 @@ export type Validators<T extends Data = Data> = {
|
|
|
28
26
|
export type ValidatorsType<T> = {
|
|
29
27
|
readonly [K in keyof T]: ValidatorType<T[K]>;
|
|
30
28
|
};
|
|
31
|
-
/**
|
|
32
|
-
export declare function getValid<T>(value: unknown, validator: Validator<T
|
|
29
|
+
/** Require a valid value for a given validator, or return `undefined` if the value could not be validated. */
|
|
30
|
+
export declare function getValid<T>(value: unknown, validator: Validator<T>): T | undefined;
|
|
31
|
+
/** Require a valid value for a given validator, or throw `RequiredError` if the value could not be validated. */
|
|
32
|
+
export declare function requireValid<T>(value: unknown, validator: Validator<T>, caller?: AnyCaller): T;
|
|
33
33
|
/**
|
|
34
34
|
* Validate an iterable set of items with a validator.
|
|
35
35
|
*
|
package/util/validate.js
CHANGED
|
@@ -1,20 +1,29 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { RequiredError } from "../error/RequiredError.js";
|
|
2
2
|
import { Feedback, ValueFeedback } from "../feedback/Feedback.js";
|
|
3
3
|
import { isArray } from "./array.js";
|
|
4
4
|
import { getDataProps } from "./data.js";
|
|
5
5
|
import { getDictionaryItems } from "./dictionary.js";
|
|
6
6
|
import { getNamedMessage } from "./error.js";
|
|
7
7
|
import { isIterable } from "./iterate.js";
|
|
8
|
-
/**
|
|
9
|
-
export function getValid(value, validator
|
|
8
|
+
/** Require a valid value for a given validator, or return `undefined` if the value could not be validated. */
|
|
9
|
+
export function getValid(value, validator) {
|
|
10
10
|
try {
|
|
11
11
|
return validator.validate(value);
|
|
12
12
|
}
|
|
13
13
|
catch (thrown) {
|
|
14
|
-
if (thrown instanceof Feedback)
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
14
|
+
if (thrown instanceof Feedback)
|
|
15
|
+
return undefined;
|
|
16
|
+
throw thrown;
|
|
17
|
+
}
|
|
18
|
+
}
|
|
19
|
+
/** Require a valid value for a given validator, or throw `RequiredError` if the value could not be validated. */
|
|
20
|
+
export function requireValid(value, validator, caller = requireValid) {
|
|
21
|
+
try {
|
|
22
|
+
return validator.validate(value);
|
|
23
|
+
}
|
|
24
|
+
catch (thrown) {
|
|
25
|
+
if (thrown instanceof Feedback)
|
|
26
|
+
throw new RequiredError(thrown.message, { cause: thrown, caller });
|
|
18
27
|
throw thrown;
|
|
19
28
|
}
|
|
20
29
|
}
|