shelving 1.68.2 → 1.68.3

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.
Files changed (52) hide show
  1. package/feedback/Feedback.js +2 -2
  2. package/firestore/client/FirestoreClientProvider.d.ts +1 -1
  3. package/firestore/client/FirestoreClientProvider.js +7 -3
  4. package/firestore/lite/FirestoreLiteProvider.js +6 -2
  5. package/firestore/server/FirestoreServerProvider.d.ts +1 -1
  6. package/firestore/server/FirestoreServerProvider.js +7 -3
  7. package/package.json +1 -1
  8. package/provider/ThroughProvider.d.ts +4 -2
  9. package/provider/ThroughProvider.js +11 -3
  10. package/provider/ValidationProvider.js +2 -3
  11. package/react/useDocument.js +3 -3
  12. package/react/useQuery.js +3 -3
  13. package/schema/AllowSchema.js +4 -4
  14. package/schema/ArraySchema.js +3 -3
  15. package/schema/DateSchema.js +4 -4
  16. package/schema/NumberSchema.js +2 -2
  17. package/schema/SlugSchema.js +2 -2
  18. package/schema/ThroughSchema.d.ts +4 -2
  19. package/schema/ThroughSchema.js +10 -4
  20. package/update/ArrayUpdate.d.ts +2 -1
  21. package/update/ArrayUpdate.js +22 -3
  22. package/update/DataUpdate.d.ts +3 -2
  23. package/update/DataUpdate.js +42 -2
  24. package/update/Delete.d.ts +11 -0
  25. package/update/Delete.js +15 -0
  26. package/update/Increment.d.ts +5 -5
  27. package/update/Increment.js +5 -5
  28. package/update/ObjectUpdate.d.ts +8 -8
  29. package/update/ObjectUpdate.js +38 -18
  30. package/update/Update.d.ts +4 -1
  31. package/update/Update.js +6 -0
  32. package/update/hydrations.js +3 -1
  33. package/update/index.d.ts +2 -2
  34. package/update/index.js +2 -2
  35. package/util/color.d.ts +6 -2
  36. package/util/color.js +17 -11
  37. package/util/constants.d.ts +0 -2
  38. package/util/constants.js +0 -2
  39. package/util/data.d.ts +6 -6
  40. package/util/data.js +3 -3
  41. package/util/date.d.ts +5 -5
  42. package/util/date.js +19 -19
  43. package/util/number.d.ts +3 -3
  44. package/util/number.js +5 -5
  45. package/util/search.js +2 -2
  46. package/util/string.d.ts +4 -4
  47. package/util/string.js +7 -7
  48. package/util/transform.js +2 -2
  49. package/util/validate.d.ts +9 -0
  50. package/util/validate.js +13 -2
  51. package/update/util.d.ts +0 -7
  52. package/update/util.js +0 -44
@@ -1,5 +1,5 @@
1
1
  import { debug } from "../util/debug.js";
2
- import { toTitle } from "../util/string.js";
2
+ import { getTitle } from "../util/string.js";
3
3
  /**
4
4
  * The `Feedback` class represents a feedback message that should be shown to the user.
5
5
  * - Basic `Feedback` is neither good nor bad, `SuccessFeedback` indicates good news, and `ErrorFeedback` indicates bad news.
@@ -27,7 +27,7 @@ export class Feedback {
27
27
  if (v instanceof Feedback)
28
28
  messages[k] = v.feedback;
29
29
  else
30
- messages[k] = toTitle(v);
30
+ messages[k] = getTitle(v);
31
31
  }
32
32
  return messages;
33
33
  }
@@ -2,9 +2,9 @@ import type { Firestore } from "firebase/firestore";
2
2
  import type { DocumentReference, QueryReference } from "../../db/Reference.js";
3
3
  import type { Data, Entities, OptionalEntity } from "../../util/data.js";
4
4
  import type { Unsubscribe } from "../../observe/Observable.js";
5
+ import { Observer } from "../../observe/Observer.js";
5
6
  import { AsynchronousProvider, Provider } from "../../provider/Provider.js";
6
7
  import { DataUpdate } from "../../update/DataUpdate.js";
7
- import { Observer } from "../../observe/Observer.js";
8
8
  /**
9
9
  * Firestore client database provider.
10
10
  * - Works with the Firebase JS SDK.
@@ -1,11 +1,13 @@
1
1
  import { orderBy as firestoreOrderBy, where as firestoreWhere, limit as firestoreLimit, increment as firestoreIncrement, arrayUnion as firestoreArrayUnion, arrayRemove as firestoreArrayRemove, deleteField as firestoreDeleteField, collection as firestoreCollection, doc as firestoreDocument, query as firestoreQuery, onSnapshot, addDoc, setDoc, updateDoc, deleteDoc, getDoc, getDocs, } from "firebase/firestore";
2
+ import { dispatchError, dispatchNext } from "../../observe/Observer.js";
2
3
  import { UnsupportedError } from "../../error/UnsupportedError.js";
3
4
  import { Provider } from "../../provider/Provider.js";
4
5
  import { ArrayUpdate } from "../../update/ArrayUpdate.js";
5
6
  import { DataUpdate } from "../../update/DataUpdate.js";
6
7
  import { Increment } from "../../update/Increment.js";
7
8
  import { ObjectUpdate } from "../../update/ObjectUpdate.js";
8
- import { dispatchError, dispatchNext } from "../../observe/Observer.js";
9
+ import { Delete } from "../../update/Delete.js";
10
+ import { Update } from "../../update/Update.js";
9
11
  // Constants.
10
12
  // const ID = "__name__"; // DH: `__name__` is the entire path of the document. `__id__` is just ID.
11
13
  const ID = "__id__"; // Internal way Firestore Queries can reference the ID of the current document.
@@ -60,7 +62,9 @@ function getOptionalEntity(snapshot) {
60
62
  /** Convert `Update` instances into corresponding Firestore `FieldValue` instances. */
