shelving 1.69.0 → 1.71.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (75) hide show
  1. package/api/Resource.js +2 -2
  2. package/error/ValidationError.js +2 -1
  3. package/feedback/Feedback.d.ts +4 -16
  4. package/feedback/Feedback.js +8 -31
  5. package/markup/index.d.ts +1 -1
  6. package/markup/index.js +1 -1
  7. package/markup/options.d.ts +16 -0
  8. package/markup/options.js +9 -0
  9. package/markup/render.d.ts +4 -4
  10. package/markup/render.js +95 -123
  11. package/markup/rules.d.ts +113 -33
  12. package/markup/rules.js +195 -130
  13. package/observe/Subject.d.ts +1 -1
  14. package/package.json +2 -2
  15. package/query/Filter.d.ts +1 -1
  16. package/query/Filter.js +3 -2
  17. package/react/useDocument.js +3 -4
  18. package/react/useQuery.js +3 -4
  19. package/schema/AllowSchema.js +4 -4
  20. package/schema/ArraySchema.js +2 -2
  21. package/schema/LinkSchema.js +2 -2
  22. package/schema/index.d.ts +0 -1
  23. package/schema/index.js +0 -1
  24. package/test/util.d.ts +3 -2
  25. package/update/DataUpdate.js +4 -6
  26. package/util/array.d.ts +1 -2
  27. package/util/array.js +7 -5
  28. package/util/clone.js +2 -2
  29. package/util/color.js +3 -2
  30. package/util/data.d.ts +13 -2
  31. package/util/data.js +19 -0
  32. package/util/date.d.ts +17 -16
  33. package/util/date.js +4 -3
  34. package/util/debug.d.ts +15 -1
  35. package/util/debug.js +62 -22
  36. package/util/equal.d.ts +2 -2
  37. package/util/equal.js +12 -7
  38. package/util/filter.d.ts +0 -4
  39. package/util/filter.js +0 -6
  40. package/util/function.d.ts +2 -0
  41. package/util/hydrate.js +8 -7
  42. package/util/index.d.ts +1 -1
  43. package/util/index.js +1 -1
  44. package/util/iterate.d.ts +1 -1
  45. package/util/jsx.d.ts +7 -10
  46. package/util/jsx.js +15 -13
  47. package/util/map.d.ts +12 -21
  48. package/util/map.js +13 -9
  49. package/util/match.js +4 -3
  50. package/util/number.d.ts +2 -2
  51. package/util/number.js +5 -1
  52. package/util/object.d.ts +12 -8
  53. package/util/object.js +13 -12
  54. package/util/regexp.d.ts +56 -0
  55. package/util/regexp.js +60 -0
  56. package/util/set.d.ts +14 -0
  57. package/util/set.js +17 -0
  58. package/util/sort.d.ts +1 -1
  59. package/util/string.d.ts +43 -34
  60. package/util/string.js +83 -77
  61. package/util/template.d.ts +9 -4
  62. package/util/template.js +14 -20
  63. package/util/timeout.js +3 -3
  64. package/util/transform.d.ts +10 -6
  65. package/util/transform.js +5 -1
  66. package/util/units.d.ts +8 -8
  67. package/util/url.d.ts +7 -5
  68. package/util/url.js +17 -14
  69. package/util/validate.js +6 -8
  70. package/markup/types.d.ts +0 -44
  71. package/markup/types.js +0 -1
  72. package/schema/MapSchema.d.ts +0 -19
  73. package/schema/MapSchema.js +0 -29
  74. package/util/search.d.ts +0 -71
  75. package/util/search.js +0 -97
package/markup/rules.js CHANGED
@@ -1,31 +1,50 @@
1
- import { getBlockRegExp, getLineRegExp, getWrapRegExp, MATCH_BLOCK, MATCH_LINE } from "../util/search.js";
2
- import { formatUrl, toURL } from "../util/url.js";
3
- // Regular expression partials (`\` slashes must be escaped as `\\`).
4
- const BULLETS = "-*•+"; // Anything that can be a bullet (used for unordered lists and horizontal rules).
5
- // Regular expressions.
6
- const MATCH_INDENT = /^ {1,2}/gm;
1
+ /* eslint-disable import/export */
2
+ import { getBlockRegExp, getLineRegExp, getNamedRegExp, getWrapRegExp, MATCH_BLOCK, MATCH_LINE } from "../util/regexp.js";
3
+ import { formatURL, getOptionalURL } from "../util/url.js";
4
+ /** React security symbol see https://github.com/facebook/react/pull/4832 */
5
+ const $$typeof = Symbol.for("react.element");
7
6
  /**
8
7
  * Headings are single line only (don't allow multiline).
9
8
  * - 1-6 hashes then 1+ spaces, then the title.
10
9
  * - Same as Markdown syntax.
11
10
  * - Markdown's underline syntax is not supported (for simplification).
12
11
  */
