@es-joy/jsoe 0.28.2 → 0.30.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 (167) hide show
  1. package/.claude/scheduled_tasks.lock +1 -0
  2. package/CHANGES.md +25 -0
  3. package/README.md +4 -0
  4. package/dist/formatAndTypeChoices.d.ts +6 -6
  5. package/dist/formatAndTypeChoices.d.ts.map +1 -1
  6. package/dist/formats/schema.d.ts +5 -0
  7. package/dist/formats/schema.d.ts.map +1 -1
  8. package/dist/formats.d.ts +2 -2
  9. package/dist/formats.d.ts.map +1 -1
  10. package/dist/fundamentalTypes/dateType.d.ts +26 -2
  11. package/dist/fundamentalTypes/dateType.d.ts.map +1 -1
  12. package/dist/fundamentalTypes/domexceptionType.d.ts +7 -0
  13. package/dist/fundamentalTypes/domexceptionType.d.ts.map +1 -1
  14. package/dist/index.d.ts +2 -0
  15. package/dist/index.d.ts.map +1 -1
  16. package/dist/index.js +2 -2
  17. package/dist/index.js.map +1 -1
  18. package/dist/jtltExtensions.d.ts +112 -0
  19. package/dist/jtltExtensions.d.ts.map +1 -0
  20. package/dist/search/fundamentalTypes/arraySearchType.d.ts +22 -0
  21. package/dist/search/fundamentalTypes/arraySearchType.d.ts.map +1 -0
  22. package/dist/search/fundamentalTypes/bigintSearchType.d.ts +10 -0
  23. package/dist/search/fundamentalTypes/bigintSearchType.d.ts.map +1 -0
  24. package/dist/search/fundamentalTypes/blobSearchType.d.ts +19 -0
  25. package/dist/search/fundamentalTypes/blobSearchType.d.ts.map +1 -0
  26. package/dist/search/fundamentalTypes/booleanSearchType.d.ts +11 -0
  27. package/dist/search/fundamentalTypes/booleanSearchType.d.ts.map +1 -0
  28. package/dist/search/fundamentalTypes/catchSearchType.d.ts +18 -0
  29. package/dist/search/fundamentalTypes/catchSearchType.d.ts.map +1 -0
  30. package/dist/search/fundamentalTypes/dateSearchType.d.ts +15 -0
  31. package/dist/search/fundamentalTypes/dateSearchType.d.ts.map +1 -0
  32. package/dist/search/fundamentalTypes/domexceptionSearchType.d.ts +16 -0
  33. package/dist/search/fundamentalTypes/domexceptionSearchType.d.ts.map +1 -0
  34. package/dist/search/fundamentalTypes/enumSearchType.d.ts +26 -0
  35. package/dist/search/fundamentalTypes/enumSearchType.d.ts.map +1 -0
  36. package/dist/search/fundamentalTypes/errorSearchType.d.ts +9 -0
  37. package/dist/search/fundamentalTypes/errorSearchType.d.ts.map +1 -0
  38. package/dist/search/fundamentalTypes/fileSearchType.d.ts +28 -0
  39. package/dist/search/fundamentalTypes/fileSearchType.d.ts.map +1 -0
  40. package/dist/search/fundamentalTypes/filelistSearchType.d.ts +24 -0
  41. package/dist/search/fundamentalTypes/filelistSearchType.d.ts.map +1 -0
  42. package/dist/search/fundamentalTypes/functionSearchType.d.ts +18 -0
  43. package/dist/search/fundamentalTypes/functionSearchType.d.ts.map +1 -0
  44. package/dist/search/fundamentalTypes/mapSearchType.d.ts +22 -0
  45. package/dist/search/fundamentalTypes/mapSearchType.d.ts.map +1 -0
  46. package/dist/search/fundamentalTypes/nanSearchType.d.ts +7 -0
  47. package/dist/search/fundamentalTypes/nanSearchType.d.ts.map +1 -0
  48. package/dist/search/fundamentalTypes/nullSearchType.d.ts +7 -0
  49. package/dist/search/fundamentalTypes/nullSearchType.d.ts.map +1 -0
  50. package/dist/search/fundamentalTypes/numberSearchType.d.ts +11 -0
  51. package/dist/search/fundamentalTypes/numberSearchType.d.ts.map +1 -0
  52. package/dist/search/fundamentalTypes/objectSearchType.d.ts +21 -0
  53. package/dist/search/fundamentalTypes/objectSearchType.d.ts.map +1 -0
  54. package/dist/search/fundamentalTypes/promiseSearchType.d.ts +20 -0
  55. package/dist/search/fundamentalTypes/promiseSearchType.d.ts.map +1 -0
  56. package/dist/search/fundamentalTypes/regexpSearchType.d.ts +20 -0
  57. package/dist/search/fundamentalTypes/regexpSearchType.d.ts.map +1 -0
  58. package/dist/search/fundamentalTypes/setSearchType.d.ts +15 -0
  59. package/dist/search/fundamentalTypes/setSearchType.d.ts.map +1 -0
  60. package/dist/search/fundamentalTypes/stringSearchType.d.ts +21 -0
  61. package/dist/search/fundamentalTypes/stringSearchType.d.ts.map +1 -0
  62. package/dist/search/fundamentalTypes/symbolSearchType.d.ts +16 -0
  63. package/dist/search/fundamentalTypes/symbolSearchType.d.ts.map +1 -0
  64. package/dist/search/fundamentalTypes/undefinedSearchType.d.ts +7 -0
  65. package/dist/search/fundamentalTypes/undefinedSearchType.d.ts.map +1 -0
  66. package/dist/search/index.d.ts +70 -0
  67. package/dist/search/index.d.ts.map +1 -0
  68. package/dist/search/queryTree.d.ts +12 -1
  69. package/dist/search/queryTree.d.ts.map +1 -1
  70. package/dist/search/queryTreeBuilders.d.ts +18 -0
  71. package/dist/search/queryTreeBuilders.d.ts.map +1 -1
  72. package/dist/search/searchDispatch.d.ts +44 -0
  73. package/dist/search/searchDispatch.d.ts.map +1 -1
  74. package/dist/search/searchElementUtils.d.ts +106 -0
  75. package/dist/search/searchElementUtils.d.ts.map +1 -0
  76. package/dist/search/searchUtils.d.ts +550 -0
  77. package/dist/search/searchUtils.d.ts.map +1 -0
  78. package/dist/search/subTypes/blobHTMLSearchType.d.ts +36 -0
  79. package/dist/search/subTypes/blobHTMLSearchType.d.ts.map +1 -0
  80. package/dist/search/subTypes/recordSearchType.d.ts +18 -0
  81. package/dist/search/subTypes/recordSearchType.d.ts.map +1 -0
  82. package/dist/search/subTypes/tupleSearchType.d.ts +21 -0
  83. package/dist/search/subTypes/tupleSearchType.d.ts.map +1 -0
  84. package/dist/search/superTypes/SpecialRealNumberSearchType.d.ts +14 -0
  85. package/dist/search/superTypes/SpecialRealNumberSearchType.d.ts.map +1 -0
  86. package/dist/search/superTypes/buffersourceSearchType.d.ts +17 -0
  87. package/dist/search/superTypes/buffersourceSearchType.d.ts.map +1 -0
  88. package/dist/search/superTypes/dommatrixSearchType.d.ts +11 -0
  89. package/dist/search/superTypes/dommatrixSearchType.d.ts.map +1 -0
  90. package/dist/search/superTypes/dompointSearchType.d.ts +9 -0
  91. package/dist/search/superTypes/dompointSearchType.d.ts.map +1 -0
  92. package/dist/search/superTypes/domrectSearchType.d.ts +9 -0
  93. package/dist/search/superTypes/domrectSearchType.d.ts.map +1 -0
  94. package/dist/search/superTypes/errorsSpecialSearchType.d.ts +11 -0
  95. package/dist/search/superTypes/errorsSpecialSearchType.d.ts.map +1 -0
  96. package/dist/search/unions/discriminatedUnionSearchType.d.ts +12 -0
  97. package/dist/search/unions/discriminatedUnionSearchType.d.ts.map +1 -0
  98. package/dist/search/unions/unionFamilySearchType.d.ts +36 -0
  99. package/dist/search/unions/unionFamilySearchType.d.ts.map +1 -0
  100. package/dist/search/unions/unionSearchType.d.ts +8 -0
  101. package/dist/search/unions/unionSearchType.d.ts.map +1 -0
  102. package/dist/search/unions/xorSearchType.d.ts +12 -0
  103. package/dist/search/unions/xorSearchType.d.ts.map +1 -0
  104. package/dist/superTypes/buffersourceType.d.ts.map +1 -1
  105. package/dist/typeChoices.d.ts +3 -3
  106. package/dist/typeChoices.d.ts.map +1 -1
  107. package/dist/types.d.ts +3 -3
  108. package/dist/types.d.ts.map +1 -1
  109. package/package.json +5 -4
  110. package/pnpm-workspace.yaml +1 -1
  111. package/rollup.config.js +12 -0
  112. package/src/formatAndTypeChoices.js +4 -4
  113. package/src/formats/schema.js +1 -1
  114. package/src/formats.js +1 -1
  115. package/src/fundamentalTypes/dateType.js +40 -14
  116. package/src/fundamentalTypes/domexceptionType.js +41 -33
  117. package/src/fundamentalTypes/regexpType.js +1 -1
  118. package/src/index.js +6 -0
  119. package/src/jsoe.css +204 -0
  120. package/src/jtltExtensions.js +103 -0
  121. package/src/search/fundamentalTypes/arraySearchType.js +101 -0
  122. package/src/search/fundamentalTypes/bigintSearchType.js +68 -0
  123. package/src/search/fundamentalTypes/blobSearchType.js +42 -0
  124. package/src/search/fundamentalTypes/booleanSearchType.js +40 -0
  125. package/src/search/fundamentalTypes/catchSearchType.js +59 -0
  126. package/src/search/fundamentalTypes/dateSearchType.js +166 -0
  127. package/src/search/fundamentalTypes/domexceptionSearchType.js +84 -0
  128. package/src/search/fundamentalTypes/enumSearchType.js +62 -0
  129. package/src/search/fundamentalTypes/errorSearchType.js +11 -0
  130. package/src/search/fundamentalTypes/fileSearchType.js +94 -0
  131. package/src/search/fundamentalTypes/filelistSearchType.js +103 -0
  132. package/src/search/fundamentalTypes/functionSearchType.js +102 -0
  133. package/src/search/fundamentalTypes/mapSearchType.js +117 -0
  134. package/src/search/fundamentalTypes/nanSearchType.js +11 -0
  135. package/src/search/fundamentalTypes/nullSearchType.js +11 -0
  136. package/src/search/fundamentalTypes/numberSearchType.js +59 -0
  137. package/src/search/fundamentalTypes/objectSearchType.js +367 -0
  138. package/src/search/fundamentalTypes/promiseSearchType.js +61 -0
  139. package/src/search/fundamentalTypes/regexpSearchType.js +72 -0
  140. package/src/search/fundamentalTypes/setSearchType.js +91 -0
  141. package/src/search/fundamentalTypes/stringSearchType.js +44 -0
  142. package/src/search/fundamentalTypes/symbolSearchType.js +39 -0
  143. package/src/search/fundamentalTypes/undefinedSearchType.js +11 -0
  144. package/src/search/index.js +96 -0
  145. package/src/search/queryTree.js +244 -0
  146. package/src/search/queryTreeBuilders.js +233 -0
  147. package/src/search/searchDispatch.js +291 -0
  148. package/src/search/searchElementUtils.js +373 -0
  149. package/src/search/searchUtils.js +847 -0
  150. package/src/search/subTypes/blobHTMLSearchType.js +122 -0
  151. package/src/search/subTypes/recordSearchType.js +105 -0
  152. package/src/search/subTypes/tupleSearchType.js +152 -0
  153. package/src/search/superTypes/SpecialRealNumberSearchType.js +42 -0
  154. package/src/search/superTypes/buffersourceSearchType.js +53 -0
  155. package/src/search/superTypes/dommatrixSearchType.js +24 -0
  156. package/src/search/superTypes/dompointSearchType.js +15 -0
  157. package/src/search/superTypes/domrectSearchType.js +15 -0
  158. package/src/search/superTypes/errorsSpecialSearchType.js +15 -0
  159. package/src/search/unions/discriminatedUnionSearchType.js +16 -0
  160. package/src/search/unions/unionFamilySearchType.js +168 -0
  161. package/src/search/unions/unionSearchType.js +12 -0
  162. package/src/search/unions/xorSearchType.js +16 -0
  163. package/src/superTypes/buffersourceType.js +13 -15
  164. package/src/typeChoices.js +2 -2
  165. package/src/types.js +2 -2
  166. package/tsconfig-prod.json +12 -0
  167. package/tsconfig.json +5 -10
