@es-joy/jsoe 0.30.0 → 0.32.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.
- package/CHANGES.md +9 -0
- package/README.md +1 -0
- package/dist/index.js +1 -1
- package/dist/index.js.map +1 -1
- package/dist/search/fundamentalTypes/arraySearchType.d.ts.map +1 -1
- package/dist/search/fundamentalTypes/bigintSearchType.d.ts.map +1 -1
- package/dist/search/fundamentalTypes/blobSearchType.d.ts.map +1 -1
- package/dist/search/fundamentalTypes/booleanSearchType.d.ts.map +1 -1
- package/dist/search/fundamentalTypes/catchSearchType.d.ts.map +1 -1
- package/dist/search/fundamentalTypes/dateSearchType.d.ts.map +1 -1
- package/dist/search/fundamentalTypes/domexceptionSearchType.d.ts.map +1 -1
- package/dist/search/fundamentalTypes/enumSearchType.d.ts.map +1 -1
- package/dist/search/fundamentalTypes/fileSearchType.d.ts.map +1 -1
- package/dist/search/fundamentalTypes/filelistSearchType.d.ts.map +1 -1
- package/dist/search/fundamentalTypes/functionSearchType.d.ts.map +1 -1
- package/dist/search/fundamentalTypes/mapSearchType.d.ts.map +1 -1
- package/dist/search/fundamentalTypes/numberSearchType.d.ts.map +1 -1
- package/dist/search/fundamentalTypes/objectSearchType.d.ts.map +1 -1
- package/dist/search/fundamentalTypes/promiseSearchType.d.ts.map +1 -1
- package/dist/search/fundamentalTypes/regexpSearchType.d.ts.map +1 -1
- package/dist/search/fundamentalTypes/setSearchType.d.ts.map +1 -1
- package/dist/search/fundamentalTypes/stringSearchType.d.ts.map +1 -1
- package/dist/search/fundamentalTypes/symbolSearchType.d.ts.map +1 -1
- package/dist/search/index.d.ts +2 -0
- package/dist/search/index.d.ts.map +1 -1
- package/dist/search/queryTree.d.ts +3 -0
- package/dist/search/queryTree.d.ts.map +1 -1
- package/dist/search/queryTreeBuilders.d.ts +2 -1
- package/dist/search/queryTreeBuilders.d.ts.map +1 -1
- package/dist/search/searchDispatch.d.ts +11 -3
- package/dist/search/searchDispatch.d.ts.map +1 -1
- package/dist/search/searchElementUtils.d.ts +31 -1
- package/dist/search/searchElementUtils.d.ts.map +1 -1
- package/dist/search/searchUtils.d.ts +197 -10
- package/dist/search/searchUtils.d.ts.map +1 -1
- package/dist/search/subTypes/blobHTMLSearchType.d.ts +11 -4
- package/dist/search/subTypes/blobHTMLSearchType.d.ts.map +1 -1
- package/dist/search/subTypes/recordSearchType.d.ts.map +1 -1
- package/dist/search/subTypes/tupleSearchType.d.ts.map +1 -1
- package/dist/search/superTypes/SpecialRealNumberSearchType.d.ts.map +1 -1
- package/dist/search/superTypes/buffersourceSearchType.d.ts.map +1 -1
- package/dist/search/unions/unionFamilySearchType.d.ts.map +1 -1
- package/dist/utils/json6Stringify.d.ts +2 -0
- package/dist/utils/json6Stringify.d.ts.map +1 -0
- package/dist/utils/rawTypesonEditor.d.ts.map +1 -1
- package/package.json +3 -3
- package/pnpm-workspace.yaml +26 -26
- package/src/search/fundamentalTypes/arraySearchType.js +28 -5
- package/src/search/fundamentalTypes/bigintSearchType.js +14 -3
- package/src/search/fundamentalTypes/blobSearchType.js +21 -3
- package/src/search/fundamentalTypes/booleanSearchType.js +15 -3
- package/src/search/fundamentalTypes/catchSearchType.js +20 -3
- package/src/search/fundamentalTypes/dateSearchType.js +42 -3
- package/src/search/fundamentalTypes/domexceptionSearchType.js +18 -4
- package/src/search/fundamentalTypes/enumSearchType.js +15 -3
- package/src/search/fundamentalTypes/fileSearchType.js +15 -3
- package/src/search/fundamentalTypes/filelistSearchType.js +25 -5
- package/src/search/fundamentalTypes/functionSearchType.js +31 -4
- package/src/search/fundamentalTypes/mapSearchType.js +33 -5
- package/src/search/fundamentalTypes/numberSearchType.js +15 -3
- package/src/search/fundamentalTypes/objectSearchType.js +94 -5
- package/src/search/fundamentalTypes/promiseSearchType.js +20 -3
- package/src/search/fundamentalTypes/regexpSearchType.js +24 -4
- package/src/search/fundamentalTypes/setSearchType.js +25 -5
- package/src/search/fundamentalTypes/stringSearchType.js +25 -3
- package/src/search/fundamentalTypes/symbolSearchType.js +21 -3
- package/src/search/index.js +22 -0
- package/src/search/queryTree.js +4 -1
- package/src/search/queryTreeBuilders.js +3 -2
- package/src/search/searchDispatch.js +14 -5
- package/src/search/searchElementUtils.js +98 -10
- package/src/search/searchUtils.js +440 -12
- package/src/search/subTypes/blobHTMLSearchType.js +186 -10
- package/src/search/subTypes/recordSearchType.js +30 -4
- package/src/search/subTypes/tupleSearchType.js +43 -5
- package/src/search/superTypes/SpecialRealNumberSearchType.js +15 -3
- package/src/search/superTypes/buffersourceSearchType.js +14 -3
- package/src/search/unions/unionFamilySearchType.js +45 -3
- package/src/utils/json6Stringify.js +50 -0
- package/src/utils/rawTypesonEditor.js +1 -51
- package/vendor/zod/classic/schemas.d.ts +3 -1
- package/vendor/zod/core/versions.js +1 -1
|
@@ -1,13 +1,105 @@
|
|
|
1
1
|
import {
|
|
2
|
-
buildPathLabel, findOwnControl, isExemptedByAncestorHasProperty
|
|
2
|
+
buildPathLabel, findOwnControl, isExemptedByAncestorHasProperty, extractLeafOfKind
|
|
3
3
|
} from '../searchUtils.js';
|
|
4
4
|
import {makeBlobHTMLLeaf} from '../queryTreeBuilders.js';
|
|
5
|
-
import {getQueryViaElement} from '../searchElementUtils.js';
|
|
5
|
+
import {getQueryViaElement, applyQueryViaElement} from '../searchElementUtils.js';
|
|
6
|
+
import regexpType from '../../fundamentalTypes/regexpType.js';
|
|
6
7
|
|
|
7
8
|
/**
|
|
8
9
|
* @typedef {import('../searchDispatch.js').SearchTypeObject} SearchTypeObject
|
|
9
10
|
*/
|
|
10
11
|
|
|
12
|
+
/**
|
|
13
|
+
* Live syntax-checks a blobHTML Value against its currently-chosen Mode -
|
|
14
|
+
* a CSS selector and an XPath expression are each tried against a detached,
|
|
15
|
+
* inert target (a fragment for the selector, the live `document` itself,
|
|
16
|
+
* required by `Element.prototype.evaluate`'s own contract, for the XPath -
|
|
17
|
+
* neither actually has to *match* anything, only parse without throwing),
|
|
18
|
+
* and a regex is tried via the same `new RegExp(value, flags)` constructor
|
|
19
|
+
* `readLiteralRegexQuery` relies on elsewhere - a "Full text search" value
|
|
20
|
+
* is a plain phrase with no format to violate, so it always passes.
|
|
21
|
+
* @param {string} mode
|
|
22
|
+
* @param {string} value
|
|
23
|
+
* @param {string} flags
|
|
24
|
+
* @returns {string}
|
|
25
|
+
*/
|
|
26
|
+
function computeBlobHTMLValueMessage (mode, value, flags) {
|
|
27
|
+
if (!value) {
|
|
28
|
+
return '';
|
|
29
|
+
}
|
|
30
|
+
try {
|
|
31
|
+
switch (mode) {
|
|
32
|
+
case 'cssSelector':
|
|
33
|
+
document.createDocumentFragment().querySelector(value);
|
|
34
|
+
break;
|
|
35
|
+
case 'xpath':
|
|
36
|
+
document.evaluate(value, document, null, XPathResult.ANY_TYPE, null);
|
|
37
|
+
break;
|
|
38
|
+
case 'rawHTMLRegex':
|
|
39
|
+
// eslint-disable-next-line no-new -- Testing
|
|
40
|
+
new RegExp(value, flags);
|
|
41
|
+
break;
|
|
42
|
+
default:
|
|
43
|
+
break;
|
|
44
|
+
}
|
|
45
|
+
return '';
|
|
46
|
+
} catch {
|
|
47
|
+
switch (mode) {
|
|
48
|
+
case 'cssSelector':
|
|
49
|
+
return 'Enter a valid CSS selector.';
|
|
50
|
+
case 'xpath':
|
|
51
|
+
return 'Enter a valid XPath expression.';
|
|
52
|
+
case 'rawHTMLRegex':
|
|
53
|
+
return 'Enter a valid regular expression.';
|
|
54
|
+
default:
|
|
55
|
+
return '';
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
/**
|
|
61
|
+
* Re-runs `computeBlobHTMLValueMessage` against whichever of the input/
|
|
62
|
+
* textarea pair the current Mode makes active, clearing the other one's
|
|
63
|
+
* custom validity (a hidden-but-still-`required` control would otherwise
|
|
64
|
+
* keep blocking the form, the same reasoning `buildUI`'s own doc gives for
|
|
65
|
+
* toggling `required` alongside `hidden`) - call on every `input` on either
|
|
66
|
+
* value control, `change` on the Mode `<select>`, and `change` on the Flags
|
|
67
|
+
* multi-select (changing flags can itself flip a regex between valid and
|
|
68
|
+
* invalid, e.g. the `u`/`v` flags' stricter escape rules).
|
|
69
|
+
* @param {Element|null} container
|
|
70
|
+
* @returns {void}
|
|
71
|
+
*/
|
|
72
|
+
function syncBlobHTMLValueValidity (container) {
|
|
73
|
+
if (!container) {
|
|
74
|
+
return;
|
|
75
|
+
}
|
|
76
|
+
const mode = /** @type {HTMLSelectElement|undefined} */ (
|
|
77
|
+
findOwnControl(container, 'select.jsoeSearchBlobHTMLMode')
|
|
78
|
+
)?.value ?? '';
|
|
79
|
+
const input = /** @type {HTMLInputElement|undefined} */ (
|
|
80
|
+
findOwnControl(container, 'input.jsoeSearchBlobHTMLValue')
|
|
81
|
+
);
|
|
82
|
+
const textarea = /** @type {HTMLTextAreaElement|undefined} */ (
|
|
83
|
+
findOwnControl(container, 'textarea.jsoeSearchBlobHTMLValue')
|
|
84
|
+
);
|
|
85
|
+
const flagsSelect = /** @type {HTMLSelectElement|undefined} */ (
|
|
86
|
+
findOwnControl(container, 'select.jsoeSearchBlobHTMLFlags')
|
|
87
|
+
);
|
|
88
|
+
const flags = mode === 'rawHTMLRegex'
|
|
89
|
+
? [...(flagsSelect?.selectedOptions ?? [])].map((opt) => opt.value).join('')
|
|
90
|
+
: '';
|
|
91
|
+
const active = mode === 'fullText' ? textarea : input;
|
|
92
|
+
if (input && input !== active) {
|
|
93
|
+
input.setCustomValidity('');
|
|
94
|
+
}
|
|
95
|
+
if (textarea && textarea !== active) {
|
|
96
|
+
textarea.setCustomValidity('');
|
|
97
|
+
}
|
|
98
|
+
if (active) {
|
|
99
|
+
active.setCustomValidity(computeBlobHTMLValueMessage(mode, active.value, flags));
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
|
|
11
103
|
/**
|
|
12
104
|
* Blob HTML: XPath, CSS selectors, full text search, regex search of raw
|
|
13
105
|
* HTML (README) - a mode `<select>` plus one value control, the same
|
|
@@ -23,7 +115,8 @@ import {getQueryViaElement} from '../searchElementUtils.js';
|
|
|
23
115
|
* of the pair is ever `required` (toggled alongside `hidden` by the mode
|
|
24
116
|
* `change` handler): a `required` field that's merely hidden, rather than
|
|
25
117
|
* also un-required, still blocks the form's validity even though the user
|
|
26
|
-
* has no way to see or fill it.
|
|
118
|
+
* has no way to see or fill it. "CSS selector" is listed ahead of "XPath"
|
|
119
|
+
* (and is the default mode) as the option most jsoe users will recognize.
|
|
27
120
|
*
|
|
28
121
|
* The `change` handler also checks `isExemptedByAncestorHasProperty` before
|
|
29
122
|
* (re-)asserting `required` on the now-visible control: nested as an
|
|
@@ -35,6 +128,15 @@ import {getQueryViaElement} from '../searchElementUtils.js';
|
|
|
35
128
|
* check, that synthetic event would immediately reassert `required` on
|
|
36
129
|
* whichever control the current mode makes visible, undoing the exemption
|
|
37
130
|
* `setDescendantsRequired` had just granted.
|
|
131
|
+
*
|
|
132
|
+
* "Regex search of raw HTML" gets its own Flags multi-select (the same
|
|
133
|
+
* `regexpType.js` `allowedFlags` list `stringSearchType.js`'s literal/regex
|
|
134
|
+
* control passes as `buildLiteralRegexControls`'s `flagOptions`), shown only
|
|
135
|
+
* while that mode is chosen, folded into the `blobHTML` leaf's own
|
|
136
|
+
* `$options` (mirroring `QueryRegexLeaf`'s field of the same name) - and,
|
|
137
|
+
* via `syncBlobHTMLValueValidity`, included when syntax-checking the Value
|
|
138
|
+
* itself, since the chosen flags can affect whether a given pattern is
|
|
139
|
+
* actually valid.
|
|
38
140
|
* @type {SearchTypeObject}
|
|
39
141
|
*/
|
|
40
142
|
const blobHTMLSearchType = {
|
|
@@ -60,11 +162,56 @@ const blobHTMLSearchType = {
|
|
|
60
162
|
if (!value || !mode) {
|
|
61
163
|
return undefined;
|
|
62
164
|
}
|
|
165
|
+
const flags = mode === 'rawHTMLRegex'
|
|
166
|
+
? [...(/** @type {HTMLSelectElement|undefined} */ (
|
|
167
|
+
findOwnControl(this, 'select.jsoeSearchBlobHTMLFlags')
|
|
168
|
+
)?.selectedOptions ?? [])].map((opt) => opt.value).join('')
|
|
169
|
+
: '';
|
|
63
170
|
return makeBlobHTMLLeaf(
|
|
64
171
|
searchPath,
|
|
65
172
|
/** @type {import('../queryTree.js').QueryBlobHTMLLeaf['mode']} */ (mode),
|
|
66
|
-
value
|
|
173
|
+
value,
|
|
174
|
+
flags || undefined
|
|
175
|
+
);
|
|
176
|
+
},
|
|
177
|
+
/**
|
|
178
|
+
* @this {HTMLElement}
|
|
179
|
+
* @param {import('../queryTree.js').QueryNode|undefined} queryNode
|
|
180
|
+
* @returns {void}
|
|
181
|
+
*/
|
|
182
|
+
applyQuery (queryNode) {
|
|
183
|
+
const {matched} = extractLeafOfKind(queryNode, 'blobHTML');
|
|
184
|
+
const modeEl = /** @type {HTMLSelectElement|undefined} */ (
|
|
185
|
+
findOwnControl(this, 'select.jsoeSearchBlobHTMLMode')
|
|
186
|
+
);
|
|
187
|
+
if (!modeEl) {
|
|
188
|
+
return;
|
|
189
|
+
}
|
|
190
|
+
const mode = matched?.mode ?? 'cssSelector';
|
|
191
|
+
modeEl.value = mode;
|
|
192
|
+
// Dispatching `change` runs the Mode `<select>`'s own handler
|
|
193
|
+
// first, which shows/hides the correct Value control and the
|
|
194
|
+
// Flags label before either is touched below.
|
|
195
|
+
modeEl.dispatchEvent(new Event('change'));
|
|
196
|
+
const activeEl = /** @type {HTMLInputElement|HTMLTextAreaElement|undefined} */ (
|
|
197
|
+
findOwnControl(
|
|
198
|
+
this, `${mode === 'fullText' ? 'textarea' : 'input'}.jsoeSearchBlobHTMLValue`
|
|
199
|
+
)
|
|
200
|
+
);
|
|
201
|
+
if (activeEl) {
|
|
202
|
+
activeEl.value = matched?.value ?? '';
|
|
203
|
+
activeEl.dispatchEvent(new Event('input'));
|
|
204
|
+
}
|
|
205
|
+
const flagsEl = /** @type {HTMLSelectElement|undefined} */ (
|
|
206
|
+
findOwnControl(this, 'select.jsoeSearchBlobHTMLFlags')
|
|
67
207
|
);
|
|
208
|
+
if (flagsEl) {
|
|
209
|
+
const flagChars = new Set((matched?.$options ?? '').split(''));
|
|
210
|
+
[...flagsEl.options].forEach((opt) => {
|
|
211
|
+
opt.selected = flagChars.has(opt.value);
|
|
212
|
+
});
|
|
213
|
+
flagsEl.dispatchEvent(new Event('change'));
|
|
214
|
+
}
|
|
68
215
|
}
|
|
69
216
|
}
|
|
70
217
|
}, [
|
|
@@ -83,8 +230,11 @@ const blobHTMLSearchType = {
|
|
|
83
230
|
const textarea = /** @type {HTMLTextAreaElement|null|undefined} */ (
|
|
84
231
|
container?.querySelector('textarea.jsoeSearchBlobHTMLValue')
|
|
85
232
|
);
|
|
86
|
-
const
|
|
87
|
-
'
|
|
233
|
+
const flagsLabel = /** @type {HTMLElement|null|undefined} */ (
|
|
234
|
+
container?.querySelector('.jsoeSearchBlobHTMLFlagsLabel')
|
|
235
|
+
);
|
|
236
|
+
const mode = /** @type {HTMLSelectElement} */ (this).value;
|
|
237
|
+
const isFullText = mode === 'fullText';
|
|
88
238
|
const exempted = container !== null &&
|
|
89
239
|
isExemptedByAncestorHasProperty(container);
|
|
90
240
|
if (input) {
|
|
@@ -95,11 +245,15 @@ const blobHTMLSearchType = {
|
|
|
95
245
|
textarea.hidden = !isFullText;
|
|
96
246
|
textarea.required = isFullText && !exempted;
|
|
97
247
|
}
|
|
248
|
+
if (flagsLabel) {
|
|
249
|
+
flagsLabel.hidden = mode !== 'rawHTMLRegex';
|
|
250
|
+
}
|
|
251
|
+
syncBlobHTMLValueValidity(container);
|
|
98
252
|
}
|
|
99
253
|
}
|
|
100
254
|
}, [
|
|
101
|
-
['option', {value: 'xpath'}, ['XPath']],
|
|
102
255
|
['option', {value: 'cssSelector'}, ['CSS selector']],
|
|
256
|
+
['option', {value: 'xpath'}, ['XPath']],
|
|
103
257
|
['option', {value: 'fullText'}, ['Full text search']],
|
|
104
258
|
['option', {value: 'rawHTMLRegex'}, ['Regex search of raw HTML']]
|
|
105
259
|
]]
|
|
@@ -108,15 +262,37 @@ const blobHTMLSearchType = {
|
|
|
108
262
|
'Value: ',
|
|
109
263
|
['input', {
|
|
110
264
|
type: 'text', name: `${name}-value`, class: 'jsoeSearchBlobHTMLValue',
|
|
111
|
-
required: true
|
|
265
|
+
required: true,
|
|
266
|
+
$on: {
|
|
267
|
+
input () {
|
|
268
|
+
syncBlobHTMLValueValidity(this.closest('jsoe-search-blob-html'));
|
|
269
|
+
}
|
|
270
|
+
}
|
|
112
271
|
}],
|
|
113
272
|
['textarea', {
|
|
114
|
-
name: `${name}-value`, class: 'jsoeSearchBlobHTMLValue', hidden: true
|
|
273
|
+
name: `${name}-value`, class: 'jsoeSearchBlobHTMLValue', hidden: true,
|
|
274
|
+
$on: {
|
|
275
|
+
input () {
|
|
276
|
+
syncBlobHTMLValueValidity(this.closest('jsoe-search-blob-html'));
|
|
277
|
+
}
|
|
278
|
+
}
|
|
115
279
|
}]
|
|
280
|
+
]],
|
|
281
|
+
['label', {class: 'jsoeSearchBlobHTMLFlagsLabel', hidden: true}, [
|
|
282
|
+
'Flags: ',
|
|
283
|
+
['select', {
|
|
284
|
+
name: `${name}-flags`, multiple: true, class: 'jsoeSearchBlobHTMLFlags',
|
|
285
|
+
$on: {
|
|
286
|
+
change () {
|
|
287
|
+
syncBlobHTMLValueValidity(this.closest('jsoe-search-blob-html'));
|
|
288
|
+
}
|
|
289
|
+
}
|
|
290
|
+
}, regexpType.allowedFlags.map((flag) => ['option', {value: flag}, [flag]])]
|
|
116
291
|
]]
|
|
117
292
|
]];
|
|
118
293
|
},
|
|
119
|
-
getQuery: getQueryViaElement
|
|
294
|
+
getQuery: getQueryViaElement,
|
|
295
|
+
applyQuery: applyQueryViaElement
|
|
120
296
|
};
|
|
121
297
|
|
|
122
298
|
export default blobHTMLSearchType;
|
|
@@ -1,9 +1,13 @@
|
|
|
1
1
|
import {
|
|
2
|
-
buildPathLabel, buildCheckbox, readCheckbox, buildOptInFieldset,
|
|
3
|
-
readOptInChecked, wireOptInFieldset, buildAtLeastOneSentinel,
|
|
2
|
+
buildPathLabel, buildCheckbox, readCheckbox, applyCheckbox, buildOptInFieldset,
|
|
3
|
+
readOptInChecked, wireOptInFieldset, applyOptIn, buildAtLeastOneSentinel,
|
|
4
|
+
syncAtLeastOneCheck, extractLeafOfKind
|
|
4
5
|
} from '../searchUtils.js';
|
|
5
6
|
import {makeMapRecordJointLeaf} from '../queryTreeBuilders.js';
|
|
6
|
-
import {
|
|
7
|
+
import {
|
|
8
|
+
findSearchElement, getQueryViaElement, hasGetQuery,
|
|
9
|
+
applyQueryViaElement, hasApplyQuery
|
|
10
|
+
} from '../searchElementUtils.js';
|
|
7
11
|
import {buildSearchWidget} from '../searchDispatch.js';
|
|
8
12
|
|
|
9
13
|
/**
|
|
@@ -85,6 +89,27 @@ const recordSearchType = {
|
|
|
85
89
|
}
|
|
86
90
|
const joint = readCheckbox(this);
|
|
87
91
|
return makeMapRecordJointLeaf(searchPath, joint, keyQuery, valueQuery);
|
|
92
|
+
},
|
|
93
|
+
/**
|
|
94
|
+
* @this {HTMLElement}
|
|
95
|
+
* @param {import('../queryTree.js').QueryNode|undefined} queryNode
|
|
96
|
+
* @returns {void}
|
|
97
|
+
*/
|
|
98
|
+
applyQuery (queryNode) {
|
|
99
|
+
const searchPath = this.dataset.searchPath ?? '';
|
|
100
|
+
const {matched: jointLeaf} = extractLeafOfKind(queryNode, 'mapRecordJoint');
|
|
101
|
+
applyCheckbox(this, Boolean(jointLeaf?.joint));
|
|
102
|
+
applyOptIn(this, jointLeaf?.keyQuery !== undefined, 'key');
|
|
103
|
+
applyOptIn(this, jointLeaf?.valueQuery !== undefined, 'value');
|
|
104
|
+
const keyEl = findSearchElement(this, `${searchPath}/*key`);
|
|
105
|
+
if (keyEl && hasApplyQuery(keyEl)) {
|
|
106
|
+
keyEl.applyQuery(jointLeaf?.keyQuery);
|
|
107
|
+
}
|
|
108
|
+
const valueEl = findSearchElement(this, `${searchPath}/*value`);
|
|
109
|
+
if (valueEl && hasApplyQuery(valueEl)) {
|
|
110
|
+
valueEl.applyQuery(jointLeaf?.valueQuery);
|
|
111
|
+
}
|
|
112
|
+
syncKeyValueValidity(this);
|
|
88
113
|
}
|
|
89
114
|
}
|
|
90
115
|
}, [
|
|
@@ -99,7 +124,8 @@ const recordSearchType = {
|
|
|
99
124
|
buildAtLeastOneSentinel()
|
|
100
125
|
]];
|
|
101
126
|
},
|
|
102
|
-
getQuery: getQueryViaElement
|
|
127
|
+
getQuery: getQueryViaElement,
|
|
128
|
+
applyQuery: applyQueryViaElement
|
|
103
129
|
};
|
|
104
130
|
|
|
105
131
|
export default recordSearchType;
|
|
@@ -1,10 +1,13 @@
|
|
|
1
1
|
import {
|
|
2
|
-
buildPathLabel, buildLengthSizeControls, readLengthSizeQuery,
|
|
3
|
-
buildOptInFieldset, readOptInChecked, wireOptInFieldset,
|
|
4
|
-
buildAtLeastOneSentinel, syncAtLeastOneCheck
|
|
2
|
+
buildPathLabel, buildLengthSizeControls, readLengthSizeQuery, applyLengthSizeQuery,
|
|
3
|
+
buildOptInFieldset, readOptInChecked, wireOptInFieldset, applyOptIn,
|
|
4
|
+
buildAtLeastOneSentinel, syncAtLeastOneCheck, extractLeafOfKind, extractClauseForPath
|
|
5
5
|
} from '../searchUtils.js';
|
|
6
6
|
import {combineAnd} from '../queryTreeBuilders.js';
|
|
7
|
-
import {
|
|
7
|
+
import {
|
|
8
|
+
findSearchElement, getQueryViaElement, hasGetQuery,
|
|
9
|
+
applyQueryViaElement, hasApplyQuery
|
|
10
|
+
} from '../searchElementUtils.js';
|
|
8
11
|
import {buildSearchWidget} from '../searchDispatch.js';
|
|
9
12
|
|
|
10
13
|
/**
|
|
@@ -142,11 +145,46 @@ const tupleSearchType = {
|
|
|
142
145
|
? restEl.getQuery()
|
|
143
146
|
: undefined;
|
|
144
147
|
return combineAnd([...itemLeaves, lengthLeaf, restLeaf]);
|
|
148
|
+
},
|
|
149
|
+
/**
|
|
150
|
+
* @this {HTMLElement}
|
|
151
|
+
* @param {import('../queryTree.js').QueryNode|undefined} queryNode
|
|
152
|
+
* @returns {void}
|
|
153
|
+
*/
|
|
154
|
+
applyQuery (queryNode) {
|
|
155
|
+
const searchPath = this.dataset.searchPath ?? '';
|
|
156
|
+
const itemCount = Number(this.dataset.itemCount ?? '0');
|
|
157
|
+
const hasRest = this.dataset.hasRest === 'true';
|
|
158
|
+
let remaining = queryNode;
|
|
159
|
+
Array.from({length: itemCount}, (_v, idx) => idx).forEach((idx) => {
|
|
160
|
+
const childPath = `${searchPath}/${idx}`;
|
|
161
|
+
const {matched, rest} = extractClauseForPath(remaining, childPath);
|
|
162
|
+
remaining = rest;
|
|
163
|
+
applyOptIn(this, matched !== undefined, String(idx));
|
|
164
|
+
const itemEl = findSearchElement(this, childPath);
|
|
165
|
+
if (itemEl && hasApplyQuery(itemEl)) {
|
|
166
|
+
itemEl.applyQuery(matched);
|
|
167
|
+
}
|
|
168
|
+
});
|
|
169
|
+
if (hasRest) {
|
|
170
|
+
const {matched: lengthLeaf, rest: afterLength} = extractLeafOfKind(remaining, 'lengthSize');
|
|
171
|
+
applyLengthSizeQuery(this, lengthLeaf);
|
|
172
|
+
remaining = afterLength;
|
|
173
|
+
const restPath = `${searchPath}/*`;
|
|
174
|
+
const {matched: restMatched} = extractClauseForPath(remaining, restPath);
|
|
175
|
+
applyOptIn(this, restMatched !== undefined, 'rest');
|
|
176
|
+
const restEl = findSearchElement(this, restPath);
|
|
177
|
+
if (restEl && hasApplyQuery(restEl)) {
|
|
178
|
+
restEl.applyQuery(restMatched);
|
|
179
|
+
}
|
|
180
|
+
}
|
|
181
|
+
syncTupleValidity(this);
|
|
145
182
|
}
|
|
146
183
|
}
|
|
147
184
|
}, children];
|
|
148
185
|
},
|
|
149
|
-
getQuery: getQueryViaElement
|
|
186
|
+
getQuery: getQueryViaElement,
|
|
187
|
+
applyQuery: applyQueryViaElement
|
|
150
188
|
};
|
|
151
189
|
|
|
152
190
|
export default tupleSearchType;
|
|
@@ -1,6 +1,8 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import {
|
|
2
|
+
buildPathLabel, buildMultiSelect, readMultiSelect, applyMultiSelect, extractLeafOfKind
|
|
3
|
+
} from '../searchUtils.js';
|
|
2
4
|
import {makeMultiSelectLeaf} from '../queryTreeBuilders.js';
|
|
3
|
-
import {getQueryViaElement} from '../searchElementUtils.js';
|
|
5
|
+
import {getQueryViaElement, applyQueryViaElement} from '../searchElementUtils.js';
|
|
4
6
|
|
|
5
7
|
/**
|
|
6
8
|
* @typedef {import('../searchDispatch.js').SearchTypeObject} SearchTypeObject
|
|
@@ -27,6 +29,15 @@ const SpecialRealNumberSearchType = {
|
|
|
27
29
|
return selected.length
|
|
28
30
|
? makeMultiSelectLeaf(this.dataset.searchPath ?? '', {$in: selected})
|
|
29
31
|
: undefined;
|
|
32
|
+
},
|
|
33
|
+
/**
|
|
34
|
+
* @this {HTMLElement}
|
|
35
|
+
* @param {import('../queryTree.js').QueryNode|undefined} queryNode
|
|
36
|
+
* @returns {void}
|
|
37
|
+
*/
|
|
38
|
+
applyQuery (queryNode) {
|
|
39
|
+
const {matched} = extractLeafOfKind(queryNode, 'multiSelect');
|
|
40
|
+
applyMultiSelect(this, matched?.$in ?? []);
|
|
30
41
|
}
|
|
31
42
|
}
|
|
32
43
|
}, [
|
|
@@ -36,7 +47,8 @@ const SpecialRealNumberSearchType = {
|
|
|
36
47
|
})
|
|
37
48
|
]];
|
|
38
49
|
},
|
|
39
|
-
getQuery: getQueryViaElement
|
|
50
|
+
getQuery: getQueryViaElement,
|
|
51
|
+
applyQuery: applyQueryViaElement
|
|
40
52
|
};
|
|
41
53
|
|
|
42
54
|
export default SpecialRealNumberSearchType;
|
|
@@ -1,8 +1,9 @@
|
|
|
1
1
|
import {
|
|
2
|
-
buildPathLabel, buildRangeInputsPair, readRangeInputsPair, syncRangeValidity
|
|
2
|
+
buildPathLabel, buildRangeInputsPair, readRangeInputsPair, syncRangeValidity,
|
|
3
|
+
applyRangeQuery, extractLeafOfKind
|
|
3
4
|
} from '../searchUtils.js';
|
|
4
5
|
import {makeRangeLeaf} from '../queryTreeBuilders.js';
|
|
5
|
-
import {getQueryViaElement} from '../searchElementUtils.js';
|
|
6
|
+
import {getQueryViaElement, applyQueryViaElement} from '../searchElementUtils.js';
|
|
6
7
|
|
|
7
8
|
/**
|
|
8
9
|
* @typedef {import('../searchDispatch.js').SearchTypeObject} SearchTypeObject
|
|
@@ -40,6 +41,15 @@ const buffersourceSearchType = {
|
|
|
40
41
|
...(gte === '' ? {} : {$gte: Number(gte)}),
|
|
41
42
|
...(lte === '' ? {} : {$lte: Number(lte)})
|
|
42
43
|
});
|
|
44
|
+
},
|
|
45
|
+
/**
|
|
46
|
+
* @this {HTMLElement}
|
|
47
|
+
* @param {import('../queryTree.js').QueryNode|undefined} queryNode
|
|
48
|
+
* @returns {void}
|
|
49
|
+
*/
|
|
50
|
+
applyQuery (queryNode) {
|
|
51
|
+
const {matched} = extractLeafOfKind(queryNode, 'range');
|
|
52
|
+
applyRangeQuery(this, matched);
|
|
43
53
|
}
|
|
44
54
|
}
|
|
45
55
|
}, [
|
|
@@ -47,7 +57,8 @@ const buffersourceSearchType = {
|
|
|
47
57
|
...buildRangeInputsPair({name, min: 0})
|
|
48
58
|
]];
|
|
49
59
|
},
|
|
50
|
-
getQuery: getQueryViaElement
|
|
60
|
+
getQuery: getQueryViaElement,
|
|
61
|
+
applyQuery: applyQueryViaElement
|
|
51
62
|
};
|
|
52
63
|
|
|
53
64
|
export default buffersourceSearchType;
|
|
@@ -1,7 +1,10 @@
|
|
|
1
1
|
import {jml} from '../../vendor-imports.js';
|
|
2
|
-
import {buildPathLabel, findOwnControl} from '../searchUtils.js';
|
|
2
|
+
import {buildPathLabel, findOwnControl, extractLeafOfKind} from '../searchUtils.js';
|
|
3
3
|
import {makeTypeOfLeaf, combineAnd} from '../queryTreeBuilders.js';
|
|
4
|
-
import {
|
|
4
|
+
import {
|
|
5
|
+
findSearchElement, getQueryViaElement, hasGetQuery,
|
|
6
|
+
applyQueryViaElement, hasApplyQuery
|
|
7
|
+
} from '../searchElementUtils.js';
|
|
5
8
|
import {getSearchSchemaType, buildSearchWidget} from '../searchDispatch.js';
|
|
6
9
|
|
|
7
10
|
/**
|
|
@@ -101,6 +104,44 @@ export function makeUnionFamilySearchType ({tagName, discriminated}) {
|
|
|
101
104
|
? branchEl.getQuery()
|
|
102
105
|
: undefined;
|
|
103
106
|
return combineAnd([typeOfLeaf, branchQuery]);
|
|
107
|
+
},
|
|
108
|
+
/**
|
|
109
|
+
* Drives the existing "Has type" `<select>`'s own `change`
|
|
110
|
+
* listener (defined below, per-instance and so already safe to
|
|
111
|
+
* close over `branches`) rather than reimplementing branch-DOM
|
|
112
|
+
* construction here - `$define` methods are installed once on the
|
|
113
|
+
* tag's shared prototype the first time it's defined (this
|
|
114
|
+
* function's own doc), so `applyQuery` can't close over
|
|
115
|
+
* `branches` itself, but dispatching a real `change` event lets
|
|
116
|
+
* the per-instance listener that *can* do the rebuild for us.
|
|
117
|
+
* @this {HTMLElement}
|
|
118
|
+
* @param {import('../queryTree.js').QueryNode|undefined} queryNode
|
|
119
|
+
* @returns {void}
|
|
120
|
+
*/
|
|
121
|
+
applyQuery (queryNode) {
|
|
122
|
+
const select = /** @type {HTMLSelectElement|undefined} */ (
|
|
123
|
+
findOwnControl(this, 'select.jsoeSearchTypeOf')
|
|
124
|
+
);
|
|
125
|
+
if (!select) {
|
|
126
|
+
return;
|
|
127
|
+
}
|
|
128
|
+
const {matched, rest} = extractLeafOfKind(queryNode, 'typeOf');
|
|
129
|
+
const optionMatch = matched && [...select.options].find((opt) => (
|
|
130
|
+
opt.dataset.searchType === matched.searchType &&
|
|
131
|
+
(opt.dataset.discriminatorValue === undefined
|
|
132
|
+
? matched.discriminatorValue === undefined
|
|
133
|
+
: JSON.parse(opt.dataset.discriminatorValue) === matched.discriminatorValue)
|
|
134
|
+
));
|
|
135
|
+
select.value = optionMatch ? optionMatch.value : '';
|
|
136
|
+
select.dispatchEvent(new Event('change'));
|
|
137
|
+
if (!optionMatch) {
|
|
138
|
+
return;
|
|
139
|
+
}
|
|
140
|
+
const searchPath = this.dataset.searchPath ?? '';
|
|
141
|
+
const branchEl = findSearchElement(this, searchPath);
|
|
142
|
+
if (branchEl && hasApplyQuery(branchEl)) {
|
|
143
|
+
branchEl.applyQuery(rest);
|
|
144
|
+
}
|
|
104
145
|
}
|
|
105
146
|
}
|
|
106
147
|
}, [
|
|
@@ -163,6 +204,7 @@ export function makeUnionFamilySearchType ({tagName, discriminated}) {
|
|
|
163
204
|
['div', {class: 'searchUnionBranch'}]
|
|
164
205
|
]];
|
|
165
206
|
},
|
|
166
|
-
getQuery: getQueryViaElement
|
|
207
|
+
getQuery: getQueryViaElement,
|
|
208
|
+
applyQuery: applyQueryViaElement
|
|
167
209
|
};
|
|
168
210
|
}
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
const identifierKeyRegex = (/^[A-Za-z_$][\w$]*$/u);
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* A minimal JSON6-*subset* stringifier: unquotes object keys that are valid
|
|
5
|
+
* identifiers (for the "simplified" display the JSON6 mode is meant to
|
|
6
|
+
* give), while always fully quoting string *values* via `JSON.stringify`.
|
|
7
|
+
*
|
|
8
|
+
* `JSON6.stringify` (from the `json-6` package) was tried first but has a
|
|
9
|
+
* real bug: it runs the same identifier-unquoting logic it uses for keys
|
|
10
|
+
* on string *values* too, so e.g. the value `"hello"` is emitted as the
|
|
11
|
+
* bare word `hello` — not valid JS/JSON6 syntax as a value, and not even
|
|
12
|
+
* re-parseable by the package's own `JSON6.parse`. Hand-rolling this
|
|
13
|
+
* avoids depending on that broken path while still using `JSON6.parse`
|
|
14
|
+
* (which is correct) to read the text back after editing.
|
|
15
|
+
* @param {unknown} value
|
|
16
|
+
* @param {string} indent
|
|
17
|
+
* @param {string} curIndent
|
|
18
|
+
* @returns {string}
|
|
19
|
+
*/
|
|
20
|
+
export const stringifyJSON6 = (value, indent, curIndent) => {
|
|
21
|
+
if (value === null || typeof value !== 'object') {
|
|
22
|
+
return JSON.stringify(value);
|
|
23
|
+
}
|
|
24
|
+
const nextIndent = curIndent + indent;
|
|
25
|
+
if (Array.isArray(value)) {
|
|
26
|
+
if (!value.length) {
|
|
27
|
+
return '[]';
|
|
28
|
+
}
|
|
29
|
+
const items = value.map((item) => {
|
|
30
|
+
return `${nextIndent}${stringifyJSON6(item, indent, nextIndent)}`;
|
|
31
|
+
});
|
|
32
|
+
return `[\n${items.join(',\n')}\n${curIndent}]`;
|
|
33
|
+
}
|
|
34
|
+
const keys = Object.keys(value);
|
|
35
|
+
if (!keys.length) {
|
|
36
|
+
return '{}';
|
|
37
|
+
}
|
|
38
|
+
const items = keys.map((key) => {
|
|
39
|
+
const keyText = identifierKeyRegex.test(key)
|
|
40
|
+
? key
|
|
41
|
+
: JSON.stringify(key);
|
|
42
|
+
return `${nextIndent}${keyText}: ${
|
|
43
|
+
stringifyJSON6(
|
|
44
|
+
/** @type {{[key: string]: unknown}} */ (value)[key], indent,
|
|
45
|
+
nextIndent
|
|
46
|
+
)
|
|
47
|
+
}`;
|
|
48
|
+
});
|
|
49
|
+
return `{\n${items.join(',\n')}\n${curIndent}}`;
|
|
50
|
+
};
|
|
@@ -6,6 +6,7 @@ import {
|
|
|
6
6
|
} from '../vendor-imports.js';
|
|
7
7
|
import {structuredCloningJsoe} from '../formats/structuredCloning.js';
|
|
8
8
|
import {parseValue} from '../formats/schema.js';
|
|
9
|
+
import {stringifyJSON6} from './json6Stringify.js';
|
|
9
10
|
|
|
10
11
|
/**
|
|
11
12
|
* @returns {import('typeson').Typeson}
|
|
@@ -14,8 +15,6 @@ const buildTypeson = () => {
|
|
|
14
15
|
return new Typeson().register(structuredCloningJsoe);
|
|
15
16
|
};
|
|
16
17
|
|
|
17
|
-
const identifierKeyRegex = (/^[A-Za-z_$][\w$]*$/u);
|
|
18
|
-
|
|
19
18
|
/** @type {ReadonlySet<string>} */
|
|
20
19
|
const typedArrayTagNames = new Set([
|
|
21
20
|
'Int8Array', 'Uint8Array', 'Uint8ClampedArray', 'Int16Array',
|
|
@@ -45,55 +44,6 @@ const bytesToBase64 = (bytes) => {
|
|
|
45
44
|
return btoa(binary);
|
|
46
45
|
};
|
|
47
46
|
|
|
48
|
-
/**
|
|
49
|
-
* A minimal JSON6-*subset* stringifier: unquotes object keys that are valid
|
|
50
|
-
* identifiers (for the "simplified" display the JSON6 mode is meant to
|
|
51
|
-
* give), while always fully quoting string *values* via `JSON.stringify`.
|
|
52
|
-
*
|
|
53
|
-
* `JSON6.stringify` (from the `json-6` package) was tried first but has a
|
|
54
|
-
* real bug: it runs the same identifier-unquoting logic it uses for keys
|
|
55
|
-
* on string *values* too, so e.g. the value `"hello"` is emitted as the
|
|
56
|
-
* bare word `hello` — not valid JS/JSON6 syntax as a value, and not even
|
|
57
|
-
* re-parseable by the package's own `JSON6.parse`. Hand-rolling this
|
|
58
|
-
* avoids depending on that broken path while still using `JSON6.parse`
|
|
59
|
-
* (which is correct) to read the text back after editing.
|
|
60
|
-
* @param {unknown} value
|
|
61
|
-
* @param {string} indent
|
|
62
|
-
* @param {string} curIndent
|
|
63
|
-
* @returns {string}
|
|
64
|
-
*/
|
|
65
|
-
const stringifyJSON6 = (value, indent, curIndent) => {
|
|
66
|
-
if (value === null || typeof value !== 'object') {
|
|
67
|
-
return JSON.stringify(value);
|
|
68
|
-
}
|
|
69
|
-
const nextIndent = curIndent + indent;
|
|
70
|
-
if (Array.isArray(value)) {
|
|
71
|
-
if (!value.length) {
|
|
72
|
-
return '[]';
|
|
73
|
-
}
|
|
74
|
-
const items = value.map((item) => {
|
|
75
|
-
return `${nextIndent}${stringifyJSON6(item, indent, nextIndent)}`;
|
|
76
|
-
});
|
|
77
|
-
return `[\n${items.join(',\n')}\n${curIndent}]`;
|
|
78
|
-
}
|
|
79
|
-
const keys = Object.keys(value);
|
|
80
|
-
if (!keys.length) {
|
|
81
|
-
return '{}';
|
|
82
|
-
}
|
|
83
|
-
const items = keys.map((key) => {
|
|
84
|
-
const keyText = identifierKeyRegex.test(key)
|
|
85
|
-
? key
|
|
86
|
-
: JSON.stringify(key);
|
|
87
|
-
return `${nextIndent}${keyText}: ${
|
|
88
|
-
stringifyJSON6(
|
|
89
|
-
/** @type {{[key: string]: unknown}} */ (value)[key], indent,
|
|
90
|
-
nextIndent
|
|
91
|
-
)
|
|
92
|
-
}`;
|
|
93
|
-
});
|
|
94
|
-
return `{\n${items.join(',\n')}\n${curIndent}}`;
|
|
95
|
-
};
|
|
96
|
-
|
|
97
47
|
/**
|
|
98
48
|
* Builds the Typeson/JSON6 text shown by both the view-mode "View raw"
|
|
99
49
|
* button and, seeded, the edit-mode "Edit raw" button's Typeson mode.
|
|
@@ -788,7 +788,9 @@ export declare const describe: typeof core.describe;
|
|
|
788
788
|
export declare const meta: typeof core.meta;
|
|
789
789
|
type ZodInstanceOfParams = core.Params<ZodCustom, core.$ZodIssueCustom, "type" | "check" | "checks" | "fn" | "abort" | "error" | "params" | "path">;
|
|
790
790
|
export interface ZodInstanceOf<T = unknown> extends ZodCustom<T, T> {
|
|
791
|
-
properties<Shape extends
|
|
791
|
+
properties<Shape extends {
|
|
792
|
+
[k in keyof T as T[k] extends Function ? never : k]?: core.$ZodType<unknown, T[k]>;
|
|
793
|
+
}>(shape: Shape, params?: string | core.$ZodCheckPropertiesParams): ZodInstanceOf<T & core.$InferObjectInput<Shape, {}>>;
|
|
792
794
|
}
|
|
793
795
|
export declare const ZodInstanceOf: core.$constructor<ZodInstanceOf>;
|
|
794
796
|
declare function _instanceof<T extends typeof util.Class>(cls: T, params?: ZodInstanceOfParams): ZodInstanceOf<InstanceType<T>>;
|