@emeryld/rrroutes-export 1.0.21 → 1.0.23

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 CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@emeryld/rrroutes-export",
3
3
  "description": "Finalized leaves export helpers and CLI for RRRoutes",
4
- "version": "1.0.21",
4
+ "version": "1.0.23",
5
5
  "private": false,
6
6
  "type": "module",
7
7
  "main": "dist/index.cjs",
@@ -381,7 +381,7 @@
381
381
  .changelog-commit-separator {
382
382
  margin-top: 12px;
383
383
  padding-top: 10px;
384
- border-top: 1px solid var(--accent);
384
+ border-top: 1px solid var(--muted);
385
385
  }
386
386
 
387
387
  .changelog-commit-separator:first-child {
@@ -389,7 +389,7 @@
389
389
  }
390
390
 
391
391
  .changelog-commit-label {
392
- color: var(--accent);
392
+ color: var(--muted);
393
393
  font-weight: 700;
394
394
  font-size: 12px;
395
395
  }
@@ -673,6 +673,14 @@
673
673
  >
674
674
  Changelog
675
675
  </button>
676
+ <button
677
+ id="tabChangelogGrouped"
678
+ class="field-chip"
679
+ type="button"
680
+ aria-pressed="false"
681
+ >
682
+ Grouped Changelog
683
+ </button>
676
684
  </div>
677
685
  </div>
678
686
  </div>
@@ -861,6 +869,7 @@
861
869
  const VIEW_IDS = {
862
870
  leaves: 'leaves',
863
871
  changelog: 'changelog',
872
+ changelogGrouped: 'changelogGrouped',
864
873
  }
865
874
 
866
875
  const VIEWER_MODE = {
@@ -895,6 +904,12 @@
895
904
  CHANGELOG_TIMELINE_FIELDS.map((field) => field.id),
896
905
  ),
897
906
  },
907
+ [VIEW_IDS.changelogGrouped]: {
908
+ fieldDefs: CHANGELOG_TIMELINE_FIELDS,
909
+ defaultSelected: new Set(
910
+ CHANGELOG_TIMELINE_FIELDS.map((field) => field.id),
911
+ ),
912
+ },
898
913
  }
899
914
 
900
915
  const state = {
@@ -912,6 +927,9 @@
912
927
  [VIEW_IDS.changelog]: new Set(
913
928
  TAB_CONFIG[VIEW_IDS.changelog].defaultSelected,
914
929
  ),
930
+ [VIEW_IDS.changelogGrouped]: new Set(
931
+ TAB_CONFIG[VIEW_IDS.changelogGrouped].defaultSelected,
932
+ ),
915
933
  },
916
934
  }
917
935
 
@@ -937,6 +955,9 @@
937
955
  const controlsEl = document.querySelector('.controls')
938
956
  const tabLeavesBtn = document.getElementById('tabLeaves')
939
957
  const tabChangelogBtn = document.getElementById('tabChangelog')
958
+ const tabChangelogGroupedBtn = document.getElementById(
959
+ 'tabChangelogGrouped',
960
+ )
940
961
 
941
962
  const URL_PARAM_KEY = 'filters'
942
963
  let isHydratingFromUrl = false
@@ -2137,6 +2158,18 @@
2137
2158
  ),
2138
2159
  )
2139
2160
  }
2161
+ if (
2162
+ Number.isInteger(options?.item?.dedupedRouteCount) &&
2163
+ options.item.dedupedRouteCount > 0
2164
+ ) {
2165
+ card.appendChild(
2166
+ el(
2167
+ 'div',
2168
+ 'meta',
2169
+ `deduped route schema events: ${options.item.dedupedRouteCount}`,
2170
+ ),
2171
+ )
2172
+ }
2140
2173
  return card
2141
2174
  }
2142
2175
 
@@ -2215,6 +2248,18 @@
2215
2248
  ),
2216
2249
  )
2217
2250
  }
2251
+ if (
2252
+ Number.isInteger(item?.dedupedRouteCount) &&
2253
+ item.dedupedRouteCount > 0
2254
+ ) {
2255
+ row.appendChild(
2256
+ el(
2257
+ 'div',
2258
+ 'meta mono',
2259
+ `deduped route schema events: ${item.dedupedRouteCount}`,
2260
+ ),
2261
+ )
2262
+ }
2218
2263
 
2219
2264
  return row
2220
2265
  }
@@ -2246,6 +2291,60 @@
2246
2291
  )
2247
2292
  }
2248
2293
 
