@es-joy/jsoe 0.28.1 → 0.29.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 (171) hide show
  1. package/CHANGES.md +23 -0
  2. package/README.md +2 -0
  3. package/badges/coverage-badge.svg +1 -1
  4. package/badges/tests-badge.svg +1 -1
  5. package/dist/deepEqual.d.ts +3 -0
  6. package/dist/deepEqual.d.ts.map +1 -0
  7. package/dist/formatAndTypeChoices.d.ts +6 -6
  8. package/dist/formatAndTypeChoices.d.ts.map +1 -1
  9. package/dist/formats/schema.d.ts +5 -0
  10. package/dist/formats/schema.d.ts.map +1 -1
  11. package/dist/formats.d.ts +2 -2
  12. package/dist/formats.d.ts.map +1 -1
  13. package/dist/fundamentalTypes/dateType.d.ts +21 -2
  14. package/dist/fundamentalTypes/dateType.d.ts.map +1 -1
  15. package/dist/fundamentalTypes/domexceptionType.d.ts +7 -0
  16. package/dist/fundamentalTypes/domexceptionType.d.ts.map +1 -1
  17. package/dist/index.d.ts +2 -0
  18. package/dist/index.d.ts.map +1 -1
  19. package/dist/index.js +2 -2
  20. package/dist/index.js.map +1 -1
  21. package/dist/jtltExtensions.d.ts +112 -0
  22. package/dist/jtltExtensions.d.ts.map +1 -0
  23. package/dist/search/fundamentalTypes/arraySearchType.d.ts +15 -0
  24. package/dist/search/fundamentalTypes/arraySearchType.d.ts.map +1 -0
  25. package/dist/search/fundamentalTypes/bigintSearchType.d.ts +10 -0
  26. package/dist/search/fundamentalTypes/bigintSearchType.d.ts.map +1 -0
  27. package/dist/search/fundamentalTypes/blobSearchType.d.ts +19 -0
  28. package/dist/search/fundamentalTypes/blobSearchType.d.ts.map +1 -0
  29. package/dist/search/fundamentalTypes/booleanSearchType.d.ts +11 -0
  30. package/dist/search/fundamentalTypes/booleanSearchType.d.ts.map +1 -0
  31. package/dist/search/fundamentalTypes/catchSearchType.d.ts +18 -0
  32. package/dist/search/fundamentalTypes/catchSearchType.d.ts.map +1 -0
  33. package/dist/search/fundamentalTypes/dateSearchType.d.ts +10 -0
  34. package/dist/search/fundamentalTypes/dateSearchType.d.ts.map +1 -0
  35. package/dist/search/fundamentalTypes/domexceptionSearchType.d.ts +16 -0
  36. package/dist/search/fundamentalTypes/domexceptionSearchType.d.ts.map +1 -0
  37. package/dist/search/fundamentalTypes/enumSearchType.d.ts +22 -0
  38. package/dist/search/fundamentalTypes/enumSearchType.d.ts.map +1 -0
  39. package/dist/search/fundamentalTypes/errorSearchType.d.ts +9 -0
  40. package/dist/search/fundamentalTypes/errorSearchType.d.ts.map +1 -0
  41. package/dist/search/fundamentalTypes/fileSearchType.d.ts +19 -0
  42. package/dist/search/fundamentalTypes/fileSearchType.d.ts.map +1 -0
  43. package/dist/search/fundamentalTypes/filelistSearchType.d.ts +32 -0
  44. package/dist/search/fundamentalTypes/filelistSearchType.d.ts.map +1 -0
  45. package/dist/search/fundamentalTypes/functionSearchType.d.ts +18 -0
  46. package/dist/search/fundamentalTypes/functionSearchType.d.ts.map +1 -0
  47. package/dist/search/fundamentalTypes/mapSearchType.d.ts +18 -0
  48. package/dist/search/fundamentalTypes/mapSearchType.d.ts.map +1 -0
  49. package/dist/search/fundamentalTypes/nanSearchType.d.ts +7 -0
  50. package/dist/search/fundamentalTypes/nanSearchType.d.ts.map +1 -0
  51. package/dist/search/fundamentalTypes/nullSearchType.d.ts +7 -0
  52. package/dist/search/fundamentalTypes/nullSearchType.d.ts.map +1 -0
  53. package/dist/search/fundamentalTypes/numberSearchType.d.ts +11 -0
  54. package/dist/search/fundamentalTypes/numberSearchType.d.ts.map +1 -0
  55. package/dist/search/fundamentalTypes/objectSearchType.d.ts +15 -0
  56. package/dist/search/fundamentalTypes/objectSearchType.d.ts.map +1 -0
  57. package/dist/search/fundamentalTypes/promiseSearchType.d.ts +20 -0
  58. package/dist/search/fundamentalTypes/promiseSearchType.d.ts.map +1 -0
  59. package/dist/search/fundamentalTypes/regexpSearchType.d.ts +14 -0
  60. package/dist/search/fundamentalTypes/regexpSearchType.d.ts.map +1 -0
  61. package/dist/search/fundamentalTypes/setSearchType.d.ts +14 -0
  62. package/dist/search/fundamentalTypes/setSearchType.d.ts.map +1 -0
  63. package/dist/search/fundamentalTypes/stringSearchType.d.ts +13 -0
  64. package/dist/search/fundamentalTypes/stringSearchType.d.ts.map +1 -0
  65. package/dist/search/fundamentalTypes/symbolSearchType.d.ts +16 -0
  66. package/dist/search/fundamentalTypes/symbolSearchType.d.ts.map +1 -0
  67. package/dist/search/fundamentalTypes/undefinedSearchType.d.ts +7 -0
  68. package/dist/search/fundamentalTypes/undefinedSearchType.d.ts.map +1 -0
  69. package/dist/search/index.d.ts +60 -0
  70. package/dist/search/index.d.ts.map +1 -0
  71. package/dist/search/queryTree.d.ts +180 -0
  72. package/dist/search/queryTree.d.ts.map +1 -0
  73. package/dist/search/queryTreeBuilders.d.ts +168 -0
  74. package/dist/search/queryTreeBuilders.d.ts.map +1 -0
  75. package/dist/search/searchDispatch.d.ts +121 -0
  76. package/dist/search/searchDispatch.d.ts.map +1 -0
  77. package/dist/search/searchElementUtils.d.ts +97 -0
  78. package/dist/search/searchElementUtils.d.ts.map +1 -0
  79. package/dist/search/searchUtils.d.ts +225 -0
  80. package/dist/search/searchUtils.d.ts.map +1 -0
  81. package/dist/search/subTypes/blobHTMLSearchType.d.ts +16 -0
  82. package/dist/search/subTypes/blobHTMLSearchType.d.ts.map +1 -0
  83. package/dist/search/subTypes/recordSearchType.d.ts +18 -0
  84. package/dist/search/subTypes/recordSearchType.d.ts.map +1 -0
  85. package/dist/search/subTypes/tupleSearchType.d.ts +21 -0
  86. package/dist/search/subTypes/tupleSearchType.d.ts.map +1 -0
  87. package/dist/search/superTypes/SpecialRealNumberSearchType.d.ts +14 -0
  88. package/dist/search/superTypes/SpecialRealNumberSearchType.d.ts.map +1 -0
  89. package/dist/search/superTypes/buffersourceSearchType.d.ts +17 -0
  90. package/dist/search/superTypes/buffersourceSearchType.d.ts.map +1 -0
  91. package/dist/search/superTypes/dommatrixSearchType.d.ts +11 -0
  92. package/dist/search/superTypes/dommatrixSearchType.d.ts.map +1 -0
  93. package/dist/search/superTypes/dompointSearchType.d.ts +9 -0
  94. package/dist/search/superTypes/dompointSearchType.d.ts.map +1 -0
  95. package/dist/search/superTypes/domrectSearchType.d.ts +9 -0
  96. package/dist/search/superTypes/domrectSearchType.d.ts.map +1 -0
  97. package/dist/search/superTypes/errorsSpecialSearchType.d.ts +11 -0
  98. package/dist/search/superTypes/errorsSpecialSearchType.d.ts.map +1 -0
  99. package/dist/search/unions/discriminatedUnionSearchType.d.ts +12 -0
  100. package/dist/search/unions/discriminatedUnionSearchType.d.ts.map +1 -0
  101. package/dist/search/unions/unionFamilySearchType.d.ts +36 -0
  102. package/dist/search/unions/unionFamilySearchType.d.ts.map +1 -0
  103. package/dist/search/unions/unionSearchType.d.ts +8 -0
  104. package/dist/search/unions/unionSearchType.d.ts.map +1 -0
  105. package/dist/search/unions/xorSearchType.d.ts +12 -0
  106. package/dist/search/unions/xorSearchType.d.ts.map +1 -0
  107. package/dist/superTypes/buffersourceType.d.ts.map +1 -1
  108. package/dist/typeChoices.d.ts +3 -3
  109. package/dist/typeChoices.d.ts.map +1 -1
  110. package/dist/types.d.ts +3 -3
  111. package/dist/types.d.ts.map +1 -1
  112. package/dist/utils/rawTypesonEditor.d.ts.map +1 -1
  113. package/docs/proposals/search-plan.md +166 -0
  114. package/package.json +5 -4
  115. package/pnpm-workspace.yaml +1 -1
  116. package/rollup.config.js +12 -0
  117. package/src/formatAndTypeChoices.js +4 -4
  118. package/src/formats/schema.js +2 -1
  119. package/src/formats.js +1 -1
  120. package/src/fundamentalTypes/dateType.js +35 -14
  121. package/src/fundamentalTypes/domexceptionType.js +41 -33
  122. package/src/index.js +6 -0
  123. package/src/jtltExtensions.js +103 -0
  124. package/src/search/fundamentalTypes/arraySearchType.js +66 -0
  125. package/src/search/fundamentalTypes/bigintSearchType.js +62 -0
  126. package/src/search/fundamentalTypes/blobSearchType.js +41 -0
  127. package/src/search/fundamentalTypes/booleanSearchType.js +40 -0
  128. package/src/search/fundamentalTypes/catchSearchType.js +59 -0
  129. package/src/search/fundamentalTypes/dateSearchType.js +73 -0
  130. package/src/search/fundamentalTypes/domexceptionSearchType.js +55 -0
  131. package/src/search/fundamentalTypes/enumSearchType.js +58 -0
  132. package/src/search/fundamentalTypes/errorSearchType.js +11 -0
  133. package/src/search/fundamentalTypes/fileSearchType.js +41 -0
  134. package/src/search/fundamentalTypes/filelistSearchType.js +78 -0
  135. package/src/search/fundamentalTypes/functionSearchType.js +78 -0
  136. package/src/search/fundamentalTypes/mapSearchType.js +87 -0
  137. package/src/search/fundamentalTypes/nanSearchType.js +11 -0
  138. package/src/search/fundamentalTypes/nullSearchType.js +11 -0
  139. package/src/search/fundamentalTypes/numberSearchType.js +52 -0
  140. package/src/search/fundamentalTypes/objectSearchType.js +148 -0
  141. package/src/search/fundamentalTypes/promiseSearchType.js +61 -0
  142. package/src/search/fundamentalTypes/regexpSearchType.js +48 -0
  143. package/src/search/fundamentalTypes/setSearchType.js +65 -0
  144. package/src/search/fundamentalTypes/stringSearchType.js +35 -0
  145. package/src/search/fundamentalTypes/symbolSearchType.js +38 -0
  146. package/src/search/fundamentalTypes/undefinedSearchType.js +11 -0
  147. package/src/search/index.js +79 -0
  148. package/src/search/queryTree.js +235 -0
  149. package/src/search/queryTreeBuilders.js +224 -0
  150. package/src/search/searchDispatch.js +291 -0
  151. package/src/search/searchElementUtils.js +289 -0
  152. package/src/search/searchUtils.js +355 -0
  153. package/src/search/subTypes/blobHTMLSearchType.js +65 -0
  154. package/src/search/subTypes/recordSearchType.js +84 -0
  155. package/src/search/subTypes/tupleSearchType.js +111 -0
  156. package/src/search/superTypes/SpecialRealNumberSearchType.js +42 -0
  157. package/src/search/superTypes/buffersourceSearchType.js +47 -0
  158. package/src/search/superTypes/dommatrixSearchType.js +24 -0
  159. package/src/search/superTypes/dompointSearchType.js +15 -0
  160. package/src/search/superTypes/domrectSearchType.js +15 -0
  161. package/src/search/superTypes/errorsSpecialSearchType.js +15 -0
  162. package/src/search/unions/discriminatedUnionSearchType.js +16 -0
  163. package/src/search/unions/unionFamilySearchType.js +160 -0
  164. package/src/search/unions/unionSearchType.js +12 -0
  165. package/src/search/unions/xorSearchType.js +16 -0
  166. package/src/superTypes/buffersourceType.js +13 -15
  167. package/src/typeChoices.js +2 -2
  168. package/src/types.js +2 -2
  169. package/src/utils/rawTypesonEditor.js +12 -2
  170. package/tsconfig-prod.json +12 -0
  171. package/tsconfig.json +5 -10