12
+ export const MATCH_HEADING = getLineRegExp(`(?<prefix>#{1,6}) +(?<heading>${MATCH_LINE.source})`);
13
13
  export const HEADING_RULE = {
14
- regexp: getLineRegExp(`(#{1,6}) +(${MATCH_LINE.source})`),
15
- render: ([, prefix = "", children = ""]) => ({ type: `h${prefix.length}`, key: null, ref: null, props: { children } }),
14
+ match: input => {
15
+ const match = MATCH_HEADING.exec(input);
16
+ if (match) {
17
+ const { index, 0: first, groups } = match;
18
+ const { prefix, heading } = groups;
19
+ return { index, 0: first, groups: { level: prefix.length, heading } };
20
+ }
21
+ },
22
+ render: ({ level, heading }) => ({
23
+ type: `h${level}`,
24
+ key: null,
25
+ ref: null,
26
+ $$typeof,
27
+ props: { children: heading.trim() },
28
+ }),
16
29
  contexts: ["block"],
17
- childContext: "inline",
30
+ subcontext: "inline",
18
31
  };
19
32
  /**
20
33
  * Horizontal rules
21
- * - Same as Markdown syntax but also allows `•` bullet character (in addition to `-` dash, `+` plus, `*` asterisk, and `_` underscore).
34
+ * - Same as Markdown syntax but also allows `•` bullet character (in addition to `-` dash, `+` plus, `*` asterisk, `_` underscore).
22
35
  * - Character must be repeated three (or more) times.
23
36
  * - Character must be the same every time (can't mix)
24
37
  * - Might have infinite number of spaces between the characters.
25
38
  */
26
39
  export const HORIZONTAL_RULE = {
27
- regexp: getLineRegExp(`([${BULLETS}])(?: *\\1){2,}`),
28
- render: () => ({ type: "hr", key: null, ref: null, props: {} }),
40
+ match: getLineRegExp(`([-*•+_=])(?: *\\1){2,}`),
41
+ render: () => ({
42
+ type: "hr",
43
+ key: null,
44
+ ref: null,
45
+ $$typeof,
46
+ props: {},
47
+ }),
29
48
  contexts: ["block"],
30
49
  };
31
50
  /**
@@ -35,64 +54,81 @@ export const HORIZONTAL_RULE = {
35
54
  * - Lists can be created with `•` bullet characters (in addition to `-` dash, `+` plus, and `*` asterisk).
36
55
  * - Second-level list can be indented with 1-2 spaces.
37
56
  */
