@emeryld/rrroutes-contract 2.7.11 → 2.7.12
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +1 -1
- package/dist/export/flattenSchema.d.ts +1 -0
- package/dist/index.cjs +22 -7
- package/dist/index.cjs.map +1 -1
- package/dist/index.mjs +22 -7
- package/dist/index.mjs.map +1 -1
- package/package.json +1 -1
- package/tools/finalized-leaves-viewer.html +421 -139
|
@@ -50,22 +50,68 @@
|
|
|
50
50
|
}
|
|
51
51
|
|
|
52
52
|
.controls {
|
|
53
|
+
display: grid;
|
|
54
|
+
gap: 14px;
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
.primary-row {
|
|
53
58
|
display: grid;
|
|
54
59
|
gap: 10px;
|
|
60
|
+
grid-template-columns: minmax(220px, 0.9fr) minmax(300px, 1.4fr) minmax(220px, 1fr);
|
|
61
|
+
align-items: end;
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
.control-block {
|
|
65
|
+
display: grid;
|
|
66
|
+
gap: 6px;
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
.control-label {
|
|
70
|
+
font-size: 12px;
|
|
71
|
+
color: var(--muted);
|
|
55
72
|
}
|
|
56
73
|
|
|
57
|
-
.
|
|
74
|
+
.quick-toggles {
|
|
58
75
|
display: flex;
|
|
59
76
|
flex-wrap: wrap;
|
|
60
77
|
gap: 8px;
|
|
78
|
+
align-items: center;
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
.scope-row {
|
|
82
|
+
border-top: 1px solid var(--border);
|
|
83
|
+
padding-top: 12px;
|
|
84
|
+
display: grid;
|
|
85
|
+
gap: 10px;
|
|
61
86
|
}
|
|
62
87
|
|
|
63
|
-
.
|
|
88
|
+
.scope-actions {
|
|
64
89
|
display: flex;
|
|
65
90
|
flex-wrap: wrap;
|
|
66
91
|
gap: 8px;
|
|
67
92
|
}
|
|
68
93
|
|
|
94
|
+
.scope-groups {
|
|
95
|
+
display: grid;
|
|
96
|
+
gap: 10px;
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
.scope-group {
|
|
100
|
+
display: grid;
|
|
101
|
+
gap: 6px;
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
.scope-group-title {
|
|
105
|
+
font-size: 12px;
|
|
106
|
+
color: var(--muted);
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
.scope-group-chips {
|
|
110
|
+
display: flex;
|
|
111
|
+
flex-wrap: wrap;
|
|
112
|
+
gap: 6px;
|
|
113
|
+
}
|
|
114
|
+
|
|
69
115
|
.field-item {
|
|
70
116
|
display: inline-flex;
|
|
71
117
|
align-items: center;
|
|
@@ -86,7 +132,7 @@
|
|
|
86
132
|
|
|
87
133
|
button {
|
|
88
134
|
border: 1px solid var(--border);
|
|
89
|
-
border-radius:
|
|
135
|
+
border-radius: 6px;
|
|
90
136
|
padding: 7px 10px;
|
|
91
137
|
font: inherit;
|
|
92
138
|
background: var(--surface-2);
|
|
@@ -190,12 +236,82 @@
|
|
|
190
236
|
background: var(--surface-2);
|
|
191
237
|
}
|
|
192
238
|
|
|
239
|
+
.chip-btn {
|
|
240
|
+
cursor: pointer;
|
|
241
|
+
}
|
|
242
|
+
|
|
193
243
|
.chip.ok {
|
|
194
244
|
border-color: #b5e4c8;
|
|
195
245
|
color: var(--ok);
|
|
196
246
|
background: #effbf4;
|
|
197
247
|
}
|
|
198
248
|
|
|
249
|
+
.pill-toggle {
|
|
250
|
+
border: 1px solid var(--border);
|
|
251
|
+
border-radius: 999px;
|
|
252
|
+
padding: 6px 12px;
|
|
253
|
+
font-size: 12px;
|
|
254
|
+
background: var(--surface-2);
|
|
255
|
+
color: var(--text);
|
|
256
|
+
}
|
|
257
|
+
|
|
258
|
+
.pill-toggle[aria-pressed='true'] {
|
|
259
|
+
border-color: var(--accent);
|
|
260
|
+
background: rgba(167, 100, 211, 0.25);
|
|
261
|
+
color: #f0d8ff;
|
|
262
|
+
}
|
|
263
|
+
|
|
264
|
+
.scope-action {
|
|
265
|
+
font-size: 12px;
|
|
266
|
+
padding: 5px 10px;
|
|
267
|
+
}
|
|
268
|
+
|
|
269
|
+
.field-chip {
|
|
270
|
+
font-size: 12px;
|
|
271
|
+
padding: 5px 10px;
|
|
272
|
+
border-radius: 999px;
|
|
273
|
+
}
|
|
274
|
+
|
|
275
|
+
.field-chip[aria-pressed='true'] {
|
|
276
|
+
border-color: var(--schema-accent);
|
|
277
|
+
background: rgba(251, 189, 35, 0.22);
|
|
278
|
+
color: #ffe7aa;
|
|
279
|
+
}
|
|
280
|
+
|
|
281
|
+
.advanced-panel {
|
|
282
|
+
border-top: 1px solid var(--border);
|
|
283
|
+
padding-top: 10px;
|
|
284
|
+
}
|
|
285
|
+
|
|
286
|
+
.advanced-panel > summary {
|
|
287
|
+
color: var(--text);
|
|
288
|
+
font-size: 13px;
|
|
289
|
+
}
|
|
290
|
+
|
|
291
|
+
.advanced-content {
|
|
292
|
+
margin-top: 10px;
|
|
293
|
+
display: flex;
|
|
294
|
+
flex-wrap: wrap;
|
|
295
|
+
gap: 12px;
|
|
296
|
+
align-items: center;
|
|
297
|
+
}
|
|
298
|
+
|
|
299
|
+
.advanced-content .field-item {
|
|
300
|
+
padding: 0;
|
|
301
|
+
}
|
|
302
|
+
|
|
303
|
+
.active-filters-row {
|
|
304
|
+
border-top: 1px solid var(--border);
|
|
305
|
+
padding-top: 10px;
|
|
306
|
+
display: grid;
|
|
307
|
+
gap: 6px;
|
|
308
|
+
}
|
|
309
|
+
|
|
310
|
+
.active-filters-title {
|
|
311
|
+
color: var(--muted);
|
|
312
|
+
font-size: 12px;
|
|
313
|
+
}
|
|
314
|
+
|
|
199
315
|
.icon-row {
|
|
200
316
|
display: flex;
|
|
201
317
|
gap: 6px;
|
|
@@ -214,6 +330,16 @@
|
|
|
214
330
|
background: var(--surface-2);
|
|
215
331
|
}
|
|
216
332
|
|
|
333
|
+
.icon-badge.feed {
|
|
334
|
+
width: auto;
|
|
335
|
+
min-width: 46px;
|
|
336
|
+
padding: 2px 8px;
|
|
337
|
+
font-weight: 700;
|
|
338
|
+
border-color: var(--schema-accent);
|
|
339
|
+
color: var(--schema-accent);
|
|
340
|
+
background: rgba(251, 189, 35, 0.15);
|
|
341
|
+
}
|
|
342
|
+
|
|
217
343
|
.icon-badge.warn {
|
|
218
344
|
border-color: #f2d5d5;
|
|
219
345
|
color: #a02424;
|
|
@@ -329,6 +455,11 @@
|
|
|
329
455
|
}
|
|
330
456
|
|
|
331
457
|
@media (max-width: 720px) {
|
|
458
|
+
.primary-row {
|
|
459
|
+
grid-template-columns: 1fr;
|
|
460
|
+
align-items: stretch;
|
|
461
|
+
}
|
|
462
|
+
|
|
332
463
|
.grid-3 {
|
|
333
464
|
grid-template-columns: 1fr;
|
|
334
465
|
}
|
|
@@ -339,47 +470,65 @@
|
|
|
339
470
|
<div class="wrap">
|
|
340
471
|
<h1>Finalized Leaves Viewer</h1>
|
|
341
472
|
<div class="card controls">
|
|
342
|
-
<
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
<label>
|
|
348
|
-
Search text:
|
|
349
|
-
<input id="searchInput" type="text" placeholder="Type to search..." />
|
|
350
|
-
</label>
|
|
351
|
-
|
|
352
|
-
<div class="field-row">
|
|
353
|
-
<label class="field-item">
|
|
354
|
-
<input id="caseSensitive" type="checkbox" />
|
|
355
|
-
<span>case sensitive</span>
|
|
356
|
-
</label>
|
|
357
|
-
<label class="field-item">
|
|
358
|
-
<input id="regexSearch" type="checkbox" />
|
|
359
|
-
<span>regex</span>
|
|
360
|
-
</label>
|
|
361
|
-
<label class="field-item">
|
|
362
|
-
<span>type match</span>
|
|
363
|
-
<select id="typeMatchMode">
|
|
364
|
-
<option value="contains" selected>contains</option>
|
|
365
|
-
<option value="exact">exact</option>
|
|
366
|
-
</select>
|
|
473
|
+
<div class="primary-row">
|
|
474
|
+
<label class="control-block">
|
|
475
|
+
<span class="control-label">Load export JSON file</span>
|
|
476
|
+
<input id="fileInput" type="file" accept="application/json,.json" />
|
|
367
477
|
</label>
|
|
368
|
-
<label class="
|
|
369
|
-
<
|
|
370
|
-
<
|
|
478
|
+
<label class="control-block">
|
|
479
|
+
<span class="control-label">Search text</span>
|
|
480
|
+
<input id="searchInput" type="text" placeholder="Type to search..." />
|
|
371
481
|
</label>
|
|
482
|
+
<div class="control-block">
|
|
483
|
+
<span class="control-label">Quick filters</span>
|
|
484
|
+
<div class="quick-toggles">
|
|
485
|
+
<button id="caseSensitive" class="pill-toggle" type="button" aria-pressed="false">
|
|
486
|
+
Case sensitive
|
|
487
|
+
</button>
|
|
488
|
+
<button id="regexSearch" class="pill-toggle" type="button" aria-pressed="false">
|
|
489
|
+
Regex
|
|
490
|
+
</button>
|
|
491
|
+
</div>
|
|
492
|
+
</div>
|
|
372
493
|
</div>
|
|
373
494
|
|
|
374
|
-
<div
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
495
|
+
<div class="scope-row">
|
|
496
|
+
<div class="scope-actions">
|
|
497
|
+
<button id="selectAllFields" class="scope-action" type="button">All</button>
|
|
498
|
+
<button id="clearAllFields" class="scope-action" type="button">None</button>
|
|
499
|
+
<button id="coreFields" class="scope-action" type="button">Core</button>
|
|
500
|
+
<button id="schemasOnlyFields" class="scope-action" type="button">Schema</button>
|
|
501
|
+
<button id="sourceOnlyFields" class="scope-action" type="button">Source</button>
|
|
502
|
+
</div>
|
|
503
|
+
<div id="fieldChipGroups" class="scope-groups"></div>
|
|
504
|
+
</div>
|
|
505
|
+
|
|
506
|
+
<details class="advanced-panel">
|
|
507
|
+
<summary>Advanced filters</summary>
|
|
508
|
+
<div class="advanced-content">
|
|
509
|
+
<label class="field-item">
|
|
510
|
+
<span>type match</span>
|
|
511
|
+
<select id="typeMatchMode">
|
|
512
|
+
<option value="contains" selected>contains</option>
|
|
513
|
+
<option value="exact">exact</option>
|
|
514
|
+
</select>
|
|
515
|
+
</label>
|
|
516
|
+
<button
|
|
517
|
+
id="schemaRowsMatchOnly"
|
|
518
|
+
class="pill-toggle"
|
|
519
|
+
type="button"
|
|
520
|
+
aria-pressed="false"
|
|
521
|
+
>
|
|
522
|
+
Schema rows match only
|
|
523
|
+
</button>
|
|
524
|
+
<button id="resetFilters" type="button">Reset filters</button>
|
|
525
|
+
</div>
|
|
526
|
+
</details>
|
|
527
|
+
|
|
528
|
+
<div class="active-filters-row">
|
|
529
|
+
<div class="active-filters-title">Active filters</div>
|
|
530
|
+
<div id="activeFilterChips" class="chips filters"></div>
|
|
381
531
|
</div>
|
|
382
|
-
<div id="activeFilterChips" class="chips filters"></div>
|
|
383
532
|
|
|
384
533
|
<div id="status" class="meta">Load a JSON export to begin.</div>
|
|
385
534
|
</div>
|
|
@@ -461,24 +610,56 @@
|
|
|
461
610
|
|
|
462
611
|
const SCHEMA_SECTIONS = ['params', 'query', 'body', 'output']
|
|
463
612
|
const SCHEMA_FIELD_IDS = new Set(SCHEMA_SECTIONS)
|
|
464
|
-
const
|
|
465
|
-
|
|
613
|
+
const SOURCE_FIELD_IDS = new Set(['sourceDefinition', 'sourceSchemas'])
|
|
614
|
+
const SCHEMA_SCOPE_FIELD_IDS = new Set([...SCHEMA_SECTIONS, 'types'])
|
|
615
|
+
const CORE_FIELD_IDS = new Set(
|
|
616
|
+
SEARCH_FIELDS.map((field) => field.id).filter(
|
|
617
|
+
(id) => !SCHEMA_SCOPE_FIELD_IDS.has(id) && !SOURCE_FIELD_IDS.has(id),
|
|
618
|
+
),
|
|
466
619
|
)
|
|
620
|
+
const FIELD_GROUPS = [
|
|
621
|
+
{
|
|
622
|
+
id: 'core',
|
|
623
|
+
label: 'Core fields',
|
|
624
|
+
fieldIds: SEARCH_FIELDS.filter((field) => CORE_FIELD_IDS.has(field.id)).map(
|
|
625
|
+
(field) => field.id,
|
|
626
|
+
),
|
|
627
|
+
},
|
|
628
|
+
{
|
|
629
|
+
id: 'schema',
|
|
630
|
+
label: 'Schema fields',
|
|
631
|
+
fieldIds: SEARCH_FIELDS.filter((field) => SCHEMA_SCOPE_FIELD_IDS.has(field.id)).map(
|
|
632
|
+
(field) => field.id,
|
|
633
|
+
),
|
|
634
|
+
},
|
|
635
|
+
{
|
|
636
|
+
id: 'source',
|
|
637
|
+
label: 'Source fields',
|
|
638
|
+
fieldIds: SEARCH_FIELDS.filter((field) => SOURCE_FIELD_IDS.has(field.id)).map(
|
|
639
|
+
(field) => field.id,
|
|
640
|
+
),
|
|
641
|
+
},
|
|
642
|
+
]
|
|
467
643
|
|
|
468
|
-
const state = {
|
|
644
|
+
const state = {
|
|
645
|
+
payload: null,
|
|
646
|
+
leaves: [],
|
|
647
|
+
selectedFieldIds: new Set(SEARCH_FIELDS.map((field) => field.id)),
|
|
648
|
+
}
|
|
469
649
|
|
|
470
650
|
const fileInput = document.getElementById('fileInput')
|
|
471
651
|
const searchInput = document.getElementById('searchInput')
|
|
472
|
-
const
|
|
473
|
-
const
|
|
652
|
+
const caseSensitiveToggle = document.getElementById('caseSensitive')
|
|
653
|
+
const regexSearchToggle = document.getElementById('regexSearch')
|
|
474
654
|
const typeMatchModeInput = document.getElementById('typeMatchMode')
|
|
475
|
-
const
|
|
476
|
-
const
|
|
655
|
+
const schemaRowsMatchOnlyToggle = document.getElementById('schemaRowsMatchOnly')
|
|
656
|
+
const fieldChipGroups = document.getElementById('fieldChipGroups')
|
|
477
657
|
const activeFilterChips = document.getElementById('activeFilterChips')
|
|
478
658
|
const selectAllFieldsBtn = document.getElementById('selectAllFields')
|
|
479
659
|
const clearAllFieldsBtn = document.getElementById('clearAllFields')
|
|
660
|
+
const coreFieldsBtn = document.getElementById('coreFields')
|
|
480
661
|
const schemasOnlyFieldsBtn = document.getElementById('schemasOnlyFields')
|
|
481
|
-
const
|
|
662
|
+
const sourceOnlyFieldsBtn = document.getElementById('sourceOnlyFields')
|
|
482
663
|
const resetFiltersBtn = document.getElementById('resetFilters')
|
|
483
664
|
const statusEl = document.getElementById('status')
|
|
484
665
|
const resultsEl = document.getElementById('results')
|
|
@@ -499,6 +680,21 @@
|
|
|
499
680
|
return value.replace(/[.*+?^${}()|[\]\\]/g, '\\$&')
|
|
500
681
|
}
|
|
501
682
|
|
|
683
|
+
function isPressed(button) {
|
|
684
|
+
return button?.getAttribute('aria-pressed') === 'true'
|
|
685
|
+
}
|
|
686
|
+
|
|
687
|
+
function setPressed(button, pressed) {
|
|
688
|
+
if (!button) return
|
|
689
|
+
button.setAttribute('aria-pressed', pressed ? 'true' : 'false')
|
|
690
|
+
}
|
|
691
|
+
|
|
692
|
+
function togglePressed(button) {
|
|
693
|
+
const next = !isPressed(button)
|
|
694
|
+
setPressed(button, next)
|
|
695
|
+
return next
|
|
696
|
+
}
|
|
697
|
+
|
|
502
698
|
function createSearchEngine(queryRaw, options) {
|
|
503
699
|
const query = queryRaw || ''
|
|
504
700
|
const caseSensitive = Boolean(options.caseSensitive)
|
|
@@ -697,13 +893,6 @@
|
|
|
697
893
|
return matched
|
|
698
894
|
}
|
|
699
895
|
|
|
700
|
-
function selectedFieldIds() {
|
|
701
|
-
return SEARCH_FIELDS.filter((field) => {
|
|
702
|
-
const input = document.getElementById(`field-${field.id}`)
|
|
703
|
-
return Boolean(input && input.checked)
|
|
704
|
-
}).map((field) => field.id)
|
|
705
|
-
}
|
|
706
|
-
|
|
707
896
|
function matchesLeaf(leaf, engine, selectedIds) {
|
|
708
897
|
if (!engine.active) return true
|
|
709
898
|
if (selectedIds.length === 0) return false
|
|
@@ -790,7 +979,10 @@
|
|
|
790
979
|
}
|
|
791
980
|
|
|
792
981
|
function sourceDisplay(source) {
|
|
793
|
-
return ''
|
|
982
|
+
if (!source || !source.file) return '—'
|
|
983
|
+
const line = Number.isFinite(source.line) ? source.line : 1
|
|
984
|
+
const column = Number.isFinite(source.column) ? source.column : 1
|
|
985
|
+
return `${source.file}:${line}:${column}`
|
|
794
986
|
}
|
|
795
987
|
|
|
796
988
|
function createSourceRow(key, source, engine) {
|
|
@@ -841,6 +1033,28 @@
|
|
|
841
1033
|
return result
|
|
842
1034
|
}
|
|
843
1035
|
|
|
1036
|
+
const SCHEMA_SOURCE_KEYS_BY_SECTION = {
|
|
1037
|
+
params: 'paramsSchema',
|
|
1038
|
+
query: 'querySchema',
|
|
1039
|
+
body: 'bodySchema',
|
|
1040
|
+
output: 'outputSchema',
|
|
1041
|
+
}
|
|
1042
|
+
|
|
1043
|
+
function getSchemaSource(source, sectionName) {
|
|
1044
|
+
const schemaKey = SCHEMA_SOURCE_KEYS_BY_SECTION[sectionName]
|
|
1045
|
+
if (!schemaKey || !source?.schemas || typeof source.schemas !== 'object') {
|
|
1046
|
+
return {
|
|
1047
|
+
schemaKey,
|
|
1048
|
+
sourceValue: undefined,
|
|
1049
|
+
}
|
|
1050
|
+
}
|
|
1051
|
+
|
|
1052
|
+
return {
|
|
1053
|
+
schemaKey,
|
|
1054
|
+
sourceValue: source.schemas[schemaKey],
|
|
1055
|
+
}
|
|
1056
|
+
}
|
|
1057
|
+
|
|
844
1058
|
function createTreeNode(name = '') {
|
|
845
1059
|
return {
|
|
846
1060
|
name,
|
|
@@ -928,14 +1142,14 @@
|
|
|
928
1142
|
return row
|
|
929
1143
|
}
|
|
930
1144
|
|
|
931
|
-
function renderSeparatedSchemas(flatSchema, engine, selectedIds) {
|
|
1145
|
+
function renderSeparatedSchemas(flatSchema, engine, selectedIds, source) {
|
|
932
1146
|
if (!flatSchema || typeof flatSchema !== 'object') return null
|
|
933
1147
|
const section = el('div', 'section')
|
|
934
1148
|
section.appendChild(el('h3', '', 'Schemas (separated by section)'))
|
|
935
1149
|
|
|
936
1150
|
const grouped = splitFlatSchemaBySection(flatSchema)
|
|
937
1151
|
let hasAnySchemaEntries = false
|
|
938
|
-
const limitToMatchedRows =
|
|
1152
|
+
const limitToMatchedRows = isPressed(schemaRowsMatchOnlyToggle) && engine.active
|
|
939
1153
|
|
|
940
1154
|
SCHEMA_SECTIONS.forEach((sectionName) => {
|
|
941
1155
|
const rawEntries = grouped[sectionName]
|
|
@@ -956,6 +1170,17 @@
|
|
|
956
1170
|
setHighlighted(header, sectionName, engine)
|
|
957
1171
|
block.appendChild(header)
|
|
958
1172
|
|
|
1173
|
+
const schemaSource = getSchemaSource(source, sectionName)
|
|
1174
|
+
if (schemaSource.sourceValue) {
|
|
1175
|
+
block.appendChild(
|
|
1176
|
+
createSourceRow(
|
|
1177
|
+
schemaSource.schemaKey || `${sectionName}Schema`,
|
|
1178
|
+
schemaSource.sourceValue,
|
|
1179
|
+
engine,
|
|
1180
|
+
),
|
|
1181
|
+
)
|
|
1182
|
+
}
|
|
1183
|
+
|
|
959
1184
|
const tree = buildSchemaTree(entries, sectionName)
|
|
960
1185
|
block.appendChild(renderTreeNode(tree, engine, true))
|
|
961
1186
|
|
|
@@ -989,9 +1214,9 @@
|
|
|
989
1214
|
|
|
990
1215
|
const iconRow = el('div', 'icon-row')
|
|
991
1216
|
if (cfg.feed) {
|
|
992
|
-
const feed = el('span', 'icon-badge')
|
|
993
|
-
feed.title = 'Feed'
|
|
994
|
-
setHighlighted(feed, '
|
|
1217
|
+
const feed = el('span', 'icon-badge feed')
|
|
1218
|
+
feed.title = 'Feed endpoint'
|
|
1219
|
+
setHighlighted(feed, 'Feed', engine)
|
|
995
1220
|
iconRow.appendChild(feed)
|
|
996
1221
|
}
|
|
997
1222
|
if (cfg.deprecated) {
|
|
@@ -1026,30 +1251,11 @@
|
|
|
1026
1251
|
|
|
1027
1252
|
const sourceByLeaf = state.payload?.sourceByLeaf || {}
|
|
1028
1253
|
const source = sourceByLeaf[leaf.key]
|
|
1029
|
-
if (source) {
|
|
1030
|
-
|
|
1031
|
-
sourceSection.appendChild(el('h3', '', 'Source'))
|
|
1032
|
-
sourceSection.appendChild(createSourceRow('definition', source.definition, engine))
|
|
1033
|
-
|
|
1034
|
-
const schemaSources = source.schemas || {}
|
|
1035
|
-
const schemaEntries = Object.entries(schemaSources)
|
|
1036
|
-
if (schemaEntries.length > 0) {
|
|
1037
|
-
const schemaGrid = el('div', 'grid-2')
|
|
1038
|
-
schemaEntries.forEach(([schemaName, schemaSource]) => {
|
|
1039
|
-
const display = schemaSource?.sourceName
|
|
1040
|
-
? `${schemaName}: ${schemaSource.sourceName}`
|
|
1041
|
-
: schemaSource?.tag
|
|
1042
|
-
? `${schemaName}: ${schemaSource.tag}`
|
|
1043
|
-
: schemaName
|
|
1044
|
-
schemaGrid.appendChild(createSourceRow(display, schemaSource, engine))
|
|
1045
|
-
})
|
|
1046
|
-
sourceSection.appendChild(schemaGrid)
|
|
1047
|
-
}
|
|
1048
|
-
|
|
1049
|
-
content.appendChild(sourceSection)
|
|
1254
|
+
if (source?.definition) {
|
|
1255
|
+
overview.appendChild(createSourceRow('definition', source.definition, engine))
|
|
1050
1256
|
}
|
|
1051
1257
|
|
|
1052
|
-
const separatedSchemas = renderSeparatedSchemas(flatSchema, engine, selectedIds)
|
|
1258
|
+
const separatedSchemas = renderSeparatedSchemas(flatSchema, engine, selectedIds, source)
|
|
1053
1259
|
if (separatedSchemas) {
|
|
1054
1260
|
content.appendChild(separatedSchemas)
|
|
1055
1261
|
}
|
|
@@ -1060,8 +1266,8 @@
|
|
|
1060
1266
|
|
|
1061
1267
|
function renderResults() {
|
|
1062
1268
|
const engine = createSearchEngine(searchInput.value.trim(), {
|
|
1063
|
-
caseSensitive:
|
|
1064
|
-
regex:
|
|
1269
|
+
caseSensitive: isPressed(caseSensitiveToggle),
|
|
1270
|
+
regex: isPressed(regexSearchToggle),
|
|
1065
1271
|
})
|
|
1066
1272
|
|
|
1067
1273
|
if (engine.error) {
|
|
@@ -1094,65 +1300,136 @@
|
|
|
1094
1300
|
syncFilterStateToUrl()
|
|
1095
1301
|
}
|
|
1096
1302
|
|
|
1097
|
-
function
|
|
1098
|
-
|
|
1099
|
-
|
|
1100
|
-
|
|
1101
|
-
const input = document.createElement('input')
|
|
1102
|
-
input.type = 'checkbox'
|
|
1103
|
-
input.id = `field-${field.id}`
|
|
1104
|
-
input.checked = true
|
|
1105
|
-
input.addEventListener('change', renderResults)
|
|
1106
|
-
label.appendChild(input)
|
|
1107
|
-
label.appendChild(el('span', '', field.label))
|
|
1108
|
-
fieldCheckboxes.appendChild(label)
|
|
1109
|
-
})
|
|
1303
|
+
function selectedFieldIds() {
|
|
1304
|
+
return SEARCH_FIELDS.filter((field) => state.selectedFieldIds.has(field.id)).map(
|
|
1305
|
+
(field) => field.id,
|
|
1306
|
+
)
|
|
1110
1307
|
}
|
|
1111
1308
|
|
|
1112
|
-
function
|
|
1113
|
-
|
|
1114
|
-
|
|
1115
|
-
|
|
1116
|
-
|
|
1309
|
+
function renderFieldChips() {
|
|
1310
|
+
fieldChipGroups.innerHTML = ''
|
|
1311
|
+
FIELD_GROUPS.forEach((group) => {
|
|
1312
|
+
const groupWrap = el('div', 'scope-group')
|
|
1313
|
+
groupWrap.appendChild(el('div', 'scope-group-title', group.label))
|
|
1314
|
+
const chipsWrap = el('div', 'scope-group-chips')
|
|
1315
|
+
|
|
1316
|
+
group.fieldIds.forEach((fieldId) => {
|
|
1317
|
+
const field = SEARCH_FIELDS.find((item) => item.id === fieldId)
|
|
1318
|
+
if (!field) return
|
|
1319
|
+
const chip = document.createElement('button')
|
|
1320
|
+
chip.type = 'button'
|
|
1321
|
+
chip.className = 'field-chip'
|
|
1322
|
+
chip.textContent = field.label
|
|
1323
|
+
setPressed(chip, state.selectedFieldIds.has(field.id))
|
|
1324
|
+
chip.addEventListener('click', () => {
|
|
1325
|
+
if (state.selectedFieldIds.has(field.id)) {
|
|
1326
|
+
state.selectedFieldIds.delete(field.id)
|
|
1327
|
+
} else {
|
|
1328
|
+
state.selectedFieldIds.add(field.id)
|
|
1329
|
+
}
|
|
1330
|
+
renderFieldChips()
|
|
1331
|
+
renderResults()
|
|
1332
|
+
})
|
|
1333
|
+
chipsWrap.appendChild(chip)
|
|
1334
|
+
})
|
|
1335
|
+
|
|
1336
|
+
groupWrap.appendChild(chipsWrap)
|
|
1337
|
+
fieldChipGroups.appendChild(groupWrap)
|
|
1117
1338
|
})
|
|
1118
|
-
|
|
1339
|
+
}
|
|
1340
|
+
|
|
1341
|
+
function setFieldSelection(allowedIds, options = {}) {
|
|
1342
|
+
const { rerender = true } = options
|
|
1343
|
+
state.selectedFieldIds = new Set(
|
|
1344
|
+
SEARCH_FIELDS.map((field) => field.id).filter((id) => allowedIds.has(id)),
|
|
1345
|
+
)
|
|
1346
|
+
renderFieldChips()
|
|
1347
|
+
if (rerender) renderResults()
|
|
1119
1348
|
}
|
|
1120
1349
|
|
|
1121
1350
|
function resetFiltersToDefault() {
|
|
1122
1351
|
searchInput.value = ''
|
|
1123
|
-
|
|
1124
|
-
|
|
1352
|
+
setPressed(caseSensitiveToggle, false)
|
|
1353
|
+
setPressed(regexSearchToggle, false)
|
|
1125
1354
|
typeMatchModeInput.value = 'contains'
|
|
1126
|
-
|
|
1127
|
-
setFieldSelection(new Set(SEARCH_FIELDS.map((field) => field.id)))
|
|
1355
|
+
setPressed(schemaRowsMatchOnlyToggle, false)
|
|
1356
|
+
setFieldSelection(new Set(SEARCH_FIELDS.map((field) => field.id)), { rerender: false })
|
|
1357
|
+
renderResults()
|
|
1128
1358
|
}
|
|
1129
1359
|
|
|
1130
1360
|
function renderActiveFilterChips({ selectedIds, hasRegexError }) {
|
|
1131
1361
|
activeFilterChips.innerHTML = ''
|
|
1132
|
-
const chips = []
|
|
1133
|
-
|
|
1134
1362
|
const searchValue = searchInput.value.trim()
|
|
1135
|
-
|
|
1136
|
-
|
|
1137
|
-
if (
|
|
1138
|
-
|
|
1139
|
-
|
|
1140
|
-
|
|
1141
|
-
|
|
1363
|
+
const chipModels = []
|
|
1364
|
+
|
|
1365
|
+
if (searchValue) {
|
|
1366
|
+
chipModels.push({
|
|
1367
|
+
text: `search: ${searchValue}`,
|
|
1368
|
+
onClick: () => {
|
|
1369
|
+
searchInput.value = ''
|
|
1370
|
+
renderResults()
|
|
1371
|
+
},
|
|
1372
|
+
})
|
|
1373
|
+
}
|
|
1374
|
+
if (typeMatchModeInput.value === 'exact') {
|
|
1375
|
+
chipModels.push({
|
|
1376
|
+
text: 'type mode: exact',
|
|
1377
|
+
onClick: () => {
|
|
1378
|
+
typeMatchModeInput.value = 'contains'
|
|
1379
|
+
renderResults()
|
|
1380
|
+
},
|
|
1381
|
+
})
|
|
1382
|
+
}
|
|
1383
|
+
if (isPressed(schemaRowsMatchOnlyToggle)) {
|
|
1384
|
+
chipModels.push({
|
|
1385
|
+
text: 'schema rows: matched only',
|
|
1386
|
+
onClick: () => {
|
|
1387
|
+
setPressed(schemaRowsMatchOnlyToggle, false)
|
|
1388
|
+
renderResults()
|
|
1389
|
+
},
|
|
1390
|
+
})
|
|
1391
|
+
}
|
|
1392
|
+
if (isPressed(caseSensitiveToggle)) {
|
|
1393
|
+
chipModels.push({
|
|
1394
|
+
text: 'case sensitive',
|
|
1395
|
+
onClick: () => {
|
|
1396
|
+
setPressed(caseSensitiveToggle, false)
|
|
1397
|
+
renderResults()
|
|
1398
|
+
},
|
|
1399
|
+
})
|
|
1400
|
+
}
|
|
1401
|
+
if (isPressed(regexSearchToggle)) {
|
|
1402
|
+
chipModels.push({
|
|
1403
|
+
text: 'regex',
|
|
1404
|
+
onClick: () => {
|
|
1405
|
+
setPressed(regexSearchToggle, false)
|
|
1406
|
+
renderResults()
|
|
1407
|
+
},
|
|
1408
|
+
})
|
|
1409
|
+
}
|
|
1410
|
+
if (hasRegexError) chipModels.push({ text: 'regex error' })
|
|
1142
1411
|
|
|
1143
1412
|
const allIds = SEARCH_FIELDS.map((field) => field.id)
|
|
1144
1413
|
if (selectedIds.length !== allIds.length) {
|
|
1145
|
-
|
|
1414
|
+
chipModels.push({
|
|
1415
|
+
text: `fields: ${selectedIds.join(', ') || 'none'}`,
|
|
1416
|
+
onClick: () => setFieldSelection(new Set(allIds)),
|
|
1417
|
+
})
|
|
1146
1418
|
}
|
|
1147
1419
|
|
|
1148
|
-
if (
|
|
1420
|
+
if (chipModels.length === 0) {
|
|
1149
1421
|
activeFilterChips.appendChild(el('span', 'empty', 'No active filters'))
|
|
1150
1422
|
return
|
|
1151
1423
|
}
|
|
1152
1424
|
|
|
1153
|
-
|
|
1154
|
-
const chip =
|
|
1155
|
-
chip.
|
|
1425
|
+
chipModels.forEach((chipModel) => {
|
|
1426
|
+
const chip = document.createElement(chipModel.onClick ? 'button' : 'span')
|
|
1427
|
+
chip.className = chipModel.onClick ? 'chip chip-btn' : 'chip'
|
|
1428
|
+
chip.textContent = chipModel.text
|
|
1429
|
+
if (chipModel.onClick) {
|
|
1430
|
+
chip.type = 'button'
|
|
1431
|
+
chip.addEventListener('click', chipModel.onClick)
|
|
1432
|
+
}
|
|
1156
1433
|
activeFilterChips.appendChild(chip)
|
|
1157
1434
|
})
|
|
1158
1435
|
}
|
|
@@ -1160,10 +1437,10 @@
|
|
|
1160
1437
|
function collectFilterState() {
|
|
1161
1438
|
return {
|
|
1162
1439
|
search: searchInput.value,
|
|
1163
|
-
caseSensitive:
|
|
1164
|
-
regex:
|
|
1440
|
+
caseSensitive: isPressed(caseSensitiveToggle),
|
|
1441
|
+
regex: isPressed(regexSearchToggle),
|
|
1165
1442
|
typeMatchMode: typeMatchModeInput.value === 'exact' ? 'exact' : 'contains',
|
|
1166
|
-
schemaRowsMatchOnly:
|
|
1443
|
+
schemaRowsMatchOnly: isPressed(schemaRowsMatchOnlyToggle),
|
|
1167
1444
|
selectedFields: selectedFieldIds(),
|
|
1168
1445
|
}
|
|
1169
1446
|
}
|
|
@@ -1190,20 +1467,15 @@
|
|
|
1190
1467
|
const parsed = JSON.parse(decodeURIComponent(raw))
|
|
1191
1468
|
isHydratingFromUrl = true
|
|
1192
1469
|
if (typeof parsed.search === 'string') searchInput.value = parsed.search
|
|
1193
|
-
|
|
1194
|
-
|
|
1470
|
+
setPressed(caseSensitiveToggle, Boolean(parsed.caseSensitive))
|
|
1471
|
+
setPressed(regexSearchToggle, Boolean(parsed.regex))
|
|
1195
1472
|
if (parsed.typeMatchMode === 'exact' || parsed.typeMatchMode === 'contains') {
|
|
1196
1473
|
typeMatchModeInput.value = parsed.typeMatchMode
|
|
1197
1474
|
}
|
|
1198
|
-
|
|
1475
|
+
setPressed(schemaRowsMatchOnlyToggle, Boolean(parsed.schemaRowsMatchOnly))
|
|
1199
1476
|
|
|
1200
1477
|
if (Array.isArray(parsed.selectedFields)) {
|
|
1201
|
-
|
|
1202
|
-
SEARCH_FIELDS.forEach((field) => {
|
|
1203
|
-
const input = document.getElementById(`field-${field.id}`)
|
|
1204
|
-
if (!input) return
|
|
1205
|
-
input.checked = allowed.has(field.id)
|
|
1206
|
-
})
|
|
1478
|
+
setFieldSelection(new Set(parsed.selectedFields), { rerender: false })
|
|
1207
1479
|
}
|
|
1208
1480
|
} catch (error) {
|
|
1209
1481
|
// Ignore malformed hash state.
|
|
@@ -1247,20 +1519,30 @@
|
|
|
1247
1519
|
})
|
|
1248
1520
|
|
|
1249
1521
|
searchInput.addEventListener('input', renderResults)
|
|
1250
|
-
|
|
1251
|
-
|
|
1522
|
+
caseSensitiveToggle.addEventListener('click', () => {
|
|
1523
|
+
togglePressed(caseSensitiveToggle)
|
|
1524
|
+
renderResults()
|
|
1525
|
+
})
|
|
1526
|
+
regexSearchToggle.addEventListener('click', () => {
|
|
1527
|
+
togglePressed(regexSearchToggle)
|
|
1528
|
+
renderResults()
|
|
1529
|
+
})
|
|
1252
1530
|
typeMatchModeInput.addEventListener('change', renderResults)
|
|
1253
|
-
|
|
1531
|
+
schemaRowsMatchOnlyToggle.addEventListener('click', () => {
|
|
1532
|
+
togglePressed(schemaRowsMatchOnlyToggle)
|
|
1533
|
+
renderResults()
|
|
1534
|
+
})
|
|
1254
1535
|
|
|
1255
1536
|
selectAllFieldsBtn.addEventListener('click', () =>
|
|
1256
1537
|
setFieldSelection(new Set(SEARCH_FIELDS.map((field) => field.id))),
|
|
1257
1538
|
)
|
|
1258
1539
|
clearAllFieldsBtn.addEventListener('click', () => setFieldSelection(new Set()))
|
|
1259
|
-
|
|
1260
|
-
|
|
1540
|
+
coreFieldsBtn.addEventListener('click', () => setFieldSelection(CORE_FIELD_IDS))
|
|
1541
|
+
schemasOnlyFieldsBtn.addEventListener('click', () => setFieldSelection(SCHEMA_SCOPE_FIELD_IDS))
|
|
1542
|
+
sourceOnlyFieldsBtn.addEventListener('click', () => setFieldSelection(SOURCE_FIELD_IDS))
|
|
1261
1543
|
resetFiltersBtn.addEventListener('click', resetFiltersToDefault)
|
|
1262
1544
|
|
|
1263
|
-
|
|
1545
|
+
renderFieldChips()
|
|
1264
1546
|
hydrateFilterStateFromUrl()
|
|
1265
1547
|
initializeFromBakedPayload()
|
|
1266
1548
|
renderResults()
|