2294
+ function schemaEntrySignature(entry) {
2295
+ if (!entry || typeof entry !== 'object') return ''
2296
+ return JSON.stringify({
2297
+ type: typeof entry.type === 'string' ? entry.type : 'unknown',
2298
+ nullable: Boolean(entry.nullable),
2299
+ optional: Boolean(entry.optional),
2300
+ literal: entry.literal,
2301
+ })
2302
+ }
2303
+
2304
+ function normalizeSignatureToken(signature) {
2305
+ if (typeof signature !== 'string') return ''
2306
+ const parsed = parseSignatureEntry(signature)
2307
+ if (!parsed) return signature
2308
+ return schemaEntrySignature(parsed)
2309
+ }
2310
+
2311
+ function normalizedSchemaDeltaParts(delta) {
2312
+ if (!delta || typeof delta !== 'object') return null
2313
+ const op = String(delta.op || 'changed')
2314
+ if ('path' in delta) {
2315
+ const location = String(delta.path || '')
2316
+ const before = delta.before ? [schemaEntrySignature(delta.before)] : []
2317
+ const after = delta.after ? [schemaEntrySignature(delta.after)] : []
2318
+ return { op, location, before, after }
2319
+ }
2320
+ if ('section' in delta) {
2321
+ const location = `${delta.section}.${delta.path}`
2322
+ const before = Array.isArray(delta.before)
2323
+ ? delta.before.map((item) => normalizeSignatureToken(item)).filter(Boolean)
2324
+ : []
2325
+ const after = Array.isArray(delta.after)
2326
+ ? delta.after.map((item) => normalizeSignatureToken(item)).filter(Boolean)
2327
+ : []
2328
+ before.sort()
2329
+ after.sort()
2330
+ return { op, location, before, after }
2331
+ }
2332
+ return null
2333
+ }
2334
+
2335
+ function schemaDiffFingerprint(schemaDiff) {
2336
+ if (!Array.isArray(schemaDiff) || schemaDiff.length === 0) return ''
2337
+ const lines = schemaDiff
2338
+ .map((delta) => normalizedSchemaDeltaParts(delta))
2339
+ .filter(Boolean)
2340
+ .map(
2341
+ (part) =>
2342
+ `${part.op}|${part.location}|${part.before.join('&')}|${part.after.join('&')}`,
2343
+ )
2344
+ .sort()
2345
+ return lines.join('||')
2346
+ }
2347
+
2249
2348
  function buildChronologicalEvents(payload) {
2250
2349
  if (!payload) return []
2251
2350
  const commitBySha = new Map(
@@ -2256,9 +2355,43 @@
2256
2355
  )
2257
2356
  const rows = []
2258
2357
  let sequence = 0
2358
+ const sourceSchemaCoverage = new Map()
2359
+
2360
+ Object.entries(payload.bySourceObject || {}).forEach(
2361
+ ([sourceObjectId, events]) => {
2362
+ ;(events || []).forEach((event) => {
2363
+ if (event?.type !== 'source_schema_changed') return
2364
+ const fingerprint = schemaDiffFingerprint(event.schemaDiff)
2365
+ if (!fingerprint) return
2366
+ const key = `${event.commitSha}|${fingerprint}`
2367
+ let coverage = sourceSchemaCoverage.get(key)
2368
+ if (!coverage) {
2369
+ coverage = {
2370
+ sourceObjectIds: new Set(),
2371
+ impactedRoutes: new Set(),
2372
+ dedupedRouteKeys: new Set(),
2373
+ }
2374
+ sourceSchemaCoverage.set(key, coverage)
2375
+ }
2376
+ coverage.sourceObjectIds.add(sourceObjectId)
2377
+ ;(event.impactedRoutes || []).forEach((routeKey) => {
2378
+ coverage.impactedRoutes.add(routeKey)
2379
+ })
2380
+ })
2381
+ },
2382
+ )
2259
2383
 
2260
2384
  Object.entries(payload.byRoute || {}).forEach(([routeKey, events]) => {
2261
2385
  ;(events || []).forEach((event) => {
2386
+ if (event?.type === 'schema_changed') {
2387
+ const fingerprint = schemaDiffFingerprint(event.schemaDiff)
2388
+ const key = `${event.commitSha}|${fingerprint}`
2389
+ const coverage = sourceSchemaCoverage.get(key)
2390
+ if (coverage && coverage.impactedRoutes.has(routeKey)) {
2391
+ coverage.dedupedRouteKeys.add(routeKey)
2392
+ return
2393
+ }
2394
+ }
2262
2395
  rows.push({
2263
2396
  eventKind: 'route',
2264
2397
  routeKey,
@@ -2274,10 +2407,21 @@
2274
2407
  Object.entries(payload.bySourceObject || {}).forEach(
2275
2408
  ([sourceObjectId, events]) => {
2276
2409
  ;(events || []).forEach((event) => {
2410
+ const fingerprint =
2411
+ event?.type === 'source_schema_changed'
2412
+ ? schemaDiffFingerprint(event.schemaDiff)
2413
+ : ''
2414
+ const coverage = sourceSchemaCoverage.get(
2415
+ `${event.commitSha}|${fingerprint}`,
2416
+ )
2277
2417
  rows.push({
2278
2418
  eventKind: 'source',
2279
2419
  sourceObjectId,
2280
2420
  displayName: event.displayName || sourceObjectId,
2421
+ dedupedRouteCount:
2422
+ event?.type === 'source_schema_changed'
2423
+ ? coverage?.dedupedRouteKeys.size || 0
2424
+ : 0,
2281
2425
  event,
2282
2426
  commit: commitBySha.get(event.commitSha),
2283
2427
  commitSort: commitTimestamp(commitBySha.get(event.commitSha)),
@@ -2301,7 +2445,10 @@
2301
2445
  function changelogItemsByTab(tabId) {
2302
2446
  const payload = state.changelogPayload
2303
2447
  if (!payload) return []
2304
- if (tabId === VIEW_IDS.changelog) {
2448
+ if (
2449
+ tabId === VIEW_IDS.changelog ||
2450
+ tabId === VIEW_IDS.changelogGrouped
2451
+ ) {
2305
2452
  return buildChronologicalEvents(payload)
2306
2453
  }
2307
2454
  return []
@@ -2330,6 +2477,10 @@
2330
2477
  function renderTabButtons() {
2331
2478
  setPressed(tabLeavesBtn, state.activeTab === VIEW_IDS.leaves)
2332
2479
  setPressed(tabChangelogBtn, state.activeTab === VIEW_IDS.changelog)
2480
+ setPressed(
2481
+ tabChangelogGroupedBtn,
2482
+ state.activeTab === VIEW_IDS.changelogGrouped,
2483
+ )
2333
2484
  tabLeavesBtn.style.display = state.availableTabs.has(VIEW_IDS.leaves)
2334
2485
  ? ''
2335
2486
  : 'none'
@@ -2338,6 +2489,11 @@
2338
2489
  )
2339
2490
  ? ''
2340
2491
  : 'none'
2492
+ tabChangelogGroupedBtn.style.display = state.availableTabs.has(
2493
+ VIEW_IDS.changelogGrouped,
2494
+ )
2495
+ ? ''
2496
+ : 'none'
2341
2497
  const isLeavesTab = state.activeTab === VIEW_IDS.leaves
2342
2498
  coreFieldsBtn.style.display = isLeavesTab ? '' : 'none'
2343
2499
  schemasOnlyFieldsBtn.style.display = isLeavesTab ? '' : 'none'
@@ -2392,7 +2548,7 @@
2392
2548
  )
2393
2549
  if (filtered.length === 0) {
2394
2550
  resultsEl.appendChild(el('div', 'empty', 'No matches.'))
2395
- } else if (state.viewerMode === VIEWER_MODE.changelog) {
2551
+ } else if (state.activeTab === VIEW_IDS.changelog) {
2396
2552
  renderChangelogWithCommitSeparators(filtered, commitBySha, (item) =>
2397
2553
  renderChangelogEventLines(item, commitBySha),
2398
2554
  )
@@ -2435,7 +2591,8 @@
2435
2591
  {
2436
2592
  id: 'tabFields',
2437
2593
  label:
2438
- state.activeTab === VIEW_IDS.changelog
2594
+ state.activeTab === VIEW_IDS.changelog ||
2595
+ state.activeTab === VIEW_IDS.changelogGrouped
2439
2596
  ? 'Changelog fields'
2440
2597
  : 'Source fields',
2441
2598
  fieldIds: fieldDefs.map((field) => field.id),
@@ -2623,7 +2780,8 @@
2623
2780
  isHydratingFromUrl = true
2624
2781
  if (
2625
2782
  parsed.activeTab === VIEW_IDS.leaves ||
2626
- parsed.activeTab === VIEW_IDS.changelog
2783
+ parsed.activeTab === VIEW_IDS.changelog ||
2784
+ parsed.activeTab === VIEW_IDS.changelogGrouped
2627
2785
  ) {
2628
2786
  state.activeTab = parsed.activeTab
2629
2787
  }
@@ -2712,12 +2870,19 @@
2712
2870
  }
2713
2871
  if (state.changelogPayload) {
2714
2872
  state.availableTabs.add(VIEW_IDS.changelog)
2873
+ if (state.viewerMode === VIEWER_MODE.bundle) {
2874
+ state.availableTabs.add(VIEW_IDS.changelogGrouped)
2875
+ }
2715
2876
  }
2716
2877
 
2717
2878
  if (!state.availableTabs.has(state.activeTab)) {
2718
- state.activeTab = state.availableTabs.has(VIEW_IDS.leaves)
2719
- ? VIEW_IDS.leaves
2720
- : VIEW_IDS.changelog
2879
+ if (state.availableTabs.has(VIEW_IDS.leaves)) {
2880
+ state.activeTab = VIEW_IDS.leaves
2881
+ } else if (state.availableTabs.has(VIEW_IDS.changelog)) {
2882
+ state.activeTab = VIEW_IDS.changelog
2883
+ } else {
2884
+ state.activeTab = VIEW_IDS.changelogGrouped
2885
+ }
2721
2886
  }
2722
2887
 
2723
2888
  if (state.leavesPayload && state.changelogPayload) {
@@ -2780,6 +2945,9 @@
2780
2945
  tabChangelogBtn.addEventListener('click', () =>
2781
2946
  setActiveTab(VIEW_IDS.changelog),
2782
2947
  )
2948
+ tabChangelogGroupedBtn.addEventListener('click', () =>
2949
+ setActiveTab(VIEW_IDS.changelogGrouped),
2950
+ )
2783
2951
 
2784
2952
  selectAllFieldsBtn.addEventListener('click', () =>
2785
2953
  setFieldSelection(new Set(currentFieldDefs().map((field) => field.id))),