shelving 1.150.4 → 1.150.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,2 +1 @@
1
1
  export * from "./Feedback.js";
2
- export * from "./Feedbacks.js";
package/feedback/index.js CHANGED
@@ -1,2 +1 @@
1
1
  export * from "./Feedback.js";
2
- export * from "./Feedbacks.js";
package/package.json CHANGED
@@ -11,7 +11,7 @@
11
11
  "state-management",
12
12
  "query-builder"
13
13
  ],
14
- "version": "1.150.4",
14
+ "version": "1.150.5",
15
15
  "repository": "https://github.com/dhoulb/shelving",
16
16
  "author": "Dave Houlbrooke <dave@shax.com>",
17
17
  "license": "0BSD",
package/util/error.d.ts CHANGED
@@ -4,3 +4,28 @@ export declare function logError(reason: unknown): void;
4
4
  export declare function isError(v: unknown): v is Error & {
5
5
  readonly code?: string | undefined;
6
6
  };
7
+ /**
8
+ * Extract the _main message(s)_ from a full error message string.
9
+ *
10
+ * - Error messages can have multiple lines separated by `\n` newline.
11
+ * - Some lines may be _named messages_ in `name: This is a message` format.
12
+ * - The _main_ message is any line(s) that is not a named message.
13
+ *
14
+ * @returns The combined main message(s) found in the full message string.
15
+ */
16
+ export declare function getMainMessage(fullMessage: string): string;
17
+ /**
18
+ * Extract the _named message(s)_ from a full error message string.
19
+ *
20
+ * - Error messages can have multiple lines separated by `\n` newline.
21
+ * - Some lines may be _named messages_ in `name: This is a message` format.
22
+ * - This function extracts any `name: This is a message` lines from the full message string.
23
+ * - Note there may be multiple lines starting with `name:` (in the case where there were multiple errors with that name).
24
+ * - This trims the `name: ` prefix from found messages and rejoins them with `\n` newline.
25
+ *
26
+ * @param fullMessage The full message string which may contain named messages in `name: This is a message` format.
27
+ * @param name The name of the prop to extract the message for.
28
+ *
29
+ * @returns The combined message(s) for the named prop found in the full message string, or an empty string if no messages were found for that prop.
30
+ */
31
+ export declare function getNamedMessage(fullMessage: string, name: string | number): string;
package/util/error.js CHANGED
@@ -6,3 +6,40 @@ export function logError(reason) {
6
6
  export function isError(v) {
7
7
  return typeof Error.isError === "function" ? Error.isError(v) : v instanceof Error;
8
8
  }
9
+ /**
10
+ * Extract the _main message(s)_ from a full error message string.
11
+ *
12
+ * - Error messages can have multiple lines separated by `\n` newline.
13
+ * - Some lines may be _named messages_ in `name: This is a message` format.
14
+ * - The _main_ message is any line(s) that is not a named message.
15
+ *
16
+ * @returns The combined main message(s) found in the full message string.
17
+ */
18
+ export function getMainMessage(fullMessage) {
19
+ let propMessages = "";
20
+ for (const [foundMessage] of fullMessage.matchAll(/^\s*(?!.*: )(.*?)\s*/g))
21
+ if (foundMessage)
22
+ propMessages = propMessages ? `${propMessages}\n${foundMessage}` : foundMessage;
23
+ return propMessages;
24
+ }
25
+ /**
26
+ * Extract the _named message(s)_ from a full error message string.
27
+ *
28
+ * - Error messages can have multiple lines separated by `\n` newline.
29
+ * - Some lines may be _named messages_ in `name: This is a message` format.
30
+ * - This function extracts any `name: This is a message` lines from the full message string.
31
+ * - Note there may be multiple lines starting with `name:` (in the case where there were multiple errors with that name).
32
+ * - This trims the `name: ` prefix from found messages and rejoins them with `\n` newline.
33
+ *
34
+ * @param fullMessage The full message string which may contain named messages in `name: This is a message` format.
35
+ * @param name The name of the prop to extract the message for.
36
+ *
37
+ * @returns The combined message(s) for the named prop found in the full message string, or an empty string if no messages were found for that prop.
38
+ */
39
+ export function getNamedMessage(fullMessage, name) {
40
+ let namedMessages = "";
41
+ for (const [foundMessage] of fullMessage.matchAll(new RegExp(`^${name}: \s*(.*?)\s*$`, "g")))
42
+ if (foundMessage)
43
+ namedMessages = namedMessages ? `${namedMessages}\n${foundMessage}` : foundMessage;
44
+ return namedMessages;
45
+ }
package/util/validate.js CHANGED
@@ -1,6 +1,5 @@
1
1
  import { ValueError } from "../error/ValueError.js";
2
- import { Feedback } from "../feedback/Feedback.js";
3
- import { ValueFeedbacks } from "../feedback/Feedbacks.js";
2
+ import { Feedback, ValueFeedback } from "../feedback/Feedback.js";
4
3
  import { isArray } from "./array.js";
5
4
  import { getDataKeys, getDataProps } from "./data.js";
6
5
  import { getDictionaryItems } from "./dictionary.js";
@@ -30,8 +29,7 @@ export function getValid(value, validator, ErrorConstructor = ValueError, caller
30
29
  */
31
30
  export function* validateItems(unsafeItems, validator) {
32
31
  let index = 0;
33
- let valid = true;
34
- const messages = {};
32
+ const messages = [];
35
33
  for (const unsafeItem of unsafeItems) {
36
34
  try {
37
35
  yield validator.validate(unsafeItem);
@@ -39,13 +37,12 @@ export function* validateItems(unsafeItems, validator) {
39
37
  catch (thrown) {
40
38
  if (!(thrown instanceof Feedback))
41
39
  throw thrown;
42
- messages[index] = thrown.message;
43
- valid = false;
40
+ messages.push(`${index}: ${thrown.message}`);
44
41
  }
45
42
  index++;
46
43
  }
47
- if (!valid)
48
- throw new ValueFeedbacks(messages, unsafeItems);
44
+ if (messages.length)
45
+ throw new ValueFeedback(messages.join("\n"), unsafeItems);
49
46
  }
50
47
  /**
51
48
  * Validate an array of items.
@@ -56,10 +53,9 @@ export function* validateItems(unsafeItems, validator) {
56
53
  */
57
54
  export function validateArray(unsafeArray, validator) {
58
55
  let index = 0;
59
- let valid = true;
60
56
  let changed = false; // start false so we can reuse original if nothing changes
61
57
  const safeArray = [];
62
- const messages = {};
58
+ const messages = [];
63
59
  for (const unsafeItem of unsafeArray) {
64
60
  try {
65
61
  const safeItem = validator.validate(unsafeItem);
@@ -70,13 +66,12 @@ export function validateArray(unsafeArray, validator) {
70
66
  catch (thrown) {
71
67
  if (!(thrown instanceof Feedback))
72
68
  throw thrown;
73
- messages[index] = thrown.message;
74
- valid = false;
69
+ messages.push(`${index}: ${thrown.message}`);
75
70
  }
76
71
  index++;
77
72
  }
78
- if (!valid)
79
- throw new ValueFeedbacks(messages, unsafeArray);
73
+ if (messages.length)
74
+ throw new ValueFeedback(messages.join("\n"), unsafeArray);
80
75
  return changed || !isArray(unsafeArray) ? safeArray : unsafeArray;
81
76
  }
82
77
  /**
@@ -86,10 +81,9 @@ export function validateArray(unsafeArray, validator) {
86
81
  * - `feedback.details` will contain an entry for each invalid item (keyed by their count in the input iterable).
87
82
  */
88
83
  export function validateDictionary(unsafeDictionary, validator) {
89
- let valid = true;
90
84
  let changed = false;
91
85
  const safeDictionary = {};
92
- const messages = {};
86
+ const messages = [];
93
87
  for (const [key, unsafeValue] of getDictionaryItems(unsafeDictionary)) {
94
88
  try {
95
89
  const safeValue = validator.validate(unsafeValue);
@@ -100,21 +94,19 @@ export function validateDictionary(unsafeDictionary, validator) {
100
94
  catch (thrown) {
101
95
  if (!(thrown instanceof Feedback))
102
96
  throw thrown;
103
- messages[key] = thrown.message;
104
- valid = false;
97
+ messages.push(`${key}: ${thrown.message}`);
105
98
  }
106
99
  }
107
- if (!valid)
108
- throw new ValueFeedbacks(messages, unsafeDictionary);
100
+ if (messages.length)
101
+ throw new ValueFeedback(messages.join("\n"), unsafeDictionary);
109
102
  return changed || isIterable(unsafeDictionary) ? safeDictionary : unsafeDictionary;
110
103
  }
111
104
  /** Keep track of whether we're doing a deep-partial match or not. */
112
105
  let isDeeplyPartial = false;
113
106
  export function validateData(unsafeData, validators, partial = isDeeplyPartial) {
114
- let valid = true;
115
107
  let changed = false;
116
108
  const safeData = {};
117
- const messages = {};
109
+ const messages = [];
118
110
  try {
119
111
  isDeeplyPartial = partial;
120
112
  const props = getDataProps(validators);
@@ -132,12 +124,11 @@ export function validateData(unsafeData, validators, partial = isDeeplyPartial)
132
124
  catch (thrown) {
133
125
  if (!(thrown instanceof Feedback))
134
126
  throw thrown;
135
- messages[key] = thrown.message;
136
- valid = false;
127
+ messages.push(`${key}: ${thrown.message}`);
137
128
  }
138
129
  }
139
- if (!valid)
140
- throw new ValueFeedbacks(messages, unsafeData);
130
+ if (messages.length)
131
+ throw new ValueFeedback(messages.join("\n"), unsafeData);
141
132
  if (changed || getDataKeys(unsafeData).length > props.length)
142
133
  return safeData;
143
134
  return unsafeData;
@@ -1,13 +0,0 @@
1
- import type { ImmutableDictionary } from "../util/dictionary.js";
2
- import { Feedback } from "./Feedback.js";
3
- /** Feedback with a set of named messages. */
4
- export declare class Feedbacks extends Feedback {
5
- /** List of named messages. */
6
- readonly messages: ImmutableDictionary<string>;
7
- constructor(messages: ImmutableDictionary<string>);
8
- }
9
- /** Feedbacks with a known and typed `.value` field. */
10
- export declare class ValueFeedbacks<T> extends Feedbacks {
11
- readonly value: T;
12
- constructor(messages: ImmutableDictionary<string>, value: T);
13
- }
@@ -1,18 +0,0 @@
1
- import { Feedback } from "./Feedback.js";
2
- /** Feedback with a set of named messages. */
3
- export class Feedbacks extends Feedback {
4
- /** List of named messages. */
5
- messages;
6
- constructor(messages) {
7
- super(Object.values(messages).join("\n"));
8
- this.messages = messages;
9
- }
10
- }
11
- /** Feedbacks with a known and typed `.value` field. */
12
- export class ValueFeedbacks extends Feedbacks {
13
- value;
14
- constructor(messages, value) {
15
- super(messages);
16
- this.value = value;
17
- }
18
- }