@emeryld/rrroutes-contract 2.7.4 → 2.7.5
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/package.json +1 -1
- package/tools/finalized-leaves-viewer.html +111 -134
package/package.json
CHANGED
|
@@ -140,6 +140,12 @@
|
|
|
140
140
|
grid-template-columns: repeat(auto-fit, minmax(220px, 1fr));
|
|
141
141
|
}
|
|
142
142
|
|
|
143
|
+
.grid-3 {
|
|
144
|
+
display: grid;
|
|
145
|
+
gap: 6px;
|
|
146
|
+
grid-template-columns: repeat(3, minmax(0, 1fr));
|
|
147
|
+
}
|
|
148
|
+
|
|
143
149
|
.kv {
|
|
144
150
|
border-bottom: 1px dashed #e2e8f2;
|
|
145
151
|
padding: 4px 0 6px;
|
|
@@ -179,6 +185,30 @@
|
|
|
179
185
|
background: #effbf4;
|
|
180
186
|
}
|
|
181
187
|
|
|
188
|
+
.icon-row {
|
|
189
|
+
display: flex;
|
|
190
|
+
gap: 6px;
|
|
191
|
+
margin-top: 2px;
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
.icon-badge {
|
|
195
|
+
display: inline-flex;
|
|
196
|
+
align-items: center;
|
|
197
|
+
justify-content: center;
|
|
198
|
+
width: 18px;
|
|
199
|
+
height: 18px;
|
|
200
|
+
border: 1px solid #e0e6f0;
|
|
201
|
+
border-radius: 4px;
|
|
202
|
+
font-size: 11px;
|
|
203
|
+
background: #f6f8fc;
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
.icon-badge.warn {
|
|
207
|
+
border-color: #f2d5d5;
|
|
208
|
+
color: #a02424;
|
|
209
|
+
background: #fff4f4;
|
|
210
|
+
}
|
|
211
|
+
|
|
182
212
|
.empty {
|
|
183
213
|
color: var(--muted);
|
|
184
214
|
}
|
|
@@ -244,6 +274,12 @@
|
|
|
244
274
|
font-size: 11px;
|
|
245
275
|
background: #f2f5fa;
|
|
246
276
|
}
|
|
277
|
+
|
|
278
|
+
@media (max-width: 720px) {
|
|
279
|
+
.grid-3 {
|
|
280
|
+
grid-template-columns: 1fr;
|
|
281
|
+
}
|
|
282
|
+
}
|
|
247
283
|
</style>
|
|
248
284
|
</head>
|
|
249
285
|
<body>
|
|
@@ -260,11 +296,6 @@
|
|
|
260
296
|
<input id="searchInput" type="text" placeholder="Type to search..." />
|
|
261
297
|
</label>
|
|
262
298
|
|
|
263
|
-
<label>
|
|
264
|
-
Type filter:
|
|
265
|
-
<input id="typeFilterInput" type="text" placeholder='Type/kind/enum (e.g. "paid")' />
|
|
266
|
-
</label>
|
|
267
|
-
|
|
268
299
|
<div class="field-row">
|
|
269
300
|
<label class="field-item">
|
|
270
301
|
<input id="caseSensitive" type="checkbox" />
|
|
@@ -274,10 +305,6 @@
|
|
|
274
305
|
<input id="regexSearch" type="checkbox" />
|
|
275
306
|
<span>regex</span>
|
|
276
307
|
</label>
|
|
277
|
-
<label class="field-item">
|
|
278
|
-
<input id="hasEnumOnly" type="checkbox" />
|
|
279
|
-
<span>has enum only</span>
|
|
280
|
-
</label>
|
|
281
308
|
<label class="field-item">
|
|
282
309
|
<span>type match</span>
|
|
283
310
|
<select id="typeMatchMode">
|
|
@@ -319,6 +346,11 @@
|
|
|
319
346
|
{ id: 'tags', label: 'tags', get: (leaf) => leaf.cfg?.tags || [] },
|
|
320
347
|
{ id: 'stability', label: 'stability', get: (leaf) => [leaf.cfg?.stability] },
|
|
321
348
|
{ id: 'docsMeta', label: 'docsMeta', get: (leaf) => [leaf.cfg?.docsMeta] },
|
|
349
|
+
{
|
|
350
|
+
id: 'types',
|
|
351
|
+
label: 'types',
|
|
352
|
+
get: (leaf, schemaFlatByLeaf) => schemaTypeTokens(schemaFlatByLeaf?.[leaf.key]),
|
|
353
|
+
},
|
|
322
354
|
{
|
|
323
355
|
id: 'params',
|
|
324
356
|
label: 'params',
|
|
@@ -355,10 +387,8 @@
|
|
|
355
387
|
|
|
356
388
|
const fileInput = document.getElementById('fileInput')
|
|
357
389
|
const searchInput = document.getElementById('searchInput')
|
|
358
|
-
const typeFilterInput = document.getElementById('typeFilterInput')
|
|
359
390
|
const caseSensitiveInput = document.getElementById('caseSensitive')
|
|
360
391
|
const regexSearchInput = document.getElementById('regexSearch')
|
|
361
|
-
const hasEnumOnlyInput = document.getElementById('hasEnumOnly')
|
|
362
392
|
const typeMatchModeInput = document.getElementById('typeMatchMode')
|
|
363
393
|
const fieldCheckboxes = document.getElementById('fieldCheckboxes')
|
|
364
394
|
const activeFilterChips = document.getElementById('activeFilterChips')
|
|
@@ -394,6 +424,9 @@
|
|
|
394
424
|
if (!query) {
|
|
395
425
|
return {
|
|
396
426
|
active: false,
|
|
427
|
+
query,
|
|
428
|
+
caseSensitive,
|
|
429
|
+
regex,
|
|
397
430
|
error: null,
|
|
398
431
|
test: () => true,
|
|
399
432
|
highlight: (text) => escapeHtml(text ?? ''),
|
|
@@ -406,6 +439,9 @@
|
|
|
406
439
|
const rx = new RegExp(query, flags)
|
|
407
440
|
return {
|
|
408
441
|
active: true,
|
|
442
|
+
query,
|
|
443
|
+
caseSensitive,
|
|
444
|
+
regex,
|
|
409
445
|
error: null,
|
|
410
446
|
test: (text) => {
|
|
411
447
|
const source = String(text ?? '')
|
|
@@ -423,6 +459,9 @@
|
|
|
423
459
|
} catch (error) {
|
|
424
460
|
return {
|
|
425
461
|
active: true,
|
|
462
|
+
query,
|
|
463
|
+
caseSensitive,
|
|
464
|
+
regex,
|
|
426
465
|
error: error instanceof Error ? error.message : 'Invalid regex',
|
|
427
466
|
test: () => false,
|
|
428
467
|
highlight: (text) => escapeHtml(text ?? ''),
|
|
@@ -436,6 +475,9 @@
|
|
|
436
475
|
|
|
437
476
|
return {
|
|
438
477
|
active: true,
|
|
478
|
+
query,
|
|
479
|
+
caseSensitive,
|
|
480
|
+
regex,
|
|
439
481
|
error: null,
|
|
440
482
|
test: (text) => {
|
|
441
483
|
const source = String(text ?? '')
|
|
@@ -499,23 +541,20 @@
|
|
|
499
541
|
return flatSchemaToSearchTokens(Object.fromEntries(sectionEntries))
|
|
500
542
|
}
|
|
501
543
|
|
|
502
|
-
function
|
|
503
|
-
if (!flatSchema || typeof flatSchema !== 'object') return
|
|
544
|
+
function schemaTypeTokens(flatSchema) {
|
|
545
|
+
if (!flatSchema || typeof flatSchema !== 'object') return []
|
|
504
546
|
|
|
505
547
|
const tokens = new Set()
|
|
506
|
-
let hasEnum = false
|
|
507
|
-
|
|
508
548
|
Object.values(flatSchema).forEach((info) => {
|
|
509
549
|
if (!info || typeof info !== 'object') return
|
|
510
550
|
if (info.type) tokens.add(String(info.type))
|
|
511
551
|
if (info.kind) tokens.add(String(info.kind))
|
|
512
|
-
if (Array.isArray(info.enumValues)
|
|
513
|
-
hasEnum = true
|
|
552
|
+
if (Array.isArray(info.enumValues)) {
|
|
514
553
|
info.enumValues.forEach((value) => tokens.add(String(value)))
|
|
515
554
|
}
|
|
516
555
|
})
|
|
517
556
|
|
|
518
|
-
return
|
|
557
|
+
return Array.from(tokens)
|
|
519
558
|
}
|
|
520
559
|
|
|
521
560
|
function selectedFieldIds() {
|
|
@@ -533,46 +572,17 @@
|
|
|
533
572
|
return SEARCH_FIELDS.some((field) => {
|
|
534
573
|
if (!selectedIds.includes(field.id)) return false
|
|
535
574
|
const tokens = toTokens(field.get(leaf, schemaFlatByLeaf))
|
|
575
|
+
if (field.id === 'types' && typeMatchModeInput.value === 'exact' && !engine.regex) {
|
|
576
|
+
const needle = engine.caseSensitive ? engine.query : engine.query.toLowerCase()
|
|
577
|
+
const normalized = engine.caseSensitive
|
|
578
|
+
? tokens
|
|
579
|
+
: tokens.map((token) => String(token).toLowerCase())
|
|
580
|
+
return normalized.includes(needle)
|
|
581
|
+
}
|
|
536
582
|
return tokens.some((token) => engine.test(token))
|
|
537
583
|
})
|
|
538
584
|
}
|
|
539
585
|
|
|
540
|
-
function matchesTypeFilter(leaf, typeEngine, matchMode, hasEnumOnly) {
|
|
541
|
-
const schemaFlatByLeaf = state.payload?.schemaFlatByLeaf || {}
|
|
542
|
-
const { tokens, hasEnum } = typeFilterTokensByLeaf(schemaFlatByLeaf?.[leaf.key])
|
|
543
|
-
|
|
544
|
-
if (hasEnumOnly && !hasEnum) return false
|
|
545
|
-
if (!typeEngine.active) return true
|
|
546
|
-
|
|
547
|
-
if (matchMode === 'exact') {
|
|
548
|
-
const sourceTokens = typeEngine.caseSensitive
|
|
549
|
-
? tokens
|
|
550
|
-
: tokens.map((token) => token.toLowerCase())
|
|
551
|
-
const needle = typeEngine.caseSensitive ? typeEngine.query : typeEngine.query.toLowerCase()
|
|
552
|
-
return sourceTokens.includes(needle)
|
|
553
|
-
}
|
|
554
|
-
|
|
555
|
-
return tokens.some((token) => typeEngine.test(token))
|
|
556
|
-
}
|
|
557
|
-
|
|
558
|
-
function createTypeSearchEngine(queryRaw) {
|
|
559
|
-
const query = queryRaw || ''
|
|
560
|
-
const caseSensitive = Boolean(caseSensitiveInput.checked)
|
|
561
|
-
if (!query) return { active: false, query, caseSensitive, test: () => true }
|
|
562
|
-
|
|
563
|
-
const needle = caseSensitive ? query : query.toLowerCase()
|
|
564
|
-
return {
|
|
565
|
-
active: true,
|
|
566
|
-
query,
|
|
567
|
-
caseSensitive,
|
|
568
|
-
test: (text) => {
|
|
569
|
-
const source = String(text ?? '')
|
|
570
|
-
const hay = caseSensitive ? source : source.toLowerCase()
|
|
571
|
-
return hay.includes(needle)
|
|
572
|
-
},
|
|
573
|
-
}
|
|
574
|
-
}
|
|
575
|
-
|
|
576
586
|
function el(tag, className, text) {
|
|
577
587
|
const node = document.createElement(tag)
|
|
578
588
|
if (className) node.className = className
|
|
@@ -728,29 +738,30 @@
|
|
|
728
738
|
}
|
|
729
739
|
|
|
730
740
|
function renderSeparatedSchemas(flatSchema, engine) {
|
|
741
|
+
if (!flatSchema || typeof flatSchema !== 'object') return null
|
|
731
742
|
const section = el('div', 'section')
|
|
732
743
|
section.appendChild(el('h3', '', 'Schemas (separated by section)'))
|
|
733
744
|
|
|
734
745
|
const grouped = splitFlatSchemaBySection(flatSchema)
|
|
746
|
+
let hasAnySchemaEntries = false
|
|
735
747
|
|
|
736
748
|
SCHEMA_SECTIONS.forEach((sectionName) => {
|
|
749
|
+
const entries = grouped[sectionName]
|
|
750
|
+
if (!entries || Object.keys(entries).length === 0) return
|
|
751
|
+
|
|
752
|
+
hasAnySchemaEntries = true
|
|
737
753
|
const block = el('div', 'schema-block')
|
|
738
754
|
const header = el('div', 'schema-header mono')
|
|
739
755
|
setHighlighted(header, sectionName, engine)
|
|
740
756
|
block.appendChild(header)
|
|
741
757
|
|
|
742
|
-
const
|
|
743
|
-
|
|
744
|
-
block.appendChild(el('div', 'empty', 'No entries'))
|
|
745
|
-
} else {
|
|
746
|
-
const tree = buildSchemaTree(entries, sectionName)
|
|
747
|
-
block.appendChild(renderTreeNode(tree, engine, true))
|
|
748
|
-
}
|
|
758
|
+
const tree = buildSchemaTree(entries, sectionName)
|
|
759
|
+
block.appendChild(renderTreeNode(tree, engine, true))
|
|
749
760
|
|
|
750
761
|
section.appendChild(block)
|
|
751
762
|
})
|
|
752
763
|
|
|
753
|
-
return section
|
|
764
|
+
return hasAnySchemaEntries ? section : null
|
|
754
765
|
}
|
|
755
766
|
|
|
756
767
|
function renderLeaf(leaf, engine) {
|
|
@@ -765,60 +776,43 @@
|
|
|
765
776
|
|
|
766
777
|
const overview = el('div', 'section')
|
|
767
778
|
overview.appendChild(el('h3', '', 'Overview'))
|
|
768
|
-
const
|
|
769
|
-
|
|
770
|
-
|
|
771
|
-
|
|
772
|
-
|
|
773
|
-
|
|
774
|
-
|
|
775
|
-
|
|
776
|
-
|
|
777
|
-
|
|
778
|
-
|
|
779
|
-
|
|
780
|
-
|
|
781
|
-
|
|
782
|
-
|
|
783
|
-
|
|
784
|
-
docGrid.appendChild(kv('description', cfg.description, engine))
|
|
785
|
-
docs.appendChild(docGrid)
|
|
786
|
-
|
|
787
|
-
const tagsRow = el('div', 'chips')
|
|
788
|
-
;(cfg.tags || []).forEach((tag) => {
|
|
789
|
-
const chip = el('span', 'chip')
|
|
790
|
-
setHighlighted(chip, tag, engine)
|
|
791
|
-
tagsRow.appendChild(chip)
|
|
792
|
-
})
|
|
793
|
-
if ((cfg.tags || []).length === 0) {
|
|
794
|
-
tagsRow.appendChild(el('span', 'empty', 'No tags'))
|
|
779
|
+
const topGrid = el('div', 'grid-3')
|
|
780
|
+
topGrid.appendChild(kv('group', cfg.docsGroup, engine))
|
|
781
|
+
topGrid.appendChild(
|
|
782
|
+
kv('tags', cfg.tags && cfg.tags.length > 0 ? cfg.tags.join(', ') : undefined, engine),
|
|
783
|
+
)
|
|
784
|
+
topGrid.appendChild(kv('stability', cfg.stability, engine))
|
|
785
|
+
overview.appendChild(topGrid)
|
|
786
|
+
overview.appendChild(kv('summary', cfg.summary, engine))
|
|
787
|
+
overview.appendChild(kv('description', cfg.description, engine))
|
|
788
|
+
|
|
789
|
+
const iconRow = el('div', 'icon-row')
|
|
790
|
+
if (cfg.feed) {
|
|
791
|
+
const feed = el('span', 'icon-badge')
|
|
792
|
+
feed.title = 'Feed'
|
|
793
|
+
setHighlighted(feed, 'F', engine)
|
|
794
|
+
iconRow.appendChild(feed)
|
|
795
795
|
}
|
|
796
|
-
|
|
797
|
-
|
|
798
|
-
|
|
799
|
-
|
|
800
|
-
|
|
801
|
-
})
|
|
796
|
+
if (cfg.deprecated) {
|
|
797
|
+
const deprecated = el('span', 'icon-badge warn')
|
|
798
|
+
deprecated.title = 'Deprecated'
|
|
799
|
+
setHighlighted(deprecated, 'D', engine)
|
|
800
|
+
iconRow.appendChild(deprecated)
|
|
802
801
|
}
|
|
803
|
-
|
|
804
|
-
|
|
805
|
-
|
|
806
|
-
|
|
807
|
-
|
|
808
|
-
|
|
809
|
-
|
|
810
|
-
|
|
811
|
-
|
|
812
|
-
|
|
813
|
-
schemaGrid.appendChild(renderSchemaSummary('output', schemaObj.output, engine))
|
|
814
|
-
schemaGrid.appendChild(renderSchemaSummary('outputMeta', schemaObj.outputMeta, engine))
|
|
815
|
-
schemaGrid.appendChild(renderSchemaSummary('queryExtension', schemaObj.queryExtension, engine))
|
|
816
|
-
schemas.appendChild(schemaGrid)
|
|
817
|
-
content.appendChild(schemas)
|
|
802
|
+
if (cfg.docsHidden) {
|
|
803
|
+
const hidden = el('span', 'icon-badge warn')
|
|
804
|
+
hidden.title = 'Hidden'
|
|
805
|
+
setHighlighted(hidden, 'H', engine)
|
|
806
|
+
iconRow.appendChild(hidden)
|
|
807
|
+
}
|
|
808
|
+
if (iconRow.childNodes.length > 0) {
|
|
809
|
+
overview.appendChild(iconRow)
|
|
810
|
+
}
|
|
811
|
+
content.appendChild(overview)
|
|
818
812
|
|
|
819
813
|
const files = el('div', 'section')
|
|
820
|
-
files.appendChild(el('h3', '', 'Body Files'))
|
|
821
814
|
if (Array.isArray(cfg.bodyFiles) && cfg.bodyFiles.length > 0) {
|
|
815
|
+
files.appendChild(el('h3', '', 'Body Files'))
|
|
822
816
|
const chips = el('div', 'chips')
|
|
823
817
|
cfg.bodyFiles.forEach((file) => {
|
|
824
818
|
const chip = el('span', 'chip ok')
|
|
@@ -826,12 +820,13 @@
|
|
|
826
820
|
chips.appendChild(chip)
|
|
827
821
|
})
|
|
828
822
|
files.appendChild(chips)
|
|
829
|
-
|
|
830
|
-
files.appendChild(el('div', 'empty', 'No file upload fields.'))
|
|
823
|
+
content.appendChild(files)
|
|
831
824
|
}
|
|
832
|
-
content.appendChild(files)
|
|
833
825
|
|
|
834
|
-
|
|
826
|
+
const separatedSchemas = renderSeparatedSchemas(flatSchema, engine)
|
|
827
|
+
if (separatedSchemas) {
|
|
828
|
+
content.appendChild(separatedSchemas)
|
|
829
|
+
}
|
|
835
830
|
|
|
836
831
|
details.appendChild(content)
|
|
837
832
|
return details
|
|
@@ -842,9 +837,6 @@
|
|
|
842
837
|
caseSensitive: caseSensitiveInput.checked,
|
|
843
838
|
regex: regexSearchInput.checked,
|
|
844
839
|
})
|
|
845
|
-
const typeEngine = createTypeSearchEngine(typeFilterInput.value.trim())
|
|
846
|
-
const hasEnumOnly = Boolean(hasEnumOnlyInput.checked)
|
|
847
|
-
const typeMatchMode = typeMatchModeInput.value === 'exact' ? 'exact' : 'contains'
|
|
848
840
|
|
|
849
841
|
if (engine.error) {
|
|
850
842
|
statusEl.textContent = `Invalid regex: ${engine.error}`
|
|
@@ -858,11 +850,7 @@
|
|
|
858
850
|
}
|
|
859
851
|
|
|
860
852
|
const selectedIds = selectedFieldIds()
|
|
861
|
-
const filtered = state.leaves.filter(
|
|
862
|
-
(leaf) =>
|
|
863
|
-
matchesLeaf(leaf, engine, selectedIds) &&
|
|
864
|
-
matchesTypeFilter(leaf, typeEngine, typeMatchMode, hasEnumOnly),
|
|
865
|
-
)
|
|
853
|
+
const filtered = state.leaves.filter((leaf) => matchesLeaf(leaf, engine, selectedIds))
|
|
866
854
|
|
|
867
855
|
statusEl.textContent = `${filtered.length} / ${state.leaves.length} routes matched.`
|
|
868
856
|
resultsEl.innerHTML = ''
|
|
@@ -906,10 +894,8 @@
|
|
|
906
894
|
|
|
907
895
|
function resetFiltersToDefault() {
|
|
908
896
|
searchInput.value = ''
|
|
909
|
-
typeFilterInput.value = ''
|
|
910
897
|
caseSensitiveInput.checked = false
|
|
911
898
|
regexSearchInput.checked = false
|
|
912
|
-
hasEnumOnlyInput.checked = false
|
|
913
899
|
typeMatchModeInput.value = 'contains'
|
|
914
900
|
setFieldSelection(new Set(SEARCH_FIELDS.map((field) => field.id)))
|
|
915
901
|
}
|
|
@@ -919,14 +905,11 @@
|
|
|
919
905
|
const chips = []
|
|
920
906
|
|
|
921
907
|
const searchValue = searchInput.value.trim()
|
|
922
|
-
const typeValue = typeFilterInput.value.trim()
|
|
923
908
|
|
|
924
909
|
if (searchValue) chips.push(`search: ${searchValue}`)
|
|
925
|
-
if (typeValue) chips.push(`type: ${typeValue}`)
|
|
926
910
|
if (typeMatchModeInput.value === 'exact') chips.push('type mode: exact')
|
|
927
911
|
if (caseSensitiveInput.checked) chips.push('case sensitive')
|
|
928
912
|
if (regexSearchInput.checked) chips.push('regex')
|
|
929
|
-
if (hasEnumOnlyInput.checked) chips.push('has enum only')
|
|
930
913
|
if (hasRegexError) chips.push('regex error')
|
|
931
914
|
|
|
932
915
|
const allIds = SEARCH_FIELDS.map((field) => field.id)
|
|
@@ -949,10 +932,8 @@
|
|
|
949
932
|
function collectFilterState() {
|
|
950
933
|
return {
|
|
951
934
|
search: searchInput.value,
|
|
952
|
-
typeFilter: typeFilterInput.value,
|
|
953
935
|
caseSensitive: caseSensitiveInput.checked,
|
|
954
936
|
regex: regexSearchInput.checked,
|
|
955
|
-
hasEnumOnly: hasEnumOnlyInput.checked,
|
|
956
937
|
typeMatchMode: typeMatchModeInput.value === 'exact' ? 'exact' : 'contains',
|
|
957
938
|
selectedFields: selectedFieldIds(),
|
|
958
939
|
}
|
|
@@ -980,10 +961,8 @@
|
|
|
980
961
|
const parsed = JSON.parse(decodeURIComponent(raw))
|
|
981
962
|
isHydratingFromUrl = true
|
|
982
963
|
if (typeof parsed.search === 'string') searchInput.value = parsed.search
|
|
983
|
-
if (typeof parsed.typeFilter === 'string') typeFilterInput.value = parsed.typeFilter
|
|
984
964
|
caseSensitiveInput.checked = Boolean(parsed.caseSensitive)
|
|
985
965
|
regexSearchInput.checked = Boolean(parsed.regex)
|
|
986
|
-
hasEnumOnlyInput.checked = Boolean(parsed.hasEnumOnly)
|
|
987
966
|
if (parsed.typeMatchMode === 'exact' || parsed.typeMatchMode === 'contains') {
|
|
988
967
|
typeMatchModeInput.value = parsed.typeMatchMode
|
|
989
968
|
}
|
|
@@ -1038,10 +1017,8 @@
|
|
|
1038
1017
|
})
|
|
1039
1018
|
|
|
1040
1019
|
searchInput.addEventListener('input', renderResults)
|
|
1041
|
-
typeFilterInput.addEventListener('input', renderResults)
|
|
1042
1020
|
caseSensitiveInput.addEventListener('change', renderResults)
|
|
1043
1021
|
regexSearchInput.addEventListener('change', renderResults)
|
|
1044
|
-
hasEnumOnlyInput.addEventListener('change', renderResults)
|
|
1045
1022
|
typeMatchModeInput.addEventListener('change', renderResults)
|
|
1046
1023
|
|
|
1047
1024
|
selectAllFieldsBtn.addEventListener('click', () =>
|