@@ -0,0 +1,72 @@
1
+ import {
2
+ buildPathLabel, buildLiteralRegexControls, readLiteralRegexQuery,
3
+ buildMultiSelect, readMultiSelect, findOwnControl
4
+ } from '../searchUtils.js';
5
+ import {makeMultiSelectLeaf, combineAnd} from '../queryTreeBuilders.js';
6
+ import {getQueryViaElement} from '../searchElementUtils.js';
7
+ import regexpType from '../../fundamentalTypes/regexpType.js';
8
+
9
+ /**
10
+ * @typedef {import('../searchDispatch.js').SearchTypeObject} SearchTypeObject
11
+ */
12
+
13
+ /**
14
+ * Regexp (source): OR literal or regex search/Does Not contain search, plus
15
+ * a multiple-select search of flags (README). `regexpType.js`'s own
16
+ * `allowedFlags` list is imported as-is for the flags control (search plan
17
+ * §4 - already a plain exported property, no extraction needed). Flags are
18
+ * a property of the regexp itself, not of how its source is being matched,
19
+ * but the Flags control is only shown (and only contributes to the query)
20
+ * while the source's own Mode is "Matches regex" - searching a source
21
+ * literal/substring while also constraining flags is a much rarer
22
+ * combination, and hiding Flags the rest of the time keeps the common case
23
+ * uncluttered.
24
+ * @type {SearchTypeObject}
25
+ */
26
+ const regexpSearchType = {
27
+ buildUI ({schemaObject, path, typeNamespace}) {
28
+ const label = buildPathLabel(schemaObject, path);
29
+ const name = `${typeNamespace}-regexp`;
30
+ return ['jsoe-search-regexp', {
31
+ dataset: {searchPath: path, searchKind: 'regexp'},
32
+ title: label,
33
+ $define: {
34
+ /** @this {HTMLElement} */
35
+ getQuery () {
36
+ const searchPath = this.dataset.searchPath ?? '';
37
+ const sourceLeaf = readLiteralRegexQuery(this, searchPath);
38
+ const mode = /** @type {HTMLSelectElement|undefined} */ (
39
+ findOwnControl(this, 'select.jsoeSearchMode--')
40
+ )?.value;
41
+ const selectedFlags = mode === 'regex' ? readMultiSelect(this) : [];
42
+ const flagsLeaf = selectedFlags.length
43
+ ? makeMultiSelectLeaf(searchPath, {$in: selectedFlags})
44
+ : undefined;
45
+ return combineAnd([sourceLeaf, flagsLeaf]);
46
+ }
47
+ }
48
+ }, [
49
+ ['span', {class: 'searchLabel'}, [`${label} (source)`]],
50
+ buildLiteralRegexControls({
51
+ name,
52
+ /** @this {HTMLElement} */
53
+ onModeChange () {
54
+ const flagsLabel = this.closest('jsoe-search-regexp')?.querySelector(
55
+ '.searchRegexpFlags'
56
+ );
57
+ if (flagsLabel) {
58
+ /** @type {HTMLElement} */ (flagsLabel).hidden =
59
+ /** @type {HTMLSelectElement} */ (this).value !== 'regex';
60
+ }
61
+ }
62
+ }),
63
+ ['label', {class: 'searchRegexpFlags', hidden: true}, [
64
+ 'Flags: ',
65
+ buildMultiSelect({name: `${name}-flags`, options: regexpType.allowedFlags})
66
+ ]]
67
+ ]];
68
+ },
69
+ getQuery: getQueryViaElement
70
+ };
71
+
72
+ export default regexpSearchType;
@@ -0,0 +1,91 @@
1
+ import {
2
+ buildPathLabel, buildLengthSizeControls, readLengthSizeQuery,
3
+ buildOptInFieldset, readOptInChecked, wireOptInFieldset,
4
+ buildAtLeastOneSentinel, syncAtLeastOneCheck
5
+ } from '../searchUtils.js';
6
+ import {combineAnd} from '../queryTreeBuilders.js';
7
+ import {findSearchElement, getQueryViaElement, hasGetQuery} from '../searchElementUtils.js';
8
+ import {buildSearchWidget} from '../searchDispatch.js';
9
+
10
+ /**
11
+ * @typedef {import('../searchDispatch.js').SearchTypeObject} SearchTypeObject
12
+ */
13
+
14
+ /**
15
+ * @param {Element} root - a `jsoe-search-set` element
16
+ * @returns {void}
17
+ */
18
+ function syncSetValidity (root) {
19
+ syncAtLeastOneCheck(
20
+ root, () => readLengthSizeQuery(root, '') !== undefined || readOptInChecked(root)
21
+ );
22
+ }
23
+
24
+ /**
25
+ * Has length/size of <number> (README) - no sparse toggle: unlike
26
+ * `array`, a `Set` cannot have holes. Also recurses into the value schema's
27
+ * own search widget (as `arraySearchType.js` does for its element), via the
28
+ * same `*` path-segment convention (`queryTree.js`), wrapped in the same
29
+ * `buildOptInFieldset` `arraySearchType.js` uses and for the same reason -
30
+ * a length/size-only search should stay valid. Leaving *both* length/size
31
+ * and the element match unconfigured is still invalid, though
32
+ * (`buildAtLeastOneSentinel`), same as `arraySearchType.js`.
33
+ * @type {SearchTypeObject}
34
+ */
35
+ const setSearchType = {
36
+ buildUI ({schemaObject, path, typeNamespace, topRoot, types, originalJSON}) {
37
+ const label = buildPathLabel(schemaObject, path);
38
+ const name = `${typeNamespace}-set`;
39
+ const setSchemaObject = /** @type {import('zodexy').SzSet} */ (
40
+ schemaObject
41
+ );
42
+ const elementPath = `${path}/*`;
43
+ const elementArr = buildSearchWidget({
44
+ schemaObject: setSchemaObject.value,
45
+ path: elementPath,
46
+ typeNamespace,
47
+ topRoot,
48
+ types,
49
+ originalJSON
50
+ });
51
+ return ['jsoe-search-set', {
52
+ dataset: {searchPath: path, searchKind: 'set'},
53
+ title: label,
54
+ $define: {
55
+ /** @this {HTMLElement} */
56
+ connectedCallback () {
57
+ wireOptInFieldset(this, '', () => syncSetValidity(this));
58
+ this.querySelector('input.jsoeSearchSize')?.addEventListener(
59
+ 'input', () => syncSetValidity(this)
60
+ );
61
+ syncSetValidity(this);
62
+ },
63
+ /** @this {HTMLElement} */
64
+ getQuery () {
65
+ const searchPath = this.dataset.searchPath ?? '';
66
+ const lengthLeaf = readLengthSizeQuery(this, searchPath);
67
+ const elementEl = findSearchElement(this, `${searchPath}/*`);
68
+ const elementLeaf = elementEl && hasGetQuery(elementEl) && readOptInChecked(this)
69
+ ? elementEl.getQuery()
70
+ : undefined;
71
+ return combineAnd([lengthLeaf, elementLeaf]);
72
+ }
73
+ }
74
+ }, [
75
+ ['span', {class: 'searchLabel'}, [label]],
76
+ ...buildLengthSizeControls({
77
+ name,
78
+ min: setSchemaObject.minSize,
79
+ max: setSchemaObject.maxSize,
80
+ includeSparse: false
81
+ }),
82
+ ...buildOptInFieldset({
83
+ name: `${name}-element`, label: 'Element matches', children: [elementArr]
84
+ }),
85
+ buildAtLeastOneSentinel()
86
+ ]];
87
+ },
88
+ getQuery: getQueryViaElement
89
+ };
90
+
91
+ export default setSearchType;
@@ -0,0 +1,44 @@
1
+ import {buildPathLabel, buildLiteralRegexControls, readLiteralRegexQuery} from '../searchUtils.js';
2
+ import {getQueryViaElement} from '../searchElementUtils.js';
3
+ import regexpType from '../../fundamentalTypes/regexpType.js';
4
+
5
+ /**
6
+ * @typedef {import('../searchDispatch.js').SearchTypeObject} SearchTypeObject
7
+ */
8
+
9
+ /**
10
+ * OR literal or regex search/Does Not contain search (README; also covers
11
+ * `StringObject` and `templateLiteral`, aliased to this same module in
12
+ * `searchDispatch.js` since the query semantics are identical), plus - only
13
+ * while "Matches regex" is the chosen mode - a Flags multi-select
14
+ * (`buildLiteralRegexControls`'s `flagOptions`, `regexpType.js`'s own
15
+ * `allowedFlags` list reused as-is, matching `regexpSearchType.js`'s flags
16
+ * for the actual regexp's own flags): unlike `regexpSearchType.js`, whose
17
+ * "source" is already a real regexp with its own flags regardless of
18
+ * search mode, here the regex is only ever the search's *own* device for
19
+ * matching the string, so flags only make sense while that regex is
20
+ * actually in play.
21
+ * @type {SearchTypeObject}
22
+ */
23
+ const stringSearchType = {
24
+ buildUI ({schemaObject, path, typeNamespace}) {
25
+ const label = buildPathLabel(schemaObject, path);
26
+ const name = `${typeNamespace}-string`;
27
+ return ['jsoe-search-string', {
28
+ dataset: {searchPath: path, searchKind: 'string'},
29
+ title: label,
30
+ $define: {
31
+ /** @this {HTMLElement} */
32
+ getQuery () {
33
+ return readLiteralRegexQuery(this, this.dataset.searchPath ?? '');
34
+ }
35
+ }
36
+ }, [
37
+ ['span', {class: 'searchLabel'}, [label]],
38
+ buildLiteralRegexControls({name, flagOptions: regexpType.allowedFlags})
39
+ ]];
40
+ },
41
+ getQuery: getQueryViaElement
42
+ };
43
+
44
+ export default stringSearchType;
@@ -0,0 +1,39 @@
1
+ import {buildPathLabel, buildLiteralRegexControls, readLiteralRegexQuery} from '../searchUtils.js';
2
+ import {getQueryViaElement} from '../searchElementUtils.js';
3
+ import regexpType from '../../fundamentalTypes/regexpType.js';
4
+
5
+ /**
6
+ * @typedef {import('../searchDispatch.js').SearchTypeObject} SearchTypeObject
7
+ */
8
+
9
+ /**
10
+ * Symbol (description): OR literal or regex search/Does Not contain search
11
+ * (README) - the same query semantics as `stringSearchType.js`'s, against
12
+ * the symbol's description rather than a string value, so it gets its own
13
+ * dedicated module/tag (matching the value-editing side's one-file-per-
14
+ * fundamental-type convention) while sharing that module's actual control
15
+ * building/reading via `searchUtils.js`.
16
+ * @type {SearchTypeObject}
17
+ */
18
+ const symbolSearchType = {
19
+ buildUI ({schemaObject, path, typeNamespace}) {
20
+ const label = buildPathLabel(schemaObject, path);
21
+ const name = `${typeNamespace}-symbol`;
22
+ return ['jsoe-search-symbol', {
23
+ dataset: {searchPath: path, searchKind: 'symbol'},
24
+ title: label,
25
+ $define: {
26
+ /** @this {HTMLElement} */
27
+ getQuery () {
28
+ return readLiteralRegexQuery(this, this.dataset.searchPath ?? '');
29
+ }
30
+ }
31
+ }, [
32
+ ['span', {class: 'searchLabel'}, [`${label} (description)`]],
33
+ buildLiteralRegexControls({name, flagOptions: regexpType.allowedFlags})
34
+ ]];
35
+ },
36
+ getQuery: getQueryViaElement
37
+ };
38
+
39
+ export default symbolSearchType;
@@ -0,0 +1,11 @@
1
+ import {makePresenceOnlySearchType} from '../searchElementUtils.js';
2
+
3
+ /**
4
+ * `undefined`/`void`: "no variants to allow for distinct search" (README).
5
+ * @type {import('../searchDispatch.js').SearchTypeObject}
6
+ */
7
+ const undefinedSearchType = makePresenceOnlySearchType({
8
+ tagName: 'jsoe-search-undefined'
9
+ });
10
+
11
+ export default undefinedSearchType;
@@ -0,0 +1,96 @@
1
+ import {jml} from '../vendor-imports.js';
2
+ import {getSearchTypeObject, buildSearchWidget, resolveIntersection} from './searchDispatch.js';
3
+
4
+ /**
5
+ * @typedef {{
6
+ * container: HTMLFormElement,
7
+ * $getQuery: () => import('./queryTree.js').QueryAnd,
8
+ * whenReady: Promise<void>
9
+ * }} SearchChoicesControl
10
+ */
11
+
12
+ /**
13
+ * The top-level entry point (search plan §5): builds the whole recursive
14
+ * search widget tree for `schemaContent` via `buildSearchWidget`
15
+ * (`searchDispatch.js`), starting at `path = '#/'`. Does not route through
16
+ * `getTypesForSchema` for the general case - that function flattens union
17
+ * members into one flat `Set` of leaf types for a type-choice pull-down,
18
+ * whereas search needs union branches to stay distinguishable nested
19
+ * sub-widgets for the "has type" affordance (`unionSearchType.js` et al.) -
20
+ * `getTypesForSchema` is only reused narrowly, for intersection resolution,
21
+ * inside `buildSearchWidget`/`resolveIntersection` - which run at *every*
22
+ * recursive call site (every composite module's own child recursion uses
23
+ * `buildSearchWidget` too), not just here at the root, so an intersection
24
+ * schema nested anywhere in the tree resolves the same way a top-level one
25
+ * does rather than falling through to the `noneditableSearchType` escape
26
+ * hatch.
27
+ *
28
+ * `resolveIntersection` is called here too (redundantly, but cheaply, since
29
+ * it's a no-op for anything that isn't itself an `intersection`) purely so
30
+ * `$getQuery()` below dispatches through the *same* resolved schema
31
+ * `buildSearchWidget` actually built the UI for, rather than the original
32
+ * (possibly-`intersection`) one.
33
+ *
34
+ * Mirrors `buildTypeChoices`'s (`src/typeChoices.js`) `whenReady`/
35
+ * pull-based convention rather than inventing a push/callback API:
36
+ * `$getQuery()` reads the live DOM into a `QueryAnd` on demand (`{$and:
37
+ * []}` when nothing has been entered anywhere, matching the empty-`$and`
38
+ * convention of "no constraint"), and a host that wants live updates wraps
39
+ * it in its own `container.addEventListener('input', ...)`. Every widget
40
+ * built so far is synchronous, so `whenReady` (unlike `buildTypeChoices`'s)
41
+ * has no deferred work to wait on and settles immediately - it's still
42
+ * returned, so a caller can `await` either function uniformly without
43
+ * checking which one it has.
44
+ *
45
+ * `container` is a real `<form>` (its own `submit` prevented, since it is
46
+ * never actually submitted) rather than a plain `<div>`, matching the edit
47
+ * side's own root `<form>` (`src/types.js`'s `Types.validValuesSet` reads
48
+ * `form.checkValidity()`) - the various cross-field checks the individual
49
+ * search widgets wire up (`searchUtils.js`'s `buildRangeInputsPair`,
50
+ * `dateSearchType.js`, etc.) are native Constraint Validation API calls
51
+ * (`setCustomValidity`/`required`), so a host only needs the one native
52
+ * `container.checkValidity()`/`container.reportValidity()` to check them
53
+ * all at once, the same way `demo/index-search.js`'s "Check validity"
54
+ * button does.
55
+ * @param {{
56
+ * schemaContent: import('../formats/schema.js').ZodexSchema,
57
+ * typeNamespace?: string,
58
+ * topRoot?: import('../types.js').RootElement,
59
+ * types?: import('../types.js').default
60
+ * }} cfg
61
+ * @returns {SearchChoicesControl}
62
+ */
63
+ export function buildSearchChoices ({schemaContent, typeNamespace, topRoot, types}) {
64
+ // Not '#/': `getJSONPointerParts` (`src/utils/jsonPointer.js`) splits on
65
+ // '/' and drops the leading '#', so '#/' parts to `['']` (one segment
66
+ // named the empty string) rather than `[]` (the root, no segments yet)
67
+ // - every recursive module here appends `${path}/segment`, so starting
68
+ // from the bare '#' is what keeps the first level from coming out as
69
+ // '#//segment'.
70
+ const path = '#';
71
+ const arr = buildSearchWidget({
72
+ schemaObject: schemaContent, path, typeNamespace, topRoot, types,
73
+ originalJSON: schemaContent
74
+ });
75
+ const container = /** @type {HTMLFormElement} */ (
76
+ jml('form', {
77
+ class: 'searchChoicesContainer',
78
+ $on: {
79
+ submit (e) {
80
+ e.preventDefault();
81
+ }
82
+ }
83
+ }, [arr])
84
+ );
85
+ const schemaObject = resolveIntersection(schemaContent, schemaContent);
86
+ return {
87
+ container,
88
+ $getQuery () {
89
+ const result = getSearchTypeObject(schemaObject).getQuery({
90
+ root: container, path
91
+ });
92
+ return {$and: result === undefined ? [] : [result]};
93
+ },
94
+ whenReady: Promise.resolve()
95
+ };
96
+ }
@@ -0,0 +1,244 @@
1
+ /**
2
+ * JSDoc-only typedefs for the schema-driven search query tree. No runtime
3
+ * exports live here; construct nodes through `queryTreeBuilders.js` instead
4
+ * so every search module (and every test fixture) goes through one place.
5
+ *
6
+ * Leaf `path` values reuse the existing JSON-Pointer convention from
7
+ * `src/utils/jsonPointer.js` (`makeJSONPointer`/`getJSONPointerParts`)
8
+ * rather than Mongo's dot-notation, so the search tree shares a path format
9
+ * with the rest of jsoe (e.g. `arrayType.js`'s `currentPath`).
10
+ *
11
+ * The leaf vocabulary borrows MongoDB's own operator names (`$and`/`$or`,
12
+ * `$gt`/`$gte`/`$lt`/`$lte`, `$in`/`$nin`, `$regex`/`$options`, `$exists`)
13
+ * wherever a leaf kind has a clean Mongo equivalent, so a host can adapt the
14
+ * common cases to `sift()` (or real MongoDB) almost for free - this is not a
15
+ * claim of full drop-in Mongo query-document compatibility: several
16
+ * jsoe-specific leaf kinds have no Mongo equivalent and stay custom, and
17
+ * every leaf keeps jsoe's own `kind`-discriminated, path-carrying shape
18
+ * rather than Mongo's field-keyed document shape.
19
+ *
20
+ * A path segment of `*` (e.g. `#/tags/*`) means "any element of the array
21
+ * at this point" - `arraySearchType.js`/`setSearchType.js`/`filelistSearchType.js`
22
+ * (element/value length/count constraint aside) and `tupleSearchType.js`'s
23
+ * `rest` portion use it when recursing into their element/value schema's
24
+ * own search widget, so a single nested leaf expresses "the collection
25
+ * contains at least one element matching this" (Mongo's `$elemMatch` is the
26
+ * natural translation target for a host walking a path that contains this
27
+ * segment) without needing a dedicated wrapper leaf kind - the collection's
28
+ * own `lengthSize` leaf and the recursed element leaf simply combine via
29
+ * `$and`. `recordSearchType.js`/`mapSearchType.js` similarly recurse into
30
+ * their `key`/`value` schemas at `*key`/`*value` segments, feeding the
31
+ * results into a single `mapRecordJoint` leaf (below) rather than combining
32
+ * via `$and`, since a host needs to know whether the two must match the
33
+ * same entry. `functionSearchType.js` recurses into its `input`/`output`
34
+ * schemas at `*args`/`*output` segments (the former itself a tuple, so its
35
+ * own per-position segments nest under `*args`), combined via `$and`.
36
+ * `promiseSearchType.js`/`catchSearchType.js` recurse into their wrapped
37
+ * value/`innerType` at the same path (there being exactly one value, no
38
+ * segment to add) and wrap the result in a `passThrough` leaf (below)
39
+ * rather than returning it bare, so the tree keeps visible that the leaf
40
+ * was reached through one of these transparent wrappers.
41
+ */
42
+
43
+ /**
44
+ * @typedef {{$and: QueryNode[]}} QueryAnd
45
+ */
46
+
47
+ /**
48
+ * @typedef {{$or: QueryNode[]}} QueryOr
49
+ */
50
+
51
+ /**
52
+ * `object` (via `$exists: true/false`).
53
+ * @typedef {{kind: 'hasProperty', path: string, $exists: boolean}} QueryHasPropertyLeaf
54
+ */
55
+
56
+ /**
57
+ * `array`/`set`/tuple-with-rest/`filelist` (via `$size`, or a range of
58
+ * operators when the schema doesn't pin an exact length), +`sparseCheck`
59
+ * for array sparse/not-sparse.
60
+ * @typedef {{
61
+ * kind: 'lengthSize',
62
+ * path: string,
63
+ * $size?: number,
64
+ * $gt?: number, $gte?: number, $lt?: number, $lte?: number,
65
+ * sparseCheck?: boolean
66
+ * }} QueryLengthSizeLeaf
67
+ */
68
+
69
+ /**
70
+ * `number`/`NumberObject`/`bigint`/`date`/`buffersource`, with `valueType`
71
+ * distinguishing them. Bounds are Mongo-style `$gt`/`$gte`/`$lt`/`$lte`
72
+ * rather than a `min`/`max` pair plus an inclusive boolean - inclusivity is
73
+ * simply which operator is present. The README's "Is Not Range" variant
74
+ * wraps the same leaf in `$not` rather than being a separate kind.
75
+ * @typedef {{
76
+ * kind: 'range',
77
+ * path: string,
78
+ * valueType: 'number'|'NumberObject'|'bigint'|'date'|'buffersource',
79
+ * $gt?: number|bigint|string,
80
+ * $gte?: number|bigint|string,
81
+ * $lt?: number|bigint|string,
82
+ * $lte?: number|bigint|string
83
+ * }} QueryRangeLeaf
84
+ */
85
+
86
+ /**
87
+ * @typedef {{kind: 'not', query: QueryNode}} QueryNotLeaf
88
+ */
89
+
90
+ /**
91
+ * `number`/`NumberObject`: is/is-not an integer.
92
+ * @typedef {{kind: 'integerCheck', path: string, isInteger: boolean}} QueryIntegerCheckLeaf
93
+ */
94
+
95
+ /**
96
+ * `date`: is/is-not a valid `Date` (a structured-cloned `Date` can itself be
97
+ * an "Invalid Date" - a `NaN` internal time value - `dateType.js`'s own
98
+ * `notANum`/`ValidDate`/`InvalidDate` handling deals with the same state on
99
+ * the edit side).
100
+ * @typedef {{kind: 'validDateCheck', path: string, isValid: boolean}} QueryValidDateCheckLeaf
101
+ */
102
+
103
+ /**
104
+ * `string`/`StringObject`/`Blob`/`File`/regexp-source/symbol-description
105
+ * (via `$in`/`$nin`).
106
+ * @typedef {{
107
+ * kind: 'literalSet',
108
+ * path: string,
109
+ * $in?: unknown[],
110
+ * $nin?: unknown[]
111
+ * }} QueryLiteralSetLeaf
112
+ */
113
+
114
+ /**
115
+ * `string`/`StringObject`/`Blob`/`File`/regexp-source/symbol-description
116
+ * (via `$regex`/`$options`, matching Mongo's own field names).
117
+ * @typedef {{
118
+ * kind: 'regex',
119
+ * path: string,
120
+ * $regex: string,
121
+ * $options?: string
122
+ * }} QueryRegexLeaf
123
+ */
124
+
125
+ /**
126
+ * `string`/`StringObject`/`Blob`/`File`/regexp-source/symbol-description:
127
+ * a substring the value must not contain.
128
+ * @typedef {{
129
+ * kind: 'notContains',
130
+ * path: string,
131
+ * value: string
132
+ * }} QueryNotContainsLeaf
133
+ */
134
+
135
+ /**
136
+ * `enum`, SpecialNumber's `Infinity`/`-Infinity`/`NaN`/`-0` (via
137
+ * `$in`/`$nin`).
138
+ * @typedef {{
139
+ * kind: 'multiSelect',
140
+ * path: string,
141
+ * $in?: unknown[],
142
+ * $nin?: unknown[]
143
+ * }} QueryMultiSelectLeaf
144
+ */
145
+
146
+ /**
147
+ * Native enum key-vs-value; no Mongo equivalent, stays custom.
148
+ * @typedef {{
149
+ * kind: 'keyValueEnum',
150
+ * path: string,
151
+ * matchKeys: boolean,
152
+ * values: unknown[]
153
+ * }} QueryKeyValueEnumLeaf
154
+ */
155
+
156
+ /**
157
+ * Union/xor/discriminatedUnion "has type", carrying `discriminatorValue`
158
+ * when applicable, including when nested under a Map/Record key or value;
159
+ * no Mongo equivalent, stays custom.
160
+ * @typedef {{
161
+ * kind: 'typeOf',
162
+ * path: string,
163
+ * searchType: string,
164
+ * discriminatorValue?: unknown
165
+ * }} QueryTypeOfLeaf
166
+ */
167
+
168
+ /**
169
+ * XPath/CSS-selector/full-text/raw-HTML-regex; no Mongo equivalent.
170
+ * @typedef {{
171
+ * kind: 'blobHTML',
172
+ * path: string,
173
+ * mode: 'xpath'|'cssSelector'|'fullText'|'rawHTMLRegex',
174
+ * value: string
175
+ * }} QueryBlobHTMLLeaf
176
+ */
177
+
178
+ /**
179
+ * Per-dimension ranges for DOMRect/Point/Matrix, each dimension itself a
180
+ * `range` leaf, + `readonlyCheck`/`dimensionCheck` (is/is-not readonly,
181
+ * is/is-not 3d); no Mongo equivalent.
182
+ * @typedef {{
183
+ * kind: 'domShape',
184
+ * path: string,
185
+ * dimensions: {[dimension: string]: QueryRangeLeaf},
186
+ * readonlyCheck?: boolean,
187
+ * dimensionCheck?: 2|3
188
+ * }} QueryDomShapeLeaf
189
+ */
190
+
191
+ /**
192
+ * Paired key+value leaves with a joint-match flag; no Mongo equivalent.
193
+ * @typedef {{
194
+ * kind: 'mapRecordJoint',
195
+ * path: string,
196
+ * keyQuery?: QueryNode,
197
+ * valueQuery?: QueryNode,
198
+ * joint: boolean
199
+ * }} QueryMapRecordJointLeaf
200
+ */
201
+
202
+ /**
203
+ * Promise/literal/catch/function: forwards to a nested `QueryNode` for the
204
+ * child schema so the tree stays uniform even where a type adds no
205
+ * constraint of its own; purely structural, no Mongo equivalent.
206
+ * @typedef {{
207
+ * kind: 'passThrough',
208
+ * path: string,
209
+ * query?: QueryNode
210
+ * }} QueryPassThroughLeaf
211
+ */
212
+
213
+ /**
214
+ * Undefined/void/null (via `$exists`).
215
+ * @typedef {{kind: 'presence', path: string, $exists: boolean}} QueryPresenceLeaf
216
+ */
217
+
218
+ /**
219
+ * Boolean/BooleanObject - Mongo would normally express this as a bare
220
+ * `{field: true}` shorthand, which doesn't fit our path-carrying leaf
221
+ * shape, so this stays a custom kind.
222
+ * @typedef {{
223
+ * kind: 'booleanEquals',
224
+ * path: string,
225
+ * value: boolean
226
+ * }} QueryBooleanEqualsLeaf
227
+ */
228
+
229
+ /**
230
+ * @typedef {QueryHasPropertyLeaf|QueryLengthSizeLeaf|QueryRangeLeaf|
231
+ * QueryNotLeaf|QueryIntegerCheckLeaf|QueryValidDateCheckLeaf|
232
+ * QueryLiteralSetLeaf|QueryRegexLeaf|
233
+ * QueryNotContainsLeaf|QueryMultiSelectLeaf|QueryKeyValueEnumLeaf|
234
+ * QueryTypeOfLeaf|QueryBlobHTMLLeaf|QueryDomShapeLeaf|
235
+ * QueryMapRecordJointLeaf|QueryPassThroughLeaf|QueryPresenceLeaf|
236
+ * QueryBooleanEqualsLeaf
237
+ * } QueryLeaf
238
+ */
239
+
240
+ /**
241
+ * @typedef {QueryAnd|QueryOr|QueryLeaf} QueryNode
242
+ */
243
+
244
+ export {};