61
63
  function* yieldFieldValues(updates, prefix = "") {
62
64
  for (const [key, update] of updates) {
63
- if (update === undefined)
65
+ if (!(update instanceof Update))
66
+ yield [`${prefix}${key}`, update];
67
+ else if (update instanceof Delete)
64
68
  yield [`${prefix}${key}`, firestoreDeleteField()];
65
69
  else if (update instanceof Increment)
66
70
  yield [`${prefix}${key}`, firestoreIncrement(update.amount)];
@@ -75,7 +79,7 @@ function* yieldFieldValues(updates, prefix = "") {
75
79
  yield [`${prefix}${key}`, firestoreArrayRemove(...update.deletes)];
76
80
  }
77
81
  else
78
- yield [`${prefix}${key}`, update];
82
+ yield [`${prefix}${key}`, update.transform()];
79
83
  }
80
84
  }
81
85
  /**
@@ -5,6 +5,8 @@ import { ArrayUpdate } from "../../update/ArrayUpdate.js";
5
5
  import { DataUpdate } from "../../update/DataUpdate.js";
6
6
  import { Increment } from "../../update/Increment.js";
7
7
  import { ObjectUpdate } from "../../update/ObjectUpdate.js";
8
+ import { Update } from "../../update/Update.js";
9
+ import { Delete } from "../../update/Delete.js";
8
10
  // Constants.
9
11
  // const ID = "__name__"; // DH: `__name__` is the entire path of the document. `__id__` is just ID.
10
12
  const ID = "__id__"; // Internal way Firestore Queries can reference the ID of the current document.
@@ -59,7 +61,9 @@ function getOptionalData(snapshot) {
59
61
  /** Convert `Update` instances into corresponding Firestore `FieldValue` instances. */
60
62
  function* yieldFieldValues(updates, prefix = "") {
61
63
  for (const [key, update] of updates) {
62
- if (update === undefined)
64
+ if (!(update instanceof Update))
65
+ yield [`${prefix}${key}`, update];
66
+ else if (update instanceof Delete)
63
67
  yield [`${prefix}${key}`, firestoreDeleteField()];
64
68
  else if (update instanceof Increment)
65
69
  yield [`${prefix}${key}`, firestoreIncrement(update.amount)];
@@ -74,7 +78,7 @@ function* yieldFieldValues(updates, prefix = "") {
74
78
  yield [`${prefix}${key}`, firestoreArrayRemove(...update.deletes)];
75
79
  }
76
80
  else
77
- yield [`${prefix}${key}`, update];
81
+ yield [`${prefix}${key}`, update.transform()];
78
82
  }
79
83
  }