38
- const UNORDERED = `[${BULLETS}] +`; // Anything that can be a bullet (used for unordered lists and horizontal rules).
39
- export const UNORDERED_LIST_RULE = {
40
- regexp: getBlockRegExp(`${UNORDERED}(${MATCH_BLOCK.source})`),
41
- render: ([, list = ""]) => {
42
- const children = list.split(SPLIT_UL_ITEMS).map(_mapUnorderedItem);
43
- return { type: "ul", key: null, ref: null, props: { children } };
44
- },
57
+ const UNORDERED_PREFIX = `[-*•+] +`;
58
+ const UNORDERED_SPLIT = new RegExp(`\n+${UNORDERED_PREFIX}`, "g");
59
+ const UNORDERED_INDENT = /^\t/gm;
60
+ export const UNORDERED_RULE = {
61
+ match: getBlockRegExp(`${UNORDERED_PREFIX}(?<list>${MATCH_BLOCK.source})`),
62
+ render: ({ list }) => ({
63
+ type: "ul",
64
+ key: null,
65
+ ref: null,
66
+ $$typeof,
67
+ props: { children: list.split(UNORDERED_SPLIT).map(_mapUnordered) },
68
+ }),
45
69
  contexts: ["block", "list"],
46
- childContext: "list",
47
- };
48
- const SPLIT_UL_ITEMS = new RegExp(`\\n+${UNORDERED}`, "g");
49
- const _mapUnorderedItem = (item, key) => {
50
- const children = item.replace(MATCH_INDENT, "");
51
- return { type: "li", key, ref: null, props: { children } };
70
+ subcontext: "list",
52
71
  };
72
+ const _mapUnordered = (item, key) => ({
73
+ type: "li",
74
+ key,
75
+ ref: null,
76
+ $$typeof,
77
+ props: { children: item.replace(UNORDERED_INDENT, "") },
78
+ });
53
79
  /**
54
80
  * Ordered list.
55
81
  * - No leading spaces are allowed for the top-level list.
56
82
  * - Second-level list can be indented with 1-3 spaces.
57
83
  */
58
- const ORDERED = "[0-9]+[.):] +"; // Number for a numbered list (e.g. `1.` or `2)` or `3:`)
59
- export const ORDERED_LIST_RULE = {
60
- regexp: getBlockRegExp(`(${ORDERED}${MATCH_BLOCK.source})`),
61
- render: ([, list = ""]) => {
62
- const children = list.split(SPLIT_OL_ITEMS).map(_mapOrderedItem);
63
- return { type: "ol", key: null, ref: null, props: { children } };
64
- },
84
+ const ORDERED_PREFIX = "[1-9][0-9]{0,8}[.):] +"; // Number for a numbered list, e.g. `1.` or `2)` or `3:`
85
+ const ORDERED_SPLIT = new RegExp(`\n+(?=${ORDERED_PREFIX})`, "g");
86
+ const ORDERED_INDENT = UNORDERED_INDENT;
87
+ export const ORDERED_RULE = {
88
+ match: getBlockRegExp(`(?<list>${ORDERED_PREFIX}${MATCH_BLOCK.source})`),
89
+ render: ({ list }) => ({
90
+ type: "ol",
91
+ key: null,
92
+ ref: null,
93
+ $$typeof,
94
+ props: { children: list.split(ORDERED_SPLIT).map(_mapOrdered) },
95
+ }),
65
96
  contexts: ["block", "list"],
66
- childContext: "list",
67
- };
68
- const SPLIT_OL_ITEMS = new RegExp(`\\n+(?=${ORDERED})`, "g");
69
- const _mapOrderedItem = (item, key) => {
70
- const firstSpace = item.indexOf(" ");
71
- const value = parseInt(item.slice(0, firstSpace), 10);
72
- const children = item
73
- .slice(firstSpace + 1)
74
- .trimStart()
75
- .replace(MATCH_INDENT, "");
76
- return { type: "li", key, ref: null, props: { value, children } };
97
+ subcontext: "list",
77
98
  };
99
+ const _mapOrdered = (item, key) => ({
100
+ type: "li",
101
+ key,
102
+ ref: null,
103
+ $$typeof,
104
+ props: {
105
+ value: parseInt(item, 10),
106
+ children: item
107
+ .slice(item.indexOf(" ") + 1)
108
+ .trim()
109
+ .replace(ORDERED_INDENT, ""),
110
+ },
111
+ });
78
112
  /**
79
113
  * Blockquote block.
80
114
  * - Same as Markdown's syntax.
81
115
  * - Block continues until it finds a line that doesn't start with `>`
82
116
  * - Quote indent symbol can be followed by zero or more spaces.
83
117
  */
118
+ const BLOCKQUOTE_PREFIX = "> *";
119
+ const BLOCKQUOTE_INDENT = new RegExp(`^${BLOCKQUOTE_PREFIX}`, "gm");
84
120
  export const BLOCKQUOTE_RULE = {
85
- regexp: getLineRegExp(`(>${MATCH_LINE.source}(?:\\n>${MATCH_LINE.source})*)`),
86
- render: ([, quote = ""]) => ({
121
+ match: getLineRegExp(`(?<quote>${BLOCKQUOTE_PREFIX}${MATCH_LINE.source}(?:\n${BLOCKQUOTE_PREFIX}${MATCH_LINE.source})*)`),
122
+ render: ({ quote }) => ({
87
123
  type: "blockquote",
88
124
  key: null,
89
125
  ref: null,
90
- props: { children: quote.replace(BLOCKQUOTE_LINES, "") },
126
+ $$typeof,
127
+ props: { children: quote.replace(BLOCKQUOTE_INDENT, "") },
91
128
  }),
92
129
  contexts: ["block", "list"],
93
- childContext: "block",
130
+ subcontext: "block",
94
131
  };
95
- const BLOCKQUOTE_LINES = /^>/gm;
96
132
  /**
97
133
  * Fenced code blocks
98
134
  * - Same as Markdown syntax.
@@ -102,95 +138,85 @@ const BLOCKQUOTE_LINES = /^>/gm;
102
138
  */
103
139
  export const FENCED_CODE_RULE = {
104
140
  // Matcher has its own end that only stops when it reaches a matching closing fence or the end of the string.
105
- regexp: getBlockRegExp(`(\`{3,}|~{3,}) *(${MATCH_LINE.source})\\n(${MATCH_BLOCK.source})`, `\\n\\1\\n+|\\n\\1$|$`),
106
- render: ([, , file, children]) => ({
141
+ match: getBlockRegExp(`(\`{3,}|~{3,}) *(?<title>${MATCH_LINE.source})\n(?<code>${MATCH_BLOCK.source})`, `\n\\1\n+|\n\\1$|$`),
142
+ render: ({ title, code }) => ({
107
143
  type: "pre",
108
144
  key: null,
109
145
  ref: null,
146
+ $$typeof,
110
147
  props: {
111
148
  children: {
112
149
  type: "code",
113
150
  key: null,
114
151
  ref: null,
115
- props: { "data-file": file || undefined, children },
152
+ $$typeof,
153
+ props: { title: (title === null || title === void 0 ? void 0 : title.trim()) || undefined, children: code.trim() },
116
154
  },
117
155
  },
118
156
  }),
119
157
  contexts: ["block", "list"],
158
+ priority: 10, // Higher priority than other blocks so e.g. lists inside fenced code don't become lists.
120
159
  };
121
160
  /**
122
161
  * Paragraph.
123
162
  * - When ordering rules, paragraph should go after other "block" context elements (because it has a very generous capture).
124
163
  */
125
164
  export const PARAGRAPH_RULE = {
126
- regexp: getBlockRegExp(` *(${MATCH_BLOCK.source})`),
127
- render: ([, children]) => ({ type: `p`, key: null, ref: null, props: { children } }),
165
+ match: getBlockRegExp(`(?<paragraph>${MATCH_BLOCK.source})`),
166
+ render: ({ paragraph }) => ({
167
+ type: `p`,
168
+ key: null,
169
+ ref: null,
170
+ $$typeof,
171
+ props: { children: paragraph.trim() },
172
+ }),
128
173
  contexts: ["block"],
129
- childContext: "inline",
174
+ subcontext: "inline",
130
175
  priority: -10, // Lower precedence than other blocks so it matches last and paragraphs can be broken by other blocks.
131
176
  };
132
177
  /**
133
- * Markdown-style link.
134
- * - Link in standard Markdown format, e.g. `[Google Maps](http://google.com/maps)`
178
+ * Autolinked URL starts with `http:` or `https:` or `mailto:` (any scheme in `options.schemes`) and matches an unlimited number of non-space characters.
179
+ * - 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).
135
180
  * - If no title is specified a cleaned up version of the URL will be used, e.g. `google.com/maps`
136
- * - Does not need space before/after the link.
137
181
  * - If link is not valid (using `new URL(url)` then unparsed text will be returned.
138
- * - For security only `http://` or `https://` links will work (if invalid the unparsed text will be returned).
182
+ * - For security only schemes that appear in `options.schemes` will match (defaults to `http:` and `https:`).
139
183
  */
140
- export const LINK_RULE = {
141
- regexp: /\[([^\]]*?)\]\(([^)]*?)\)/,
142
- // Custom matcher to check the URL against the allowed schemes.
143
- match: (content, { schemes, url: base }) => {
144
- const matches = content.match(LINK_RULE.regexp);
145
- if (matches) {
146
- const [, title = "", href = ""] = matches;
147
- const url = toURL(href, base);
148
- if (url && url.protocol && schemes.includes(url.protocol)) {
149
- matches[1] = title.trim();
150
- matches[2] = url.href;
151
- return matches;
152
- }
153
- }
154
- },
155
- render: ([, title, href = ""], { rel }) => ({
184
+ export const URL_CHAR = "[-$_@.&!*,=;/#?:%a-zA-Z0-9]";
185
+ export const URL_MATCH = getNamedRegExp(`(?<href>[a-z]+:${URL_CHAR}+)(?: +(?:\\((?<title>[^)]*?)\\)))?`);
186
+ export const URL_RULE = {
187
+ match: (input, options) => _urlMatch(URL_MATCH.exec(input), options),
188
+ render: ({ href, title }, { rel }) => ({
156
189
  type: "a",
157
190
  key: null,
158
191
  ref: null,
159
- props: { children: title || formatUrl(href), href, rel },
192
+ $$typeof,
193
+ props: { children: title, href, rel },
160
194
  }),
161
195
  contexts: ["inline", "list"],
162
- childContext: "link",
196
+ subcontext: "link",
163
197
  };
198
+ function _urlMatch(match, { schemes, url: base }) {
199
+ if (match) {
200
+ const { 0: first, index, groups } = match;
201
+ const { href, title } = groups;
202
+ const url = getOptionalURL(href, base);
203
+ if (url && schemes.includes(url.protocol)) {
204
+ return { 0: first, index, groups: { href: url.href, title: (title === null || title === void 0 ? void 0 : title.trim()) || formatURL(url) } };
205
+ }
206
+ }
207
+ }
164
208
  /**
165
- * Autolinked URL starts with `http:` or `https:` or `mailto:` (any scheme in `options.schemes`) and matches an unlimited number of non-space characters.
166
- * - 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).
209
+ * Markdown-style link.
210
+ * - Link in standard Markdown format, e.g. `[Google Maps](http://google.com/maps)`
167
211
  * - If no title is specified a cleaned up version of the URL will be used, e.g. `google.com/maps`
212
+ * - Does not need space before/after the link.
168
213
  * - If link is not valid (using `new URL(url)` then unparsed text will be returned.
169
- * - For security only schemes that appear in `options.schemes` will match (defaults to `http:` and `https:`).
214
+ * - For security only `http://` or `https://` links will work (if invalid the unparsed text will be returned).
170
215
  */
171
- export const AUTOLINK_RULE = {
172
- regexp: /([a-z]+:\S+)(?: +(?:\(([^)]*?)\)|\[([^\]]*?)\]))?/,
173
- // Custom matcher to check the URL against the allowed schemes.
174
- match: (content, { schemes, url: base }) => {
175
- const matches = content.match(AUTOLINK_RULE.regexp);
176
- if (matches && typeof matches.index === "number") {
177
- const [, href = "", roundTitle = "", squareTitle = ""] = matches;
178
- const url = toURL(href, base);
179
- if (url && url.protocol && schemes.includes(url.protocol)) {
180
- matches[1] = url.href;
181
- matches[2] = (roundTitle || squareTitle).trim();
182
- return matches;
183
- }
184
- }
185
- },
186
- render: ([, href = "", title], { rel }) => ({
187
- type: "a",
188
- key: null,
189
- ref: null,
190
- props: { children: title || formatUrl(href), href, rel },
191
- }),
192
- contexts: ["inline", "list"],
193
- childContext: "link",
216
+ export const LINK_MATCH = getNamedRegExp(/\[(?<title>[^\]]*?)\]\((?<href>[^)]*?)\)/);
217
+ export const LINK_RULE = {
218
+ ...URL_RULE,
219
+ match: (input, options) => _urlMatch(LINK_MATCH.exec(input), options),
194
220
  };
195
221
  /**
196
222
  * Inline code.
@@ -200,10 +226,16 @@ export const AUTOLINK_RULE = {
200
226
  * - Same as Markdown syntax.
201
227
  */
202
228
  export const CODE_RULE = {
203
- regexp: getWrapRegExp("`+", MATCH_BLOCK.source),
204
- render: ([, , children]) => ({ type: "code", key: null, ref: null, props: { children } }),
229
+ match: getWrapRegExp("`+", MATCH_BLOCK.source),
230
+ render: ({ text }) => ({
231
+ type: "code",
232
+ key: null,
233
+ ref: null,
234
+ $$typeof,
235
+ props: { children: text },
236
+ }),
205
237
  contexts: ["inline", "list"],
206
- 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.")
238
+ priority: 10, // Higher priority than e.g. `strong` or `em` (from CommonMark spec: "Code span backticks have higher precedence than any other inline constructs except HTML tags and autolinks.")
207
239
  };
208
240
  /**
209
241
  * Inline strong.
@@ -214,10 +246,16 @@ export const CODE_RULE = {
214
246
  * - Different to Markdown: strong is always surrounded by `*asterisks*` and emphasis is always surrounded by `_underscores_` (strong isn't 'double emphasis').
215
247
  */
216
248
  export const STRONG_RULE = {
217
- regexp: getWrapRegExp("\\*+"),
218
- render: ([, , children]) => ({ type: "strong", key: null, ref: null, props: { children } }),
249
+ match: getWrapRegExp("\\*+"),
250
+ render: ({ text }) => ({
251
+ type: "strong",
252
+ key: null,
253
+ ref: null,
254
+ $$typeof,
255
+ props: { children: text },
256
+ }),
219
257
  contexts: ["inline", "list", "link"],
220
- childContext: "inline",
258
+ subcontext: "inline",
221
259
  };
222
260
  /**
223
261
  * Inline emphasis.
@@ -228,24 +266,36 @@ export const STRONG_RULE = {
228
266
  * - Different to Markdown: strong is always surrounded by `*asterisks*` and emphasis is always surrounded by `_underscores_` (strong isn't 'double emphasis').
229
267
  */
230
268
  export const EMPHASIS_RULE = {
231
- regexp: getWrapRegExp("_+"),
232
- render: ([, , children]) => ({ type: "em", key: null, ref: null, props: { children } }),
269
+ match: getWrapRegExp("_+"),
270
+ render: ({ text }) => ({
271
+ type: "em",
272
+ key: null,
273
+ ref: null,
274
+ $$typeof,
275
+ props: { children: text },
276
+ }),
233
277
  contexts: ["inline", "list", "link"],
234
- childContext: "inline",
278
+ subcontext: "inline",
235
279
  };
236
280
  /**
237
281
  * Inserted text (`<ins>` tag),
238
282
  * - Inline text wrapped in two or more `++` pluses.
239
- * - Works inside words (e.g. `magi+karp+carp`).
283
+ * - Works inside words (e.g. `magi++karp++carp`).
240
284
  * - Whitespace cannot be the first or last character of the element (e.g. `+ abc +` will not work).
241
285
  * - Closing characters must exactly match opening characters.
242
286
  * - Markdown doesn't have this.
243
287
  */
244
288
  export const INSERT_RULE = {
245
- regexp: getWrapRegExp("\\+\\++"),
246
- render: ([, , children]) => ({ type: "ins", key: null, ref: null, props: { children } }),
289
+ match: getWrapRegExp("\\+\\++"),
290
+ render: ({ text }) => ({
291
+ type: "ins",
292
+ key: null,
293
+ ref: null,
294
+ $$typeof,
295
+ props: { children: text },
296
+ }),
247
297
  contexts: ["inline", "list", "link"],
248
- childContext: "inline",
298
+ subcontext: "inline",
249
299
  };
250
300
  /**
251
301
  * Deleted text (`<del>` tag),
@@ -256,10 +306,16 @@ export const INSERT_RULE = {
256
306
  * - Markdown doesn't have this.
257
307
  */
258
308
  export const DELETE_RULE = {
259
- regexp: getWrapRegExp("--+|~~+"),
260
- render: ([, , children]) => ({ type: "del", key: null, ref: null, props: { children } }),
309
+ match: getWrapRegExp("--+|~~+"),
310
+ render: ({ text }) => ({
311
+ type: "del",
312
+ key: null,
313
+ ref: null,
314
+ $$typeof,
315
+ props: { children: text },
316
+ }),
261
317
  contexts: ["inline", "list", "link"],
262
- childContext: "inline",
318
+ subcontext: "inline",
263
319
  };
264
320
  /**
265
321
  * Hard linebreak (`<br />` tag).
@@ -270,10 +326,16 @@ export const DELETE_RULE = {
270
326
  * - This works better with textareas that wrap text (since manually breaking up long lines is no longer necessary).
271
327
  */
272
328
  export const LINEBREAK_RULE = {
273
- regexp: /\n/,
274
- render: () => ({ type: "br", key: null, ref: null, props: {} }),
329
+ match: /\n/,
330
+ render: () => ({
331
+ type: "br",
332
+ key: null,
333
+ ref: null,
334
+ $$typeof,
335
+ props: {},
336
+ }),
275
337
  contexts: ["inline", "list", "link"],
276
- childContext: "inline",
338
+ subcontext: "inline",
277
339
  };
278
340
  /**
279
341
  * All markup rules.
@@ -287,13 +349,13 @@ export const LINEBREAK_RULE = {
287
349
  export const MARKUP_RULES = [
288
350
  HEADING_RULE,
289
351
  HORIZONTAL_RULE,
290
- UNORDERED_LIST_RULE,
291
- ORDERED_LIST_RULE,
352
+ UNORDERED_RULE,
353
+ ORDERED_RULE,
292
354
  BLOCKQUOTE_RULE,
293
355
  FENCED_CODE_RULE,
294
356
  PARAGRAPH_RULE,
295
357
  LINK_RULE,
296
- AUTOLINK_RULE,
358
+ URL_RULE,
297
359
  CODE_RULE,
298
360
  STRONG_RULE,
299
361
  EMPHASIS_RULE,
@@ -305,34 +367,37 @@ export const MARKUP_RULES = [
305
367
  export const MARKUP_RULES_BLOCK = [
306
368
  HEADING_RULE,
307
369
  HORIZONTAL_RULE,
308
- UNORDERED_LIST_RULE,
309
- ORDERED_LIST_RULE,
370
+ UNORDERED_RULE,
371
+ ORDERED_RULE,
310
372
  BLOCKQUOTE_RULE,
311
373
  FENCED_CODE_RULE,
312
374
  PARAGRAPH_RULE,
375
+ //
313
376
  ];
314
377
  /** Subset of markup rules that work in an inline context. */
315
378
  export const MARKUP_RULES_INLINE = [
316
379
  LINK_RULE,
317
- AUTOLINK_RULE,
380
+ URL_RULE,
318
381
  CODE_RULE,
319
382
  STRONG_RULE,
320
383
  EMPHASIS_RULE,
321
384
  INSERT_RULE,
322
385
  DELETE_RULE,
323
386
  LINEBREAK_RULE,
387
+ //
324
388
  ];
325
389
  /** Subset of markup rules that are relevant for collapsed shortform content. */
326
390
  export const MARKUP_RULES_SHORTFORM = [
327
- UNORDERED_LIST_RULE,
328
- ORDERED_LIST_RULE,
391
+ UNORDERED_RULE,
392
+ ORDERED_RULE,
329
393
  PARAGRAPH_RULE,
330
394
  LINK_RULE,
331
- AUTOLINK_RULE,
395
+ URL_RULE,
332
396
  CODE_RULE,
333
397
  STRONG_RULE,
334
398
  EMPHASIS_RULE,
335
399
  INSERT_RULE,
336
400
  DELETE_RULE,
337
401
  LINEBREAK_RULE,
402
+ //
338
403
  ];
@@ -24,7 +24,7 @@ export declare class Subject<T> implements Observable<T>, ConnectableObserver<T>
24
24
  error(reason: Error | unknown): void;
25
25
  complete(): void;
26
26
  /** Close this subject (called by `error()` and `complete()`). */
27
- private _close;
27
+ protected _close(): void;
28
28
  /** Connect this subject to a source. */
29
29
  connect(source: Subscribable<T>): Unsubscribe;
30
30
  /** Disconnect this subject from all sources. */
package/package.json CHANGED
@@ -11,7 +11,7 @@
11
11
  "state-management",
12
12
  "query-builder"
13
13
  ],
14
- "version": "1.69.0",
14
+ "version": "1.71.0",
15
15
  "repository": "https://github.com/dhoulb/shelving",
16
16
  "author": "Dave Houlbrooke <dave@shax.com>",
17
17
  "license": "0BSD",
@@ -62,7 +62,7 @@
62
62
  },
63
63
  "devDependencies": {
64
64
  "@google-cloud/firestore": "^5.0.2",
65
- "@types/jest": "^27.5.1",
65
+ "@types/jest": "^28.1.6",
66
66
  "@types/react": "^18.0.9",
67
67
  "@types/react-dom": "^18.0.4",
68
68
  "@typescript-eslint/eslint-plugin": "^5.23.0",
package/query/Filter.d.ts CHANGED
@@ -1,4 +1,4 @@
1
- import type { ArrayType, ImmutableArray } from "../util/array.js";
1
+ import { ArrayType, ImmutableArray } from "../util/array.js";
2
2
  import { Data, Key } from "../util/data.js";
3
3
  import { Matchable } from "../util/match.js";
4
4
  import { Rule } from "./Rule.js";
package/query/Filter.js CHANGED
@@ -1,3 +1,4 @@
1
+ import { isArray } from "../util/array.js";
1
2
  import { getProp } from "../util/data.js";
2
3
  import { isArrayWith, isEqual, isEqualGreater, isEqualLess, isGreater, isInArray, isLess, notEqual, notInArray } from "../util/match.js";
3
4
  import { filterItems } from "../util/filter.js";
@@ -31,7 +32,7 @@ export class Filter extends Rule {
31
32
  /** Parse a set of FilterProps and return the corresponding array of `Filter` instances. */
32
33
  static on(key, value) {
33
34
  return key.startsWith("!")
34
- ? new Filter(key.slice(1), value instanceof Array ? "OUT" : "NOT", value)
35
+ ? new Filter(key.slice(1), isArray(value) ? "OUT" : "NOT", value)
35
36
  : key.endsWith(">")
36
37
  ? new Filter(key.slice(0, -1), "GT", value)
37
38
  : key.endsWith(">=")
@@ -42,7 +43,7 @@ export class Filter extends Rule {
42
43
  ? new Filter(key.slice(0, -2), "LTE", value)
43
44
  : key.endsWith("[]")
44
45
  ? new Filter(key.slice(0, -2), "CONTAINS", value)
45
- : new Filter(key, value instanceof Array ? "IN" : "IS", value);
46
+ : new Filter(key, isArray(value) ? "IN" : "IS", value);
46
47
  }
47
48
  match(item) {
48
49
  return MATCHERS[this.operator](getProp(item, this.key), this.value);
@@ -1,4 +1,4 @@
1
- import { reduceMapItem } from "../util/map.js";
1
+ import { setMapItem } from "../util/map.js";
2
2
  import { getDocumentData } from "../db/Reference.js";
3
3
  import { CacheProvider } from "../provider/CacheProvider.js";
4
4
  import { getOptionalSourceProvider } from "../provider/ThroughProvider.js";
@@ -76,11 +76,10 @@ export class DocumentState extends State {
76
76
  this.disconnect();
77
77
  }
78
78
  }
79
- /** Reuse the previous `DocumentState` or create a new one. */
80
- const _reduceDocumentState = (existing, ref) => existing || new DocumentState(ref);
81
79
  export function useDocument(ref) {
82
80
  const cache = useCache();
83
- const state = ref ? reduceMapItem(cache, ref.toString(), _reduceDocumentState, ref) : undefined;
81
+ const key = ref === null || ref === void 0 ? void 0 : ref.toString();
82
+ const state = ref && key ? cache.get(key) || setMapItem(cache, key, new DocumentState(ref)) : undefined;
84
83
  useSubscribe(state);
85
84
  return state;
86
85
  }
package/react/useQuery.js CHANGED
@@ -1,4 +1,4 @@
1
- import { reduceMapItem } from "../util/map.js";
1
+ import { setMapItem } from "../util/map.js";
2
2
  import { getQueryFirstData, getQueryFirstValue } from "../db/Reference.js";
3
3
  import { CacheProvider } from "../provider/CacheProvider.js";
4
4
  import { getOptionalSourceProvider } from "../provider/ThroughProvider.js";
@@ -123,11 +123,10 @@ export class QueryState extends State {
123
123
  }
124
124
  }
125
125
  }
126
- /** Reuse the previous `QueryState` or create a new one. */
127
- const _reduceQueryState = (existing, ref) => existing || new QueryState(ref);
128
126
  export function useQuery(ref) {
129
127
  const cache = useCache();
130
- const state = ref ? reduceMapItem(cache, ref.toString(), _reduceQueryState, ref) : undefined;
128
+ const key = ref === null || ref === void 0 ? void 0 : ref.toString();
129
+ const state = ref && key ? cache.get(key) || setMapItem(cache, key, new QueryState(ref)) : undefined;
131
130
  useSubscribe(state);
132
131
  return state;
133
132
  }
@@ -1,17 +1,17 @@
1
1
  import { InvalidFeedback } from "../feedback/InvalidFeedback.js";
2
2
  import { getString } from "../util/string.js";
3
- import { isItem } from "../util/array.js";
3
+ import { isArray, isItem } from "../util/array.js";
4
4
  import { getOptionalNumber } from "../util/number.js";
5
- import { isKey } from "../util/object.js";
5
+ import { isEntry } from "../util/object.js";
6
6
  import { Schema } from "./Schema.js";
7
7
  /** Validate a value against a specific set of allowed values. */
8
8
  export function validateAllowed(value, allowed) {
9
- if (allowed instanceof Array) {
9
+ if (isArray(allowed)) {
10
10
  if (isItem(allowed, value))
11
11
  return value;
12
12
  }
13
13
  else {
14
- if (isKey(allowed, value))
14
+ if (isEntry(allowed, value))
15
15
  return value;
16
16
  }
17
17
  throw new InvalidFeedback("Unknown value", { value });
@@ -1,5 +1,5 @@
1
1
  import { InvalidFeedback } from "../feedback/InvalidFeedback.js";
2
- import { uniqueArray } from "../util/array.js";
2
+ import { isArray, uniqueArray } from "../util/array.js";
3
3
  import { validateArray } from "../util/validate.js";
4
4
  import { Schema } from "./Schema.js";
5
5
  /**
@@ -39,7 +39,7 @@ export class ArraySchema extends Schema {
39
39
  this.max = max;
40
40
  }
41
41
  validate(unsafeValue = this.value) {
42
- if (!(unsafeValue instanceof Array))
42
+ if (!isArray(unsafeValue))
43
43
  throw new InvalidFeedback("Must be array", { value: unsafeValue });
44
44
  const safeArray = validateArray(unsafeValue, this.items);
45
45
  const dedupedArray = this.unique ? uniqueArray(safeArray) : safeArray;
@@ -1,5 +1,5 @@
1
1
  import { InvalidFeedback } from "../feedback/InvalidFeedback.js";
2
- import { toURL } from "../util/url.js";
2
+ import { getOptionalURL } from "../util/url.js";
3
3
  import { OPTIONAL } from "./OptionalSchema.js";
4
4
  import { StringSchema } from "./StringSchema.js";
5
5
  /**
@@ -22,7 +22,7 @@ export class LinkSchema extends StringSchema {
22
22
  // Override to clean the URL using the builtin `URL` class and check the schemes and hosts against the whitelists.
23
23
  validate(unsafeValue) {
24
24
  const string = super.validate(unsafeValue);
25
- const url = toURL(super.sanitize(string));
25
+ const url = getOptionalURL(super.sanitize(string));
26
26
  if (!url)
27
27
  throw new InvalidFeedback(string ? "Invalid format" : "Required", { value: string });
28
28
  if (!this.schemes.includes(url.protocol))