shelving 1.159.4 → 1.159.5

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,4 +1,4 @@
1
- import type { DataSchema, DataSchemas } from "../schema/DataSchema.js";
1
+ import { type DataSchema, type DataSchemas } from "../schema/DataSchema.js";
2
2
  import type { Schema } from "../schema/Schema.js";
3
3
  import type { Database, DataKey } from "../util/data.js";
4
4
  import type { Identifier, Items, OptionalItem } from "../util/item.js";
@@ -1,7 +1,7 @@
1
1
  import { ValueError } from "../error/ValueError.js";
2
2
  import { Feedback } from "../feedback/Feedback.js";
3
- import { getNamedMessage } from "../index.js";
4
- import { updateData } from "../util/update.js";
3
+ import { PARTIAL } from "../schema/DataSchema.js";
4
+ import { getNamedMessage } from "../util/error.js";
5
5
  import { validateData } from "../util/validate.js";
6
6
  import { AsyncThroughProvider, ThroughProvider } from "./ThroughProvider.js";
7
7
  /** Validate a synchronous source provider (source can have any type because validation guarantees the type). */
@@ -32,8 +32,7 @@ export class ValidationProvider extends ThroughProvider {
32
32
  super.setItem(collection, id, validateData(data, this.getSchema(collection).props));
33
33
  }
34
34
  updateItem(collection, id, updates) {
35
- validateData(updateData({}, updates), this.getSchema(collection).props, true);
36
- super.updateItem(collection, id, updates);
35
+ super.updateItem(collection, id, _validateUpdates(collection, updates, this.getSchema(collection), this.updateItem));
37
36
  }
