shelving 1.171.0 → 1.173.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/db/ValidationProvider.js +6 -7
- package/endpoint/Endpoint.d.ts +3 -3
- package/endpoint/Endpoint.js +7 -13
- package/index.d.ts +0 -1
- package/index.js +0 -1
- package/package.json +1 -2
- package/schema/AddressSchema.d.ts +13 -0
- package/schema/AddressSchema.js +22 -0
- package/schema/ArraySchema.js +3 -4
- package/schema/BooleanSchema.js +1 -2
- package/schema/ChoiceSchema.js +1 -2
- package/schema/CountrySchema.d.ts +17 -0
- package/schema/CountrySchema.js +26 -0
- package/schema/DataSchema.d.ts +2 -1
- package/schema/DataSchema.js +3 -2
- package/schema/DateSchema.js +3 -4
- package/schema/DictionarySchema.js +3 -4
- package/schema/EntitySchema.js +2 -3
- package/schema/FileSchema.js +2 -3
- package/schema/NumberSchema.js +3 -4
- package/schema/RequiredSchema.d.ts +1 -1
- package/schema/RequiredSchema.js +2 -3
- package/schema/StringSchema.js +4 -5
- package/schema/URISchema.js +2 -3
- package/schema/URLSchema.js +2 -3
- package/schema/index.d.ts +2 -0
- package/schema/index.js +2 -0
- package/util/address.d.ts +12 -0
- package/util/address.js +5 -0
- package/util/array.d.ts +4 -0
- package/util/array.js +11 -0
- package/util/country.d.ts +260 -0
- package/util/country.js +278 -0
- package/util/http.d.ts +1 -1
- package/util/http.js +4 -5
- package/util/index.d.ts +2 -0
- package/util/index.js +2 -0
- package/util/validate.d.ts +5 -5
- package/util/validate.js +19 -20
- package/feedback/Feedback.d.ts +0 -19
- package/feedback/Feedback.js +0 -26
- package/feedback/index.d.ts +0 -1
- package/feedback/index.js +0 -1
package/db/ValidationProvider.js
CHANGED
|
@@ -1,5 +1,4 @@
|
|
|
1
1
|
import { ValueError } from "../error/ValueError.js";
|
|
2
|
-
import { Feedback } from "../feedback/Feedback.js";
|
|
3
2
|
import { PARTIAL } from "../schema/DataSchema.js";
|
|
4
3
|
import { getNamedMessage } from "../util/error.js";
|
|
5
4
|
import { validateData } from "../util/validate.js";
|
|
@@ -119,9 +118,9 @@ function _validateItem(collection, item, identifier, schema, caller) {
|
|
|
119
118
|
return validateData(item, { id: identifier, ...schema.props });
|
|
120
119
|
}
|
|
121
120
|
catch (thrown) {
|
|
122
|
-
if (
|
|
121
|
+
if (typeof thrown !== "string")
|
|
123
122
|
throw thrown;
|
|
124
|
-
throw new ValueError(`Invalid data for "${collection}"\n${thrown
|
|
123
|
+
throw new ValueError(`Invalid data for "${collection}"\n${thrown}`, { item, caller });
|
|
125
124
|
}
|
|
126
125
|
}
|
|
127
126
|
/**
|
|
@@ -138,9 +137,9 @@ function* _yieldValidItems(collection, items, validators, caller) {
|
|
|
138
137
|
yield validateData(item, validators);
|
|
139
138
|
}
|
|
140
139
|
catch (thrown) {
|
|
141
|
-
if (
|
|
140
|
+
if (typeof thrown !== "string")
|
|
142
141
|
throw thrown;
|
|
143
|
-
messages.push(getNamedMessage(item.id, thrown
|
|
142
|
+
messages.push(getNamedMessage(item.id, thrown));
|
|
144
143
|
}
|
|
145
144
|
}
|
|
146
145
|
if (messages.length)
|
|
@@ -151,8 +150,8 @@ function _validateUpdates(collection, updates, schema, caller) {
|
|
|
151
150
|
return validateData(updates, PARTIAL(schema).props);
|
|
152
151
|
}
|
|
153
152
|
catch (thrown) {
|
|
154
|
-
if (
|
|
153
|
+
if (typeof thrown !== "string")
|
|
155
154
|
throw thrown;
|
|
156
|
-
throw new ValueError(`Invalid updates for "${collection}"\n${thrown
|
|
155
|
+
throw new ValueError(`Invalid updates for "${collection}"\n${thrown}`, { updates, caller });
|
|
157
156
|
}
|
|
158
157
|
}
|
package/endpoint/Endpoint.d.ts
CHANGED
|
@@ -34,7 +34,7 @@ export declare class Endpoint<P, R> {
|
|
|
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
36
|
*
|
|
37
|
-
* @throws `
|
|
37
|
+
* @throws `string` if the payload is invalid.
|
|
38
38
|
* @throws `ValueError` if `callback()` returns an invalid result.
|
|
39
39
|
*/
|
|
40
40
|
handle(callback: EndpointCallback<P, R>, unsafePayload: unknown, request: Request, caller?: AnyCaller): Promise<Response>;
|
|
@@ -48,7 +48,7 @@ export declare class Endpoint<P, R> {
|
|
|
48
48
|
* - Validates a payload against this endpoints payload schema
|
|
49
49
|
* - Return an HTTP `Request` that will send it the valid payload to this endpoint.
|
|
50
50
|
*
|
|
51
|
-
* @throws `
|
|
51
|
+
* @throws `string` if the payload is invalid.
|
|
52
52
|
*/
|
|
53
53
|
request(payload: P, options?: RequestOptions, caller?: AnyCaller): Request;
|
|
54
54
|
/**
|
|
@@ -63,7 +63,7 @@ export declare class Endpoint<P, R> {
|
|
|
63
63
|
* - Validate the `payload` against this endpoint's payload schema.
|
|
64
64
|
* - Validate the returned response against this endpoint's result schema.
|
|
65
65
|
*
|
|
66
|
-
* @throws `
|
|
66
|
+
* @throws `string` if the payload is invalid.
|
|
67
67
|
* @throws `ResponseError` if the response status is not ok (200-299)
|
|
68
68
|
* @throws `ResponseError` if the response content is invalid.
|
|
69
69
|
*/
|
package/endpoint/Endpoint.js
CHANGED
|
@@ -1,6 +1,5 @@
|
|
|
1
1
|
import { ResponseError } from "../error/ResponseError.js";
|
|
2
2
|
import { ValueError } from "../error/ValueError.js";
|
|
3
|
-
import { Feedback } from "../feedback/Feedback.js";
|
|
4
3
|
import { UNDEFINED } from "../schema/Schema.js";
|
|
5
4
|
import { assertDictionary } from "../util/dictionary.js";
|
|
6
5
|
import { getMessage } from "../util/error.js";
|
|
@@ -44,7 +43,7 @@ export class Endpoint {
|
|
|
44
43
|
* @param unsafePayload The payload to pass into the callback (will be validated against this endpoint's payload schema).
|
|
45
44
|
* @param request The entire HTTP request that is being handled (payload was possibly extracted from this somehow).
|
|
46
45
|
*
|
|
47
|
-
* @throws `
|
|
46
|
+
* @throws `string` if the payload is invalid.
|
|
48
47
|
* @throws `ValueError` if `callback()` returns an invalid result.
|
|
49
48
|
*/
|
|
50
49
|
async handle(callback, unsafePayload, request, caller = this.handle) {
|
|
@@ -57,8 +56,8 @@ export class Endpoint {
|
|
|
57
56
|
return getResponse(this.result.validate(unsafeResult));
|
|
58
57
|
}
|
|
59
58
|
catch (thrown) {
|
|
60
|
-
if (thrown
|
|
61
|
-
throw new ValueError(`Invalid result for ${this.toString()}:\n${thrown
|
|
59
|
+
if (typeof thrown === "string")
|
|
60
|
+
throw new ValueError(`Invalid result for ${this.toString()}:\n${thrown}`, {
|
|
62
61
|
endpoint: this,
|
|
63
62
|
callback,
|
|
64
63
|
cause: thrown,
|
|
@@ -87,7 +86,7 @@ export class Endpoint {
|
|
|
87
86
|
* - Validates a payload against this endpoints payload schema
|
|
88
87
|
* - Return an HTTP `Request` that will send it the valid payload to this endpoint.
|
|
89
88
|
*
|
|
90
|
-
* @throws `
|
|
89
|
+
* @throws `string` if the payload is invalid.
|
|
91
90
|
*/
|
|
92
91
|
request(payload, options = {}, caller = this.request) {
|
|
93
92
|
return getRequest(this.method, this.url, this.payload.validate(payload), options, caller);
|
|
@@ -110,13 +109,8 @@ export class Endpoint {
|
|
|
110
109
|
return this.result.validate(content);
|
|
111
110
|
}
|
|
112
111
|
catch (thrown) {
|
|
113
|
-
if (thrown
|
|
114
|
-
throw new ResponseError(`Invalid result for ${this.toString()}:\n${thrown
|
|
115
|
-
endpoint: this,
|
|
116
|
-
code: 422,
|
|
117
|
-
cause: thrown,
|
|
118
|
-
caller,
|
|
119
|
-
});
|
|
112
|
+
if (typeof thrown === "string")
|
|
113
|
+
throw new ResponseError(`Invalid result for ${this.toString()}:\n${thrown}`, { endpoint: this, code: 422, caller });
|
|
120
114
|
throw thrown;
|
|
121
115
|
}
|
|
122
116
|
}
|
|
@@ -125,7 +119,7 @@ export class Endpoint {
|
|
|
125
119
|
* - Validate the `payload` against this endpoint's payload schema.
|
|
126
120
|
* - Validate the returned response against this endpoint's result schema.
|
|
127
121
|
*
|
|
128
|
-
* @throws `
|
|
122
|
+
* @throws `string` if the payload is invalid.
|
|
129
123
|
* @throws `ResponseError` if the response status is not ok (200-299)
|
|
130
124
|
* @throws `ResponseError` if the response content is invalid.
|
|
131
125
|
*/
|
package/index.d.ts
CHANGED
package/index.js
CHANGED
|
@@ -6,7 +6,6 @@
|
|
|
6
6
|
export * from "./db/index.js";
|
|
7
7
|
export * from "./endpoint/index.js";
|
|
8
8
|
export * from "./error/index.js";
|
|
9
|
-
export * from "./feedback/index.js";
|
|
10
9
|
// export * from "./firestore/client/index.js"; // Not exported.
|
|
11
10
|
// export * from "./firestore/lite/index.js"; // Not exported.
|
|
12
11
|
// export * from "./firestore/server/index.js"; // Not exported.
|
package/package.json
CHANGED
|
@@ -11,7 +11,7 @@
|
|
|
11
11
|
"state-management",
|
|
12
12
|
"query-builder"
|
|
13
13
|
],
|
|
14
|
-
"version": "1.
|
|
14
|
+
"version": "1.173.0",
|
|
15
15
|
"repository": {
|
|
16
16
|
"type": "git",
|
|
17
17
|
"url": "git+https://github.com/dhoulb/shelving.git"
|
|
@@ -27,7 +27,6 @@
|
|
|
27
27
|
"./api": "./api/index.js",
|
|
28
28
|
"./db": "./db/index.js",
|
|
29
29
|
"./error": "./error/index.js",
|
|
30
|
-
"./feedback": "./feedback/index.js",
|
|
31
30
|
"./firestore/client": "./firestore/client/index.js",
|
|
32
31
|
"./firestore/lite": "./firestore/lite/index.js",
|
|
33
32
|
"./firestore/server": "./firestore/server/index.js",
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import type { AddressData } from "../util/address.js";
|
|
2
|
+
import { DataSchema, type DataSchemaOptions } from "./DataSchema.js";
|
|
3
|
+
/** Allowed options for `AddressSchema` */
|
|
4
|
+
export interface AddressSchemaOptions extends Omit<DataSchemaOptions<AddressData>, "props"> {
|
|
5
|
+
}
|
|
6
|
+
/** Schema that validates a postal address. */
|
|
7
|
+
export declare class AddressSchema extends DataSchema<AddressData> {
|
|
8
|
+
constructor({ one, title, ...options }?: AddressSchemaOptions);
|
|
9
|
+
}
|
|
10
|
+
/** Valid postal address data. */
|
|
11
|
+
export declare const ADDRESS: AddressSchema;
|
|
12
|
+
/** Valid postal address data, or `null` */
|
|
13
|
+
export declare const NULLABLE_ADDRESS: import("./NullableSchema.js").NullableSchema<AddressData>;
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import { COUNTRY } from "./CountrySchema.js";
|
|
2
|
+
import { DataSchema } from "./DataSchema.js";
|
|
3
|
+
import { NULLABLE } from "./NullableSchema.js";
|
|
4
|
+
import { StringSchema } from "./StringSchema.js";
|
|
5
|
+
const ADDRESS_PROPS = {
|
|
6
|
+
address1: new StringSchema({ title: "Address 1", max: 60, min: 1 }),
|
|
7
|
+
address2: new StringSchema({ title: "Address 2", max: 60, min: 0 }),
|
|
8
|
+
city: new StringSchema({ title: "City", min: 1, max: 60 }),
|
|
9
|
+
state: new StringSchema({ title: "State", min: 1, max: 60 }),
|
|
10
|
+
postcode: new StringSchema({ title: "Postcode", min: 1, max: 12, case: "upper" }),
|
|
11
|
+
country: COUNTRY,
|
|
12
|
+
};
|
|
13
|
+
/** Schema that validates a postal address. */
|
|
14
|
+
export class AddressSchema extends DataSchema {
|
|
15
|
+
constructor({ one = "address", title = "Address", ...options } = {}) {
|
|
16
|
+
super({ one, title, props: ADDRESS_PROPS, ...options });
|
|
17
|
+
}
|
|
18
|
+
}
|
|
19
|
+
/** Valid postal address data. */
|
|
20
|
+
export const ADDRESS = new AddressSchema({});
|
|
21
|
+
/** Valid postal address data, or `null` */
|
|
22
|
+
export const NULLABLE_ADDRESS = NULLABLE(ADDRESS);
|
package/schema/ArraySchema.js
CHANGED
|
@@ -1,4 +1,3 @@
|
|
|
1
|
-
import { ValueFeedback } from "../feedback/Feedback.js";
|
|
2
1
|
import { getUniqueArray, isArray } from "../util/array.js";
|
|
3
2
|
import { validateArray } from "../util/validate.js";
|
|
4
3
|
import { Schema } from "./Schema.js";
|
|
@@ -39,13 +38,13 @@ export class ArraySchema extends Schema {
|
|
|
39
38
|
validate(unsafeValue = this.value) {
|
|
40
39
|
const unsafeArray = typeof unsafeValue === "string" ? unsafeValue.split(this.separator).filter(Boolean) : unsafeValue;
|
|
41
40
|
if (!isArray(unsafeArray))
|
|
42
|
-
throw
|
|
41
|
+
throw "Must be array";
|
|
43
42
|
const validArray = validateArray(unsafeArray, this.items);
|
|
44
43
|
const uniqueArray = this.unique ? getUniqueArray(validArray) : validArray;
|
|
45
44
|
if (uniqueArray.length < this.min)
|
|
46
|
-
throw
|
|
45
|
+
throw uniqueArray.length ? `Minimum ${this.min} ${this.many}` : "Required";
|
|
47
46
|
if (uniqueArray.length > this.max)
|
|
48
|
-
throw
|
|
47
|
+
throw `Maximum ${this.max} ${this.many}`;
|
|
49
48
|
return uniqueArray;
|
|
50
49
|
}
|
|
51
50
|
}
|
package/schema/BooleanSchema.js
CHANGED
|
@@ -1,4 +1,3 @@
|
|
|
1
|
-
import { Feedback } from "../feedback/Feedback.js";
|
|
2
1
|
import { Schema } from "./Schema.js";
|
|
3
2
|
const NEGATIVE = ["", "false", "0", "no", "n", "off"];
|
|
4
3
|
/** Define a valid boolean. */
|
|
@@ -10,7 +9,7 @@ export class BooleanSchema extends Schema {
|
|
|
10
9
|
validate(unsafeValue = this.value) {
|
|
11
10
|
const value = typeof unsafeValue === "string" ? !NEGATIVE.includes(unsafeValue.toLowerCase().trim()) : !!unsafeValue;
|
|
12
11
|
if (this.required && !value)
|
|
13
|
-
throw
|
|
12
|
+
throw "Required";
|
|
14
13
|
return value;
|
|
15
14
|
}
|
|
16
15
|
}
|
package/schema/ChoiceSchema.js
CHANGED
|
@@ -1,4 +1,3 @@
|
|
|
1
|
-
import { ValueFeedback } from "../feedback/Feedback.js";
|
|
2
1
|
import { isArray, requireFirst } from "../util/array.js";
|
|
3
2
|
import { getKeys, getProps, isProp } from "../util/object.js";
|
|
4
3
|
import { Schema } from "./Schema.js";
|
|
@@ -27,7 +26,7 @@ export class ChoiceSchema extends Schema {
|
|
|
27
26
|
validate(unsafeValue = this.value) {
|
|
28
27
|
if (typeof unsafeValue === "string" && isOption(this.options, unsafeValue))
|
|
29
28
|
return unsafeValue;
|
|
30
|
-
throw
|
|
29
|
+
throw "Unknown value";
|
|
31
30
|
}
|
|
32
31
|
// Implement iterable.
|
|
33
32
|
*[Symbol.iterator]() {
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import { type Country } from "../util/country.js";
|
|
2
|
+
import { ChoiceSchema, type ChoiceSchemaOptions } from "./ChoiceSchema.js";
|
|
3
|
+
/** Allowed options for `CountrySchema` */
|
|
4
|
+
export interface CountrySchemaOptions extends Omit<ChoiceSchemaOptions<Country>, "options" | "value"> {
|
|
5
|
+
/** Country value, or `"detect"` to resolve from browser language. */
|
|
6
|
+
readonly value?: Country | "detect";
|
|
7
|
+
}
|
|
8
|
+
/** Schema that validates an ISO country code. */
|
|
9
|
+
export declare class CountrySchema extends ChoiceSchema<Country> {
|
|
10
|
+
readonly defaultValue: Country | "detect";
|
|
11
|
+
constructor({ one, title, value, ...options }?: CountrySchemaOptions);
|
|
12
|
+
validate(unsafeValue?: unknown): Country;
|
|
13
|
+
}
|
|
14
|
+
/** Valid country code, e.g. `GB` (required because falsy values are invalid). */
|
|
15
|
+
export declare const COUNTRY: CountrySchema;
|
|
16
|
+
/** Valid country code, e.g. `GB`, or `null` */
|
|
17
|
+
export declare const NULLABLE_COUNTRY: import("./NullableSchema.js").NullableSchema<"AF" | "AX" | "AL" | "DZ" | "AS" | "AD" | "AO" | "AI" | "AQ" | "AG" | "AR" | "AM" | "AW" | "AU" | "AT" | "AZ" | "BS" | "BH" | "BD" | "BB" | "BY" | "BE" | "BZ" | "BJ" | "BM" | "BT" | "BO" | "BA" | "BW" | "BV" | "BR" | "IO" | "BN" | "BG" | "BF" | "BI" | "KH" | "CM" | "CA" | "CV" | "KY" | "CF" | "TD" | "CL" | "CN" | "CX" | "CC" | "CO" | "KM" | "CG" | "CD" | "CK" | "CR" | "CI" | "HR" | "CU" | "CY" | "CZ" | "DK" | "DJ" | "DM" | "DO" | "EC" | "EG" | "SV" | "GQ" | "ER" | "EE" | "ET" | "FK" | "FO" | "FJ" | "FI" | "FR" | "GF" | "PF" | "TF" | "GA" | "GM" | "GE" | "DE" | "GH" | "GI" | "GR" | "GL" | "GD" | "GP" | "GU" | "GT" | "GG" | "GN" | "GW" | "GY" | "HT" | "HM" | "VA" | "HN" | "HK" | "HU" | "IS" | "IN" | "ID" | "IR" | "IQ" | "IE" | "IM" | "IL" | "IT" | "JM" | "JP" | "JE" | "JO" | "KZ" | "KE" | "KI" | "KR" | "KP" | "KW" | "KG" | "LA" | "LV" | "LB" | "LS" | "LR" | "LY" | "LI" | "LT" | "LU" | "MO" | "MK" | "MG" | "MW" | "MY" | "MV" | "ML" | "MT" | "MH" | "MQ" | "MR" | "MU" | "YT" | "MX" | "FM" | "MD" | "MC" | "MN" | "ME" | "MS" | "MA" | "MZ" | "MM" | "NA" | "NR" | "NP" | "NL" | "AN" | "NC" | "NZ" | "NI" | "NE" | "NG" | "NU" | "NF" | "MP" | "NO" | "OM" | "PK" | "PW" | "PS" | "PA" | "PG" | "PY" | "PE" | "PH" | "PN" | "PL" | "PT" | "PR" | "QA" | "RE" | "RO" | "RU" | "RW" | "BL" | "SH" | "KN" | "LC" | "MF" | "PM" | "VC" | "WS" | "SM" | "ST" | "SA" | "SN" | "RS" | "SC" | "SL" | "SG" | "SK" | "SI" | "SB" | "SO" | "ZA" | "GS" | "ES" | "LK" | "SD" | "SR" | "SJ" | "SZ" | "SE" | "CH" | "SY" | "TW" | "TJ" | "TZ" | "TH" | "TL" | "TG" | "TK" | "TO" | "TT" | "TN" | "TR" | "TM" | "TC" | "TV" | "UG" | "UA" | "AE" | "GB" | "US" | "UM" | "UY" | "UZ" | "VU" | "VE" | "VN" | "VG" | "VI" | "WF" | "EH" | "YE" | "ZM" | "ZW">;
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import { COUNTRIES, getCountry } from "../util/country.js";
|
|
2
|
+
import { isProp } from "../util/object.js";
|
|
3
|
+
import { ChoiceSchema } from "./ChoiceSchema.js";
|
|
4
|
+
import { NULLABLE } from "./NullableSchema.js";
|
|
5
|
+
/** Schema that validates an ISO country code. */
|
|
6
|
+
export class CountrySchema extends ChoiceSchema {
|
|
7
|
+
defaultValue;
|
|
8
|
+
constructor({ one = "country", title = "Country", value = "detect", ...options } = {}) {
|
|
9
|
+
super({ one, title, options: COUNTRIES, value: value === "detect" ? "GB" : value, ...options });
|
|
10
|
+
this.defaultValue = value;
|
|
11
|
+
}
|
|
12
|
+
validate(unsafeValue = this.defaultValue) {
|
|
13
|
+
const country = getCountry(unsafeValue);
|
|
14
|
+
if (country)
|
|
15
|
+
return super.validate(country);
|
|
16
|
+
if (unsafeValue === undefined || unsafeValue === null || unsafeValue === "" || unsafeValue === "detect")
|
|
17
|
+
throw "Required";
|
|
18
|
+
if (typeof unsafeValue === "string" && isProp(COUNTRIES, unsafeValue.toUpperCase()))
|
|
19
|
+
return super.validate(unsafeValue.toUpperCase());
|
|
20
|
+
return super.validate(unsafeValue);
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
/** Valid country code, e.g. `GB` (required because falsy values are invalid). */
|
|
24
|
+
export const COUNTRY = new CountrySchema({});
|
|
25
|
+
/** Valid country code, e.g. `GB`, or `null` */
|
|
26
|
+
export const NULLABLE_COUNTRY = NULLABLE(COUNTRY);
|
package/schema/DataSchema.d.ts
CHANGED
|
@@ -6,7 +6,6 @@ import type { SchemaOptions, Schemas } from "./Schema.js";
|
|
|
6
6
|
import { Schema } from "./Schema.js";
|
|
7
7
|
/** Allowed options for `PropsSchema` (a schema that has props). */
|
|
8
8
|
export interface DataSchemaOptions<T extends Data> extends SchemaOptions {
|
|
9
|
-
readonly id?: Schema<string>;
|
|
10
9
|
readonly props: Schemas<T>;
|
|
11
10
|
readonly value?: Partial<T> | undefined;
|
|
12
11
|
}
|
|
@@ -16,7 +15,9 @@ export declare class DataSchema<T extends Data> extends Schema<unknown> {
|
|
|
16
15
|
readonly props: Schemas<T>;
|
|
17
16
|
constructor({ one, title, props, value: partialValue, ...options }: DataSchemaOptions<T>);
|
|
18
17
|
validate(unsafeValue?: unknown): T;
|
|
18
|
+
/** Make a new `DataSchema` that only uses a defined subset of the current props. */
|
|
19
19
|
pick<K extends Key<T>>(...keys: K[]): DataSchema<Pick<T, K>>;
|
|
20
|
+
/** Make a new `DataSchema` that omits one or more of the current props. */
|
|
20
21
|
omit<K extends Key<T>>(...keys: K[]): DataSchema<Omit<T, K>>;
|
|
21
22
|
}
|
|
22
23
|
/** Set of named data schemas. */
|
package/schema/DataSchema.js
CHANGED
|
@@ -1,4 +1,3 @@
|
|
|
1
|
-
import { ValueFeedback } from "../feedback/Feedback.js";
|
|
2
1
|
import { isData } from "../util/data.js";
|
|
3
2
|
import { omitProps, pickProps } from "../util/object.js";
|
|
4
3
|
import { mapProps } from "../util/transform.js";
|
|
@@ -17,12 +16,14 @@ export class DataSchema extends Schema {
|
|
|
17
16
|
}
|
|
18
17
|
validate(unsafeValue = this.value) {
|
|
19
18
|
if (!isData(unsafeValue))
|
|
20
|
-
throw
|
|
19
|
+
throw "Must be object";
|
|
21
20
|
return validateData(unsafeValue, this.props);
|
|
22
21
|
}
|
|
22
|
+
/** Make a new `DataSchema` that only uses a defined subset of the current props. */
|
|
23
23
|
pick(...keys) {
|
|
24
24
|
return new DataSchema({ ...this, props: pickProps(this.props, ...keys) });
|
|
25
25
|
}
|
|
26
|
+
/** Make a new `DataSchema` that omits one or more of the current props. */
|
|
26
27
|
omit(...keys) {
|
|
27
28
|
return new DataSchema({ ...this, props: omitProps(this.props, ...keys) });
|
|
28
29
|
}
|
package/schema/DateSchema.js
CHANGED
|
@@ -1,4 +1,3 @@
|
|
|
1
|
-
import { ValueFeedback } from "../feedback/Feedback.js";
|
|
2
1
|
import { getDate, requireDateString } from "../util/date.js";
|
|
3
2
|
import { formatDate } from "../util/format.js";
|
|
4
3
|
import { roundStep } from "../util/number.js";
|
|
@@ -19,12 +18,12 @@ export class DateSchema extends Schema {
|
|
|
19
18
|
validate(value = this.value) {
|
|
20
19
|
const date = getDate(value);
|
|
21
20
|
if (!date)
|
|
22
|
-
throw
|
|
21
|
+
throw value ? "Invalid date" : "Required";
|
|
23
22
|
const rounded = typeof this.step === "number" ? new Date(roundStep(date.getTime(), this.step)) : date;
|
|
24
23
|
if (this.min && rounded < this.min)
|
|
25
|
-
throw
|
|
24
|
+
throw `Minimum ${this.format(this.min)}`;
|
|
26
25
|
if (this.max && rounded > this.max)
|
|
27
|
-
throw
|
|
26
|
+
throw `Maximum ${this.format(this.max)}`;
|
|
28
27
|
return this.stringify(rounded);
|
|
29
28
|
}
|
|
30
29
|
stringify(value) {
|
|
@@ -1,4 +1,3 @@
|
|
|
1
|
-
import { ValueFeedback } from "../feedback/Feedback.js";
|
|
2
1
|
import { isDictionary } from "../util/dictionary.js";
|
|
3
2
|
import { validateDictionary } from "../util/validate.js";
|
|
4
3
|
import { Schema } from "./Schema.js";
|
|
@@ -15,13 +14,13 @@ export class DictionarySchema extends Schema {
|
|
|
15
14
|
}
|
|
16
15
|
validate(unsafeValue = this.value) {
|
|
17
16
|
if (!isDictionary(unsafeValue))
|
|
18
|
-
throw
|
|
17
|
+
throw "Must be object";
|
|
19
18
|
const validDictionary = validateDictionary(unsafeValue, this.items);
|
|
20
19
|
const length = Object.keys(validDictionary).length;
|
|
21
20
|
if (length < this.min)
|
|
22
|
-
throw
|
|
21
|
+
throw length ? `Minimum ${this.min} ${this.many}` : "Required";
|
|
23
22
|
if (length > this.max)
|
|
24
|
-
throw
|
|
23
|
+
throw `Maximum ${this.max} ${this.many}`;
|
|
25
24
|
return validDictionary;
|
|
26
25
|
}
|
|
27
26
|
}
|
package/schema/EntitySchema.js
CHANGED
|
@@ -1,4 +1,3 @@
|
|
|
1
|
-
import { ValueFeedback } from "../feedback/Feedback.js";
|
|
2
1
|
import { isArrayItem } from "../util/array.js";
|
|
3
2
|
import { getEntity } from "../util/entity.js";
|
|
4
3
|
import { NULLABLE } from "./NullableSchema.js";
|
|
@@ -14,9 +13,9 @@ export class EntitySchema extends StringSchema {
|
|
|
14
13
|
const entity = super.validate(unsafeValue);
|
|
15
14
|
const [type] = getEntity(entity);
|
|
16
15
|
if (!type)
|
|
17
|
-
throw
|
|
16
|
+
throw "Must be entity";
|
|
18
17
|
if (this.types && !isArrayItem(this.types, type))
|
|
19
|
-
throw
|
|
18
|
+
throw "Invalid entity type";
|
|
20
19
|
return entity;
|
|
21
20
|
}
|
|
22
21
|
}
|
package/schema/FileSchema.js
CHANGED
|
@@ -1,4 +1,3 @@
|
|
|
1
|
-
import { ValueFeedback } from "../feedback/Feedback.js";
|
|
2
1
|
import { getFileExtension } from "../util/file.js";
|
|
3
2
|
import { isProp } from "../util/object.js";
|
|
4
3
|
import { NULLABLE } from "./NullableSchema.js";
|
|
@@ -14,9 +13,9 @@ export class FileSchema extends StringSchema {
|
|
|
14
13
|
const path = super.validate(unsafeValue);
|
|
15
14
|
const extension = getFileExtension(path);
|
|
16
15
|
if (!extension)
|
|
17
|
-
throw
|
|
16
|
+
throw "Must be file name with extension";
|
|
18
17
|
if (this.types && !isProp(this.types, extension))
|
|
19
|
-
throw
|
|
18
|
+
throw "Invalid file type";
|
|
20
19
|
return path;
|
|
21
20
|
}
|
|
22
21
|
}
|
package/schema/NumberSchema.js
CHANGED
|
@@ -1,4 +1,3 @@
|
|
|
1
|
-
import { ValueFeedback } from "../feedback/Feedback.js";
|
|
2
1
|
import { formatNumber } from "../util/format.js";
|
|
3
2
|
import { getNumber, roundStep } from "../util/number.js";
|
|
4
3
|
import { NULLABLE } from "./NullableSchema.js";
|
|
@@ -17,12 +16,12 @@ export class NumberSchema extends Schema {
|
|
|
17
16
|
validate(unsafeValue = this.value) {
|
|
18
17
|
const optionalNumber = getNumber(unsafeValue);
|
|
19
18
|
if (typeof optionalNumber !== "number")
|
|
20
|
-
throw
|
|
19
|
+
throw "Must be number";
|
|
21
20
|
const roundedNumber = typeof this.step === "number" ? roundStep(optionalNumber, this.step) : optionalNumber;
|
|
22
21
|
if (roundedNumber < this.min)
|
|
23
|
-
throw
|
|
22
|
+
throw !optionalNumber ? "Required" : `Minimum ${formatNumber(this.min)}`;
|
|
24
23
|
if (roundedNumber > this.max)
|
|
25
|
-
throw
|
|
24
|
+
throw `Maximum ${formatNumber(this.max)}`;
|
|
26
25
|
return roundedNumber;
|
|
27
26
|
}
|
|
28
27
|
}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import type { Schema } from "./Schema.js";
|
|
2
2
|
import { ThroughSchema } from "./ThroughSchema.js";
|
|
3
|
-
/** Validate a value of a specifed type, but throw `
|
|
3
|
+
/** Validate a value of a specifed type, but throw `"Required"` if the validated value is falsy. */
|
|
4
4
|
export declare class RequiredSchema<T> extends ThroughSchema<T> {
|
|
5
5
|
validate(unsafeValue: unknown): T;
|
|
6
6
|
}
|
package/schema/RequiredSchema.js
CHANGED
|
@@ -1,11 +1,10 @@
|
|
|
1
|
-
import { ValueFeedback } from "../feedback/Feedback.js";
|
|
2
1
|
import { ThroughSchema } from "./ThroughSchema.js";
|
|
3
|
-
/** Validate a value of a specifed type, but throw `
|
|
2
|
+
/** Validate a value of a specifed type, but throw `"Required"` if the validated value is falsy. */
|
|
4
3
|
export class RequiredSchema extends ThroughSchema {
|
|
5
4
|
validate(unsafeValue) {
|
|
6
5
|
const safeValue = super.validate(unsafeValue);
|
|
7
6
|
if (!safeValue)
|
|
8
|
-
throw
|
|
7
|
+
throw "Required";
|
|
9
8
|
return safeValue;
|
|
10
9
|
}
|
|
11
10
|
}
|
package/schema/StringSchema.js
CHANGED
|
@@ -1,4 +1,3 @@
|
|
|
1
|
-
import { ValueFeedback } from "../feedback/Feedback.js";
|
|
2
1
|
import { sanitizeMultilineText, sanitizeText } from "../util/string.js";
|
|
3
2
|
import { NULLABLE } from "./NullableSchema.js";
|
|
4
3
|
import { Schema } from "./Schema.js";
|
|
@@ -36,14 +35,14 @@ export class StringSchema extends Schema {
|
|
|
36
35
|
validate(unsafeValue = this.value) {
|
|
37
36
|
const possibleString = typeof unsafeValue === "number" ? unsafeValue.toString() : unsafeValue;
|
|
38
37
|
if (typeof possibleString !== "string")
|
|
39
|
-
throw
|
|
38
|
+
throw "Must be string";
|
|
40
39
|
const saneString = this.sanitize(possibleString);
|
|
41
40
|
if (saneString.length < this.min)
|
|
42
|
-
throw
|
|
41
|
+
throw possibleString.length ? `Minimum ${this.min} characters` : "Required";
|
|
43
42
|
if (saneString.length > this.max)
|
|
44
|
-
throw
|
|
43
|
+
throw `Maximum ${this.max} characters`;
|
|
45
44
|
if (this.match && !this.match.test(saneString))
|
|
46
|
-
throw
|
|
45
|
+
throw saneString ? "Invalid format" : "Required";
|
|
47
46
|
return saneString;
|
|
48
47
|
}
|
|
49
48
|
/** Sanitize the string by removing unwanted characters. */
|
package/schema/URISchema.js
CHANGED
|
@@ -1,4 +1,3 @@
|
|
|
1
|
-
import { ValueFeedback } from "../feedback/Feedback.js";
|
|
2
1
|
import { getURI, HTTP_SCHEMES } from "../util/uri.js";
|
|
3
2
|
import { NULLABLE } from "./NullableSchema.js";
|
|
4
3
|
import { StringSchema } from "./StringSchema.js";
|
|
@@ -26,9 +25,9 @@ export class URISchema extends StringSchema {
|
|
|
26
25
|
const str = super.validate(unsafeValue);
|
|
27
26
|
const uri = getURI(str);
|
|
28
27
|
if (!uri)
|
|
29
|
-
throw
|
|
28
|
+
throw str ? "Invalid format" : "Required";
|
|
30
29
|
if (this.schemes && !this.schemes.includes(uri.protocol))
|
|
31
|
-
throw
|
|
30
|
+
throw "Invalid URI scheme";
|
|
32
31
|
return uri.href;
|
|
33
32
|
}
|
|
34
33
|
}
|
package/schema/URLSchema.js
CHANGED
|
@@ -1,4 +1,3 @@
|
|
|
1
|
-
import { ValueFeedback } from "../feedback/Feedback.js";
|
|
2
1
|
import { HTTP_SCHEMES } from "../util/uri.js";
|
|
3
2
|
import { getURL } from "../util/url.js";
|
|
4
3
|
import { NULLABLE } from "./NullableSchema.js";
|
|
@@ -29,9 +28,9 @@ export class URLSchema extends StringSchema {
|
|
|
29
28
|
const str = super.validate(unsafeValue);
|
|
30
29
|
const url = getURL(str, this.base);
|
|
31
30
|
if (!url)
|
|
32
|
-
throw
|
|
31
|
+
throw str ? "Invalid format" : "Required";
|
|
33
32
|
if (this.schemes && !this.schemes.includes(url.protocol))
|
|
34
|
-
throw
|
|
33
|
+
throw "Invalid URL scheme";
|
|
35
34
|
return url.href;
|
|
36
35
|
}
|
|
37
36
|
}
|
package/schema/index.d.ts
CHANGED
|
@@ -1,7 +1,9 @@
|
|
|
1
|
+
export * from "./AddressSchema.js";
|
|
1
2
|
export * from "./ArraySchema.js";
|
|
2
3
|
export * from "./BooleanSchema.js";
|
|
3
4
|
export * from "./ChoiceSchema.js";
|
|
4
5
|
export * from "./ColorSchema.js";
|
|
6
|
+
export * from "./CountrySchema.js";
|
|
5
7
|
export * from "./DataSchema.js";
|
|
6
8
|
export * from "./DateSchema.js";
|
|
7
9
|
export * from "./DateTimeSchema.js";
|
package/schema/index.js
CHANGED
|
@@ -1,7 +1,9 @@
|
|
|
1
|
+
export * from "./AddressSchema.js";
|
|
1
2
|
export * from "./ArraySchema.js";
|
|
2
3
|
export * from "./BooleanSchema.js";
|
|
3
4
|
export * from "./ChoiceSchema.js";
|
|
4
5
|
export * from "./ColorSchema.js";
|
|
6
|
+
export * from "./CountrySchema.js";
|
|
5
7
|
export * from "./DataSchema.js";
|
|
6
8
|
export * from "./DateSchema.js";
|
|
7
9
|
export * from "./DateTimeSchema.js";
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import { type Country } from "./country.js";
|
|
2
|
+
/** Valid shape for postal address data. */
|
|
3
|
+
export type AddressData = {
|
|
4
|
+
readonly address1: string;
|
|
5
|
+
readonly address2: string;
|
|
6
|
+
readonly city: string;
|
|
7
|
+
readonly state: string;
|
|
8
|
+
readonly postcode: string;
|
|
9
|
+
readonly country: Country;
|
|
10
|
+
};
|
|
11
|
+
/** Format address lines into a printable string. */
|
|
12
|
+
export declare function formatAddress({ address1, address2, city, state, postcode, country }: AddressData): string;
|
package/util/address.js
ADDED
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
import { formatCountry } from "./country.js";
|
|
2
|
+
/** Format address lines into a printable string. */
|
|
3
|
+
export function formatAddress({ address1, address2, city, state, postcode, country }) {
|
|
4
|
+
return `${address1}\n${address2 ? `${address2}\n` : ""}${city}\n${state}\n${postcode}\n${formatCountry(country)}`;
|
|
5
|
+
}
|
package/util/array.d.ts
CHANGED
|
@@ -105,6 +105,10 @@ export declare function countArray<T>(arr: ImmutableArray<T>): number;
|
|
|
105
105
|
/** Interleave array items with a separator */
|
|
106
106
|
export declare function interleaveArray<T>(items: PossibleArray<T>, separator: T): ImmutableArray<T>;
|
|
107
107
|
export declare function interleaveArray<A, B>(items: PossibleArray<A>, separator: B): ImmutableArray<A | B>;
|
|
108
|
+
/** Return a new array with a new value replacing a specific index in the array (or the same array if the value was unchanged). */
|
|
109
|
+
export declare function withArrayIndex<T>(arr: ImmutableArray<T>, index: number, value: T): ImmutableArray<T>;
|
|
110
|
+
/** Return a new array without a specific index in the array (or the same array if the value was unchanged). */
|
|
111
|
+
export declare function omitArrayIndex<T>(arr: ImmutableArray<T>, index: number): ImmutableArray<T>;
|
|
108
112
|
/** Get the first item from an array or iterable, or `undefined` if it didn't exist. */
|
|
109
113
|
export declare function getFirst<T>(items: PossibleArray<T>): T | undefined;
|
|
110
114
|
/** Get the first item from an array or iterable. */
|
package/util/array.js
CHANGED
|
@@ -124,6 +124,17 @@ export function interleaveArray(items, separator) {
|
|
|
124
124
|
return items; // Return same empty array if empty or only one item.
|
|
125
125
|
return Array.from(interleaveItems(items, separator));
|
|
126
126
|
}
|
|
127
|
+
/** Return a new array with a new value replacing a specific index in the array (or the same array if the value was unchanged). */
|
|
128
|
+
export function withArrayIndex(arr, index, value) {
|
|
129
|
+
if (arr[index] === value)
|
|
130
|
+
return arr;
|
|
131
|
+
return [...arr.slice(0, index), value, ...arr.slice(index + 1)];
|
|
132
|
+
}
|
|
133
|
+
/** Return a new array without a specific index in the array (or the same array if the value was unchanged). */
|
|
134
|
+
export function omitArrayIndex(arr, index) {
|
|
135
|
+
const output = [...arr.slice(0, index), ...arr.slice(index + 1)];
|
|
136
|
+
return arr.length !== output.length ? output : arr;
|
|
137
|
+
}
|
|
127
138
|
/** Get the first item from an array or iterable, or `undefined` if it didn't exist. */
|
|
128
139
|
export function getFirst(items) {
|
|
129
140
|
if (isArray(items))
|