@emeryld/rrroutes-contract 2.7.5 → 2.7.6

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.
@@ -312,6 +312,10 @@
312
312
  <option value="exact">exact</option>
313
313
  </select>
314
314
  </label>
315
+ <label class="field-item">
316
+ <input id="schemaRowsMatchOnly" type="checkbox" />
317
+ <span>schema rows match filter</span>
318
+ </label>
315
319
  </div>
316
320
 
317
321
  <div id="fieldCheckboxes" class="field-row"></div>
@@ -346,6 +350,31 @@
346
350
  { id: 'tags', label: 'tags', get: (leaf) => leaf.cfg?.tags || [] },
347
351
  { id: 'stability', label: 'stability', get: (leaf) => [leaf.cfg?.stability] },
348
352
  { id: 'docsMeta', label: 'docsMeta', get: (leaf) => [leaf.cfg?.docsMeta] },
353
+ {
354
+ id: 'sourceDefinition',
355
+ label: 'source definition',
356
+ get: (leaf, schemaFlatByLeaf, payload) => {
357
+ const source = payload?.sourceByLeaf?.[leaf.key]?.definition
358
+ if (!source) return []
359
+ return [`${source.file}:${source.line}:${source.column}`]
360
+ },
361
+ },
362
+ {
363
+ id: 'sourceSchemas',
364
+ label: 'source schemas',
365
+ get: (leaf, schemaFlatByLeaf, payload) => {
366
+ const schemas = payload?.sourceByLeaf?.[leaf.key]?.schemas
367
+ if (!schemas || typeof schemas !== 'object') return []
368
+ return Object.values(schemas).flatMap((schema) => {
369
+ if (!schema || typeof schema !== 'object') return []
370
+ const tokens = []
371
+ if (schema.sourceName) tokens.push(schema.sourceName)
372
+ if (schema.tag) tokens.push(schema.tag)
373
+ tokens.push(`${schema.file}:${schema.line}:${schema.column}`)
374
+ return tokens
375
+ })
376
+ },
377
+ },
349
378
  {
350
379
  id: 'types',
351
380
  label: 'types',
@@ -390,6 +419,7 @@
390
419
  const caseSensitiveInput = document.getElementById('caseSensitive')
391
420
  const regexSearchInput = document.getElementById('regexSearch')
392
421
  const typeMatchModeInput = document.getElementById('typeMatchMode')
422
+ const schemaRowsMatchOnlyInput = document.getElementById('schemaRowsMatchOnly')
393
423
  const fieldCheckboxes = document.getElementById('fieldCheckboxes')
394
424
  const activeFilterChips = document.getElementById('activeFilterChips')
395
425
  const selectAllFieldsBtn = document.getElementById('selectAllFields')
@@ -557,6 +587,63 @@
557
587
  return Array.from(tokens)
558
588
  }
559
589
 
