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.
@@ -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 (!(thrown instanceof Feedback))
121
+ if (typeof thrown !== "string")
123
122
  throw thrown;
124
- throw new ValueError(`Invalid data for "${collection}"\n${thrown.message}`, { item, caller });
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 (!(thrown instanceof Feedback))
140
+ if (typeof thrown !== "string")
142
141
  throw thrown;
143
- messages.push(getNamedMessage(item.id, thrown.message));
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 (!(thrown instanceof Feedback))
153
+ if (typeof thrown !== "string")
155
154
  throw thrown;
156
- throw new ValueError(`Invalid updates for "${collection}"\n${thrown.message}`, { updates, caller });
155
+ throw new ValueError(`Invalid updates for "${collection}"\n${thrown}`, { updates, caller });
157
156
  }
158
157
  }
@@ -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 `Feedback` if the payload is invalid.
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 `Feedback` if the payload is invalid.
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 `Feedback` if the payload is invalid.
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
  */
@@ -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 `Feedback` if the payload is invalid.
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 instanceof Feedback)
61
- throw new ValueError(`Invalid result for ${this.toString()}:\n${thrown.message}`, {
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 `Feedback` if the payload is invalid.
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 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
- });
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 `Feedback` if the payload is invalid.
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
@@ -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 "./markup/index.js";
11
10
  export * from "./schema/index.js";
12
11
  export * from "./sequence/index.js";
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.171.0",
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);
@@ -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 new ValueFeedback("Must be array", unsafeValue);
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 new ValueFeedback(uniqueArray.length ? `Minimum ${this.min} ${this.many}` : "Required", uniqueArray);
45
+ throw uniqueArray.length ? `Minimum ${this.min} ${this.many}` : "Required";
47
46
  if (uniqueArray.length > this.max)
48
- throw new ValueFeedback(`Maximum ${this.max} ${this.many}`, uniqueArray);
47
+ throw `Maximum ${this.max} ${this.many}`;
49
48
  return uniqueArray;
50
49
  }
51
50
  }
@@ -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 new Feedback("Required");
12
+ throw "Required";
14
13
  return value;
15
14
  }
16
15
  }
@@ -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 new ValueFeedback("Unknown value", unsafeValue);
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);
@@ -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. */
@@ -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 new ValueFeedback("Must be object", unsafeValue);
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
  }
@@ -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 new ValueFeedback(value ? "Invalid date" : "Required", value);
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 new ValueFeedback(`Minimum ${this.format(this.min)}`, rounded);
24
+ throw `Minimum ${this.format(this.min)}`;
26
25
  if (this.max && rounded > this.max)
27
- throw new ValueFeedback(`Maximum ${this.format(this.max)}`, rounded);
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 new ValueFeedback("Must be object", unsafeValue);
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 new ValueFeedback(length ? `Minimum ${this.min} ${this.many}` : "Required", validDictionary);
21
+ throw length ? `Minimum ${this.min} ${this.many}` : "Required";
23
22
  if (length > this.max)
24
- throw new ValueFeedback(`Maximum ${this.max} ${this.many}`, validDictionary);
23
+ throw `Maximum ${this.max} ${this.many}`;
25
24
  return validDictionary;
26
25
  }
27
26
  }
@@ -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 new ValueFeedback("Must be entity", unsafeValue);
16
+ throw "Must be entity";
18
17
  if (this.types && !isArrayItem(this.types, type))
19
- throw new ValueFeedback("Invalid entity type", type);
18
+ throw "Invalid entity type";
20
19
  return entity;
21
20
  }
22
21
  }
@@ -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 new ValueFeedback("Must be file name with extension", unsafeValue);
16
+ throw "Must be file name with extension";
18
17
  if (this.types && !isProp(this.types, extension))
19
- throw new ValueFeedback("Invalid file type", extension);
18
+ throw "Invalid file type";
20
19
  return path;
21
20
  }
22
21
  }
@@ -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 new ValueFeedback("Must be number", unsafeValue);
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 new ValueFeedback(!optionalNumber ? "Required" : `Minimum ${formatNumber(this.min)}`, roundedNumber);
22
+ throw !optionalNumber ? "Required" : `Minimum ${formatNumber(this.min)}`;
24
23
  if (roundedNumber > this.max)
25
- throw new ValueFeedback(`Maximum ${formatNumber(this.max)}`, roundedNumber);
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 `Feedback` if the validated value is falsy. */
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
  }
@@ -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 `Feedback` if the validated value is falsy. */
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 new ValueFeedback("Required", safeValue);
7
+ throw "Required";
9
8
  return safeValue;
10
9
  }
11
10
  }
@@ -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 new ValueFeedback("Must be string", unsafeValue);
38
+ throw "Must be string";
40
39
  const saneString = this.sanitize(possibleString);
41
40
  if (saneString.length < this.min)
42
- throw new ValueFeedback(possibleString.length ? `Minimum ${this.min} characters` : "Required", saneString);
41
+ throw possibleString.length ? `Minimum ${this.min} characters` : "Required";
43
42
  if (saneString.length > this.max)
44
- throw new ValueFeedback(`Maximum ${this.max} characters`, saneString);
43
+ throw `Maximum ${this.max} characters`;
45
44
  if (this.match && !this.match.test(saneString))
46
- throw new ValueFeedback(saneString ? "Invalid format" : "Required", saneString);
45
+ throw saneString ? "Invalid format" : "Required";
47
46
  return saneString;
48
47
  }
49
48
  /** Sanitize the string by removing unwanted characters. */
@@ -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 new ValueFeedback(str ? "Invalid format" : "Required", str);
28
+ throw str ? "Invalid format" : "Required";
30
29
  if (this.schemes && !this.schemes.includes(uri.protocol))
31
- throw new ValueFeedback("Invalid URI scheme", str);
30
+ throw "Invalid URI scheme";
32
31
  return uri.href;
33
32
  }
34
33
  }
@@ -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 new ValueFeedback(str ? "Invalid format" : "Required", str);
31
+ throw str ? "Invalid format" : "Required";
33
32
  if (this.schemes && !this.schemes.includes(url.protocol))
34
- throw new ValueFeedback("Invalid URL scheme", str);
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;
@@ -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))