shelving 1.49.2 → 1.50.2
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/markup/render.d.ts +0 -5
- package/markup/render.js +1 -13
- package/markup/rules.d.ts +118 -3
- package/markup/rules.js +89 -40
- package/package.json +11 -11
- package/stream/DataState.js +3 -3
- package/update/DataUpdate.js +2 -2
- package/util/color.d.ts +2 -1
- package/util/color.js +8 -6
- package/util/iterate.d.ts +5 -0
- package/util/iterate.js +6 -0
- package/util/random.d.ts +2 -1
- package/util/random.js +4 -3
- package/util/string.d.ts +2 -2
- package/util/string.js +7 -7
- package/util/transform.d.ts +1 -8
- package/util/transform.js +8 -8
- package/util/undefined.js +1 -1
- package/util/validate.d.ts +2 -10
- package/util/validate.js +16 -15
package/markup/render.d.ts
CHANGED
|
@@ -33,8 +33,3 @@ import type { MarkupOptions, MarkupNode } from "./types.js";
|
|
|
33
33
|
* @returns ReactNode, i.e. either a complete `ReactElement`, `null`, `undefined`, `string`, or an array of zero or more of those.
|
|
34
34
|
*/
|
|
35
35
|
export declare function renderMarkup(content: string, options?: Partial<MarkupOptions>): MarkupNode;
|
|
36
|
-
/**
|
|
37
|
-
* Parse a text string as user-generated markup.
|
|
38
|
-
* - Like `renderMarkup()` but only enables a subset of rules and applies `rel="nofollow ugc"` to all links.
|
|
39
|
-
*/
|
|
40
|
-
export declare function renderUgcMarkup(content: string, options?: Partial<MarkupOptions>): MarkupNode;
|
package/markup/render.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
/* eslint-disable no-param-reassign */
|
|
2
2
|
import { sanitizeLines } from "../index.js";
|
|
3
|
-
import { MARKUP_RULES
|
|
3
|
+
import { MARKUP_RULES } from "./rules.js";
|
|
4
4
|
/** Convert a string into an array of React nodes using a set of rules. */
|
|
5
5
|
function renderString(content, options) {
|
|
6
6
|
// If there's no context return the unmodified string.
|
|
@@ -154,15 +154,3 @@ const defaults = {
|
|
|
154
154
|
rel: undefined,
|
|
155
155
|
schemes: ["http:", "https:"],
|
|
156
156
|
};
|
|
157
|
-
/**
|
|
158
|
-
* Parse a text string as user-generated markup.
|
|
159
|
-
* - Like `renderMarkup()` but only enables a subset of rules and applies `rel="nofollow ugc"` to all links.
|
|
160
|
-
*/
|
|
161
|
-
export function renderUgcMarkup(content, options) {
|
|
162
|
-
return renderString(sanitizeLines(content), { ...defaultsUgc, ...options });
|
|
163
|
-
}
|
|
164
|
-
const defaultsUgc = {
|
|
165
|
-
...defaults,
|
|
166
|
-
rules: MARKUP_RULES_UGC,
|
|
167
|
-
rel: "nofollow ugc",
|
|
168
|
-
};
|
package/markup/rules.d.ts
CHANGED
|
@@ -1,4 +1,115 @@
|
|
|
1
|
-
import type { MarkupRule } from "./types.js";
|
|
1
|
+
import type { MarkupRule, MarkupRuleMatcher } from "./types.js";
|
|
2
|
+
export declare const getMarkupMatcher: (regexp: RegExp) => MarkupRuleMatcher;
|
|
3
|
+
export declare const getMarkupBlockMatcher: (middle?: string, end?: string, start?: string) => MarkupRuleMatcher;
|
|
4
|
+
export declare const getMarkupLineMatcher: (middle?: string, end?: string, start?: string) => MarkupRuleMatcher;
|
|
5
|
+
export declare const getMarkupWrapMatcher: (chars: string, middle?: string) => MarkupRuleMatcher;
|
|
6
|
+
/**
|
|
7
|
+
* Headings are single line only (don't allow multiline).
|
|
8
|
+
* - 1-6 hashes then 1+ spaces, then the title.
|
|
9
|
+
* - Same as Markdown syntax.
|
|
10
|
+
* - Markdown's underline syntax is not supported (for simplification).
|
|
11
|
+
*/
|
|
12
|
+
export declare const HEADING_RULE: MarkupRule;
|
|
13
|
+
/**
|
|
14
|
+
* Horizontal rules
|
|
15
|
+
* - Same as Markdown syntax but also allows `•` bullet character (in addition to `-` dash, `+` plus, `*` asterisk, and `_` underscore).
|
|
16
|
+
* - Character must be repeated three (or more) times.
|
|
17
|
+
* - Character must be the same every time (can't mix)
|
|
18
|
+
* - Might have infinite number of spaces between the characters.
|
|
19
|
+
*/
|
|
20
|
+
export declare const HORIZONTAL_RULE: MarkupRule;
|
|
21
|
+
export declare const UNORDERED_LIST_RULE: MarkupRule;
|
|
22
|
+
export declare const ORDERED_LIST_RULE: MarkupRule;
|
|
23
|
+
/**
|
|
24
|
+
* Blockquote block.
|
|
25
|
+
* - Same as Markdown's syntax.
|
|
26
|
+
* - Block continues until it finds a line that doesn't start with `>`
|
|
27
|
+
* - Quote indent symbol can be followed by zero or more spaces.
|
|
28
|
+
*/
|
|
29
|
+
export declare const BLOCKQUOTE_RULE: MarkupRule;
|
|
30
|
+
/**
|
|
31
|
+
* Fenced code blocks
|
|
32
|
+
* - Same as Markdown syntax.
|
|
33
|
+
* - Closing fence must be exactly the same as the opening fence, and can be made of at least three "```" backticks or three `~~~` tildes.
|
|
34
|
+
* - If there's no closing fence the code block will run to the end of the current string.
|
|
35
|
+
* - Markdown-style four-space indent syntax is not supported (only fenced code, since it's easier to use).
|
|
36
|
+
*/
|
|
37
|
+
export declare const FENCED_CODE_RULE: MarkupRule;
|
|
38
|
+
/**
|
|
39
|
+
* Paragraph.
|
|
40
|
+
* - When ordering rules, paragraph should go after other "block" context elements (because it has a very generous capture).
|
|
41
|
+
*/
|
|
42
|
+
export declare const PARAGRAPH_RULE: MarkupRule;
|
|
43
|
+
/**
|
|
44
|
+
* Markdown-style link.
|
|
45
|
+
* - Link in standard Markdown format, e.g. `[http://google.com/maps](Google Maps)`
|
|
46
|
+
* - If no title is specified a cleaned up version of the URL will be used, e.g. `google.com/maps`
|
|
47
|
+
* - Does not need space before/after the link.
|
|
48
|
+
* - If link is not valid (using `new URL(url)` then unparsed text will be returned.
|
|
49
|
+
* - For security only `http://` or `https://` links will work (if invalid the unparsed text will be returned).
|
|
50
|
+
*/
|
|
51
|
+
export declare const LINK_MARKUP: MarkupRule;
|
|
52
|
+
/**
|
|
53
|
+
* Autolinked URL starts with `http:` or `https:` and matches an unlimited number of non-space characters.
|
|
54
|
+
* - If followed by space and then text in `()` round or `[]` square brackets that will be used as the title, e.g. `http://google.com/maps (Google Maps)` or `http://google.com/maps [Google Maps]` (this syntax is from Todoist and maybe other things too).
|
|
55
|
+
* - If no title is specified a cleaned up version of the URL will be used, e.g. `google.com/maps`
|
|
56
|
+
* - If link is not valid (using `new URL(url)` then unparsed text will be returned.
|
|
57
|
+
* - For security only schemes that appear in the `options.schemes` will match (defaults to `http:` and `https:`).
|
|
58
|
+
*/
|
|
59
|
+
export declare const AUTOLINK_RULE: MarkupRule;
|
|
60
|
+
/**
|
|
61
|
+
* Inline code.
|
|
62
|
+
* - Text surrounded by one or more "`" backtick tilde characters.
|
|
63
|
+
* - Unlike strong/emphasis first or last character of the element can be space, (e.g. `- abc -` will not work).
|
|
64
|
+
* - Closing characters must exactly match opening characters.
|
|
65
|
+
* - Same as Markdown syntax.
|
|
66
|
+
*/
|
|
67
|
+
export declare const CODE_RULE: MarkupRule;
|
|
68
|
+
/**
|
|
69
|
+
* Inline strong.
|
|
70
|
+
* - Inline text wrapped in one or more `*` asterisks.
|
|
71
|
+
* - Must be surrounded by space (e.g. ` *abc* `) — so formatting cannot be applied inside a word (e.g. `a*b*c`).
|
|
72
|
+
* - Whitespace cannot be the first or last character of the element (e.g. `* abc *` will not work).
|
|
73
|
+
* - Closing characters must exactly match opening characters.
|
|
74
|
+
* - Different to Markdown: strong is always surrounded by `*asterisks*` and emphasis is always surrounded by `_underscores_` (strong isn't 'double emphasis').
|
|
75
|
+
*/
|
|
76
|
+
export declare const STRONG_MARKUP: MarkupRule;
|
|
77
|
+
/**
|
|
78
|
+
* Inline emphasis.
|
|
79
|
+
* - Inline text wrapped in one or more `_` underscore symbols.
|
|
80
|
+
* - Works inside words (e.g. `magi_carp_carp`).
|
|
81
|
+
* - Whitespace cannot be the first or last character of the element (e.g. `_ abc _` will not work).
|
|
82
|
+
* - Closing characters must exactly match opening characters.
|
|
83
|
+
* - Different to Markdown: strong is always surrounded by `*asterisks*` and emphasis is always surrounded by `_underscores_` (strong isn't 'double emphasis').
|
|
84
|
+
*/
|
|
85
|
+
export declare const EMPHASIS_RULE: MarkupRule;
|
|
86
|
+
/**
|
|
87
|
+
* Inserted text (`<ins>` tag),
|
|
88
|
+
* - Inline text wrapped in two or more `++` pluses.
|
|
89
|
+
* - Works inside words (e.g. `magi+karp+carp`).
|
|
90
|
+
* - Whitespace cannot be the first or last character of the element (e.g. `+ abc +` will not work).
|
|
91
|
+
* - Closing characters must exactly match opening characters.
|
|
92
|
+
* - Markdown doesn't have this.
|
|
93
|
+
*/
|
|
94
|
+
export declare const INSERT_RULE: MarkupRule;
|
|
95
|
+
/**
|
|
96
|
+
* Deleted text (`<del>` tag),
|
|
97
|
+
* - Inline text wrapped in two or more `--` hyphens or `~~` tildes.
|
|
98
|
+
* - Works inside words (e.g. `magi--karp--carp`).
|
|
99
|
+
* - Whitespace cannot be the first or last character of the element (e.g. `-- abc --` will not work).
|
|
100
|
+
* - Closing characters must exactly match opening characters.
|
|
101
|
+
* - Markdown doesn't have this.
|
|
102
|
+
*/
|
|
103
|
+
export declare const DELETE_RULE: MarkupRule;
|
|
104
|
+
/**
|
|
105
|
+
* Hard linebreak (`<br />` tag).
|
|
106
|
+
* - Any line break in a paragraph will become a hard `<br />` tag.
|
|
107
|
+
* - Different to Markdown:
|
|
108
|
+
* - Markdown needs two spaces at the end of a line, any line break in a paragraph will be a `<br />` tag (lines without two spaces are not joined together).
|
|
109
|
+
* - This is more intuitive (a linebreak becomes a linebreak is isn't silently ignored).
|
|
110
|
+
* - This works better with textareas that wrap text (since manually breaking up long lines is no longer necessary).
|
|
111
|
+
*/
|
|
112
|
+
export declare const LINEBREAK_RULE: MarkupRule;
|
|
2
113
|
/**
|
|
3
114
|
* All markup rules.
|
|
4
115
|
* - Syntax parsed by `renderMarkup()` is defined entirely by the list of rules (i.e. not by code).
|
|
@@ -9,5 +120,9 @@ import type { MarkupRule } from "./types.js";
|
|
|
9
120
|
* - HTML tags and character entities are never allowed (our use cases generally require a locked-down subset of syntax).
|
|
10
121
|
*/
|
|
11
122
|
export declare const MARKUP_RULES: MarkupRule[];
|
|
12
|
-
/**
|
|
13
|
-
export declare const
|
|
123
|
+
/** Subset of markup rules that work in a block context. */
|
|
124
|
+
export declare const MARKUP_RULES_BLOCK: MarkupRule[];
|
|
125
|
+
/** Subset of markup rules that work in an inline context. */
|
|
126
|
+
export declare const MARKUP_RULES_INLINE: MarkupRule[];
|
|
127
|
+
/** Subset of markup rules that are relevant for collapsed shortform content. */
|
|
128
|
+
export declare const MARKUP_RULES_SHORTFORM: MarkupRule[];
|
package/markup/rules.js
CHANGED
|
@@ -11,10 +11,10 @@ const WORDS = `\\S(?:[\\s\\S]*?\\S)?`; // Run of text that starts and ends with
|
|
|
11
11
|
// Regular expressions.
|
|
12
12
|
const REPLACE_INDENT = /^ {1,2}/gm;
|
|
13
13
|
// Regular expression makers.
|
|
14
|
-
const
|
|
15
|
-
const
|
|
16
|
-
const
|
|
17
|
-
const
|
|
14
|
+
export const getMarkupMatcher = regexp => content => content.match(regexp);
|
|
15
|
+
export const getMarkupBlockMatcher = (middle = BLOCK, end = BLOCK_END, start = BLOCK_START) => getMarkupMatcher(new RegExp(`(?:${start})${middle}(?:${end})`));
|
|
16
|
+
export const getMarkupLineMatcher = (middle = LINE, end = LINE_END, start = LINE_START) => getMarkupMatcher(new RegExp(`(?:${start})${middle}(?:${end})`));
|
|
17
|
+
export const getMarkupWrapMatcher = (chars, middle = WORDS) => {
|
|
18
18
|
const regexp = new RegExp(`(${chars})(${middle})\\1`);
|
|
19
19
|
return content => content.match(regexp);
|
|
20
20
|
};
|
|
@@ -24,8 +24,8 @@ const getWrapMatcher = (chars, middle = WORDS) => {
|
|
|
24
24
|
* - Same as Markdown syntax.
|
|
25
25
|
* - Markdown's underline syntax is not supported (for simplification).
|
|
26
26
|
*/
|
|
27
|
-
const
|
|
28
|
-
match:
|
|
27
|
+
export const HEADING_RULE = {
|
|
28
|
+
match: getMarkupLineMatcher(`(#{1,6}) +(${LINE})`),
|
|
29
29
|
render: ([, prefix = "", children = ""]) => ({ type: `h${prefix.length}`, key: null, props: { children } }),
|
|
30
30
|
contexts: ["block"],
|
|
31
31
|
childContext: "inline",
|
|
@@ -37,8 +37,8 @@ const HEADING = {
|
|
|
37
37
|
* - Character must be the same every time (can't mix)
|
|
38
38
|
* - Might have infinite number of spaces between the characters.
|
|
39
39
|
*/
|
|
40
|
-
const
|
|
41
|
-
match:
|
|
40
|
+
export const HORIZONTAL_RULE = {
|
|
41
|
+
match: getMarkupLineMatcher(`([${BULLETS}])(?: *\\1){2,}`),
|
|
42
42
|
render: () => ({ type: "hr", key: null, props: {} }),
|
|
43
43
|
contexts: ["block"],
|
|
44
44
|
};
|
|
@@ -50,8 +50,8 @@ const HR = {
|
|
|
50
50
|
* - Second-level list can be indented with 1-2 spaces.
|
|
51
51
|
*/
|
|
52
52
|
const UNORDERED = `[${BULLETS}] +`; // Anything that can be a bullet (used for unordered lists and horizontal rules).
|
|
53
|
-
const
|
|
54
|
-
match:
|
|
53
|
+
export const UNORDERED_LIST_RULE = {
|
|
54
|
+
match: getMarkupBlockMatcher(`${UNORDERED}(${BLOCK})`),
|
|
55
55
|
render: ([, list = ""]) => {
|
|
56
56
|
const children = list.split(SPLIT_UL_ITEMS).map(mapUnorderedItem);
|
|
57
57
|
return { type: "ul", key: null, props: { children } };
|
|
@@ -70,8 +70,8 @@ const mapUnorderedItem = (item, key) => {
|
|
|
70
70
|
* - Second-level list can be indented with 1-3 spaces.
|
|
71
71
|
*/
|
|
72
72
|
const ORDERED = "[0-9]+[.):] +"; // Number for a numbered list (e.g. `1.` or `2)` or `3:`)
|
|
73
|
-
const
|
|
74
|
-
match:
|
|
73
|
+
export const ORDERED_LIST_RULE = {
|
|
74
|
+
match: getMarkupBlockMatcher(`(${ORDERED}${BLOCK})`),
|
|
75
75
|
render: ([, list = ""]) => {
|
|
76
76
|
const children = list.split(SPLIT_OL_ITEMS).map(mapOrderedItem);
|
|
77
77
|
return { type: "ol", key: null, props: { children } };
|
|
@@ -95,8 +95,8 @@ const mapOrderedItem = (item, key) => {
|
|
|
95
95
|
* - Block continues until it finds a line that doesn't start with `>`
|
|
96
96
|
* - Quote indent symbol can be followed by zero or more spaces.
|
|
97
97
|
*/
|
|
98
|
-
const
|
|
99
|
-
match:
|
|
98
|
+
export const BLOCKQUOTE_RULE = {
|
|
99
|
+
match: getMarkupLineMatcher(`(>${LINE}(?:\\n>${LINE})*)`),
|
|
100
100
|
render: ([, quote = ""]) => ({
|
|
101
101
|
type: "blockquote",
|
|
102
102
|
key: null,
|
|
@@ -113,9 +113,9 @@ const BLOCKQUOTE_LINES = /^>/gm;
|
|
|
113
113
|
* - If there's no closing fence the code block will run to the end of the current string.
|
|
114
114
|
* - Markdown-style four-space indent syntax is not supported (only fenced code, since it's easier to use).
|
|
115
115
|
*/
|
|
116
|
-
const
|
|
116
|
+
export const FENCED_CODE_RULE = {
|
|
117
117
|
// Matcher has its own end that only stops when it reaches a matching closing fence or the end of the string.
|
|
118
|
-
match:
|
|
118
|
+
match: getMarkupBlockMatcher(`(\`{3,}|~{3,}) *(${LINE})\\n(${BLOCK})`, `\\n\\1\\n+|\\n\\1$|$`),
|
|
119
119
|
render: ([, , file, children]) => ({
|
|
120
120
|
type: "pre",
|
|
121
121
|
key: null,
|
|
@@ -133,8 +133,8 @@ const FENCED = {
|
|
|
133
133
|
* Paragraph.
|
|
134
134
|
* - When ordering rules, paragraph should go after other "block" context elements (because it has a very generous capture).
|
|
135
135
|
*/
|
|
136
|
-
const
|
|
137
|
-
match:
|
|
136
|
+
export const PARAGRAPH_RULE = {
|
|
137
|
+
match: getMarkupBlockMatcher(` *(${BLOCK})`),
|
|
138
138
|
render: ([, children]) => ({ type: `p`, key: null, props: { children } }),
|
|
139
139
|
contexts: ["block"],
|
|
140
140
|
childContext: "inline",
|
|
@@ -148,7 +148,7 @@ const PARAGRAPH = {
|
|
|
148
148
|
* - If link is not valid (using `new URL(url)` then unparsed text will be returned.
|
|
149
149
|
* - For security only `http://` or `https://` links will work (if invalid the unparsed text will be returned).
|
|
150
150
|
*/
|
|
151
|
-
const
|
|
151
|
+
export const LINK_MARKUP = {
|
|
152
152
|
// Custom matcher to check the URL against the allowed schemes.
|
|
153
153
|
match: (content, { schemes, url: base }) => {
|
|
154
154
|
const matches = content.match(MATCH_LINK);
|
|
@@ -178,7 +178,7 @@ const MATCH_LINK = /\[([^\]]*?)\]\(([^)]*?)\)/;
|
|
|
178
178
|
* - If link is not valid (using `new URL(url)` then unparsed text will be returned.
|
|
179
179
|
* - For security only schemes that appear in the `options.schemes` will match (defaults to `http:` and `https:`).
|
|
180
180
|
*/
|
|
181
|
-
const
|
|
181
|
+
export const AUTOLINK_RULE = {
|
|
182
182
|
// Custom matcher to check the URL against the allowed schemes.
|
|
183
183
|
match: (content, { schemes, url: base }) => {
|
|
184
184
|
const matches = content.match(MATCH_AUTOLINK);
|
|
@@ -192,7 +192,7 @@ const AUTOLINK = {
|
|
|
192
192
|
}
|
|
193
193
|
}
|
|
194
194
|
},
|
|
195
|
-
render:
|
|
195
|
+
render: LINK_MARKUP.render,
|
|
196
196
|
contexts: ["inline", "list"],
|
|
197
197
|
childContext: "link",
|
|
198
198
|
};
|
|
@@ -204,8 +204,8 @@ const MATCH_AUTOLINK = /([a-z][a-z0-9-]*[a-z0-9]:\S+)(?: +(?:\(([^)]*?)\)|\[([^\
|
|
|
204
204
|
* - Closing characters must exactly match opening characters.
|
|
205
205
|
* - Same as Markdown syntax.
|
|
206
206
|
*/
|
|
207
|
-
const
|
|
208
|
-
match:
|
|
207
|
+
export const CODE_RULE = {
|
|
208
|
+
match: getMarkupWrapMatcher("`+", BLOCK),
|
|
209
209
|
render: ([, , children]) => ({ type: "code", key: null, props: { children } }),
|
|
210
210
|
contexts: ["inline", "list"],
|
|
211
211
|
priority: 10, // Higher priority than other inlines so it matches first before e.g. `strong` or `em` (from CommonMark spec: "Code span backticks have higher precedence than any other inline constructs except HTML tags and autolinks.")
|
|
@@ -218,8 +218,8 @@ const CODE = {
|
|
|
218
218
|
* - Closing characters must exactly match opening characters.
|
|
219
219
|
* - Different to Markdown: strong is always surrounded by `*asterisks*` and emphasis is always surrounded by `_underscores_` (strong isn't 'double emphasis').
|
|
220
220
|
*/
|
|
221
|
-
const
|
|
222
|
-
match:
|
|
221
|
+
export const STRONG_MARKUP = {
|
|
222
|
+
match: getMarkupWrapMatcher("\\*+"),
|
|
223
223
|
render: ([, , children]) => ({ type: "strong", key: null, props: { children } }),
|
|
224
224
|
contexts: ["inline", "list", "link"],
|
|
225
225
|
childContext: "inline",
|
|
@@ -232,36 +232,36 @@ const STRONG = {
|
|
|
232
232
|
* - Closing characters must exactly match opening characters.
|
|
233
233
|
* - Different to Markdown: strong is always surrounded by `*asterisks*` and emphasis is always surrounded by `_underscores_` (strong isn't 'double emphasis').
|
|
234
234
|
*/
|
|
235
|
-
const
|
|
236
|
-
match:
|
|
235
|
+
export const EMPHASIS_RULE = {
|
|
236
|
+
match: getMarkupWrapMatcher("_+"),
|
|
237
237
|
render: ([, , children]) => ({ type: "em", key: null, props: { children } }),
|
|
238
238
|
contexts: ["inline", "list", "link"],
|
|
239
239
|
childContext: "inline",
|
|
240
240
|
};
|
|
241
241
|
/**
|
|
242
242
|
* Inserted text (`<ins>` tag),
|
|
243
|
-
* - Inline text wrapped in
|
|
243
|
+
* - Inline text wrapped in two or more `++` pluses.
|
|
244
244
|
* - Works inside words (e.g. `magi+karp+carp`).
|
|
245
245
|
* - Whitespace cannot be the first or last character of the element (e.g. `+ abc +` will not work).
|
|
246
246
|
* - Closing characters must exactly match opening characters.
|
|
247
247
|
* - Markdown doesn't have this.
|
|
248
248
|
*/
|
|
249
|
-
const
|
|
250
|
-
match:
|
|
249
|
+
export const INSERT_RULE = {
|
|
250
|
+
match: getMarkupWrapMatcher("\\+\\++"),
|
|
251
251
|
render: ([, , children]) => ({ type: "ins", key: null, props: { children } }),
|
|
252
252
|
contexts: ["inline", "list", "link"],
|
|
253
253
|
childContext: "inline",
|
|
254
254
|
};
|
|
255
255
|
/**
|
|
256
256
|
* Deleted text (`<del>` tag),
|
|
257
|
-
* - Inline text wrapped in
|
|
258
|
-
* - Works inside words (e.g. `magi
|
|
259
|
-
* - Whitespace cannot be the first or last character of the element (e.g.
|
|
257
|
+
* - Inline text wrapped in two or more `--` hyphens or `~~` tildes.
|
|
258
|
+
* - Works inside words (e.g. `magi--karp--carp`).
|
|
259
|
+
* - Whitespace cannot be the first or last character of the element (e.g. `-- abc --` will not work).
|
|
260
260
|
* - Closing characters must exactly match opening characters.
|
|
261
261
|
* - Markdown doesn't have this.
|
|
262
262
|
*/
|
|
263
|
-
const
|
|
264
|
-
match:
|
|
263
|
+
export const DELETE_RULE = {
|
|
264
|
+
match: getMarkupWrapMatcher("--+|~~+"),
|
|
265
265
|
render: ([, , children]) => ({ type: "del", key: null, props: { children } }),
|
|
266
266
|
contexts: ["inline", "list", "link"],
|
|
267
267
|
childContext: "inline",
|
|
@@ -274,8 +274,8 @@ const DEL = {
|
|
|
274
274
|
* - This is more intuitive (a linebreak becomes a linebreak is isn't silently ignored).
|
|
275
275
|
* - This works better with textareas that wrap text (since manually breaking up long lines is no longer necessary).
|
|
276
276
|
*/
|
|
277
|
-
const
|
|
278
|
-
match:
|
|
277
|
+
export const LINEBREAK_RULE = {
|
|
278
|
+
match: getMarkupMatcher(/\n/),
|
|
279
279
|
render: () => ({ type: "br", key: null, props: {} }),
|
|
280
280
|
contexts: ["inline", "list", "link"],
|
|
281
281
|
childContext: "inline",
|
|
@@ -289,6 +289,55 @@ const BR = {
|
|
|
289
289
|
* 3. more aligned with smaller textboxes and editors that have line wrapping
|
|
290
290
|
* - HTML tags and character entities are never allowed (our use cases generally require a locked-down subset of syntax).
|
|
291
291
|
*/
|
|
292
|
-
export const MARKUP_RULES = [
|
|
293
|
-
|
|
294
|
-
|
|
292
|
+
export const MARKUP_RULES = [
|
|
293
|
+
HEADING_RULE,
|
|
294
|
+
HORIZONTAL_RULE,
|
|
295
|
+
UNORDERED_LIST_RULE,
|
|
296
|
+
ORDERED_LIST_RULE,
|
|
297
|
+
BLOCKQUOTE_RULE,
|
|
298
|
+
FENCED_CODE_RULE,
|
|
299
|
+
PARAGRAPH_RULE,
|
|
300
|
+
LINK_MARKUP,
|
|
301
|
+
AUTOLINK_RULE,
|
|
302
|
+
CODE_RULE,
|
|
303
|
+
STRONG_MARKUP,
|
|
304
|
+
EMPHASIS_RULE,
|
|
305
|
+
INSERT_RULE,
|
|
306
|
+
DELETE_RULE,
|
|
307
|
+
LINEBREAK_RULE,
|
|
308
|
+
];
|
|
309
|
+
/** Subset of markup rules that work in a block context. */
|
|
310
|
+
export const MARKUP_RULES_BLOCK = [
|
|
311
|
+
HEADING_RULE,
|
|
312
|
+
HORIZONTAL_RULE,
|
|
313
|
+
UNORDERED_LIST_RULE,
|
|
314
|
+
ORDERED_LIST_RULE,
|
|
315
|
+
BLOCKQUOTE_RULE,
|
|
316
|
+
FENCED_CODE_RULE,
|
|
317
|
+
PARAGRAPH_RULE,
|
|
318
|
+
];
|
|
319
|
+
/** Subset of markup rules that work in an inline context. */
|
|
320
|
+
export const MARKUP_RULES_INLINE = [
|
|
321
|
+
LINK_MARKUP,
|
|
322
|
+
AUTOLINK_RULE,
|
|
323
|
+
CODE_RULE,
|
|
324
|
+
STRONG_MARKUP,
|
|
325
|
+
EMPHASIS_RULE,
|
|
326
|
+
INSERT_RULE,
|
|
327
|
+
DELETE_RULE,
|
|
328
|
+
LINEBREAK_RULE,
|
|
329
|
+
];
|
|
330
|
+
/** Subset of markup rules that are relevant for collapsed shortform content. */
|
|
331
|
+
export const MARKUP_RULES_SHORTFORM = [
|
|
332
|
+
UNORDERED_LIST_RULE,
|
|
333
|
+
ORDERED_LIST_RULE,
|
|
334
|
+
PARAGRAPH_RULE,
|
|
335
|
+
LINK_MARKUP,
|
|
336
|
+
AUTOLINK_RULE,
|
|
337
|
+
CODE_RULE,
|
|
338
|
+
STRONG_MARKUP,
|
|
339
|
+
EMPHASIS_RULE,
|
|
340
|
+
INSERT_RULE,
|
|
341
|
+
DELETE_RULE,
|
|
342
|
+
LINEBREAK_RULE,
|
|
343
|
+
];
|
package/package.json
CHANGED
|
@@ -11,7 +11,7 @@
|
|
|
11
11
|
"state-management",
|
|
12
12
|
"query-builder"
|
|
13
13
|
],
|
|
14
|
-
"version": "1.
|
|
14
|
+
"version": "1.50.2",
|
|
15
15
|
"repository": "https://github.com/dhoulb/shelving",
|
|
16
16
|
"author": "Dave Houlbrooke <dave@shax.com>",
|
|
17
17
|
"license": "0BSD",
|
|
@@ -25,9 +25,9 @@
|
|
|
25
25
|
"./db": "./db/index.js",
|
|
26
26
|
"./error": "./error/index.js",
|
|
27
27
|
"./feedback": "./feedback/index.js",
|
|
28
|
-
"./firestore/client": "./firestore
|
|
29
|
-
"./firestore/lite": "./firestore
|
|
30
|
-
"./firestore/server": "./firestore
|
|
28
|
+
"./firestore/client": "./firestore/client/index.js",
|
|
29
|
+
"./firestore/lite": "./firestore/lite/index.js",
|
|
30
|
+
"./firestore/server": "./firestore/server/index.js",
|
|
31
31
|
"./markup": "./markup/index.js",
|
|
32
32
|
"./provider": "./provider/index.js",
|
|
33
33
|
"./query": "./query/index.js",
|
|
@@ -61,22 +61,22 @@
|
|
|
61
61
|
"devDependencies": {
|
|
62
62
|
"@google-cloud/firestore": "^4.15.1",
|
|
63
63
|
"@types/jest": "^27.4.0",
|
|
64
|
-
"@types/react": "^17.0.
|
|
64
|
+
"@types/react": "^17.0.39",
|
|
65
65
|
"@types/react-dom": "^17.0.11",
|
|
66
|
-
"@typescript-eslint/eslint-plugin": "^5.
|
|
67
|
-
"@typescript-eslint/parser": "^5.
|
|
68
|
-
"eslint": "^8.
|
|
66
|
+
"@typescript-eslint/eslint-plugin": "^5.10.2",
|
|
67
|
+
"@typescript-eslint/parser": "^5.10.2",
|
|
68
|
+
"eslint": "^8.8.0",
|
|
69
69
|
"eslint-config-prettier": "^8.3.0",
|
|
70
70
|
"eslint-plugin-import": "^2.25.4",
|
|
71
71
|
"eslint-plugin-prettier": "^4.0.0",
|
|
72
|
-
"firebase": "^9.6.
|
|
73
|
-
"jest": "^27.
|
|
72
|
+
"firebase": "^9.6.6",
|
|
73
|
+
"jest": "^27.5.0",
|
|
74
74
|
"jest-ts-webcompat-resolver": "^1.0.0",
|
|
75
75
|
"prettier": "^2.5.1",
|
|
76
76
|
"react": "^17.0.2",
|
|
77
77
|
"react-dom": "^17.0.2",
|
|
78
78
|
"ts-jest": "^27.1.3",
|
|
79
|
-
"typescript": "^4.5.
|
|
79
|
+
"typescript": "^4.5.5"
|
|
80
80
|
},
|
|
81
81
|
"peerDependencies": {
|
|
82
82
|
"@google-cloud/firestore": ">=4.0.0",
|
package/stream/DataState.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { withProp,
|
|
1
|
+
import { withProp, transformData, NOERROR, LOADING, awaitNext, getData } from "../util/index.js";
|
|
2
2
|
import { State } from "./State.js";
|
|
3
3
|
/** State that stores a data object and has additional methods to help with that. */
|
|
4
4
|
export class DataState extends State {
|
|
@@ -12,7 +12,7 @@ export class DataState extends State {
|
|
|
12
12
|
}
|
|
13
13
|
/** Update several props in this object. */
|
|
14
14
|
update(updates) {
|
|
15
|
-
this.next(
|
|
15
|
+
this.next(transformData(this.data, updates));
|
|
16
16
|
}
|
|
17
17
|
}
|
|
18
18
|
/** State that stores an optional data object and has additional methods to help with that. */
|
|
@@ -35,7 +35,7 @@ export class ResultState extends State {
|
|
|
35
35
|
}
|
|
36
36
|
/** Update several props in this object. */
|
|
37
37
|
update(updates) {
|
|
38
|
-
this.next(
|
|
38
|
+
this.next(transformData(this.data, updates));
|
|
39
39
|
}
|
|
40
40
|
/** Delete this result. */
|
|
41
41
|
delete() {
|
package/update/DataUpdate.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { transformData, isNullish } from "../util/index.js";
|
|
2
2
|
import { Update } from "./Update.js";
|
|
3
3
|
/** Update that can be applied to a data object to update its props. */
|
|
4
4
|
export class DataUpdate extends Update {
|
|
@@ -11,7 +11,7 @@ export class DataUpdate extends Update {
|
|
|
11
11
|
return new DataUpdate(!isNullish(key) ? { [key]: value } : {});
|
|
12
12
|
}
|
|
13
13
|
transform(existing) {
|
|
14
|
-
return
|
|
14
|
+
return transformData(existing, this.updates);
|
|
15
15
|
}
|
|
16
16
|
/** Return a data update with a specific prop marked for update. */
|
|
17
17
|
with(key, value) {
|
package/util/color.d.ts
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
/** Things that can be converted to a `Color` instance. */
|
|
2
2
|
export declare type PossibleColor = Color | string;
|
|
3
|
+
export declare type PossibleOptionalColor = PossibleColor | null;
|
|
3
4
|
/** Represent a color. */
|
|
4
5
|
export declare class Color {
|
|
5
6
|
readonly r: number;
|
|
@@ -20,7 +21,7 @@ export declare class Color {
|
|
|
20
21
|
/** Convert a number or string to a color channel number that's within bounds (strings like `0a` or `ff` are parsed as hexadecimal). */
|
|
21
22
|
export declare function getColorChannel(channel: number | string): number;
|
|
22
23
|
/** Convert a possible color to a `Color` instance or `null` */
|
|
23
|
-
export declare function toColor(color:
|
|
24
|
+
export declare function toColor(color: unknown): Color | null;
|
|
24
25
|
/** Convert a possible color to a `Color` instance */
|
|
25
26
|
export declare function getColor(input: PossibleColor): Color;
|
|
26
27
|
/** Is a color light? */
|
package/util/color.js
CHANGED
|
@@ -49,12 +49,14 @@ export function getColorChannel(channel) {
|
|
|
49
49
|
export function toColor(color) {
|
|
50
50
|
if (color instanceof Color)
|
|
51
51
|
return color;
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
52
|
+
if (typeof color === "string") {
|
|
53
|
+
const hex3 = color.match(HEX3);
|
|
54
|
+
if (hex3)
|
|
55
|
+
return new Color(hex3[1], hex3[2], hex3[3]);
|
|
56
|
+
const hex6 = color.match(HEX6);
|
|
57
|
+
if (hex6)
|
|
58
|
+
return new Color(hex6[1], hex6[2], hex6[3], hex6[4]);
|
|
59
|
+
}
|
|
58
60
|
return null;
|
|
59
61
|
}
|
|
60
62
|
/** Convert a possible color to a `Color` instance */
|
package/util/iterate.d.ts
CHANGED
|
@@ -52,6 +52,11 @@ export declare function yieldRange(start: number, end: number): Generator<number
|
|
|
52
52
|
* - Checks `items.size` or `items.length` first to see if the limit is necessary.
|
|
53
53
|
*/
|
|
54
54
|
export declare function limitItems<T>(items: Iterable<T>, limit: number): TypedIterable<T, void, void>;
|
|
55
|
+
/**
|
|
56
|
+
* Reduce an iterable set of items using a reducer function.
|
|
57
|
+
*/
|
|
58
|
+
export declare function reduceItems<T, R>(items: Iterable<T>, reducer: (previous: R, item: T) => R, initial: R): R;
|
|
59
|
+
export declare function reduceItems<T, R>(items: Iterable<T>, reducer: (previous: R | undefined, item: T) => R, initial?: R): R | undefined;
|
|
55
60
|
/** Yield items from a source iterable until we hit a maximum iteration count. */
|
|
56
61
|
export declare function yieldUntilLimit<T>(source: Iterable<T>, limit: number): Generator<T, void, void>;
|
|
57
62
|
/** Infinite iterator that yields the result of calling a function with a given set of arguments. */
|
package/util/iterate.js
CHANGED
|
@@ -70,6 +70,12 @@ export function limitItems(items, limit) {
|
|
|
70
70
|
const size = (_a = getKnownSize(items)) !== null && _a !== void 0 ? _a : Infinity;
|
|
71
71
|
return size <= limit ? items : yieldUntilLimit(items, limit);
|
|
72
72
|
}
|
|
73
|
+
export function reduceItems(items, reducer, initial) {
|
|
74
|
+
let current = initial;
|
|
75
|
+
for (const item of items)
|
|
76
|
+
current = reducer(current, item);
|
|
77
|
+
return current;
|
|
78
|
+
}
|
|
73
79
|
/** Yield items from a source iterable until we hit a maximum iteration count. */
|
|
74
80
|
export function* yieldUntilLimit(source, limit) {
|
|
75
81
|
const iterator = source[Symbol.iterator]();
|
package/util/random.d.ts
CHANGED
|
@@ -2,9 +2,10 @@ import type { ImmutableArray } from "./array.js";
|
|
|
2
2
|
/** Generate a random integer between two numbers. */
|
|
3
3
|
export declare const getRandom: (min: number, max: number) => number;
|
|
4
4
|
/**
|
|
5
|
-
* Make a random key, e.g.
|
|
5
|
+
* Make a random key, e.g. `xs23r34hhsdx` or `e4m29klrugef`
|
|
6
6
|
* - Not designed to be cryptographically random!
|
|
7
7
|
* - Will probably clash — if you're making a random ID, check for existence of the record before saving.
|
|
8
|
+
* - Designed to be semi-readable, doesn't use capital letters or `i` or `o` or `l` or `u`
|
|
8
9
|
*/
|
|
9
10
|
export declare const getRandomKey: (length?: number) => string;
|
|
10
11
|
/** Get a random character from a string. */
|
package/util/random.js
CHANGED
|
@@ -1,14 +1,15 @@
|
|
|
1
1
|
import { yieldUntilLimit, yieldCall } from "./iterate.js";
|
|
2
|
-
import {
|
|
2
|
+
import { joinStrings } from "./string.js";
|
|
3
3
|
import { getDefined } from "./undefined.js";
|
|
4
4
|
/** Generate a random integer between two numbers. */
|
|
5
5
|
export const getRandom = (min, max) => Math.round(Math.random() * (max - min) + min);
|
|
6
6
|
/**
|
|
7
|
-
* Make a random key, e.g.
|
|
7
|
+
* Make a random key, e.g. `xs23r34hhsdx` or `e4m29klrugef`
|
|
8
8
|
* - Not designed to be cryptographically random!
|
|
9
9
|
* - Will probably clash — if you're making a random ID, check for existence of the record before saving.
|
|
10
|
+
* - Designed to be semi-readable, doesn't use capital letters or `i` or `o` or `l` or `u`
|
|
10
11
|
*/
|
|
11
|
-
export const getRandomKey = (length =
|
|
12
|
+
export const getRandomKey = (length = 12) => joinStrings(yieldUntilLimit(yieldCall(getRandomCharacter, KEY_CHARS), length));
|
|
12
13
|
const KEY_CHARS = "0123456789abcdefghjkmnpqrstvwxyz";
|
|
13
14
|
/** Get a random character from a string. */
|
|
14
15
|
export const getRandomCharacter = (str) => str[getRandom(0, str.length - 1)];
|
package/util/string.d.ts
CHANGED
|
@@ -16,8 +16,6 @@ export declare const isString: (v: unknown) => v is string;
|
|
|
16
16
|
* -
|
|
17
17
|
*/
|
|
18
18
|
export declare function toString(value: unknown): string;
|
|
19
|
-
/** Concatenate a set of potential strings together. */
|
|
20
|
-
export declare function concatStrings(values: Iterable<unknown>): string;
|
|
21
19
|
/**
|
|
22
20
|
* Convert an unknown value into a title string for user-facing use.
|
|
23
21
|
* - Strings return the string.
|
|
@@ -32,6 +30,8 @@ export declare function concatStrings(values: Iterable<unknown>): string;
|
|
|
32
30
|
* - Everything else returns `"Unknown"`
|
|
33
31
|
*/
|
|
34
32
|
export declare function toTitle(value: unknown): string;
|
|
33
|
+
/** Concatenate an iterable set of strings together. */
|
|
34
|
+
export declare function joinStrings(strs: Iterable<string>, joiner?: string): string;
|
|
35
35
|
/**
|
|
36
36
|
* Sanitize unexpected characters from a string by:
|
|
37
37
|
* - Stripping control characters.
|
package/util/string.js
CHANGED
|
@@ -32,13 +32,6 @@ export function toString(value) {
|
|
|
32
32
|
return value.name || "function";
|
|
33
33
|
return typeof value; // "symbol" etc.
|
|
34
34
|
}
|
|
35
|
-
/** Concatenate a set of potential strings together. */
|
|
36
|
-
export function concatStrings(values) {
|
|
37
|
-
let output = "";
|
|
38
|
-
for (const value of values)
|
|
39
|
-
output += toString(value);
|
|
40
|
-
return output;
|
|
41
|
-
}
|
|
42
35
|
/**
|
|
43
36
|
* Convert an unknown value into a title string for user-facing use.
|
|
44
37
|
* - Strings return the string.
|
|
@@ -73,6 +66,13 @@ export function toTitle(value) {
|
|
|
73
66
|
return "None";
|
|
74
67
|
return "Unknown";
|
|
75
68
|
}
|
|
69
|
+
/** Concatenate an iterable set of strings together. */
|
|
70
|
+
export function joinStrings(strs, joiner = "") {
|
|
71
|
+
let output = "";
|
|
72
|
+
for (const str of strs)
|
|
73
|
+
output += `${output.length ? joiner : ""}${str}`;
|
|
74
|
+
return output;
|
|
75
|
+
}
|
|
76
76
|
/**
|
|
77
77
|
* Sanitize unexpected characters from a string by:
|
|
78
78
|
* - Stripping control characters.
|
package/util/transform.d.ts
CHANGED
|
@@ -1,4 +1,3 @@
|
|
|
1
|
-
import type { Entry } from "./entry.js";
|
|
2
1
|
import type { ArrayType, ImmutableArray } from "./array.js";
|
|
3
2
|
import type { ImmutableMap } from "./map.js";
|
|
4
3
|
import { ImmutableObject } from "./object.js";
|
|
@@ -35,12 +34,6 @@ export declare function yieldTransformed<I, O>(items: Iterable<I>, transformer:
|
|
|
35
34
|
export declare function transformArray<T extends ImmutableArray>(arr: T, transformer: Transformer<ArrayType<T>, ArrayType<T>>): T;
|
|
36
35
|
export declare function transformArray<I, O>(arr: Iterable<I>, transformer: (v: I) => O): ImmutableArray<O>;
|
|
37
36
|
export declare function transformArray<I, O>(arr: Iterable<I>, transformer: Transformer<I, O>): ImmutableArray<O>;
|
|
38
|
-
/**
|
|
39
|
-
* Transform the _values_ of a set of entries using a transformer.
|
|
40
|
-
* @yield Transformed entry after calling transforming the new value for each entry.
|
|
41
|
-
*/
|
|
42
|
-
export declare function yieldTransformedValues<I, O>(entries: Iterable<Entry<I>>, transformer: (v: I) => O): Iterable<Entry<O>>;
|
|
43
|
-
export declare function yieldTransformedValues<I, O>(entries: Iterable<Entry<I>>, transformer: Transformer<I, O>): Iterable<Entry<O>>;
|
|
44
37
|
/**
|
|
45
38
|
* Transform the _values_ of a map using a transformer.
|
|
46
39
|
* @return New map after transforming its values.
|
|
@@ -65,7 +58,7 @@ export declare type PropTransformers<T extends Data> = {
|
|
|
65
58
|
* Transform the props of a data object using a set of transformers for its props.
|
|
66
59
|
* @returns New object with changed props (or the same object if no changes were made).
|
|
67
60
|
*/
|
|
68
|
-
export declare function
|
|
61
|
+
export declare function transformData<T extends Data>(existing: T, transformers: PropTransformers<T>): T;
|
|
69
62
|
/** Set of named transformers for a a map-like object. */
|
|
70
63
|
export declare type EntryTransformers<T> = ImmutableObject<Transformer<T | undefined, T>>;
|
|
71
64
|
/** Transform some of the entries of a map-like object using a set of named transformers. */
|
package/util/transform.js
CHANGED
|
@@ -16,18 +16,18 @@ export function* yieldTransformed(items, transformer) {
|
|
|
16
16
|
export function transformArray(arr, transformer) {
|
|
17
17
|
return Array.from(yieldTransformed(arr, transformer));
|
|
18
18
|
}
|
|
19
|
-
|
|
19
|
+
function* _yieldTransformedValues(entries, transformer) {
|
|
20
20
|
for (const [k, v] of entries)
|
|
21
21
|
yield [k, transform(v, transformer)];
|
|
22
22
|
}
|
|
23
23
|
export function transformMap(map, transformer) {
|
|
24
|
-
return new Map(
|
|
24
|
+
return new Map(_yieldTransformedValues(map, transformer));
|
|
25
25
|
}
|
|
26
26
|
export function transformObject(obj, transformer) {
|
|
27
|
-
return Object.fromEntries(
|
|
27
|
+
return Object.fromEntries(_yieldTransformedValues(Object.entries(obj), transformer));
|
|
28
28
|
}
|
|
29
29
|
/** Apply transformers to the props of a data object and yield any props that changed. */
|
|
30
|
-
function*
|
|
30
|
+
function* _yieldTransformedProps(existing, transformers) {
|
|
31
31
|
for (const [k, v] of toProps(transformers))
|
|
32
32
|
yield [k, transform(existing[k], v)];
|
|
33
33
|
}
|
|
@@ -35,15 +35,15 @@ function* yieldTransformedProps(existing, transformers) {
|
|
|
35
35
|
* Transform the props of a data object using a set of transformers for its props.
|
|
36
36
|
* @returns New object with changed props (or the same object if no changes were made).
|
|
37
37
|
*/
|
|
38
|
-
export function
|
|
39
|
-
return Object.fromEntries(yieldMerged(toProps(existing),
|
|
38
|
+
export function transformData(existing, transformers) {
|
|
39
|
+
return Object.fromEntries(yieldMerged(toProps(existing), _yieldTransformedProps(existing, transformers)));
|
|
40
40
|
}
|
|
41
41
|
/** Apply named transformers to the entries of a map-like object and yield any entries that changed. */
|
|
42
|
-
function*
|
|
42
|
+
function* _yieldTransformedEntries(existing, updates) {
|
|
43
43
|
for (const [k, t] of Object.entries(updates))
|
|
44
44
|
yield [k, transform(existing[k], t)];
|
|
45
45
|
}
|
|
46
46
|
/** Transform some of the entries of a map-like object using a set of named transformers. */
|
|
47
47
|
export function transformEntries(existing, updates, deletes) {
|
|
48
|
-
return Object.fromEntries(yieldFiltered(yieldMerged(Object.entries(existing),
|
|
48
|
+
return Object.fromEntries(yieldFiltered(yieldMerged(Object.entries(existing), _yieldTransformedEntries(existing, updates)), isKeyInArray, deletes));
|
|
49
49
|
}
|
package/util/undefined.js
CHANGED
package/util/validate.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import type { Entry } from "./entry.js";
|
|
2
|
-
import { Data,
|
|
2
|
+
import { Data, Result } from "./data.js";
|
|
3
3
|
/** Object that can validate an unknown value with its `validate()` method. */
|
|
4
4
|
export interface Validatable<T> {
|
|
5
5
|
/**
|
|
@@ -50,14 +50,6 @@ export declare function validateItems<T>(unsafeItems: Iterable<unknown>, validat
|
|
|
50
50
|
* - `feedback.details` will contain an entry for each invalid item (keyed by their count in the input iterable).
|
|
51
51
|
*/
|
|
52
52
|
export declare function validateValues<T>(unsafeValues: Iterable<Entry>, validator: Validator<T>): Generator<Entry<T>, void>;
|
|
53
|
-
/**
|
|
54
|
-
* Validate a set of object props with a set of validators.
|
|
55
|
-
*
|
|
56
|
-
* @yield Valid entries for each specified validator.
|
|
57
|
-
* @throw InvalidFeedback if one or more entries did not validate.
|
|
58
|
-
* - `feedback.details` will contain an entry for each invalid item (keyed by their count in the input iterable).
|
|
59
|
-
*/
|
|
60
|
-
export declare function validateProps<T extends Data>(unsafeData: Data, validators: Validators<T>): Generator<Prop<T>, void>;
|
|
61
53
|
/**
|
|
62
54
|
* Validate an entire object with a set of validators.
|
|
63
55
|
* - Defined props in the object will be validated against the corresponding validator.
|
|
@@ -69,7 +61,7 @@ export declare function validateProps<T extends Data>(unsafeData: Data, validato
|
|
|
69
61
|
*/
|
|
70
62
|
export declare function validateData<T extends Data>(unsafeData: Data, validators: Validators<T>): T;
|
|
71
63
|
/**
|
|
72
|
-
* Validate a data result.
|
|
64
|
+
* Validate a data result against a validator for that data.
|
|
73
65
|
* @return Valid object or `null`
|
|
74
66
|
*/
|
|
75
67
|
export declare function validateResult<T extends Data>(unsafeResult: unknown, validator: Validator<T>): Result<T>;
|
package/util/validate.js
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { Feedback, InvalidFeedback } from "../feedback/index.js";
|
|
2
2
|
import { isData, toProps } from "./data.js";
|
|
3
|
+
import { isNullish } from "./null.js";
|
|
3
4
|
/** Is a given value a validator? */
|
|
4
5
|
export const isValidator = (v) => typeof v === "function" || (isData(v) && typeof v.validate === "function");
|
|
5
6
|
/** Validate an unknown value with a validator. */
|
|
@@ -56,6 +57,18 @@ export function* validateValues(unsafeValues, validator) {
|
|
|
56
57
|
if (invalid)
|
|
57
58
|
throw new InvalidFeedback("Invalid items", details);
|
|
58
59
|
}
|
|
60
|
+
/**
|
|
61
|
+
* Validate an entire object with a set of validators.
|
|
62
|
+
* - Defined props in the object will be validated against the corresponding validator.
|
|
63
|
+
* - `undefined` props in the object will be set to the default value of that prop.
|
|
64
|
+
*
|
|
65
|
+
* @return Valid object.
|
|
66
|
+
* @throw InvalidFeedback if one or more entries did not validate.
|
|
67
|
+
* - `feedback.details` will contain an entry for each invalid item (keyed by their count in the input iterable).
|
|
68
|
+
*/
|
|
69
|
+
export function validateData(unsafeData, validators) {
|
|
70
|
+
return Object.fromEntries(_yieldValidatedProps(unsafeData, validators));
|
|
71
|
+
}
|
|
59
72
|
/**
|
|
60
73
|
* Validate a set of object props with a set of validators.
|
|
61
74
|
*
|
|
@@ -63,7 +76,7 @@ export function* validateValues(unsafeValues, validator) {
|
|
|
63
76
|
* @throw InvalidFeedback if one or more entries did not validate.
|
|
64
77
|
* - `feedback.details` will contain an entry for each invalid item (keyed by their count in the input iterable).
|
|
65
78
|
*/
|
|
66
|
-
|
|
79
|
+
function* _yieldValidatedProps(unsafeData, validators) {
|
|
67
80
|
let invalid = false;
|
|
68
81
|
const details = {};
|
|
69
82
|
for (const [k, validator] of toProps(validators)) {
|
|
@@ -83,21 +96,9 @@ export function* validateProps(unsafeData, validators) {
|
|
|
83
96
|
throw new InvalidFeedback("Invalid data", details);
|
|
84
97
|
}
|
|
85
98
|
/**
|
|
86
|
-
* Validate
|
|
87
|
-
* - Defined props in the object will be validated against the corresponding validator.
|
|
88
|
-
* - `undefined` props in the object will be set to the default value of that prop.
|
|
89
|
-
*
|
|
90
|
-
* @return Valid object.
|
|
91
|
-
* @throw InvalidFeedback if one or more entries did not validate.
|
|
92
|
-
* - `feedback.details` will contain an entry for each invalid item (keyed by their count in the input iterable).
|
|
93
|
-
*/
|
|
94
|
-
export function validateData(unsafeData, validators) {
|
|
95
|
-
return Object.fromEntries(validateProps(unsafeData, validators));
|
|
96
|
-
}
|
|
97
|
-
/**
|
|
98
|
-
* Validate a data result.
|
|
99
|
+
* Validate a data result against a validator for that data.
|
|
99
100
|
* @return Valid object or `null`
|
|
100
101
|
*/
|
|
101
102
|
export function validateResult(unsafeResult, validator) {
|
|
102
|
-
return unsafeResult
|
|
103
|
+
return !isNullish(unsafeResult) ? validate(unsafeResult, validator) : null;
|
|
103
104
|
}
|