590
+ function schemaEntryTokens(path, info) {
591
+ const tokens = new Set()
592
+
593
+ if (path) {
594
+ tokens.add(path)
595
+ path.split('.').forEach((segment) => {
596
+ if (segment) tokens.add(segment)
597
+ })
598
+ }
599
+
600
+ if (info && typeof info === 'object') {
601
+ tokens.add(JSON.stringify(info))
602
+ if (info.type) tokens.add(String(info.type))
603
+ if (info.kind) tokens.add(String(info.kind))
604
+ if (info.description) tokens.add(String(info.description))
605
+ if (Array.isArray(info.enumValues)) {
606
+ info.enumValues.forEach((value) => tokens.add(String(value)))
607
+ }
608
+ } else if (info !== null && info !== undefined) {
609
+ tokens.add(String(info))
610
+ }
611
+
612
+ return Array.from(tokens)
613
+ }
614
+
615
+ function schemaEntryMatchesActiveFilter(sectionName, fullPath, info, engine, selectedIds) {
616
+ if (!engine.active) return true
617
+
618
+ let matched = false
619
+ if (selectedIds.includes(sectionName)) {
620
+ matched = schemaEntryTokens(fullPath, info).some((token) => engine.test(token))
621
+ }
622
+
623
+ if (!matched && selectedIds.includes('types')) {
624
+ const typeTokens = []
625
+ if (info && typeof info === 'object') {
626
+ if (info.type) typeTokens.push(String(info.type))
627
+ if (info.kind) typeTokens.push(String(info.kind))
628
+ if (Array.isArray(info.enumValues)) {
629
+ info.enumValues.forEach((value) => typeTokens.push(String(value)))
630
+ }
631
+ }
632
+
633
+ if (typeMatchModeInput.value === 'exact' && !engine.regex) {
634
+ const needle = engine.caseSensitive ? engine.query : engine.query.toLowerCase()
635
+ const normalized = engine.caseSensitive
636
+ ? typeTokens
637
+ : typeTokens.map((token) => token.toLowerCase())
638
+ matched = normalized.includes(needle)
639
+ } else {
640
+ matched = typeTokens.some((token) => engine.test(token))
641
+ }
642
+ }
643
+
644
+ return matched
645
+ }
646
+
560
647
  function selectedFieldIds() {
561
648
  return SEARCH_FIELDS.filter((field) => {
562
649
  const input = document.getElementById(`field-${field.id}`)
@@ -568,10 +655,11 @@
568
655
  if (!engine.active) return true
569
656
  if (selectedIds.length === 0) return false
570
657
  const schemaFlatByLeaf = state.payload?.schemaFlatByLeaf || {}
658
+ const payload = state.payload || {}
571
659
 
572
660
  return SEARCH_FIELDS.some((field) => {
573
661
  if (!selectedIds.includes(field.id)) return false
574
- const tokens = toTokens(field.get(leaf, schemaFlatByLeaf))
662
+ const tokens = toTokens(field.get(leaf, schemaFlatByLeaf, payload))
575
663
  if (field.id === 'types' && typeMatchModeInput.value === 'exact' && !engine.regex) {
576
664
  const needle = engine.caseSensitive ? engine.query : engine.query.toLowerCase()
577
665
  const normalized = engine.caseSensitive
@@ -629,6 +717,35 @@
629
717
  return row
630
718
  }
631
719
 
720
+ function sourceHref(source) {
721
+ if (!source || !source.file) return undefined
722
+ const normalizedPath = String(source.file).replace(/\\/g, '/')
723
+ const prefix = normalizedPath.startsWith('/') ? 'file://' : 'file:///'
724
+ return `${prefix}${encodeURI(normalizedPath)}`
725
+ }
726
+
727
+ function createSourceRow(key, source, engine) {
728
+ const box = el('div', 'kv')
729
+ box.appendChild(el('div', 'k', key))
730
+ const valueNode = el('div', 'v mono')
731
+
732
+ if (!source || !source.file) {
733
+ setHighlighted(valueNode, '—', engine)
734
+ box.appendChild(valueNode)
735
+ return box
736
+ }
737
+
738
+ const href = sourceHref(source)
739
+ const link = document.createElement('a')
740
+ link.href = href
741
+ link.target = '_blank'
742
+ link.rel = 'noopener noreferrer'
743
+ link.textContent = `${source.file}:${source.line}:${source.column}`
744
+ valueNode.appendChild(link)
745
+ box.appendChild(valueNode)
746
+ return box
747
+ }
748
+
632
749
  function splitFlatSchemaBySection(flatSchema) {
633
750
  const result = {
634
751
  params: {},
@@ -737,17 +854,27 @@
737
854
  return row
738
855
  }
739
856
 
740
- function renderSeparatedSchemas(flatSchema, engine) {
857
+ function renderSeparatedSchemas(flatSchema, engine, selectedIds) {
741
858
  if (!flatSchema || typeof flatSchema !== 'object') return null
742
859
  const section = el('div', 'section')
743
860
  section.appendChild(el('h3', '', 'Schemas (separated by section)'))
744
861
 
745
862
  const grouped = splitFlatSchemaBySection(flatSchema)
746
863
  let hasAnySchemaEntries = false
864
+ const limitToMatchedRows = schemaRowsMatchOnlyInput.checked && engine.active
747
865
 
748
866
  SCHEMA_SECTIONS.forEach((sectionName) => {
749
- const entries = grouped[sectionName]
750
- if (!entries || Object.keys(entries).length === 0) return
867
+ const rawEntries = grouped[sectionName]
868
+ if (!rawEntries || Object.keys(rawEntries).length === 0) return
869
+
870
+ const entries = limitToMatchedRows
871
+ ? Object.fromEntries(
872
+ Object.entries(rawEntries).filter(([fullPath, info]) =>
873
+ schemaEntryMatchesActiveFilter(sectionName, fullPath, info, engine, selectedIds),
874
+ ),
875
+ )
876
+ : rawEntries
877
+ if (Object.keys(entries).length === 0) return
751
878
 
752
879
  hasAnySchemaEntries = true
753
880
  const block = el('div', 'schema-block')
@@ -764,7 +891,7 @@
764
891
  return hasAnySchemaEntries ? section : null
765
892
  }
766
893
 
767
- function renderLeaf(leaf, engine) {
894
+ function renderLeaf(leaf, engine, selectedIds) {
768
895
  const details = el('details', 'leaf')
769
896
  const summary = el('summary')
770
897
  setHighlighted(summary, `${String(leaf.method || '').toUpperCase()} ${leaf.path || ''}`, engine)
@@ -823,7 +950,32 @@
823
950
  content.appendChild(files)
824
951
  }
825
952
 
826
- const separatedSchemas = renderSeparatedSchemas(flatSchema, engine)
953
+ const sourceByLeaf = state.payload?.sourceByLeaf || {}
954
+ const source = sourceByLeaf[leaf.key]
955
+ if (source) {
956
+ const sourceSection = el('div', 'section')
957
+ sourceSection.appendChild(el('h3', '', 'Source'))
958
+ sourceSection.appendChild(createSourceRow('definition', source.definition, engine))
959
+
960
+ const schemaSources = source.schemas || {}
961
+ const schemaEntries = Object.entries(schemaSources)
962
+ if (schemaEntries.length > 0) {
963
+ const schemaGrid = el('div', 'grid-2')
964
+ schemaEntries.forEach(([schemaName, schemaSource]) => {
965
+ const display = schemaSource?.sourceName
966
+ ? `${schemaName}: ${schemaSource.sourceName}`
967
+ : schemaSource?.tag
968
+ ? `${schemaName}: ${schemaSource.tag}`
969
+ : schemaName
970
+ schemaGrid.appendChild(createSourceRow(display, schemaSource, engine))
971
+ })
972
+ sourceSection.appendChild(schemaGrid)
973
+ }
974
+
975
+ content.appendChild(sourceSection)
976
+ }
977
+
978
+ const separatedSchemas = renderSeparatedSchemas(flatSchema, engine, selectedIds)
827
979
  if (separatedSchemas) {
828
980
  content.appendChild(separatedSchemas)
829
981
  }
@@ -863,7 +1015,7 @@
863
1015
  }
864
1016
 
865
1017
  filtered.forEach((leaf) => {
866
- resultsEl.appendChild(renderLeaf(leaf, engine))
1018
+ resultsEl.appendChild(renderLeaf(leaf, engine, selectedIds))
867
1019
  })
868
1020
  syncFilterStateToUrl()
869
1021
  }
@@ -897,6 +1049,7 @@
897
1049
  caseSensitiveInput.checked = false
898
1050
  regexSearchInput.checked = false
899
1051
  typeMatchModeInput.value = 'contains'
1052
+ schemaRowsMatchOnlyInput.checked = false
900
1053
  setFieldSelection(new Set(SEARCH_FIELDS.map((field) => field.id)))
901
1054
  }
902
1055
 
@@ -908,6 +1061,7 @@
908
1061
 
909
1062
  if (searchValue) chips.push(`search: ${searchValue}`)
910
1063
  if (typeMatchModeInput.value === 'exact') chips.push('type mode: exact')
1064
+ if (schemaRowsMatchOnlyInput.checked) chips.push('schema rows: matched only')
911
1065
  if (caseSensitiveInput.checked) chips.push('case sensitive')
912
1066
  if (regexSearchInput.checked) chips.push('regex')
913
1067
  if (hasRegexError) chips.push('regex error')
@@ -935,6 +1089,7 @@
935
1089
  caseSensitive: caseSensitiveInput.checked,
936
1090
  regex: regexSearchInput.checked,
937
1091
  typeMatchMode: typeMatchModeInput.value === 'exact' ? 'exact' : 'contains',
1092
+ schemaRowsMatchOnly: schemaRowsMatchOnlyInput.checked,
938
1093
  selectedFields: selectedFieldIds(),
939
1094
  }
940
1095
  }
@@ -966,6 +1121,7 @@
966
1121
  if (parsed.typeMatchMode === 'exact' || parsed.typeMatchMode === 'contains') {
967
1122
  typeMatchModeInput.value = parsed.typeMatchMode
968
1123
  }
1124
+ schemaRowsMatchOnlyInput.checked = Boolean(parsed.schemaRowsMatchOnly)
969
1125
 
970
1126
  if (Array.isArray(parsed.selectedFields)) {
971
1127
  const allowed = new Set(parsed.selectedFields)
@@ -1020,6 +1176,7 @@
1020
1176
  caseSensitiveInput.addEventListener('change', renderResults)
1021
1177
  regexSearchInput.addEventListener('change', renderResults)
1022
1178
  typeMatchModeInput.addEventListener('change', renderResults)
1179
+ schemaRowsMatchOnlyInput.addEventListener('change', renderResults)
1023
1180
 
1024
1181
  selectAllFieldsBtn.addEventListener('click', () =>
1025
1182
  setFieldSelection(new Set(SEARCH_FIELDS.map((field) => field.id))),