38
37
  deleteItem(collection, id) {
39
38
  super.deleteItem(collection, id);
@@ -48,8 +47,7 @@ export class ValidationProvider extends ThroughProvider {
48
47
  super.setQuery(collection, query, this.getSchema(collection).validate(data));
49
48
  }
50
49
  updateQuery(collection, query, updates) {
51
- validateData(updateData({}, updates), this.getSchema(collection).props, true);
52
- super.updateQuery(collection, query, updates);
50
+ super.updateQuery(collection, query, _validateUpdates(collection, updates, this.getSchema(collection), this.updateQuery));
53
51
  }
54
52
  deleteQuery(collection, query) {
55
53
  super.deleteQuery(collection, query);
@@ -88,8 +86,7 @@ export class AsyncValidationProvider extends AsyncThroughProvider {
88
86
  return super.setItem(collection, id, validateData(data, this.getSchema(collection).props));
89
87
  }
90
88
  updateItem(collection, id, updates) {
91
- validateData(updateData({}, updates), this.getSchema(collection).props, true);
92
- return super.updateItem(collection, id, updates);
89
+ return super.updateItem(collection, id, _validateUpdates(collection, updates, this.getSchema(collection), this.updateItem));
93
90
  }
94
91
  deleteItem(collection, id) {
95
92
  return super.deleteItem(collection, id);
@@ -109,8 +106,7 @@ export class AsyncValidationProvider extends AsyncThroughProvider {
109
106
  return super.setQuery(collection, query, validateData(data, this.getSchema(collection).props));
110
107
  }
111
108
  updateQuery(collection, query, updates) {
112
- validateData(updateData({}, updates), this.getSchema(collection).props, true);
113
- return super.updateQuery(collection, query, updates);
109
+ return super.updateQuery(collection, query, _validateUpdates(collection, updates, this.getSchema(collection), this.updateQuery));
114
110
  }
115
111
  deleteQuery(collection, query) {
116
112
  return super.deleteQuery(collection, query);
@@ -150,3 +146,13 @@ function* _yieldValidItems(collection, items, validators, caller) {
150
146
  if (messages.length)
151
147
  throw new ValueError(`Invalid data for "${collection}"\n${messages.join("\n")}`, { items, caller });
152
148
  }
149
+ function _validateUpdates(collection, updates, schema, caller) {
150
+ try {
151
+ return validateData(updates, PARTIAL(schema).props);
152
+ }
153
+ catch (thrown) {
154
+ if (!(thrown instanceof Feedback))
155
+ throw thrown;
156
+ throw new ValueError(`Invalid updates for "${collection}"\n${thrown.message}`, { updates, caller });
157
+ }
158
+ }
package/package.json CHANGED
@@ -11,7 +11,7 @@
11
11
  "state-management",
12
12
  "query-builder"
13
13
  ],
14
- "version": "1.159.4",
14
+ "version": "1.159.5",
15
15
  "repository": "https://github.com/dhoulb/shelving",
16
16
  "author": "Dave Houlbrooke <dave@shax.com>",
17
17
  "license": "0BSD",
@@ -4,7 +4,6 @@ import type { Constructor } from "./class.js";
4
4
  import type { Data } from "./data.js";
5
5
  import type { ImmutableDictionary } from "./dictionary.js";
6
6
  import type { AnyCaller } from "./function.js";
7
- import type { DeepPartial } from "./object.js";
8
7
  /** Object that can validate an unknown value with its `validate()` method. */
9
8
  export interface Validator<T> {
10
9
  /**
@@ -57,10 +56,7 @@ export declare function validateDictionary<T>(unsafeDictionary: ImmutableDiction
57
56
  * - `undefined` props in the object will be set to the default value of that prop.
58
57
  * - `undefined` props after validation will not be set in the output object.
59
58
  *
60
- * @param partial Whether we're validating a partial match or not. This allows props to be missing without error.
61
- *
62
59
  * @return Valid object.
63
60
  * @throw Feedback if one or more props did not validate.
64
61
  */
65
- export declare function validateData<T extends Data>(unsafeData: Data, validators: Validators<T>, partial: true): DeepPartial<T>;
66
- export declare function validateData<T extends Data>(unsafeData: Data, validators: Validators<T>, partial?: false): T;
62
+ export declare function validateData<T extends Data>(unsafeData: Data, validators: Validators<T>): T;
package/util/validate.js CHANGED
@@ -1,7 +1,7 @@
1
1
  import { ValueError } from "../error/ValueError.js";
2
2
  import { Feedback, ValueFeedback } from "../feedback/Feedback.js";
3
3
  import { isArray } from "./array.js";
4
- import { getDataKeys, getDataProps } from "./data.js";
4
+ import { getDataProps } from "./data.js";
5
5
  import { getDictionaryItems } from "./dictionary.js";
6
6
  import { getNamedMessage } from "./error.js";
7
7
  import { isIterable } from "./iterate.js";
@@ -96,39 +96,50 @@ export function validateDictionary(unsafeDictionary, validator) {
96
96
  throw new ValueFeedback(messages.join("\n"), unsafeDictionary);
97
97
  return changed || isIterable(unsafeDictionary) ? safeDictionary : unsafeDictionary;
98
98
  }
99
- /** Keep track of whether we're doing a deep-partial match or not. */
100
- let isDeeplyPartial = false;
101
- export function validateData(unsafeData, validators, partial = isDeeplyPartial) {
102
- let changed = false;
99
+ /**
100
+ * Validate a data object with a set of validators.
101
+ * - Defined props in the object will be validated against the corresponding validator.
102
+ * - `undefined` props in the object will be set to the default value of that prop.
103
+ * - `undefined` props after validation will not be set in the output object.
104
+ *
105
+ * @return Valid object.
106
+ * @throw Feedback if one or more props did not validate.
107
+ */
108
+ export function validateData(unsafeData, validators) {
109
+ let changes = 0;
103
110
  const safeData = {};
104
111
  const messages = [];
105
- try {
106
- isDeeplyPartial = partial;
107
- const props = getDataProps(validators);
108
- for (const [key, validator] of props) {
109
- const unsafeValue = unsafeData[key];
110
- if (unsafeValue === undefined && partial)
111
- continue; // Silently skip `undefined` props if in partial mode.
112
- try {
113
- const safeValue = validator.validate(unsafeValue);
114
- if (safeValue !== undefined)
115
- safeData[key] = safeValue;
116
- if (!changed && safeValue !== unsafeValue)
117
- changed = true;
112
+ // Validate the props in `validators`.
113
+ const props = getDataProps(validators);
114
+ for (const [key, validator] of props) {
115
+ const unsafeValue = unsafeData[key];
116
+ try {
117
+ const safeValue = validator.validate(unsafeValue);
118
+ if (safeValue === undefined) {
119
+ // Undefined values are not included in the output object
120
+ if (key in unsafeData)
121
+ changes++;
118
122
  }
119
- catch (thrown) {
120
- if (!(thrown instanceof Feedback))
121
- throw thrown;
122
- messages.push(getNamedMessage(key, thrown.message));
123
+ else {
124
+ // Defined values are included in the output object.
125
+ safeData[key] = safeValue;
126
+ if (safeValue !== unsafeValue)
127
+ changes++;
123
128
  }
124
129
  }
125
- if (messages.length)
126
- throw new ValueFeedback(messages.join("\n"), unsafeData);
127
- if (changed || getDataKeys(unsafeData).length > props.length)
128
- return safeData;
129
- return unsafeData;
130
- }
131
- finally {
132
- isDeeplyPartial = false;
130
+ catch (thrown) {
131
+ if (!(thrown instanceof Feedback))
132
+ throw thrown;
133
+ messages.push(getNamedMessage(key, thrown.message));
134
+ }
133
135
  }
136
+ if (messages.length)
137
+ throw new ValueFeedback(messages.join("\n"), unsafeData);
138
+ if (changes)
139
+ return safeData;
140
+ // Check that no excess keys exist.
141
+ for (const key of Object.keys(unsafeData))
142
+ if (!(key in validators))
143
+ return safeData;
144
+ return unsafeData;
134
145
  }