@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
package/src/jsoe.css CHANGED
@@ -151,6 +151,24 @@ video.recordedMedia {
151
151
  color: #b00;
152
152
  }
153
153
 
154
+ /* `demo/index-search.js`'s "Check validity" button and its immediately-
155
+ following result text (`.validityResult`) are both inline (a `<button>`
156
+ and a `<span>`), so without this they run together with no gap. */
157
+ .checkValidityButton {
158
+ margin-inline-end: 0.5em;
159
+ }
160
+
161
+ /* `demo/index-search.js`'s per-section "Get query" result: a query for a
162
+ deeply-nested schema can be a long single-line JSON string, so this needs
163
+ the same scrollable-box treatment as `.jsoe-raw-editor` rather than
164
+ growing the page. */
165
+ .queryResult {
166
+ max-width: 100%;
167
+ max-height: 300px;
168
+ overflow: auto;
169
+ border: 1px solid;
170
+ }
171
+
154
172
  .visualizer {
155
173
  height: 60px;
156
174
  }
@@ -211,6 +229,7 @@ video.recordedMedia {
211
229
  background-color: purple;
212
230
  }
213
231
  [data-type="date"] {
232
+ color: white;
214
233
  background-color: teal;
215
234
  }
216
235
  [data-type="regexp"] {
@@ -258,6 +277,191 @@ video.recordedMedia {
258
277
  background-color: lightgray;
259
278
  }
260
279
 
280
+ /* Search widgets (`buildSearchChoices`/`buildSearchWidget`, `src/search/`):
281
+ every built widget's custom-element root carries `data-search-kind`
282
+ (the leaf/composite kind it was built for - `object`, `array`, or, for
283
+ the "checked"-type factories that have no structural schema of their
284
+ own, the full tag name e.g. `jsoe-search-error`). Block-level with the
285
+ same border/padding treatment as `fieldset.xorTypeChoices` above, so a
286
+ hierarchical type's recursively-nested child widgets land on their own
287
+ indented line rather than running together inline, and the same
288
+ background colors as the corresponding view/edit UI `[data-type]` rules
289
+ above, so a given type reads the same whether browsing or searching. */
290
+ [data-search-kind] {
291
+ display: block;
292
+ margin-block: 0.3em;
293
+ padding: 0.3em 0.6em 0.4em;
294
+ border: 1px solid #ccc;
295
+ border-radius: 4px;
296
+ /* Reset explicitly rather than relying on inheriting the page default:
297
+ a light-background kind (e.g. `tuple`'s "args" widget nested inside a
298
+ dark-background `function`) would otherwise inherit that ancestor's
299
+ `color: white` and become unreadable against its own light
300
+ background. */
301
+ color: #000;
302
+ }
303
+
304
+ /* `objectSearchType.js` hides a "Doesn't have" property's own recursed
305
+ widget via the native `hidden` attribute - restore its effect, since the
306
+ unconditional `display: block` above (an author rule) would otherwise
307
+ beat the browser's default `[hidden] { display: none }` UA rule. */
308
+ [data-search-kind][hidden] {
309
+ display: none;
310
+ }
311
+
312
+ /* A descendant selector rather than `>` (direct child): every search module
313
+ places its own `.searchLabel` as a direct child of its widget root except
314
+ `objectSearchType.js`, which nests it one level deeper inside its own
315
+ `.searchObjectControls` wrapper (alongside the "Add property" pull-down) -
316
+ a `>` combinator would leave that one span inline, squished directly
317
+ against the pull-down's label right after it with no gap. */
318
+ [data-search-kind] .searchLabel {
319
+ display: block;
320
+ font-weight: bold;
321
+ margin-block-end: 0.2em;
322
+ }
323
+
324
+ /* `objectSearchType.js`'s required-property row, and `searchUtils.js`'s
325
+ shared `buildOptInFieldset` (array/set/tuple/filelist's element match(es),
326
+ map/record's key/value, file's name/content-type), both wrap a recursed
327
+ widget in a `<fieldset disabled>` (native constraint-validation opt-out
328
+ for the whole subtree, toggled by an "opt in" checkbox) purely for that
329
+ behavior, not for its default border/padding - which would otherwise
330
+ double up with the child widget's own `[data-search-kind]` box drawn just
331
+ inside it. `[class^=...]` matches `buildOptInFieldset`'s `key`-suffixed
332
+ class regardless of `key`. */
333
+ fieldset.searchRequiredPropertyFieldset,
334
+ fieldset[class^="jsoeSearchOptInFieldset--"] {
335
+ border: none;
336
+ margin: 0;
337
+ padding: 0;
338
+ }
339
+
340
+ /* `buildOptInFieldset`'s own "Key matches"/"Element matches"/etc. checkbox
341
+ label - `[data-search-kind] label`'s `margin-inline-end` above is meant
342
+ for short *inline* control pairs sharing one line (a "From:"/"To:"
343
+ pair), not a facet-toggling label like this one, which reads better
344
+ started on its own line rather than run together with whatever came
345
+ before it (an adjacent length/size control, another facet's own label,
346
+ etc). */
347
+ [data-search-kind] .jsoeSearchOptInLabel {
348
+ display: block;
349
+ }
350
+
351
+ /* `searchUtils.js`'s `buildAtLeastOneSentinel` - kept off-screen without
352
+ `display: none`/the `hidden` attribute, which would exempt it from the
353
+ Constraint Validation API entirely (the same reason `[hidden]` needed a
354
+ `display: none` override above for `objectSearchType.js`'s "Doesn't
355
+ have" case, just the opposite problem: there, hiding needed to *win*;
356
+ here, hiding must *not* affect validity). Standard "visually-hidden"
357
+ technique - present and rendered, just clipped to nothing. */
358
+ .searchAtLeastOneSentinel {
359
+ position: absolute;
360
+ width: 1px;
361
+ height: 1px;
362
+ margin: -1px;
363
+ padding: 0;
364
+ overflow: hidden;
365
+ clip: rect(0, 0, 0, 0);
366
+ white-space: nowrap;
367
+ border: 0;
368
+ }
369
+
370
+ /* Inline control pairs built by `searchUtils.js` (a "Mode:"/"Value:" pair
371
+ from `buildLiteralRegexControls`, a "From:"/"To:" pair from
372
+ `buildRangeInputsPair`, a trailing tri-state like "Integer:") are each
373
+ their own `<label>` with no whitespace between them in the built markup,
374
+ so without this they visually run together (e.g. "To: [input]Integer:
375
+ [select]"). */
376
+ [data-search-kind] label {
377
+ margin-inline-end: 0.6em;
378
+ }
379
+
380
+ /* Nested child widgets get progressively inset, so a hierarchical type's
381
+ structure (object properties, array/set elements, map/record key-value,
382
+ union branches, etc.) stays legible several levels deep. */
383
+ [data-search-kind] [data-search-kind] {
384
+ margin-inline-start: 0.8em;
385
+ }
386
+
387
+ /* Dark backgrounds get a light text color so labels/inputs stay readable
388
+ (unlike the view/edit `[data-type]` rules above, which - for these same
389
+ colors - rely on their content being a short, bolded inline leaf rather
390
+ than a whole widget's worth of labels and form controls). */
391
+ [data-search-kind="jsoe-search-undefined"] {
392
+ background-color: green;
393
+ color: white;
394
+ }
395
+ [data-search-kind="jsoe-search-null"] {
396
+ background-color: lightgreen;
397
+ }
398
+ [data-search-kind="jsoe-search-nan"] {
399
+ background-color: green;
400
+ color: white;
401
+ }
402
+ [data-search-kind="boolean"] {
403
+ background-color: aqua;
404
+ }
405
+ [data-search-kind="number"] {
406
+ background-color: violet;
407
+ }
408
+ [data-search-kind="string"] {
409
+ background-color: lightblue;
410
+ }
411
+ [data-search-kind="array"], [data-search-kind="tuple"] {
412
+ background-color: lightyellow;
413
+ }
414
+ [data-search-kind="object"], [data-search-kind="record"] {
415
+ background-color: orange;
416
+ }
417
+ [data-search-kind="symbol"] {
418
+ background-color: lightgreen;
419
+ }
420
+ [data-search-kind="promise"] {
421
+ background-color: orange;
422
+ }
423
+ [data-search-kind="function"] {
424
+ background-color: teal;
425
+ color: white;
426
+ }
427
+ [data-search-kind="date"] {
428
+ background-color: teal;
429
+ color: white;
430
+ }
431
+ [data-search-kind="regexp"] {
432
+ background-color: yellow;
433
+ }
434
+ [data-search-kind="SpecialRealNumber"] {
435
+ background-color: red;
436
+ color: white;
437
+ }
438
+ [data-search-kind="blobHTML"] {
439
+ background-color: sandybrown;
440
+ }
441
+ [data-search-kind="jsoe-search-error"],
442
+ [data-search-kind="jsoe-search-errors-special"] {
443
+ background-color: red;
444
+ color: white;
445
+ }
446
+ [data-search-kind="file"] {
447
+ background-color: beige;
448
+ }
449
+ [data-search-kind="filelist"] {
450
+ background-color: bisque;
451
+ }
452
+ [data-search-kind="blob"] {
453
+ background-color: lightgray;
454
+ }
455
+ [data-search-kind="buffersource"] {
456
+ background-color: beige;
457
+ }
458
+ [data-search-kind="map"], [data-search-kind="set"],
459
+ [data-search-kind="bigint"], [data-search-kind="domexception"],
460
+ [data-search-kind="jsoe-search-domrect"], [data-search-kind="jsoe-search-dompoint"],
461
+ [data-search-kind="jsoe-search-dommatrix"] {
462
+ background-color: lightgray;
463
+ }
464
+
261
465
  .typedArrayArea {
262
466
  overflow: auto;
263
467
  max-height: 250px;
@@ -0,0 +1,103 @@
1
+ /**
2
+ * A minimal structural stand-in for the jtlt template `this` context — just
3
+ * the two members these extensions use. Defined locally (rather than
4
+ * imported from jtlt) so this module adds no dependency on jtlt itself;
5
+ * jtlt's own `ContextExtensions` augmentation is a wider, compatible type.
6
+ * `get` mirrors `JSONPathTransformerContext#get(select, wrap)`: a JSONPath
7
+ * query against the current template data (or that data itself when
8
+ * `select` is falsy).
9
+ * @typedef {{
10
+ * appendOutput: (item: unknown) => unknown,
11
+ * get: (select: string|undefined, wrap: boolean) => unknown
12
+ * }} JtltExtensionContext
13
+ */
14
+
15
+ /**
16
+ * The argument object a declarative `{$generateJsoeEditUI: {...}}` /
17
+ * `{$generateJsoeViewUI: {...}}` node supplies, entirely unresolved — see
18
+ * jtlt's own extension-call mechanism (`jsonTemplate.js`'s `runOperation`,
19
+ * jtlt 0.21.0+): jtlt does not interpret any of these keys itself, so
20
+ * resolving `select` (via `this.get`) is this module's own job, not
21
+ * jtlt's. `db`/`store`, when given, name which store's schema should shape
22
+ * the built controls — independent of `select`, since the *value* being
23
+ * edited/viewed and the *store it belongs to* are two separate facts a
24
+ * template may need to supply together (e.g. when a `behavior` reads rows
25
+ * from a store other than the one its own route targets).
26
+ * @typedef {{select: string, db?: string, store?: string}} JsoeUIArgs
27
+ */
28
+
29
+ /**
30
+ * Builds a `generateJsoeEditUI` / `generateJsoeViewUI` pair, ready to spread
31
+ * into a jtlt `config.extensions` object — see
32
+ * `~/idb-manager/ROUTE-OVERRIDES-PLAN.md` §5 ("idb-manager behavior
33
+ * extensions"). Each function is a jtlt extension called with the
34
+ * declarative node's own argument object, e.g. `{$generateJsoeEditUI:
35
+ * {select: '$.record', db: 'myDb', store: 'myStore'}}` → `this.
36
+ * generateJsoeEditUI({select: '$.record', db: 'myDb', store: 'myStore'})`
37
+ * (or, from the raw-JS override tier, the same call made directly). It
38
+ * resolves `select` itself (`this.get(select, false)`), looks up that
39
+ * store's schema via `getSchemaContent` when `db`/`store` are both given,
40
+ * builds jsoe's own edit/view control tree for the resolved value, and
41
+ * inserts it into the current output itself via `this.appendOutput(...)`
42
+ * — matching jtlt's own `$renderDefault` convention of the extension doing
43
+ * its own insertion rather than returning a value for the interpreter to
44
+ * place.
45
+ *
46
+ * Takes the caller's own `Types` instance rather than constructing one:
47
+ * idb-manager (for example) already has a single, app-wide `Types`
48
+ * instance (`new Types()` in `Router.js`) configured with whatever
49
+ * constructor options it needs, and every control this builds should share
50
+ * that configuration, not a second, independently-configured one.
51
+ * @param {import('./types.js').default} types
52
+ * @param {{
53
+ * typeNamespace?: string,
54
+ * getSchemaContent?: (db: string, store: string) => (
55
+ * import('./formats/schema.js').ZodexSchema | undefined |
56
+ * Promise<import('./formats/schema.js').ZodexSchema | undefined>
57
+ * )
58
+ * }} [options] - `typeNamespace` scopes the jsoe-internal type-choice state
59
+ * so this doesn't collide with unrelated controls the same page builds
60
+ * elsewhere (see other `getControlsForFormatAndValue` call sites' own
61
+ * `typeNamespace` values); defaults to `'jtlt'`. `getSchemaContent`, when
62
+ * given, is called with each call's own `db`/`store` (e.g. wrapping
63
+ * idb-manager's `Schemas.js` lookup) — resolved fresh per call, so a
64
+ * single bundle correctly serves a `behavior` that edits/views records
65
+ * from more than one store, each with its own schema. Omitted (or a
66
+ * call's `db`/`store` omitted) means no schema — jsoe's controls still
67
+ * work, inferring type choices from the value alone.
68
+ * @returns {{
69
+ * generateJsoeEditUI: (
70
+ * this: JtltExtensionContext, argObject: JsoeUIArgs
71
+ * ) => Promise<void>,
72
+ * generateJsoeViewUI: (
73
+ * this: JtltExtensionContext, argObject: JsoeUIArgs
74
+ * ) => Promise<void>
75
+ * }}
76
+ */
77
+ export default function createJtltExtensions (
78
+ types, {typeNamespace = 'jtlt', getSchemaContent} = {}
79
+ ) {
80
+ /**
81
+ * @param {boolean} readonly
82
+ * @returns {(
83
+ * this: JtltExtensionContext, argObject: JsoeUIArgs
84
+ * ) => Promise<void>}
85
+ */
86
+ const buildAndAppend = (readonly) => async function (argObject) {
87
+ const {select, db, store} = argObject;
88
+ const value = this.get(select, false);
89
+ const schemaContent = db !== undefined && store !== undefined &&
90
+ getSchemaContent
91
+ ? await getSchemaContent(db, store)
92
+ : undefined;
93
+ const el = await types.getControlsForFormatAndValue(
94
+ 'structuredCloning', value, {readonly, typeNamespace, schemaContent}
95
+ );
96
+ this.appendOutput(el);
97
+ };
98
+
99
+ return {
100
+ generateJsoeEditUI: buildAndAppend(false),
101
+ generateJsoeViewUI: buildAndAppend(true)
102
+ };
103
+ }
@@ -0,0 +1,101 @@
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-array` element
16
+ * @returns {void}
17
+ */
18
+ function syncArrayValidity (root) {
19
+ syncAtLeastOneCheck(
20
+ root, () => readLengthSizeQuery(root, '') !== undefined || readOptInChecked(root)
21
+ );
22
+ }
23
+
24
+ /**
25
+ * Has length/size of &lt;number&gt;; Is/Is not sparse (README), plus - by
26
+ * recursing into the element schema's own search widget (search plan §6
27
+ * build order) - "the array contains at least one element matching Y",
28
+ * expressed via a `*` path segment (`queryTree.js`) so no dedicated wrapper
29
+ * leaf kind is needed: the two constraints simply combine with `$and`. The
30
+ * element match is wrapped in `buildOptInFieldset` (default unchecked/
31
+ * disabled) so a length/size-only search stays valid: unlike the length/
32
+ * size inputs (plain, un-`required` numbers), the recursed element widget
33
+ * can itself carry `required` controls (e.g. a `string` element's Value),
34
+ * which would otherwise force this whole array invalid just for existing,
35
+ * whether or not the user actually wants an element constraint. Leaving
36
+ * both length/size and the element match unconfigured at once is still
37
+ * invalid, though (`buildAtLeastOneSentinel`) - the same "no absent values"
38
+ * reasoning as everywhere else, just with two facets where either alone
39
+ * already suffices.
40
+ * @type {SearchTypeObject}
41
+ */
42
+ const arraySearchType = {
43
+ buildUI ({schemaObject, path, typeNamespace, topRoot, types, originalJSON}) {
44
+ const label = buildPathLabel(schemaObject, path);
45
+ const name = `${typeNamespace}-array`;
46
+ const arraySchemaObject = /** @type {import('zodexy').SzArray} */ (
47
+ schemaObject
48
+ );
49
+ const elementPath = `${path}/*`;
50
+ const elementArr = buildSearchWidget({
51
+ schemaObject: arraySchemaObject.element,
52
+ path: elementPath,
53
+ typeNamespace,
54
+ topRoot,
55
+ types,
56
+ originalJSON
57
+ });
58
+ return ['jsoe-search-array', {
59
+ dataset: {searchPath: path, searchKind: 'array'},
60
+ title: label,
61
+ $define: {
62
+ /** @this {HTMLElement} */
63
+ connectedCallback () {
64
+ wireOptInFieldset(this, '', () => syncArrayValidity(this));
65
+ this.querySelector('input.jsoeSearchSize')?.addEventListener(
66
+ 'input', () => syncArrayValidity(this)
67
+ );
68
+ this.querySelector('select.jsoeSearchTriState--')?.addEventListener(
69
+ 'change', () => syncArrayValidity(this)
70
+ );
71
+ syncArrayValidity(this);
72
+ },
73
+ /** @this {HTMLElement} */
74
+ getQuery () {
75
+ const searchPath = this.dataset.searchPath ?? '';
76
+ const lengthLeaf = readLengthSizeQuery(this, searchPath);
77
+ const elementEl = findSearchElement(this, `${searchPath}/*`);
78
+ const elementLeaf = elementEl && hasGetQuery(elementEl) && readOptInChecked(this)
79
+ ? elementEl.getQuery()
80
+ : undefined;
81
+ return combineAnd([lengthLeaf, elementLeaf]);
82
+ }
83
+ }
84
+ }, [
85
+ ['span', {class: 'searchLabel'}, [label]],
86
+ ...buildLengthSizeControls({
87
+ name,
88
+ min: arraySchemaObject.minLength,
89
+ max: arraySchemaObject.maxLength,
90
+ includeSparse: true
91
+ }),
92
+ ...buildOptInFieldset({
93
+ name: `${name}-element`, label: 'Element matches', children: [elementArr]
94
+ }),
95
+ buildAtLeastOneSentinel()
96
+ ]];
97
+ },
98
+ getQuery: getQueryViaElement
99
+ };
100
+
101
+ export default arraySearchType;
@@ -0,0 +1,68 @@
1
+ import {
2
+ buildPathLabel, buildRangeInputsPair, readRangeInputsPair, syncRangeValidity
3
+ } from '../searchUtils.js';
4
+ import {makeRangeLeaf} from '../queryTreeBuilders.js';
5
+ import {getQueryViaElement} from '../searchElementUtils.js';
6
+
7
+ /**
8
+ * @typedef {import('../searchDispatch.js').SearchTypeObject} SearchTypeObject
9
+ */
10
+
11
+ /**
12
+ * @param {string} str
13
+ * @returns {string|undefined}
14
+ */
15
+ function parseBigIntBound (str) {
16
+ if (!str) {
17
+ return undefined;
18
+ }
19
+ try {
20
+ // Kept as a decimal string (not a runtime `BigInt`) so the tree stays
21
+ // JSON-serializable, mirroring `dateSearchType.js`'s ISO-string bounds.
22
+ return String(BigInt(str));
23
+ } catch {
24
+ return undefined;
25
+ }
26
+ }
27
+
28
+ /**
29
+ * OR Ranges/Is Not Range (README) - text inputs rather than
30
+ * `<input type="number">`, since a native number input silently loses
31
+ * precision beyond `Number.MAX_SAFE_INTEGER`.
32
+ * @type {SearchTypeObject}
33
+ */
34
+ const bigintSearchType = {
35
+ buildUI ({schemaObject, path, typeNamespace}) {
36
+ const label = buildPathLabel(schemaObject, path);
37
+ const name = `${typeNamespace}-bigint`;
38
+ return ['jsoe-search-bigint', {
39
+ dataset: {searchPath: path, searchKind: 'bigint'},
40
+ title: label,
41
+ $define: {
42
+ /** @this {HTMLElement} */
43
+ connectedCallback () {
44
+ syncRangeValidity(this);
45
+ },
46
+ /** @this {HTMLElement} */
47
+ getQuery () {
48
+ const {gte: gteStr, lte: lteStr} = readRangeInputsPair(this);
49
+ const $gte = parseBigIntBound(gteStr);
50
+ const $lte = parseBigIntBound(lteStr);
51
+ if ($gte === undefined && $lte === undefined) {
52
+ return undefined;
53
+ }
54
+ return makeRangeLeaf(this.dataset.searchPath ?? '', 'bigint', {
55
+ ...($gte === undefined ? {} : {$gte}),
56
+ ...($lte === undefined ? {} : {$lte})
57
+ });
58
+ }
59
+ }
60
+ }, [
61
+ ['span', {class: 'searchLabel'}, [label]],
62
+ ...buildRangeInputsPair({name, type: 'text'})
63
+ ]];
64
+ },
65
+ getQuery: getQueryViaElement
66
+ };
67
+
68
+ export default bigintSearchType;
@@ -0,0 +1,42 @@
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
+ * Blob: OR literal or regex search/Does Not contain search (README, grouped
11
+ * with string/StringObject/File/regexp-source/symbol-description) - against
12
+ * the Blob's `.type` (MIME string), a value-level facet the schema itself
13
+ * carries no constraint for (`blob` is one of jsoe's "checked" types,
14
+ * `{type: 'any', checks: [{name: 'blob'}]}`, with no further structural
15
+ * fields to read), the same way `symbolSearchType.js` targets
16
+ * `.description`. Matching *content* rather than the MIME type belongs to
17
+ * `blobHTMLSearchType.js` (XPath/CSS-selector/full-text/raw-HTML-regex,
18
+ * README) for the Blob-as-HTML case specifically, not this generic module.
19
+ * @type {SearchTypeObject}
20
+ */
21
+ const blobSearchType = {
22
+ buildUI ({schemaObject, path, typeNamespace}) {
23
+ const label = buildPathLabel(schemaObject, path);
24
+ const name = `${typeNamespace}-blob`;
25
+ return ['jsoe-search-blob', {
26
+ dataset: {searchPath: path, searchKind: 'blob'},
27
+ title: label,
28
+ $define: {
29
+ /** @this {HTMLElement} */
30
+ getQuery () {
31
+ return readLiteralRegexQuery(this, this.dataset.searchPath ?? '');
32
+ }
33
+ }
34
+ }, [
35
+ ['span', {class: 'searchLabel'}, [`${label} (MIME type)`]],
36
+ buildLiteralRegexControls({name, flagOptions: regexpType.allowedFlags})
37
+ ]];
38
+ },
39
+ getQuery: getQueryViaElement
40
+ };
41
+
42
+ export default blobSearchType;
@@ -0,0 +1,40 @@
1
+ import {buildPathLabel, buildTriStateSelect, readTriStateSelect} from '../searchUtils.js';
2
+ import {makeBooleanEqualsLeaf} from '../queryTreeBuilders.js';
3
+ import {getQueryViaElement} from '../searchElementUtils.js';
4
+
5
+ /**
6
+ * @typedef {import('../searchDispatch.js').SearchTypeObject} SearchTypeObject
7
+ */
8
+
9
+ /**
10
+ * True or false (README).
11
+ * @type {SearchTypeObject}
12
+ */
13
+ const booleanSearchType = {
14
+ buildUI ({schemaObject, path, typeNamespace}) {
15
+ const label = buildPathLabel(schemaObject, path);
16
+ const name = `${typeNamespace}-boolean`;
17
+ return ['jsoe-search-boolean', {
18
+ dataset: {searchPath: path, searchKind: 'boolean'},
19
+ title: label,
20
+ $define: {
21
+ /** @this {HTMLElement} */
22
+ getQuery () {
23
+ const value = readTriStateSelect(this);
24
+ return value === undefined
25
+ ? undefined
26
+ : makeBooleanEqualsLeaf(this.dataset.searchPath ?? '', value);
27
+ }
28
+ }
29
+ }, [
30
+ ['span', {class: 'searchLabel'}, [label]],
31
+ ['label', [
32
+ `${label}: `,
33
+ buildTriStateSelect({name, trueLabel: 'True', falseLabel: 'False', required: true})
34
+ ]]
35
+ ]];
36
+ },
37
+ getQuery: getQueryViaElement
38
+ };
39
+
40
+ export default booleanSearchType;
@@ -0,0 +1,59 @@
1
+ import {buildPathLabel} from '../searchUtils.js';
2
+ import {makePassThroughLeaf} from '../queryTreeBuilders.js';
3
+ import {findSearchElement, getQueryViaElement, hasGetQuery} from '../searchElementUtils.js';
4
+ import {buildSearchWidget} from '../searchDispatch.js';
5
+
6
+ /**
7
+ * @typedef {import('../searchDispatch.js').SearchTypeObject} SearchTypeObject
8
+ */
9
+
10
+ /**
11
+ * Promise, Literal, catch: pass on children (README) - same shape as
12
+ * `promiseSearchType.js`'s pass-through (recurse into `.innerType` at the
13
+ * same path, wrap in a `passThrough` leaf), except `getSchemaType`
14
+ * (`src/formats/schema.js`) has no case for `catch` at all - unlike
15
+ * `promise`/`function`, which `zodexToStructuredCloningTypeMap` already
16
+ * maps - so `getSearchSchemaType` adds `catch` as its own extra case purely
17
+ * for the search side, the same way it already does for `tuple`/`record`/
18
+ * the union family.
19
+ * @type {SearchTypeObject}
20
+ */
21
+ const catchSearchType = {
22
+ buildUI ({schemaObject, path, typeNamespace, topRoot, types, originalJSON}) {
23
+ const label = buildPathLabel(schemaObject, path);
24
+ const catchSchemaObject = /** @type {import('zodexy').SzCatch} */ (
25
+ schemaObject
26
+ );
27
+ const childArr = buildSearchWidget({
28
+ schemaObject: catchSchemaObject.innerType,
29
+ path,
30
+ typeNamespace,
31
+ topRoot,
32
+ types,
33
+ originalJSON
34
+ });
35
+ return ['jsoe-search-catch', {
36
+ dataset: {searchPath: path, searchKind: 'catch'},
37
+ title: label,
38
+ $define: {
39
+ /** @this {HTMLElement} */
40
+ getQuery () {
41
+ const searchPath = this.dataset.searchPath ?? '';
42
+ const childEl = findSearchElement(this, searchPath);
43
+ const childQuery = childEl && hasGetQuery(childEl)
44
+ ? childEl.getQuery()
45
+ : undefined;
46
+ return childQuery === undefined
47
+ ? undefined
48
+ : makePassThroughLeaf(searchPath, childQuery);
49
+ }
50
+ }
51
+ }, [
52
+ ['span', {class: 'searchLabel'}, [label]],
53
+ childArr
54
+ ]];
55
+ },
56
+ getQuery: getQueryViaElement
57
+ };
58
+
59
+ export default catchSearchType;