@@ -0,0 +1,291 @@
1
+ import {getSchemaType, getTypesForSchema} from '../formats/schema.js';
2
+
3
+ import dateSearchType from './fundamentalTypes/dateSearchType.js';
4
+ import numberSearchType from './fundamentalTypes/numberSearchType.js';
5
+ import bigintSearchType from './fundamentalTypes/bigintSearchType.js';
6
+ import stringSearchType from './fundamentalTypes/stringSearchType.js';
7
+ import regexpSearchType from './fundamentalTypes/regexpSearchType.js';
8
+ import booleanSearchType from './fundamentalTypes/booleanSearchType.js';
9
+ import symbolSearchType from './fundamentalTypes/symbolSearchType.js';
10
+ import undefinedSearchType from './fundamentalTypes/undefinedSearchType.js';
11
+ import nullSearchType from './fundamentalTypes/nullSearchType.js';
12
+ import nanSearchType from './fundamentalTypes/nanSearchType.js';
13
+ import enumSearchType from './fundamentalTypes/enumSearchType.js';
14
+ import arraySearchType from './fundamentalTypes/arraySearchType.js';
15
+ import setSearchType from './fundamentalTypes/setSearchType.js';
16
+ import objectSearchType from './fundamentalTypes/objectSearchType.js';
17
+ import filelistSearchType from './fundamentalTypes/filelistSearchType.js';
18
+ import tupleSearchType from './subTypes/tupleSearchType.js';
19
+ import recordSearchType from './subTypes/recordSearchType.js';
20
+ import mapSearchType from './fundamentalTypes/mapSearchType.js';
21
+ import promiseSearchType from './fundamentalTypes/promiseSearchType.js';
22
+ import catchSearchType from './fundamentalTypes/catchSearchType.js';
23
+ import functionSearchType from './fundamentalTypes/functionSearchType.js';
24
+ import fileSearchType from './fundamentalTypes/fileSearchType.js';
25
+ import blobSearchType from './fundamentalTypes/blobSearchType.js';
26
+ import unionSearchType from './unions/unionSearchType.js';
27
+ import xorSearchType from './unions/xorSearchType.js';
28
+ import discriminatedUnionSearchType from './unions/discriminatedUnionSearchType.js';
29
+ import errorSearchType from './fundamentalTypes/errorSearchType.js';
30
+ import errorsSpecialSearchType from './superTypes/errorsSpecialSearchType.js';
31
+ import domexceptionSearchType from './fundamentalTypes/domexceptionSearchType.js';
32
+ import domrectSearchType from './superTypes/domrectSearchType.js';
33
+ import dompointSearchType from './superTypes/dompointSearchType.js';
34
+ import dommatrixSearchType from './superTypes/dommatrixSearchType.js';
35
+ import SpecialRealNumberSearchType from './superTypes/SpecialRealNumberSearchType.js';
36
+ import buffersourceSearchType from './superTypes/buffersourceSearchType.js';
37
+ import blobHTMLSearchType from './subTypes/blobHTMLSearchType.js';
38
+
39
+ /**
40
+ * @typedef {import('./queryTree.js').QueryNode} QueryNode
41
+ */
42
+
43
+ /**
44
+ * The search-side analogue of `TypeObject` (`src/types.js:308-408`): two
45
+ * methods, `buildUI` (a `jml` array) and `getQuery` (reads the built DOM
46
+ * back into a `QueryNode`, or `undefined` when no constraint was entered).
47
+ * @typedef {{
48
+ * buildUI: (cfg: {
49
+ * schemaObject: import('../formats/schema.js').ZodexSchema,
50
+ * path: string,
51
+ * typeNamespace?: string,
52
+ * topRoot?: import('../types.js').RootElement,
53
+ * types?: import('../types.js').default,
54
+ * originalJSON?: import('../formats/schema.js').ZodexSchema
55
+ * }) => import('../types.js').JamilihArray,
56
+ * getQuery: (cfg: {
57
+ * root: HTMLElement,
58
+ * path: string
59
+ * }) => QueryNode|undefined
60
+ * }} SearchTypeObject
61
+ */
62
+
63
+ /**
64
+ * Temporary placeholder used while a given search type's real module is
65
+ * still to-do (build order, `docs/proposals/search-plan.md` §6). Renders a
66
+ * plainly-labelled marker rather than throwing, so the full dispatch table
67
+ * is exercisable end-to-end before every leaf/composite is implemented.
68
+ * @param {string} searchSchemaType
69
+ * @returns {SearchTypeObject}
70
+ */
71
+ function stubSearchType (searchSchemaType) {
72
+ return {
73
+ buildUI: () => ['span', [`TODO: ${searchSchemaType}`]],
74
+ getQuery: () => undefined
75
+ };
76
+ }
77
+
78
+ /**
79
+ * Escape hatch for `instanceof`-outside-`FileList` and any schema shape not
80
+ * (yet) recognized: no widget is rendered for it, only this stand-in.
81
+ * @type {SearchTypeObject}
82
+ */
83
+ const noneditableSearchType = stubSearchType('noneditable');
84
+
85
+ /**
86
+ * `Record<string, SearchTypeObject>` registry, analogous to
87
+ * `Types.prototype.availableTypes` (`src/types.js:473-581`). Every entry is
88
+ * a stub for now (build order step 1); later steps replace individual
89
+ * entries with their real `*SearchType.js` module, one at a time, without
90
+ * touching this shape.
91
+ *
92
+ * A few keys deliberately share one module reference rather than getting
93
+ * their own: `BooleanObject`/`NumberObject`/`StringObject`/`bigintObject`
94
+ * point at their primitive counterpart's module because a search leaf only
95
+ * cares about the query semantics (e.g. "true or false"), never about how
96
+ * the value-editing side constructs the boxed wrapper at runtime. Likewise
97
+ * `arrayNonindexKeys` mirrors `array`, `looseRecord` mirrors `record`, and
98
+ * `templateLiteral` mirrors `string` (README: a template literal is still
99
+ * fundamentally a string-shape constraint for this pass; see §3 of the
100
+ * search plan).
101
+ * @type {{[key: string]: SearchTypeObject}}
102
+ */
103
+ const availableSearchTypes = {
104
+ // fundamentalTypes
105
+ date: dateSearchType,
106
+ number: numberSearchType,
107
+ NumberObject: numberSearchType,
108
+ bigint: bigintSearchType,
109
+ bigintObject: bigintSearchType,
110
+ string: stringSearchType,
111
+ StringObject: stringSearchType,
112
+ regexp: regexpSearchType,
113
+ boolean: booleanSearchType,
114
+ BooleanObject: booleanSearchType,
115
+ symbol: symbolSearchType,
116
+ undef: undefinedSearchType,
117
+ null: nullSearchType,
118
+ nan: nanSearchType,
119
+ array: arraySearchType,
120
+ arrayNonindexKeys: arraySearchType,
121
+ object: objectSearchType,
122
+ map: mapSearchType,
123
+ set: setSearchType,
124
+ filelist: filelistSearchType,
125
+ file: fileSearchType,
126
+ blob: blobSearchType,
127
+ error: errorSearchType,
128
+ domexception: domexceptionSearchType,
129
+ promise: promiseSearchType,
130
+ function: functionSearchType,
131
+ enum: enumSearchType,
132
+
133
+ // subTypes
134
+ tuple: tupleSearchType,
135
+ record: recordSearchType,
136
+ looseRecord: recordSearchType,
137
+ blobHTML: blobHTMLSearchType,
138
+
139
+ // superTypes
140
+ domrect: domrectSearchType,
141
+ dompoint: dompointSearchType,
142
+ dommatrix: dommatrixSearchType,
143
+ errors: errorsSpecialSearchType,
144
+ SpecialNumber: stubSearchType('SpecialNumber'),
145
+ SpecialRealNumber: SpecialRealNumberSearchType,
146
+ buffersource: buffersourceSearchType,
147
+
148
+ // unions
149
+ union: unionSearchType,
150
+ xor: xorSearchType,
151
+ discriminatedUnion: discriminatedUnionSearchType,
152
+
153
+ // string-shape alias (§3)
154
+ templateLiteral: stringSearchType,
155
+
156
+ // `getSchemaType` has no case for `catch` at all (unlike `promise`/
157
+ // `function`, already in `zodexToStructuredCloningTypeMap`), so it's
158
+ // added as its own extra case in `getSearchSchemaType` below
159
+ catch: catchSearchType,
160
+
161
+ // escape hatch, also the fallback for any unrecognized/not-yet-supported
162
+ // schema shape (see `getSearchTypeObject`)
163
+ resurrectable: noneditableSearchType
164
+ };
165
+
166
+ /**
167
+ * Schema-only analogue of `getSchemaType` (`src/formats/schema.js:145-166`).
168
+ * Must not reimplement that function's special-casing (stringbool pipe,
169
+ * `codec`/filelist, `instanceof`, `literal`, `enum`, `templateLiteral`,
170
+ * `getCheckedType` fallback) - it calls straight through to it, and only
171
+ * adds the extra shape distinctions `getSchemaType`'s own
172
+ * `AvailableArbitraryType` output collapses (tuple, record/looseRecord,
173
+ * union/xor/discriminatedUnion all currently reduce to `'array'`/`'object'`/
174
+ * nothing, per `zodexToStructuredCloningTypeMap`,
175
+ * `src/formats/schema.js:30-56`).
176
+ *
177
+ * Intersection schemas are deliberately not a case here - they're resolved
178
+ * before dispatch, by the search-widget walker calling `getTypesForSchema`
179
+ * (search plan §3), reusing jsoe's existing intersection-merging machinery
180
+ * rather than this function reinventing it.
181
+ * @param {import('../formats/schema.js').ZodexSchema} schemaObject
182
+ * @returns {string}
183
+ */
184
+ export function getSearchSchemaType (schemaObject) {
185
+ if (schemaObject.type === 'tuple') {
186
+ return 'tuple';
187
+ }
188
+ if (schemaObject.type === 'record' || schemaObject.type === 'looseRecord') {
189
+ return schemaObject.type;
190
+ }
191
+ if (['union', 'xor', 'discriminatedUnion'].includes(schemaObject.type)) {
192
+ return schemaObject.type;
193
+ }
194
+
195
+ // An `enum` control can show a multiple-select list and template literal
196
+ // parts might justify their own search controls, so need to detect
197
+ // these schema types.
198
+ if (['templateLiteral', 'enum'].includes(schemaObject.type)) {
199
+ return schemaObject.type;
200
+ }
201
+
202
+ // `getSchemaType` (`src/formats/schema.js`) has no `catch` case at all -
203
+ // its `zodexToStructuredCloningTypeMap` never mapped it, unlike
204
+ // `promise`/`function` - so it's added here rather than there, matching
205
+ // how tuple/record/union already extend the search-only shape space.
206
+ if (schemaObject.type === 'catch') {
207
+ return 'catch';
208
+ }
209
+
210
+ return /** @type {string} */ (
211
+ getSchemaType(schemaObject)
212
+ ); // inherits stringbool/codec/instanceof/checks handling verbatim
213
+ }
214
+
215
+ /**
216
+ * The one exported call site every recursive search widget uses (analogous
217
+ * to `types.getTypeObject(type)`, `src/types.js:1106-1108`, but schema-in
218
+ * rather than type-string-in, since there's no value to key off). Falls
219
+ * back to `noneditableSearchType` for any schema shape `getSearchSchemaType`
220
+ * reports that isn't (yet) registered, rather than throwing - the same
221
+ * escape hatch a bare top-level `instanceof` schema falls through to.
222
+ * @param {import('../formats/schema.js').ZodexSchema} schemaObject
223
+ * @returns {SearchTypeObject}
224
+ */
225
+ export function getSearchTypeObject (schemaObject) {
226
+ const searchSchemaType = getSearchSchemaType(schemaObject);
227
+ return availableSearchTypes[searchSchemaType] ?? noneditableSearchType;
228
+ }
229
+
230
+ /**
231
+ * Intersection schemas are never registered in `availableSearchTypes` -
232
+ * resolved here instead of in `getSearchSchemaType`, reusing jsoe's
233
+ * existing intersection-merging machinery (`getTypesForSchema`,
234
+ * `src/formats/schema.js`) rather than reinventing it (search plan §3/§5).
235
+ * A single merged result replaces the intersection outright; more than one
236
+ * (e.g. one side of the intersection is itself a union) is handed to
237
+ * `unionSearchType.js` unchanged via a synthesized `union` wrapper, the
238
+ * same way `unionSearchType.js` already treats distinguishable branches.
239
+ * @param {import('../formats/schema.js').ZodexSchema} schemaObject
240
+ * @param {import('../formats/schema.js').ZodexSchema} originalJSON
241
+ * @returns {import('../formats/schema.js').ZodexSchema}
242
+ */
243
+ function resolveIntersection (schemaObject, originalJSON) {
244
+ if (schemaObject.type !== 'intersection') {
245
+ return schemaObject;
246
+ }
247
+ const merged = [...getTypesForSchema(schemaObject, originalJSON)];
248
+ return merged.length === 1
249
+ ? merged[0]
250
+ : /** @type {import('../formats/schema.js').ZodexSchema} */ (
251
+ /** @type {unknown} */ ({type: 'union', options: merged})
252
+ );
253
+ }
254
+
255
+ /**
256
+ * The call site every *recursive* search widget uses in place of calling
257
+ * `getSearchTypeObject(...).buildUI(...)` directly - it's the same thing,
258
+ * plus `resolveIntersection` run first, so an intersection schema at any
259
+ * depth (not just the one `buildSearchChoices` is called with) still
260
+ * resolves instead of falling through to the `noneditableSearchType`
261
+ * escape hatch. `originalJSON` (needed for the `getTypesForSchema` call's
262
+ * own `$ref` resolution) defaults to `schemaObject` itself only as a last
263
+ * resort for a call site that has none to pass down - every recursive
264
+ * caller should be threading the true document root through via its own
265
+ * `originalJSON` parameter instead.
266
+ * @param {{
267
+ * schemaObject: import('../formats/schema.js').ZodexSchema,
268
+ * path: string,
269
+ * typeNamespace?: string,
270
+ * topRoot?: import('../types.js').RootElement,
271
+ * types?: import('../types.js').default,
272
+ * originalJSON?: import('../formats/schema.js').ZodexSchema
273
+ * }} cfg
274
+ * @returns {import('../types.js').JamilihArray}
275
+ */
276
+ export function buildSearchWidget ({
277
+ schemaObject, path, typeNamespace, topRoot, types, originalJSON
278
+ }) {
279
+ const resolvedOriginalJSON = originalJSON ?? schemaObject;
280
+ const resolvedSchema = resolveIntersection(schemaObject, resolvedOriginalJSON);
281
+ return getSearchTypeObject(resolvedSchema).buildUI({
282
+ schemaObject: resolvedSchema,
283
+ path,
284
+ typeNamespace,
285
+ topRoot,
286
+ types,
287
+ originalJSON: resolvedOriginalJSON
288
+ });
289
+ }
290
+
291
+ export {availableSearchTypes, noneditableSearchType, resolveIntersection};
@@ -0,0 +1,289 @@
1
+ import {
2
+ buildPathLabel, buildCheckbox, readCheckbox,
3
+ buildLiteralRegexControls, readLiteralRegexQuery,
4
+ buildRangeInputsPair, readRangeInputsPair,
5
+ buildTriStateSelect, readTriStateSelect
6
+ } from './searchUtils.js';
7
+ import {combineAnd, makeRangeLeaf, makeDomShapeLeaf} from './queryTreeBuilders.js';
8
+
9
+ /**
10
+ * Locates a built search element by the JSON-Pointer path it was built for.
11
+ * Compares `dataset.searchPath` in JS rather than interpolating `path` into
12
+ * a `querySelector` attribute-value string, since a schema path can embed
13
+ * arbitrary property-name characters (only `~`/`/` are escaped by
14
+ * `makeJSONPointer`) that would otherwise need CSS-selector escaping.
15
+ * @param {ParentNode} root
16
+ * @param {string} path
17
+ * @returns {HTMLElement|undefined}
18
+ */
19
+ export function findSearchElement (root, path) {
20
+ return /** @type {HTMLElement[]} */ (
21
+ [...root.querySelectorAll('[data-search-path]')]
22
+ ).find((el) => el.dataset.searchPath === path);
23
+ }
24
+
25
+ /**
26
+ * @typedef {HTMLElement & {
27
+ * getQuery: () => import('./queryTree.js').QueryNode|undefined
28
+ * }} SearchElement
29
+ */
30
+
31
+ /**
32
+ * The `SearchTypeObject.getQuery` every leaf module implements identically:
33
+ * locate the element it built under `root` and delegate to its own
34
+ * (per-element, `dataset`-driven) `getQuery()` method.
35
+ * @param {{root: HTMLElement, path: string}} cfg
36
+ * @returns {import('./queryTree.js').QueryNode|undefined}
37
+ */
38
+ export function getQueryViaElement ({root, path}) {
39
+ const el = /** @type {SearchElement|undefined} */ (
40
+ findSearchElement(root, path)
41
+ );
42
+ return el?.getQuery();
43
+ }
44
+
45
+ /**
46
+ * Type-predicate guard distinguishing a built search element (leaf or
47
+ * container, either answers to `.getQuery()`) from a plain DOM node - used
48
+ * by container elements (e.g. `objectSearchType.js`'s `<jsoe-search-object>`)
49
+ * that walk `this.children` polymorphically (search plan §8) without
50
+ * knowing which children are search elements and which are plain layout
51
+ * (a controls `<div>`, etc.).
52
+ * @param {Element} el
53
+ * @returns {el is SearchElement}
54
+ */
55
+ export function hasGetQuery (el) {
56
+ return typeof (/** @type {{getQuery?: unknown}} */ (el)).getQuery ===
57
+ 'function';
58
+ }
59
+
60
+ /**
61
+ * Factory for the README's "no variants to allow for distinct search"
62
+ * leaves (`undefined`/`void`, `null`, `NaN`): the only meaningful question
63
+ * is whether the path is present at all, which only matters once the path
64
+ * is optional or nested in a union - so all three share one tiny, single-
65
+ * checkbox implementation, differing only in tag name/label.
66
+ * @param {{tagName: string}} cfg
67
+ * @returns {import('./searchDispatch.js').SearchTypeObject}
68
+ */
69
+ export function makePresenceOnlySearchType ({tagName}) {
70
+ return {
71
+ buildUI ({schemaObject, path, typeNamespace}) {
72
+ const label = buildPathLabel(schemaObject, path);
73
+ const name = `${typeNamespace}-${tagName}`;
74
+ return [tagName, {
75
+ dataset: {searchPath: path, searchKind: tagName},
76
+ title: label,
77
+ $define: {
78
+ /** @this {HTMLElement} */
79
+ getQuery () {
80
+ const checked = readCheckbox(this);
81
+ return checked
82
+ ? {kind: 'presence', path: this.dataset.searchPath ?? '', $exists: true}
83
+ : undefined;
84
+ }
85
+ }
86
+ }, [
87
+ ['span', {class: 'searchLabel'}, [label]],
88
+ buildCheckbox({name, label: `Require ${label} present`})
89
+ ]];
90
+ },
91
+ getQuery: getQueryViaElement
92
+ };
93
+ }
94
+
95
+ /**
96
+ * The Error family's fixed known properties (`errorType.js`'s own
97
+ * `setValue`/`getValue` hard-code this same set - `message`/`name`/
98
+ * `fileName`/`stack` as strings, `lineNumber`/`columnNumber` as numbers -
99
+ * since a `checked`-type schema like `error`/`errors` carries no structural
100
+ * detail of its own to derive property names/types from, unlike `object`).
101
+ * @type {string[]}
102
+ */
103
+ const errorStringProps = ['message', 'name', 'fileName', 'stack'];
104
+
105
+ /** @type {string[]} */
106
+ const errorNumberProps = ['lineNumber', 'columnNumber'];
107
+
108
+ /**
109
+ * @param {{label: string, name: string}} cfg
110
+ * @returns {import('./searchUtils.js').JamilihArray[]}
111
+ */
112
+ function buildErrorFamilyChildren ({label, name}) {
113
+ /** @type {import('./searchUtils.js').JamilihArray[]} */
114
+ const children = [['span', {class: 'searchLabel'}, [label]]];
115
+ errorStringProps.forEach((prop) => {
116
+ children.push(['div', {class: `errorProp-${prop}`}, [
117
+ ['span', [`${prop}: `]],
118
+ buildLiteralRegexControls({name: `${name}-${prop}`, key: prop})
119
+ ]]);
120
+ });
121
+ errorNumberProps.forEach((prop) => {
122
+ children.push(['div', {class: `errorProp-${prop}`}, [
123
+ ['span', [`${prop}: `]],
124
+ ...buildRangeInputsPair({name: `${name}-${prop}`, key: prop})
125
+ ]]);
126
+ });
127
+ return children;
128
+ }
129
+
130
+ /**
131
+ * Factory for `errorSearchType.js`/`errorsSpecialSearchType.js`, offering
132
+ * literal/regex search of child string properties, numeric of number
133
+ * children (README) - one `buildLiteralRegexControls`/`buildRangeInputsPair` pair
134
+ * per known property (`key`-distinguished so they coexist in one widget;
135
+ * see those helpers' docs), combined via `$and`. `.cause` (a recursive,
136
+ * arbitrarily-typed property) and `errorsSpecial`'s `AggregateError.errors`
137
+ * array are deliberately left out of this pass - the README's bullet asks
138
+ * only for the flat string/number children, and open-ended recursion into
139
+ * an arbitrary `cause` chain is a materially bigger feature than "the same
140
+ * shape, twice more".
141
+ * @param {{tagName: string}} cfg
142
+ * @returns {import('./searchDispatch.js').SearchTypeObject}
143
+ */
144
+ export function makeErrorFamilySearchType ({tagName}) {
145
+ return {
146
+ buildUI ({schemaObject, path, typeNamespace}) {
147
+ const label = buildPathLabel(schemaObject, path);
148
+ const name = `${typeNamespace}-${tagName}`;
149
+ return [tagName, {
150
+ dataset: {searchPath: path, searchKind: tagName},
151
+ title: label,
152
+ $define: {
153
+ /** @this {HTMLElement} */
154
+ getQuery () {
155
+ const searchPath = this.dataset.searchPath ?? '';
156
+ const stringLeaves = errorStringProps.map((prop) => (
157
+ readLiteralRegexQuery(this, `${searchPath}/${prop}`, prop)
158
+ ));
159
+ const numberLeaves = errorNumberProps.map((prop) => {
160
+ const {gte, lte} = readRangeInputsPair(this, prop);
161
+ if (gte === '' && lte === '') {
162
+ return undefined;
163
+ }
164
+ return makeRangeLeaf(`${searchPath}/${prop}`, 'number', {
165
+ ...(gte === '' ? {} : {$gte: Number(gte)}),
166
+ ...(lte === '' ? {} : {$lte: Number(lte)})
167
+ });
168
+ });
169
+ return combineAnd([...stringLeaves, ...numberLeaves]);
170
+ }
171
+ }
172
+ }, buildErrorFamilyChildren({label, name})];
173
+ },
174
+ getQuery: getQueryViaElement
175
+ };
176
+ }
177
+
178
+ /**
179
+ * @param {{
180
+ * label: string, name: string, dimensionKeys: string[],
181
+ * includeReadonly: boolean, includeDimensionCheck: boolean
182
+ * }} cfg
183
+ * @returns {import('./searchUtils.js').JamilihArray[]}
184
+ */
185
+ function buildDomShapeChildren ({
186
+ label, name, dimensionKeys, includeReadonly, includeDimensionCheck
187
+ }) {
188
+ /** @type {import('./searchUtils.js').JamilihArray[]} */
189
+ const children = [['span', {class: 'searchLabel'}, [label]]];
190
+ dimensionKeys.forEach((dim) => {
191
+ children.push(['div', {class: `domShapeDimension-${dim}`}, [
192
+ ['span', [`${dim}: `]],
193
+ ...buildRangeInputsPair({name: `${name}-${dim}`, key: dim})
194
+ ]]);
195
+ });
196
+ if (includeReadonly) {
197
+ children.push(['label', [
198
+ 'Readonly: ',
199
+ buildTriStateSelect({
200
+ name: `${name}-readonly`, key: 'readonly',
201
+ trueLabel: 'Readonly', falseLabel: 'Not readonly'
202
+ })
203
+ ]]);
204
+ }
205
+ if (includeDimensionCheck) {
206
+ children.push(['label', [
207
+ '3d: ',
208
+ buildTriStateSelect({
209
+ name: `${name}-dimension`, key: 'dimension',
210
+ trueLabel: '3d', falseLabel: 'Not 3d'
211
+ })
212
+ ]]);
213
+ }
214
+ return children;
215
+ }
216
+
217
+ /**
218
+ * Factory for `domrectSearchType.js`/`dompointSearchType.js`/
219
+ * `dommatrixSearchType.js`: numeric search ranges for children; "Is/Is not
220
+ * Readonly"; DOMMatrix: "Is/Is not 3d" (README) - one `buildRangeInputsPair`
221
+ * per known dimension (`key`-distinguished so they coexist in one widget;
222
+ * `dimensionKeys` covers DOMMatrix's 2D (`a`-`f`) *and* 3D (`m11`-`m44`)
223
+ * shapes at once rather than switching the control set on an "Is/Is not 3d"
224
+ * answer that's itself just another optional search constraint, not a
225
+ * schema fact - `domrect`/`dompoint`/`dommatrix` are all "checked" types
226
+ * with no structural schema to derive dimension names from in the first
227
+ * place, same reasoning as `makeErrorFamilySearchType`), combined into one
228
+ * `domShape` leaf (`queryTree.js`) rather than separate leaves via `$and`.
229
+ * @param {{
230
+ * tagName: string,
231
+ * dimensionKeys: string[],
232
+ * includeReadonly?: boolean,
233
+ * includeDimensionCheck?: boolean
234
+ * }} cfg
235
+ * @returns {import('./searchDispatch.js').SearchTypeObject}
236
+ */
237
+ export function makeDomShapeSearchType ({
238
+ tagName, dimensionKeys, includeReadonly = false, includeDimensionCheck = false
239
+ }) {
240
+ return {
241
+ buildUI ({schemaObject, path, typeNamespace}) {
242
+ const label = buildPathLabel(schemaObject, path);
243
+ const name = `${typeNamespace}-${tagName}`;
244
+ return [tagName, {
245
+ dataset: {searchPath: path, searchKind: tagName},
246
+ title: label,
247
+ $define: {
248
+ /** @this {HTMLElement} */
249
+ getQuery () {
250
+ const searchPath = this.dataset.searchPath ?? '';
251
+ /** @type {{[dim: string]: import('./queryTree.js').QueryRangeLeaf}} */
252
+ const dimensions = {};
253
+ dimensionKeys.forEach((dim) => {
254
+ const {gte, lte} = readRangeInputsPair(this, dim);
255
+ if (gte === '' && lte === '') {
256
+ return;
257
+ }
258
+ dimensions[dim] = makeRangeLeaf(`${searchPath}/${dim}`, 'number', {
259
+ ...(gte === '' ? {} : {$gte: Number(gte)}),
260
+ ...(lte === '' ? {} : {$lte: Number(lte)})
261
+ });
262
+ });
263
+ const readonlyCheck = includeReadonly
264
+ ? readTriStateSelect(this, 'readonly')
265
+ : undefined;
266
+ const dimensionCheck = includeDimensionCheck
267
+ ? readTriStateSelect(this, 'dimension')
268
+ : undefined;
269
+ if (
270
+ readonlyCheck === undefined && dimensionCheck === undefined &&
271
+ Object.keys(dimensions).length === 0
272
+ ) {
273
+ return undefined;
274
+ }
275
+ return makeDomShapeLeaf(searchPath, dimensions, {
276
+ ...(readonlyCheck === undefined ? {} : {readonlyCheck}),
277
+ ...(dimensionCheck === undefined
278
+ ? {}
279
+ : {dimensionCheck: dimensionCheck ? 3 : 2})
280
+ });
281
+ }
282
+ }
283
+ }, buildDomShapeChildren({
284
+ label, name, dimensionKeys, includeReadonly, includeDimensionCheck
285
+ })];
286
+ },
287
+ getQuery: getQueryViaElement
288
+ };
289
+ }