80
84
  /**
@@ -2,9 +2,9 @@ import { Firestore } from "@google-cloud/firestore";
2
2
  import type { DocumentReference, QueryReference } from "../../db/Reference.js";
3
3
  import type { Data, Entities, OptionalEntity } from "../../util/data.js";
4
4
  import type { Unsubscribe } from "../../observe/Observable.js";
5
+ import { Observer } from "../../observe/Observer.js";
5
6
  import { AsynchronousProvider, Provider } from "../../provider/Provider.js";
6
7
  import { DataUpdate } from "../../update/DataUpdate.js";
7
- import { Observer } from "../../observe/Observer.js";
8
8
  /**
9
9
  * Firestore server database provider.
10
10
  * - Works with the Firebase Admin SDK for Node.JS
@@ -1,11 +1,13 @@
1
1
  import { Firestore, FieldValue } from "@google-cloud/firestore";
2
+ import { dispatchError, dispatchNext } from "../../observe/Observer.js";
2
3
  import { UnsupportedError } from "../../error/UnsupportedError.js";
3
4
  import { Provider } from "../../provider/Provider.js";
4
5
  import { ArrayUpdate } from "../../update/ArrayUpdate.js";
5
6
  import { DataUpdate } from "../../update/DataUpdate.js";
6
7
  import { Increment } from "../../update/Increment.js";
7
8
  import { ObjectUpdate } from "../../update/ObjectUpdate.js";
8
- import { dispatchError, dispatchNext } from "../../observe/Observer.js";
9
+ import { Delete } from "../../update/Delete.js";
10
+ import { Update } from "../../update/Update.js";
9
11
  // Constants.
10
12
  // const ID = "__name__"; // DH: `__name__` is the entire path of the document. `__id__` is just ID.
11
13
  const ID = "__id__"; // Internal way Firestore Queries can reference the ID of the current document.
@@ -60,7 +62,9 @@ function getOptionalEntity(snapshot) {
60
62
  /** Convert `Update` instances into corresponding Firestore `FieldValue` instances. */
61
63
  function* yieldFieldValues(updates, prefix = "") {
62
64
  for (const [key, update] of updates) {
63
- if (update === undefined)
65
+ if (!(update instanceof Update))
66
+ yield [`${prefix}${key}`, update];
67
+ else if (update instanceof Delete)
64
68
  yield [`${prefix}${key}`, FieldValue.delete()];
65
69
  else if (update instanceof Increment)
66
70
  yield [`${prefix}${key}`, FieldValue.increment(update.amount)];
@@ -75,7 +79,7 @@ function* yieldFieldValues(updates, prefix = "") {
75
79
  yield [`${prefix}${key}`, FieldValue.arrayRemove(...update.deletes)];
76
80
  }
77
81
  else
78
- yield [`${prefix}${key}`, update];
82
+ yield [`${prefix}${key}`, update.transform()];
79
83
  }
80
84
  }
81
85
  /**
package/package.json CHANGED
@@ -11,7 +11,7 @@
11
11
  "state-management",
12
12
  "query-builder"
13
13
  ],
14
- "version": "1.68.2",
14
+ "version": "1.68.3",
15
15
  "repository": "https://github.com/dhoulb/shelving",
16
16
  "author": "Dave Houlbrooke <dave@shax.com>",
17
17
  "license": "0BSD",
@@ -31,5 +31,7 @@ export interface SynchronousThroughProvider extends SynchronousProvider {
31
31
  export interface AsynchronousThroughProvider extends AsynchronousProvider {
32
32
  new (source: AsynchronousProvider): AsynchronousProvider;
33
33
  }
34
- /** Find a specific source provider in a database's provider stack. */
35
- export declare function findSourceProvider<P extends Provider>(provider: Provider, type: Class<P>): P | undefined;
34
+ /** Find a possible source provider in a database's provider stack (if it exists). */
35
+ export declare function getOptionalSourceProvider<P extends Provider>(provider: Provider, type: Class<P>): P | undefined;
36
+ /** Find a source provider in a database's provider stack. */
37
+ export declare function getSourceProvider<P extends Provider>(provider: Provider, type: Class<P>): P;
@@ -1,3 +1,4 @@
1
+ import { AssertionError } from "../error/AssertionError.js";
1
2
  import { Provider } from "./Provider.js";
2
3
  /**
3
4
  * Pass all reads and writes through to a source provider.
@@ -41,10 +42,17 @@ export class ThroughProvider extends Provider {
41
42
  return this.source.deleteQuery(ref);
42
43
  }
43
44
  }
44
- /** Find a specific source provider in a database's provider stack. */
45
- export function findSourceProvider(provider, type) {
45
+ /** Find a possible source provider in a database's provider stack (if it exists). */
46
+ export function getOptionalSourceProvider(provider, type) {
46
47
  if (provider instanceof type)
47
48
  return provider;
48
49
  if (provider instanceof ThroughProvider)
49
- return findSourceProvider(provider.source, type);
50
+ return getSourceProvider(provider.source, type);
51
+ }
52
+ /** Find a source provider in a database's provider stack. */
53
+ export function getSourceProvider(provider, type) {
54
+ const source = getOptionalSourceProvider(provider, type);
55
+ if (!source)
56
+ throw new AssertionError(`Source provider ${type.name} not found`, provider);
57
+ return source;
50
58
  }
@@ -1,6 +1,5 @@
1
1
  import { callAsync } from "../util/async.js";
2
2
  import { validate } from "../util/validate.js";
3
- import { validateUpdate } from "../update/util.js";
4
3
  import { Feedback } from "../feedback/Feedback.js";
5
4
  import { ValidationError } from "../error/ValidationError.js";
6
5
  import { InvalidFeedback } from "../feedback/InvalidFeedback.js";
@@ -21,7 +20,7 @@ export class ValidationProvider extends ThroughProvider {
21
20
  return super.setDocument(ref, validate(value, ref.validator));
22
21
  }
23
22
  updateDocument(ref, update) {
24
- return super.updateDocument(ref, validateUpdate(update, ref.validator));
23
+ return super.updateDocument(ref, update.validate(ref.validator));
25
24
  }
26
25
  getQuery(ref) {
27
26
  return callAsync(_validateEntities, super.getQuery(ref), ref);
@@ -33,7 +32,7 @@ export class ValidationProvider extends ThroughProvider {
33
32
  return super.setQuery(ref, validate(value, ref.validator));
34
33
  }
35
34
  updateQuery(ref, update) {
36
- return super.updateQuery(ref, validateUpdate(update, ref.validator));
35
+ return super.updateQuery(ref, update.validate(ref.validator));
37
36
  }
38
37
  }
39
38
  /** Validate an entity for a document reference. */
@@ -1,7 +1,7 @@
1
1
  import { reduceMapItem } from "../util/map.js";
2
2
  import { getDocumentData } from "../db/Reference.js";
3
3
  import { CacheProvider } from "../provider/CacheProvider.js";
4
- import { findSourceProvider } from "../provider/ThroughProvider.js";
4
+ import { getOptionalSourceProvider } from "../provider/ThroughProvider.js";
5
5
  import { State } from "../state/State.js";
6
6
  import { BooleanState } from "../state/BooleanState.js";
7
7
  import { ConditionError } from "../error/ConditionError.js";
@@ -12,7 +12,7 @@ import { useCache } from "./useCache.js";
12
12
  export class DocumentState extends State {
13
13
  constructor(ref) {
14
14
  var _a;
15
- const table = (_a = findSourceProvider(ref.db.provider, CacheProvider)) === null || _a === void 0 ? void 0 : _a.memory.getTable(ref);
15
+ const table = (_a = getOptionalSourceProvider(ref.db.provider, CacheProvider)) === null || _a === void 0 ? void 0 : _a.memory.getTable(ref);
16
16
  const time = table ? table.getDocumentTime(ref.id) : null;
17
17
  const isCached = typeof time === "number";
18
18
  super(table && isCached ? table.getDocument(ref.id) : NOVALUE);
@@ -63,7 +63,7 @@ export class DocumentState extends State {
63
63
  /** Subscribe this state to any `CacheProvider` that exists in the provider chain. */
64
64
  connectCache() {
65
65
  var _a;
66
- const table = (_a = findSourceProvider(this.ref.db.provider, CacheProvider)) === null || _a === void 0 ? void 0 : _a.memory.getTable(this.ref);
66
+ const table = (_a = getOptionalSourceProvider(this.ref.db.provider, CacheProvider)) === null || _a === void 0 ? void 0 : _a.memory.getTable(this.ref);
67
67
  table && this.connect(() => table.subscribeCachedDocument(this.ref.id, this));
68
68
  }
69
69
  // Override to subscribe to the cache when an observer is added.
package/react/useQuery.js CHANGED
@@ -1,7 +1,7 @@
1
1
  import { reduceMapItem } from "../util/map.js";
2
2
  import { getQueryFirstData, getQueryFirstValue } from "../db/Reference.js";
3
3
  import { CacheProvider } from "../provider/CacheProvider.js";
4
- import { findSourceProvider } from "../provider/ThroughProvider.js";
4
+ import { getOptionalSourceProvider } from "../provider/ThroughProvider.js";
5
5
  import { State } from "../state/State.js";
6
6
  import { ConditionError } from "../error/ConditionError.js";
7
7
  import { BooleanState } from "../state/BooleanState.js";
@@ -12,7 +12,7 @@ import { useCache } from "./useCache.js";
12
12
  export class QueryState extends State {
13
13
  constructor(ref) {
14
14
  var _a, _b;
15
- const table = (_a = findSourceProvider(ref.db.provider, CacheProvider)) === null || _a === void 0 ? void 0 : _a.memory.getTable(ref);
15
+ const table = (_a = getOptionalSourceProvider(ref.db.provider, CacheProvider)) === null || _a === void 0 ? void 0 : _a.memory.getTable(ref);
16
16
  const time = table ? table.getQueryTime(ref) : null;
17
17
  const isCached = typeof time === "number";
18
18
  super(table && isCached ? table.getQuery(ref) : NOVALUE);
@@ -96,7 +96,7 @@ export class QueryState extends State {
96
96
  /** Subscribe this state to any `CacheProvider` that exists in the provider chain. */
97
97
  connectCache() {
98
98
  var _a;
99
- const table = (_a = findSourceProvider(this.ref.db.provider, CacheProvider)) === null || _a === void 0 ? void 0 : _a.memory.getTable(this.ref);
99
+ const table = (_a = getOptionalSourceProvider(this.ref.db.provider, CacheProvider)) === null || _a === void 0 ? void 0 : _a.memory.getTable(this.ref);
100
100
  return table && this.connect(() => table.subscribeCachedQuery(this.ref, this));
101
101
  }
102
102
  // Override to subscribe to the cache when an observer is added.
@@ -1,7 +1,7 @@
1
1
  import { InvalidFeedback } from "../feedback/InvalidFeedback.js";
2
- import { toString } from "../util/string.js";
2
+ import { getString } from "../util/string.js";
3
3
  import { isItem } from "../util/array.js";
4
- import { toNumber } from "../util/number.js";
4
+ import { getOptionalNumber } from "../util/number.js";
5
5
  import { isKey } from "../util/object.js";
6
6
  import { Schema } from "./Schema.js";
7
7
  /** Validate a value against a specific set of allowed values. */
@@ -27,13 +27,13 @@ export class AllowSchema extends Schema {
27
27
  /** Define a valid string from an allowed set of strings. */
28
28
  export class AllowStringSchema extends AllowSchema {
29
29
  validate(unsafeValue = this.value) {
30
- return validateAllowed(toString(unsafeValue), this.allow);
30
+ return validateAllowed(getString(unsafeValue), this.allow);
31
31
  }
32
32
  }
33
33
  /** Define a valid number from an allowed set of numbers. */
34
34
  export class AllowNumberSchema extends AllowSchema {
35
35
  validate(unsafeValue = this.value) {
36
- return validateAllowed(toNumber(unsafeValue), this.allow);
36
+ return validateAllowed(getOptionalNumber(unsafeValue), this.allow);
37
37
  }
38
38
  }
39
39
  /** Valid string from an allowed set of strings. */
@@ -1,6 +1,6 @@
1
1
  import { InvalidFeedback } from "../feedback/InvalidFeedback.js";
2
- import { getArray, uniqueArray } from "../util/array.js";
3
- import { validateItems } from "../util/validate.js";
2
+ import { uniqueArray } from "../util/array.js";
3
+ import { validateArray } from "../util/validate.js";
4
4
  import { Schema } from "./Schema.js";
5
5
  /**
6
6
  * Define a valid array.
@@ -41,7 +41,7 @@ export class ArraySchema extends Schema {
41
41
  validate(unsafeValue = this.value) {
42
42
  if (!(unsafeValue instanceof Array))
43
43
  throw new InvalidFeedback("Must be array", { value: unsafeValue });
44
- const safeArray = getArray(validateItems(unsafeValue, this.items));
44
+ const safeArray = validateArray(unsafeValue, this.items);
45
45
  const dedupedArray = this.unique ? uniqueArray(safeArray) : safeArray;
46
46
  if (typeof this.min === "number" && dedupedArray.length < this.min)
47
47
  throw new InvalidFeedback(dedupedArray.length ? `Minimum ${this.min} items` : "Required", { value: dedupedArray });
@@ -1,4 +1,4 @@
1
- import { toDate, getYmd } from "../util/date.js";
1
+ import { getOptionalDate, getYmd } from "../util/date.js";
2
2
  import { InvalidFeedback } from "../feedback/InvalidFeedback.js";
3
3
  import { Schema } from "./Schema.js";
4
4
  import { OPTIONAL } from "./OptionalSchema.js";
@@ -11,13 +11,13 @@ export class DateSchema extends Schema {
11
11
  this.max = max;
12
12
  }
13
13
  validate(unsafeValue = this.value) {
14
- const date = toDate(unsafeValue);
14
+ const date = getOptionalDate(unsafeValue);
15
15
  if (!date)
16
16
  throw new InvalidFeedback(unsafeValue ? "Invalid date" : "Required", { value: unsafeValue });
17
- const minDate = toDate(this.min);
17
+ const minDate = getOptionalDate(this.min);
18
18
  if (minDate && date.getTime() < minDate.getTime())
19
19
  throw new InvalidFeedback(`Minimum ${minDate.toLocaleDateString()}`, { value: date });
20
- const maxDate = toDate(this.max);
20
+ const maxDate = getOptionalDate(this.max);
21
21
  if (maxDate && date.getTime() > maxDate.getTime())
22
22
  throw new InvalidFeedback(`Maximum ${maxDate.toLocaleDateString()}`, { value: date });
23
23
  return getYmd(date);
@@ -1,4 +1,4 @@
1
- import { toNumber, roundStep } from "../util/number.js";
1
+ import { getOptionalNumber, roundStep } from "../util/number.js";
2
2
  import { InvalidFeedback } from "../feedback/InvalidFeedback.js";
3
3
  import { Schema } from "./Schema.js";
4
4
  import { OPTIONAL } from "./OptionalSchema.js";
@@ -12,7 +12,7 @@ export class NumberSchema extends Schema {
12
12
  this.step = step;
13
13
  }
14
14
  validate(unsafeValue = this.value) {
15
- const unsafeNumber = toNumber(unsafeValue);
15
+ const unsafeNumber = getOptionalNumber(unsafeValue);
16
16
  if (typeof unsafeNumber !== "number")
17
17
  throw new InvalidFeedback("Must be number", { value: unsafeValue });
18
18
  const safeNumber = typeof this.step === "number" ? roundStep(unsafeNumber, this.step) : unsafeNumber;
@@ -1,4 +1,4 @@
1
- import { toSlug } from "../util/string.js";
1
+ import { getSlug } from "../util/string.js";
2
2
  import { OPTIONAL } from "./OptionalSchema.js";
3
3
  import { StringSchema } from "./StringSchema.js";
4
4
  /**
@@ -16,7 +16,7 @@ export class SlugSchema extends StringSchema {
16
16
  this.max = 32;
17
17
  }
18
18
  sanitize(unsafeString) {
19
- return toSlug(unsafeString);
19
+ return getSlug(unsafeString);
20
20
  }
21
21
  }
22
22
  /** Valid slug, e.g. `this-is-a-slug` */
@@ -7,5 +7,7 @@ export declare abstract class ThroughSchema<T> extends Schema<T> {
7
7
  });
8
8
  validate(unsafeValue: unknown): T;
9
9
  }
10
- /** Find a specific source schema in a schema. */
11
- export declare function findSourceSchema<X extends Schema>(schema: Schema, type: Class<X>): X;
10
+ /** Find a possible source schema in a schema (if it exists). */
11
+ export declare function getOptionalSourceSchema<X extends Schema>(schema: Schema, type: Class<X>): X | undefined;
12
+ /** Find a source schema in a schema. */
13
+ export declare function getSourceSchema<X extends Schema>(schema: Schema, type: Class<X>): X;
@@ -9,11 +9,17 @@ export class ThroughSchema extends Schema {
9
9
  return this.source.validate(unsafeValue);
10
10
  }
11
11
  }
12
- /** Find a specific source schema in a schema. */
13
- export function findSourceSchema(schema, type) {
12
+ /** Find a possible source schema in a schema (if it exists). */
13
+ export function getOptionalSourceSchema(schema, type) {
14
14
  if (schema instanceof type)
15
15
  return schema;
16
16
  if (schema instanceof ThroughSchema)
17
- return findSourceSchema(schema.source, type);
18
- throw new AssertionError(`Source schema "${type.name}" not found`, schema);
17
+ return getSourceSchema(schema.source, type);
18
+ }
19
+ /** Find a source schema in a schema. */
20
+ export function getSourceSchema(schema, type) {
21
+ const source = getOptionalSourceSchema(schema, type);
22
+ if (!source)
23
+ throw new AssertionError(`Source schema "${type.name}" not found`, schema);
24
+ return source;
19
25
  }
@@ -1,4 +1,5 @@
1
1
  import { ImmutableArray } from "../util/array.js";
2
+ import { Validator } from "../util/validate.js";
2
3
  import { Update } from "./Update.js";
3
4
  /** Update that can be applied to an array to add/remove items. */
4
5
  export declare class ArrayUpdate<T> extends Update<ImmutableArray<T>> {
@@ -9,8 +10,8 @@ export declare class ArrayUpdate<T> extends Update<ImmutableArray<T>> {
9
10
  readonly adds: ImmutableArray<T>;
10
11
  readonly deletes: ImmutableArray<T>;
11
12
  constructor(adds?: ImmutableArray<T>, deletes?: ImmutableArray<T>);
12
- /** Transform an array using this array update. */
13
13
  transform(arr?: ImmutableArray<T>): ImmutableArray<T>;
14
+ validate(validator: Validator<ImmutableArray<T>>): this;
14
15
  /** Return an array update with an additional item marked for addition. */
15
16
  with(...adds: T[]): this;
16
17
  /** Return an array update with an additional item marked for deletion. */
@@ -1,4 +1,6 @@
1
+ import { ArraySchema } from "../schema/ArraySchema.js";
1
2
  import { withItems, withoutItems } from "../util/array.js";
3
+ import { validateArray } from "../util/validate.js";
2
4
  import { Update } from "./Update.js";
3
5
  /** Update that can be applied to an array to add/remove items. */
4
6
  export class ArrayUpdate extends Update {
@@ -15,16 +17,33 @@ export class ArrayUpdate extends Update {
15
17
  static without(...deletes) {
16
18
  return new ArrayUpdate([], deletes);
17
19
  }
18
- /** Transform an array using this array update. */
19
20
  transform(arr = []) {
20
21
  return withoutItems(withItems(arr, this.adds), this.deletes);
21
22
  }
23
+ validate(validator) {
24
+ if (!(validator instanceof ArraySchema))
25
+ return super.validate(validator);
26
+ return {
27
+ __proto__: Object.getPrototypeOf(this),
28
+ ...this,
29
+ adds: validateArray(this.adds, validator.items),
30
+ deletes: validateArray(this.deletes, validator.items),
31
+ };
32
+ }
22
33
  /** Return an array update with an additional item marked for addition. */
23
34
  with(...adds) {
24
- return { __proto__: Object.getPrototypeOf(this), ...this, adds: [...this.adds, ...adds] };
35
+ return {
36
+ __proto__: Object.getPrototypeOf(this),
37
+ ...this,
38
+ adds: [...this.adds, ...adds],
39
+ };
25
40
  }
26
41
  /** Return an array update with an additional item marked for deletion. */
27
42
  without(...deletes) {
28
- return { __proto__: Object.getPrototypeOf(this), ...this, deletes: [...this.deletes, ...deletes] };
43
+ return {
44
+ __proto__: Object.getPrototypeOf(this),
45
+ ...this,
46
+ deletes: [...this.deletes, ...deletes],
47
+ };
29
48
  }
30
49
  }
@@ -1,6 +1,7 @@
1
- import type { Data, Key, Prop } from "../util/data.js";
1
+ import { Data, Key, Prop } from "../util/data.js";
2
2
  import { Nullish } from "../util/null.js";
3
3
  import { Transformable } from "../util/transform.js";
4
+ import { Validator } from "../util/validate.js";
4
5
  import { Update } from "./Update.js";
5
6
  /**
6
7
  * Set of named transforms for the props of a data object.
@@ -18,8 +19,8 @@ export declare class DataUpdate<T extends Data> extends Update<T> implements Ite
18
19
  static with<X extends Data, K extends Key<X>>(key: Nullish<K>, value: X[K] | Update<X[K]>): DataUpdate<X>;
19
20
  readonly updates: PropUpdates<T>;
20
21
  constructor(props: PropUpdates<T>);
21
- /** Transform a a data object using this update. */
22
22
  transform(data: T): T;
23
+ validate(validator: Validator<T>): this;
23
24
  /** Return a data update with a specific prop marked for update. */
24
25
  with<K extends Key<T>>(key: Nullish<K>, value: T[K] | Update<T[K]>): this;
25
26
  /** Iterate over the transforms in this object. */
@@ -1,5 +1,10 @@
1
+ import { Feedback } from "../feedback/Feedback.js";
2
+ import { InvalidFeedback } from "../feedback/InvalidFeedback.js";
3
+ import { DataSchema } from "../schema/DataSchema.js";
4
+ import { getProps } from "../util/data.js";
1
5
  import { isNullish } from "../util/null.js";
2
6
  import { transformData } from "../util/transform.js";
7
+ import { validate } from "../util/validate.js";
3
8
  import { Update } from "./Update.js";
4
9
  /** Update that can be applied to a data object to update its props. */
5
10
  export class DataUpdate extends Update {
@@ -11,18 +16,53 @@ export class DataUpdate extends Update {
11
16
  static with(key, value) {
12
17
  return new DataUpdate(!isNullish(key) ? { [key]: value } : {});
13
18
  }
14
- /** Transform a a data object using this update. */
15
19
  transform(data) {
16
20
  return transformData(data, this.updates);
17
21
  }
22
+ validate(validator) {
23
+ if (!(validator instanceof DataSchema))
24
+ return super.validate(validator);
25
+ return {
26
+ __proto__: Object.getPrototypeOf(this),
27
+ ...this,
28
+ updates: Object.fromEntries(_validateUpdates(this.updates, validator.props)),
29
+ };
30
+ }
18
31
  /** Return a data update with a specific prop marked for update. */
19
32
  with(key, value) {
20
33
  if (isNullish(key))
21
34
  return this;
22
- return { __proto__: Object.getPrototypeOf(this), ...this, updates: { ...this.updates, [key]: value } };
35
+ return {
36
+ __proto__: Object.getPrototypeOf(this),
37
+ ...this,
38
+ updates: { ...this.updates, [key]: value },
39
+ };
23
40
  }
24
41
  /** Iterate over the transforms in this object. */
25
42
  [Symbol.iterator]() {
26
43
  return Object.entries(this.updates).values();
27
44
  }
28
45
  }
46
+ /** Validate a set of transforms against a set of validators. */
47
+ function* _validateUpdates(unsafeUpdates, validators) {
48
+ let invalid = false;
49
+ const details = {};
50
+ for (const [k, validator] of getProps(validators)) {
51
+ const unsafeUpdate = unsafeUpdates[k];
52
+ if (unsafeUpdate !== undefined) {
53
+ try {
54
+ yield [k, unsafeUpdate instanceof Update ? unsafeUpdate.validate(validator) : validate(unsafeUpdate, validator)];
55
+ }
56
+ catch (thrown) {
57
+ if (thrown instanceof Feedback) {
58
+ invalid = true;
59
+ details[k] = thrown;
60
+ }
61
+ else
62
+ throw thrown;
63
+ }
64
+ }
65
+ }
66
+ if (invalid)
67
+ throw new InvalidFeedback("Invalid updates", details);
68
+ }
@@ -0,0 +1,11 @@
1
+ import { Update } from "./Update.js";
2
+ /**
3
+ * Delete update: an object that deletes a value.
4
+ * - Hint: you can use negative numbers to decrement the number too!
5
+ */
6
+ export declare class Delete extends Update<undefined> {
7
+ transform(): undefined;
8
+ validate(): this;
9
+ }
10
+ /** Update that deletes any value. */
11
+ export declare const DELETE: Delete;
@@ -0,0 +1,15 @@
1
+ import { Update } from "./Update.js";
2
+ /**
3
+ * Delete update: an object that deletes a value.
4
+ * - Hint: you can use negative numbers to decrement the number too!
5
+ */
6
+ export class Delete extends Update {
7
+ transform() {
8
+ return undefined;
9
+ }
10
+ validate() {
11
+ return this;
12
+ }
13
+ }
14
+ /** Update that deletes any value. */
15
+ export const DELETE = new Delete();
@@ -1,6 +1,6 @@
1
1
  import { Update } from "./Update.js";
2
2
  /**
3
- * Increment transform: an object that increments/decrements a value.
3
+ * Increment update: an object that increments/decrements a value.
4
4
  * - Hint: you can use negative numbers to decrement the number too!
5
5
  */
6
6
  export declare class Increment extends Update<number> {
@@ -8,11 +8,11 @@ export declare class Increment extends Update<number> {
8
8
  constructor(amount: number);
9
9
  transform(existing?: unknown): number;
10
10
  }
11
- /** Transform that increments a value by one. */
11
+ /** Update that increments a value by one. */
12
12
  export declare const INCREMENT: Increment;
13
- /** Transform that increments a value by a specific amount. */
13
+ /** Update that increments a value by a specific amount. */
14
14
  export declare const INCREMENT_BY: (amount: number) => Increment;
15
- /** Transform that decrements a value by one. */
15
+ /** Update that decrements a value by one. */
16
16
  export declare const DECREMENT: Increment;
17
- /** Transform that decrements a value by a specific amount. */
17
+ /** Update that decrements a value by a specific amount. */
18
18
  export declare const DECREMENT_BY: (amount: number) => Increment;
@@ -1,6 +1,6 @@
1
1
  import { Update } from "./Update.js";
2
2
  /**
3
- * Increment transform: an object that increments/decrements a value.
3
+ * Increment update: an object that increments/decrements a value.
4
4
  * - Hint: you can use negative numbers to decrement the number too!
5
5
  */
6
6
  export class Increment extends Update {
@@ -12,11 +12,11 @@ export class Increment extends Update {
12
12
  return typeof existing === "number" ? existing + this.amount : this.amount;
13
13
  }
14
14
  }
15
- /** Transform that increments a value by one. */
15
+ /** Update that increments a value by one. */
16
16
  export const INCREMENT = new Increment(1);
17
- /** Transform that increments a value by a specific amount. */
17
+ /** Update that increments a value by a specific amount. */
18
18
  export const INCREMENT_BY = (amount) => new Increment(amount);
19
- /** Transform that decrements a value by one. */
19
+ /** Update that decrements a value by one. */
20
20
  export const DECREMENT = new Increment(-1);
21
- /** Transform that decrements a value by a specific amount. */
21
+ /** Update that decrements a value by a specific amount. */
22
22
  export const DECREMENT_BY = (amount) => new Increment(0 - amount);