@grimoire-rs/indexer 0.4.0 → 0.4.2

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 (50) hide show
  1. package/README.md +85 -9
  2. package/dist/cli/enrich.d.ts +1 -0
  3. package/dist/cli/enrich.d.ts.map +1 -1
  4. package/dist/cli/enrich.js +38 -0
  5. package/dist/cli/enrich.js.map +1 -1
  6. package/dist/cli/main.d.ts.map +1 -1
  7. package/dist/cli/main.js +1 -0
  8. package/dist/cli/main.js.map +1 -1
  9. package/dist/data/index.d.ts.map +1 -1
  10. package/dist/data/index.js +13 -0
  11. package/dist/data/index.js.map +1 -1
  12. package/dist/enrich/checkpoint.d.ts +53 -0
  13. package/dist/enrich/checkpoint.d.ts.map +1 -0
  14. package/dist/enrich/checkpoint.js +296 -0
  15. package/dist/enrich/checkpoint.js.map +1 -0
  16. package/dist/enrich/index.d.ts +14 -0
  17. package/dist/enrich/index.d.ts.map +1 -1
  18. package/dist/enrich/index.js +3 -3
  19. package/dist/enrich/index.js.map +1 -1
  20. package/dist/ratings/paging.d.ts +16 -0
  21. package/dist/ratings/paging.d.ts.map +1 -0
  22. package/dist/ratings/paging.js +36 -0
  23. package/dist/ratings/paging.js.map +1 -0
  24. package/dist/ratings/provider.d.ts +1 -9
  25. package/dist/ratings/provider.d.ts.map +1 -1
  26. package/dist/ratings/provider.js +11 -10
  27. package/dist/ratings/provider.js.map +1 -1
  28. package/dist/ratings/provider_github.d.ts.map +1 -1
  29. package/dist/ratings/provider_github.js +2 -1
  30. package/dist/ratings/provider_github.js.map +1 -1
  31. package/dist/ratings/provider_gitlab.d.ts.map +1 -1
  32. package/dist/ratings/provider_gitlab.js +2 -1
  33. package/dist/ratings/provider_gitlab.js.map +1 -1
  34. package/dist/ratings/reconcile.d.ts.map +1 -1
  35. package/dist/ratings/reconcile.js +14 -4
  36. package/dist/ratings/reconcile.js.map +1 -1
  37. package/dist/renderer/astro/components/Catalog.js +3 -3
  38. package/dist/renderer/astro/components/Catalog.tsx +15 -9
  39. package/dist/renderer/astro/components/CommandField.astro +1 -0
  40. package/dist/renderer/astro/components/VersionMenu.astro +31 -23
  41. package/dist/renderer/astro/layouts/Base.astro +212 -264
  42. package/dist/renderer/astro/pages/p/[...slug].astro +99 -89
  43. package/dist/renderer/astro/styles/tokens.css +157 -0
  44. package/dist/validate/adapters/http.d.ts +23 -0
  45. package/dist/validate/adapters/http.d.ts.map +1 -1
  46. package/dist/validate/adapters/http.js +12 -1
  47. package/dist/validate/adapters/http.js.map +1 -1
  48. package/package.json +1 -1
  49. package/templates/ci/github-enrich.yml +9 -1
  50. package/templates/ci/gitlab-enrich.sh +10 -1
@@ -166,7 +166,7 @@ const updatedTitle = pkg.revision ? `${updated} · revision ${pkg.revision}` : u
166
166
 
167
167
  <Base title={`${title} — ${config.brand}`} description={description} image={pkg.logo}>
168
168
  <div class="detail">
169
- <header class="detail-head">
169
+ <header class="detail-head" data-slot="detail-header">
170
170
  {
171
171
  pkg.logo ? (
172
172
  // The placeholder ships in the markup and the image fades in over
@@ -182,7 +182,7 @@ const updatedTitle = pkg.revision ? `${updated} · revision ${pkg.revision}` : u
182
182
  <img src={withBase(pkg.logo)} alt="" />
183
183
  </span>
184
184
  ) : (
185
- <div class="tile letter" style={`background: var(--kind-${pkg.kind}, var(--muted))`}>
185
+ <div class="tile letter" style={`background: var(--grim-color-kind-${pkg.kind}, var(--grim-color-muted))`}>
186
186
  {pkg.name[0]?.toUpperCase()}
187
187
  </div>
188
188
  )
@@ -202,7 +202,7 @@ const updatedTitle = pkg.revision ? `${updated} · revision ${pkg.revision}` : u
202
202
 
203
203
  {
204
204
  pkg.deprecated && (
205
- <p class="deprecated-banner">
205
+ <p class="deprecated-banner" data-slot="deprecated-banner">
206
206
  ⚠️ deprecated{pkg.replacedBy ? ` — replaced by ${pkg.replacedBy}` : ""}
207
207
  </p>
208
208
  )
@@ -229,7 +229,7 @@ const updatedTitle = pkg.revision ? `${updated} · revision ${pkg.revision}` : u
229
229
  }
230
230
  </div>
231
231
 
232
- <div class="detail-body">
232
+ <div class="detail-body" data-slot="detail-body">
233
233
  <main class="detail-main">
234
234
  {/* Radio tabs, not a scripted tablist: the ribbon works on a page
235
235
  whose JS never ran, and a radio group already has the keyboard
@@ -324,7 +324,7 @@ const updatedTitle = pkg.revision ? `${updated} · revision ${pkg.revision}` : u
324
324
  {openTab === null && <p class="panel-empty">No content available.</p>}
325
325
  </main>
326
326
 
327
- <aside class="detail-rail">
327
+ <aside class="detail-rail" data-slot="detail-rail">
328
328
  {tags.length > 0 && (
329
329
  <section>
330
330
  <h2>Versions</h2>
@@ -509,33 +509,39 @@ const updatedTitle = pkg.revision ? `${updated} · revision ${pkg.revision}` : u
509
509
  </script>
510
510
 
511
511
  <style>
512
+ @layer grimoire {
513
+ /* Layered so a user CSS file (unlayered) outranks these rules. Astro
514
+ scopes a component style to `.foo[data-astro-cid-…]` — specificity
515
+ (0,2,0) — which beats a consumer's `.foo` in EITHER source order, so
516
+ source order alone does not make this overridable. Not reindented:
517
+ the wrapper stays a two-line diff. */
512
518
  .detail {
513
519
  display: flex;
514
520
  flex-direction: column;
515
- gap: 1.25rem;
521
+ gap: var(--grim-space-7);
516
522
  }
517
523
  .detail-head {
518
524
  display: flex;
519
525
  align-items: center;
520
- gap: 1rem;
521
- background: var(--card);
522
- border: 1px solid var(--border);
523
- border-radius: 10px;
524
- padding: 1rem 1.25rem;
526
+ gap: var(--grim-space-6);
527
+ background: var(--grim-color-card);
528
+ border: var(--grim-border-width) solid var(--grim-color-border);
529
+ border-radius: var(--grim-radius-xl);
530
+ padding: var(--grim-space-6) var(--grim-space-7);
525
531
  }
526
532
  .tile {
527
533
  flex-shrink: 0;
528
534
  width: 3.5rem;
529
535
  height: 3.5rem;
530
- border-radius: 10px;
536
+ border-radius: var(--grim-radius-xl);
531
537
  object-fit: cover;
532
538
  }
533
539
  .tile.letter {
534
540
  display: flex;
535
541
  align-items: center;
536
542
  justify-content: center;
537
- color: var(--on-accent);
538
- font-size: 1.5rem;
543
+ color: var(--grim-color-on-accent);
544
+ font-size: var(--grim-text-xl);
539
545
  font-weight: 700;
540
546
  font-family: ui-monospace, "SFMono-Regular", monospace;
541
547
  }
@@ -543,18 +549,18 @@ const updatedTitle = pkg.revision ? `${updated} · revision ${pkg.revision}` : u
543
549
  margin: 0;
544
550
  display: flex;
545
551
  align-items: center;
546
- gap: 0.6rem;
547
- font-size: 1.5rem;
552
+ gap: var(--grim-space-4);
553
+ font-size: var(--grim-text-xl);
548
554
  font-family: ui-monospace, "SFMono-Regular", monospace;
549
555
  }
550
556
  .summary {
551
- margin: 0.3rem 0 0;
552
- color: var(--muted);
557
+ margin: var(--grim-space-2) 0 0;
558
+ color: var(--grim-color-muted);
553
559
  }
554
560
  .byline {
555
- margin: 0.35rem 0 0;
556
- color: var(--muted);
557
- font-size: 0.82rem;
561
+ margin: var(--grim-space-3) 0 0;
562
+ color: var(--grim-color-muted);
563
+ font-size: var(--grim-text-sm);
558
564
  }
559
565
  /* The kind badge names a closed set of five and shouts accordingly. A
560
566
  compatibility hint is a free string off the registry — upper-casing it
@@ -570,31 +576,31 @@ const updatedTitle = pkg.revision ? `${updated} · revision ${pkg.revision}` : u
570
576
  padding: 0;
571
577
  display: flex;
572
578
  flex-direction: column;
573
- gap: 0.45rem;
579
+ gap: var(--grim-space-4);
574
580
  }
575
581
  .support li {
576
582
  display: flex;
577
583
  align-items: center;
578
- gap: 0.4rem;
579
- font-size: 0.9rem;
584
+ gap: var(--grim-space-3);
585
+ font-size: var(--grim-text-md);
580
586
  }
581
587
  .support li :global(svg) {
582
588
  flex-shrink: 0;
583
- color: var(--muted);
589
+ color: var(--grim-color-muted);
584
590
  }
585
591
  .deprecated-banner {
586
592
  margin: 0;
587
- background: var(--banner-bg);
588
- color: var(--banner-fg);
589
- border: 1px solid var(--banner-border);
590
- border-radius: 8px;
591
- padding: 0.6rem 0.9rem;
593
+ background: var(--grim-color-banner-bg);
594
+ color: var(--grim-color-banner-fg);
595
+ border: var(--grim-border-width) solid var(--grim-color-banner-border);
596
+ border-radius: var(--grim-radius-lg);
597
+ padding: var(--grim-space-4) var(--grim-space-6);
592
598
  font-weight: 600;
593
599
  }
594
600
  .detail-body {
595
601
  display: grid;
596
602
  grid-template-columns: 1fr 18rem;
597
- gap: 2rem;
603
+ gap: var(--grim-space-8);
598
604
  align-items: start;
599
605
  }
600
606
  @media (max-width: 55rem) {
@@ -604,10 +610,10 @@ const updatedTitle = pkg.revision ? `${updated} · revision ${pkg.revision}` : u
604
610
  }
605
611
  .detail-main {
606
612
  min-width: 0;
607
- background: var(--card);
608
- border: 1px solid var(--border);
609
- border-radius: 10px;
610
- padding: 1.25rem 1.5rem;
613
+ background: var(--grim-color-card);
614
+ border: var(--grim-border-width) solid var(--grim-color-border);
615
+ border-radius: var(--grim-radius-xl);
616
+ padding: var(--grim-space-7) var(--grim-space-7);
611
617
  }
612
618
  /* Markdown opens with its own heading, whose margin lands on top of the
613
619
  panel padding and reads as a hole. Every panel sets its own top edge. */
@@ -624,23 +630,26 @@ const updatedTitle = pkg.revision ? `${updated} · revision ${pkg.revision}` : u
624
630
  panel's own top edge rather than a line floating in the content. */
625
631
  .ribbon {
626
632
  display: flex;
627
- gap: 1.25rem;
628
- margin: -1.25rem -1.5rem 1.25rem;
629
- padding: 0 1.5rem;
630
- border-bottom: 1px solid var(--border);
633
+ gap: var(--grim-space-7);
634
+ /* The negation of `.detail-main`'s own padding, written as that token so
635
+ the two cannot drift apart into a bleed that no longer reaches the edge. */
636
+ margin: calc(-1 * var(--grim-space-7)) calc(-1 * var(--grim-space-7))
637
+ var(--grim-space-7);
638
+ padding: 0 var(--grim-space-7);
639
+ border-bottom: var(--grim-border-width) solid var(--grim-color-border);
631
640
  }
632
641
  .ribbon label {
633
- padding: 0.7rem 0;
634
- margin-bottom: -1px; /* the active underline sits on the panel rule */
642
+ padding: var(--grim-space-5) 0;
643
+ margin-bottom: calc(-1 * var(--grim-border-width)); /* the active underline sits on the panel rule */
635
644
  border-bottom: 2px solid transparent;
636
- font-size: 0.85rem;
645
+ font-size: var(--grim-text-sm);
637
646
  text-transform: uppercase;
638
647
  letter-spacing: 0.04em;
639
- color: var(--muted);
648
+ color: var(--grim-color-muted);
640
649
  cursor: pointer;
641
650
  }
642
651
  .ribbon label:hover {
643
- color: var(--fg);
652
+ color: var(--grim-color-fg);
644
653
  }
645
654
  /* Nothing behind it. Still shown, so the ribbon is the same shape on every
646
655
  package page, but visibly not a thing to click. */
@@ -653,7 +662,7 @@ const updatedTitle = pkg.revision ? `${updated} · revision ${pkg.revision}` : u
653
662
  #panel-readme:disabled ~ .ribbon label[for="panel-readme"]:hover,
654
663
  #panel-contents:disabled ~ .ribbon label[for="panel-contents"]:hover,
655
664
  #panel-changelog:disabled ~ .ribbon label[for="panel-changelog"]:hover {
656
- color: var(--muted);
665
+ color: var(--grim-color-muted);
657
666
  }
658
667
  /* Every tab empty: say it once, in the middle. */
659
668
  .panel-empty {
@@ -661,7 +670,7 @@ const updatedTitle = pkg.revision ? `${updated} · revision ${pkg.revision}` : u
661
670
  place-items: center;
662
671
  min-height: 11rem;
663
672
  margin: 0;
664
- color: var(--muted);
673
+ color: var(--grim-color-muted);
665
674
  }
666
675
  .tab-panel {
667
676
  display: none;
@@ -674,16 +683,16 @@ const updatedTitle = pkg.revision ? `${updated} · revision ${pkg.revision}` : u
674
683
  #panel-readme:checked ~ .ribbon label[for="panel-readme"],
675
684
  #panel-contents:checked ~ .ribbon label[for="panel-contents"],
676
685
  #panel-changelog:checked ~ .ribbon label[for="panel-changelog"] {
677
- color: var(--fg);
678
- border-bottom-color: var(--accent);
686
+ color: var(--grim-color-fg);
687
+ border-bottom-color: var(--grim-color-accent);
679
688
  }
680
689
  /* The input carries the focus, the label shows it. */
681
690
  #panel-readme:focus-visible ~ .ribbon label[for="panel-readme"],
682
691
  #panel-contents:focus-visible ~ .ribbon label[for="panel-contents"],
683
692
  #panel-changelog:focus-visible ~ .ribbon label[for="panel-changelog"] {
684
- outline: 2px solid var(--accent);
693
+ outline: 2px solid var(--grim-color-accent);
685
694
  outline-offset: 2px;
686
- border-radius: 3px;
695
+ border-radius: var(--grim-radius-sm);
687
696
  }
688
697
 
689
698
  /* A bundle's member list: what it installs, one row each. A grid, not a
@@ -696,7 +705,7 @@ const updatedTitle = pkg.revision ? `${updated} · revision ${pkg.revision}` : u
696
705
  padding: 0;
697
706
  display: grid;
698
707
  grid-template-columns: auto auto 1fr;
699
- gap: 0.45rem 0.75rem;
708
+ gap: var(--grim-space-4) var(--grim-space-5);
700
709
  align-items: center;
701
710
  }
702
711
  .members li {
@@ -711,8 +720,8 @@ const updatedTitle = pkg.revision ? `${updated} · revision ${pkg.revision}` : u
711
720
  justify-self: start;
712
721
  }
713
722
  .members code {
714
- color: var(--muted);
715
- font-size: 0.82rem;
723
+ color: var(--grim-color-muted);
724
+ font-size: var(--grim-text-sm);
716
725
  overflow-wrap: anywhere;
717
726
  }
718
727
  /* Narrow: three columns of a long ref do not fit, so each member becomes
@@ -729,32 +738,32 @@ const updatedTitle = pkg.revision ? `${updated} · revision ${pkg.revision}` : u
729
738
  .detail-rail {
730
739
  display: flex;
731
740
  flex-direction: column;
732
- gap: 1.25rem;
741
+ gap: var(--grim-space-7);
733
742
  }
734
743
  .detail-rail section {
735
- background: var(--card);
736
- border: 1px solid var(--border);
737
- border-radius: 10px;
738
- padding: 1rem;
744
+ background: var(--grim-color-card);
745
+ border: var(--grim-border-width) solid var(--grim-color-border);
746
+ border-radius: var(--grim-radius-xl);
747
+ padding: var(--grim-space-6);
739
748
  }
740
749
  /* Panel label. The main panel's ribbon labels match this by hand — they
741
750
  are <label>s, not headings, so they cannot join the selector. */
742
751
  .detail-rail h2 {
743
- margin: 0 0 0.6rem;
744
- font-size: 0.85rem;
752
+ margin: 0 0 var(--grim-space-4);
753
+ font-size: var(--grim-text-sm);
745
754
  text-transform: uppercase;
746
755
  letter-spacing: 0.04em;
747
- color: var(--muted);
756
+ color: var(--grim-color-muted);
748
757
  }
749
758
  /* Loose badges, both for the cascade and the older tags — VersionMenu owns
750
759
  the pill itself, this only lays them out. */
751
760
  .cascade {
752
761
  display: flex;
753
762
  flex-wrap: wrap;
754
- gap: 0.3rem;
763
+ gap: var(--grim-space-2);
755
764
  }
756
765
  .older {
757
- margin-top: 0.7rem;
766
+ margin-top: var(--grim-space-5);
758
767
  }
759
768
  /* The UA marker is a glyph that flips between two characters with no
760
769
  in-between; a real chevron can turn. `list-style` kills it everywhere
@@ -764,22 +773,22 @@ const updatedTitle = pkg.revision ? `${updated} · revision ${pkg.revision}` : u
764
773
  .older > summary {
765
774
  display: flex;
766
775
  align-items: center;
767
- gap: 0.35rem;
776
+ gap: var(--grim-space-3);
768
777
  list-style: none;
769
- padding-block: 0.15rem;
778
+ padding-block: var(--grim-space-1);
770
779
  cursor: pointer;
771
- font-size: 0.8rem;
772
- color: var(--muted);
780
+ font-size: var(--grim-text-sm);
781
+ color: var(--grim-color-muted);
773
782
  }
774
783
  .older > summary::-webkit-details-marker {
775
784
  display: none;
776
785
  }
777
786
  .older > summary:hover {
778
- color: var(--fg);
787
+ color: var(--grim-color-fg);
779
788
  }
780
789
  .older > summary :global(svg) {
781
790
  flex-shrink: 0;
782
- transition: transform 160ms ease;
791
+ transition: transform var(--grim-duration-base) ease;
783
792
  }
784
793
  .older[open] > summary :global(svg) {
785
794
  transform: rotate(90deg);
@@ -790,7 +799,7 @@ const updatedTitle = pkg.revision ? `${updated} · revision ${pkg.revision}` : u
790
799
  }
791
800
  }
792
801
  .older[open] > summary {
793
- margin-bottom: 0.5rem;
802
+ margin-bottom: var(--grim-space-4);
794
803
  }
795
804
  dl {
796
805
  margin: 0;
@@ -800,14 +809,14 @@ const updatedTitle = pkg.revision ? `${updated} · revision ${pkg.revision}` : u
800
809
  `stretch` sits each at the top of its own box, so the size difference
801
810
  reads as two lines that never quite line up. */
802
811
  align-items: center;
803
- gap: 0.45rem 0.7rem;
812
+ gap: var(--grim-space-4) var(--grim-space-5);
804
813
  }
805
814
  dt {
806
815
  display: flex;
807
816
  align-items: center;
808
- gap: 0.4rem;
809
- color: var(--muted);
810
- font-size: 0.85rem;
817
+ gap: var(--grim-space-3);
818
+ color: var(--grim-color-muted);
819
+ font-size: var(--grim-text-sm);
811
820
  }
812
821
  /* Every icon is the same box, so the label text lands on one x without an
813
822
  icon column of its own. */
@@ -816,15 +825,15 @@ const updatedTitle = pkg.revision ? `${updated} · revision ${pkg.revision}` : u
816
825
  }
817
826
  dd {
818
827
  margin: 0;
819
- font-size: 0.9rem;
828
+ font-size: var(--grim-text-md);
820
829
  }
821
830
  .keywords {
822
831
  list-style: none;
823
- margin: 0.75rem 0 0;
832
+ margin: var(--grim-space-5) 0 0;
824
833
  padding: 0;
825
834
  display: flex;
826
835
  flex-wrap: wrap;
827
- gap: 0.35rem;
836
+ gap: var(--grim-space-3);
828
837
  }
829
838
  /* The chip is the link, not a box around it — otherwise the padding is
830
839
  dead space the pointer falls through. Bordered and hovering to accent
@@ -833,27 +842,28 @@ const updatedTitle = pkg.revision ? `${updated} · revision ${pkg.revision}` : u
833
842
  that answers differently reads as a different kind of thing. */
834
843
  .keywords a {
835
844
  display: inline-block;
836
- border: 1px solid var(--border);
837
- border-radius: 999px;
838
- background: var(--chip-bg);
839
- padding: 0.15rem 0.6rem;
840
- font-size: 0.78rem;
841
- color: var(--muted);
845
+ border: var(--grim-border-width) solid var(--grim-color-border);
846
+ border-radius: var(--grim-radius-pill);
847
+ background: var(--grim-color-chip-bg);
848
+ padding: var(--grim-space-1) var(--grim-space-4);
849
+ font-size: var(--grim-text-xs);
850
+ color: var(--grim-color-muted);
842
851
  text-decoration: none;
843
852
  transition:
844
- color 120ms ease,
845
- border-color 120ms ease,
846
- background-color 120ms ease;
853
+ color var(--grim-duration-fast) ease,
854
+ border-color var(--grim-duration-fast) ease,
855
+ background-color var(--grim-duration-fast) ease;
847
856
  }
848
857
  .keywords a:hover,
849
858
  .keywords a:focus-visible {
850
- color: var(--accent);
851
- border-color: var(--accent);
852
- background: var(--accent-soft);
859
+ color: var(--grim-color-accent);
860
+ border-color: var(--grim-color-accent);
861
+ background: var(--grim-color-accent-soft);
853
862
  }
854
863
  @media (prefers-reduced-motion: reduce) {
855
864
  .keywords a {
856
865
  transition: none;
857
866
  }
858
867
  }
868
+ }
859
869
  </style>
@@ -0,0 +1,157 @@
1
+ /* SPDX-License-Identifier: Apache-2.0
2
+ Copyright 2026 The Grimoire Authors
3
+
4
+ THE THEMING CONTRACT — these custom properties are the whole customization
5
+ surface. There is no component-override API; see `data-slot` below for the
6
+ other half of the contract.
7
+
8
+ Everything the renderer ships is layered. Unlayered CSS — i.e. any user
9
+ override file — beats a layered declaration outright, at any specificity,
10
+ so a single `:root { --grim-color-accent: … }` wins in light AND dark
11
+ without !important and without knowing where Astro injects its bundle.
12
+
13
+ Names are `--grim-<category>-<role>`. The category segment is not
14
+ decoration: it is what stops a colour named `--grim-text` from colliding
15
+ with the type-scale tokens below.
16
+
17
+ COLOUR is the one family that differs per scheme, so it is the one family
18
+ declared twice — `:root` and `[data-theme="dark"]`. Redeclare BOTH when you
19
+ override one: `:root` is scheme-agnostic, so a `:root`-only override
20
+ silently takes dark mode with it. Every other family is a measurement, not
21
+ an appearance, and is declared once.
22
+
23
+ Adding a token is additive. Removing or renaming one is a breaking change.
24
+
25
+ `test/renderer/style_contract.test.ts` holds this file to it, and
26
+ `test/renderer/build.test.ts` proves the layer survives a real build. */
27
+
28
+ @layer grimoire {
29
+ :root {
30
+ /* ---- Colour: surface ------------------------------------------- */
31
+ --grim-color-bg: #f7f6f3;
32
+ --grim-color-fg: #1d1c1a;
33
+ --grim-color-muted: #6d6a63;
34
+ --grim-color-card: #ffffff;
35
+ --grim-color-border: #dedbd3;
36
+ --grim-color-chip-bg: #edebe5;
37
+
38
+ /* ---- Colour: accent -------------------------------------------- */
39
+ --grim-color-accent: #6d4fc4;
40
+ --grim-color-accent-soft: #efeafd;
41
+ --grim-color-on-accent: #ffffff;
42
+
43
+ /* ---- Colour: package kind --------------------------------------
44
+ Read through a name composed at runtime — `Catalog.tsx` and the detail
45
+ page build `var(--grim-color-kind-${pkg.kind})`. Grepping for one of
46
+ these finds the declaration and no use site; all five are used. */
47
+ --grim-color-kind-skill: #6d4fc4;
48
+ --grim-color-kind-rule: #1a7f6b;
49
+ --grim-color-kind-agent: #b0641a;
50
+ --grim-color-kind-mcp: #2565c7;
51
+ --grim-color-kind-bundle: #a63d68;
52
+
53
+ /* ---- Colour: state --------------------------------------------- */
54
+ /* ~5.7:1 on --grim-color-card (white) */
55
+ --grim-color-deprecated: #9c5400;
56
+ --grim-color-banner-bg: #fff3cd;
57
+ --grim-color-banner-fg: #6b4c00;
58
+ --grim-color-banner-border: #e0b84c;
59
+
60
+ color-scheme: light;
61
+
62
+ /* ---- Space -----------------------------------------------------
63
+ Numbered, not role-named, because a spacing step is pure ordinal
64
+ magnitude: bigger number, more room. The steps are sparse and the
65
+ distance between them GROWS — 2, 2, 2, 4, 4, 8, 8, 16px — so the
66
+ scale stays usable at the top without minting a step per pixel.
67
+ A bare number is only honest with a published role, so here it is:
68
+
69
+ 1 hairline inset — badge and pill padding
70
+ 2 tight — an icon and its label
71
+ 3 chip padding, keyword gaps
72
+ 4 default gap inside one control
73
+ 5 between related controls
74
+ 6 card padding, row gap
75
+ 7 between sections
76
+ 8 page block separation
77
+ 9 page bottom */
78
+ --grim-space-1: 0.125rem;
79
+ --grim-space-2: 0.25rem;
80
+ --grim-space-3: 0.375rem;
81
+ --grim-space-4: 0.5rem;
82
+ --grim-space-5: 0.75rem;
83
+ --grim-space-6: 1rem;
84
+ --grim-space-7: 1.5rem;
85
+ --grim-space-8: 2rem;
86
+ --grim-space-9: 3rem;
87
+
88
+ /* ---- Type ------------------------------------------------------
89
+ Role-named, unlike space: a type step is "metadata vs body vs
90
+ heading", which is a job, not a magnitude. `xl` is a detail-page
91
+ heading and `2xl` the landing hero — two sizes because those are two
92
+ deliberately different headings, not drift. */
93
+ --grim-text-2xs: 0.72rem;
94
+ --grim-text-xs: 0.78rem;
95
+ --grim-text-sm: 0.85rem;
96
+ --grim-text-md: 0.95rem;
97
+ --grim-text-lg: 1.05rem;
98
+ --grim-text-xl: 1.5rem;
99
+ --grim-text-2xl: 1.6rem;
100
+
101
+ /* ---- Radius ---------------------------------------------------- */
102
+ --grim-radius-sm: 4px;
103
+ --grim-radius-md: 6px;
104
+ --grim-radius-lg: 8px;
105
+ --grim-radius-xl: 10px;
106
+ --grim-radius-pill: 999px;
107
+
108
+ /* ---- Border ----------------------------------------------------
109
+ One width carries every rule in the UI. It is a token so a consumer
110
+ can thicken every seam at once, and so the two places that pull a
111
+ border into its neighbour can say `calc(-1 * …)` instead of
112
+ hardcoding the negation of a number declared elsewhere. */
113
+ --grim-border-width: 1px;
114
+
115
+ /* ---- Motion ---------------------------------------------------- */
116
+ --grim-duration-fast: 120ms;
117
+ --grim-duration-base: 160ms;
118
+ --grim-duration-slow: 200ms;
119
+
120
+ /* ---- Elevation -------------------------------------------------
121
+ A drop shadow is a black scrim rather than a palette colour: it reads
122
+ correctly over either scheme, which is why these are not declared
123
+ twice. Overriding one is how a consumer changes elevation. */
124
+ --grim-shadow-raised: 0 6px 20px rgb(0 0 0 / 18%);
125
+ --grim-shadow-overlay: 0 8px 24px rgb(0 0 0 / 18%);
126
+ }
127
+
128
+ /* Colour only. Every other family above is a measurement that does not
129
+ change with the scheme, and redeclaring one here would be a second
130
+ source of truth for the same value. */
131
+ [data-theme="dark"] {
132
+ --grim-color-bg: #16151a;
133
+ --grim-color-fg: #e8e6e1;
134
+ --grim-color-muted: #98948b;
135
+ --grim-color-card: #201f26;
136
+ --grim-color-border: #35333d;
137
+ --grim-color-chip-bg: #2a2931;
138
+
139
+ --grim-color-accent: #a58cf0;
140
+ --grim-color-accent-soft: #2c2540;
141
+ --grim-color-on-accent: #ffffff;
142
+
143
+ --grim-color-kind-skill: #a58cf0;
144
+ --grim-color-kind-rule: #4cc2a9;
145
+ --grim-color-kind-agent: #e0a35c;
146
+ --grim-color-kind-mcp: #6fa8f5;
147
+ --grim-color-kind-bundle: #e07ba6;
148
+
149
+ /* ~7.6:1 on --grim-color-card (#201f26) */
150
+ --grim-color-deprecated: #e3a640;
151
+ --grim-color-banner-bg: #4a3b0a;
152
+ --grim-color-banner-fg: #f0d78c;
153
+ --grim-color-banner-border: #8a6d1a;
154
+
155
+ color-scheme: dark;
156
+ }
157
+ }
@@ -9,6 +9,17 @@
9
9
  export declare const USER_AGENT = "grim-indexer-bot";
10
10
  export declare const TIMEOUT_MS = 30000;
11
11
  export declare const MAX_RESPONSE_BYTES: number;
12
+ /**
13
+ * The cap for the two calls that legitimately read more than a small JSON
14
+ * document: the enrichment checkpoint, which is the whole sidecar tree, and a
15
+ * ratings page, which carries every thread's full body — including bodies this
16
+ * bot did not write.
17
+ *
18
+ * The same ceiling `enrich` already accepts from one `grim` subprocess
19
+ * (`MAX_OUTPUT_BYTES`), so the largest thing this toolchain holds in memory is
20
+ * one number rather than three.
21
+ */
22
+ export declare const LARGE_RESPONSE_BYTES: number;
12
23
  export interface HttpResponse {
13
24
  /** HTTP status, or 0 when the request never completed. */
14
25
  status: number;
@@ -19,6 +30,18 @@ export interface HttpResponse {
19
30
  export interface RequestOptions {
20
31
  method?: string;
21
32
  body?: string;
33
+ /**
34
+ * Response-size cap for this call, in bytes. Defaults to
35
+ * [`MAX_RESPONSE_BYTES`], so every call site that does not ask keeps the
36
+ * reply it already got, byte for byte.
37
+ *
38
+ * Per-call because the default is sized for a small JSON document, and two
39
+ * callers legitimately read more: the enrichment checkpoint is the whole
40
+ * sidecar tree, and a ratings page carries every thread's full body. Both
41
+ * used to surface as `{status: 0}` — indistinguishable from a transport
42
+ * failure, which is how an oversized page got reported as one.
43
+ */
44
+ maxBytes?: number;
22
45
  }
23
46
  export declare function request(url: string, headers?: Record<string, string>, options?: RequestOptions): Promise<HttpResponse>;
24
47
  /** `request` plus a `JSON.parse` that yields `null` instead of throwing. */
@@ -1 +1 @@
1
- {"version":3,"file":"http.d.ts","sourceRoot":"","sources":["../../../src/validate/adapters/http.ts"],"names":[],"mappings":"AAGA;;;;;;;GAOG;AAEH,eAAO,MAAM,UAAU,qBAAqB,CAAC;AAC7C,eAAO,MAAM,UAAU,QAAS,CAAC;AACjC,eAAO,MAAM,kBAAkB,QAAc,CAAC;AAE9C,MAAM,WAAW,YAAY;IAC3B,0DAA0D;IAC1D,MAAM,EAAE,MAAM,CAAC;IACf,IAAI,EAAE,MAAM,CAAC;IACb,MAAM,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAAC;CAC9B;AA4BD,0EAA0E;AAC1E,MAAM,WAAW,cAAc;IAC7B,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,IAAI,CAAC,EAAE,MAAM,CAAC;CACf;AAED,wBAAsB,OAAO,CAC3B,GAAG,EAAE,MAAM,EACX,OAAO,GAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAM,EACpC,OAAO,GAAE,cAAmB,GAC3B,OAAO,CAAC,YAAY,CAAC,CAwBvB;AAED,4EAA4E;AAC5E,wBAAsB,WAAW,CAC/B,GAAG,EAAE,MAAM,EACX,OAAO,GAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAM,GACnC,OAAO,CAAC;IAAE,MAAM,EAAE,MAAM,CAAC;IAAC,IAAI,EAAE,OAAO,CAAA;CAAE,CAAC,CAQ5C"}
1
+ {"version":3,"file":"http.d.ts","sourceRoot":"","sources":["../../../src/validate/adapters/http.ts"],"names":[],"mappings":"AAGA;;;;;;;GAOG;AAEH,eAAO,MAAM,UAAU,qBAAqB,CAAC;AAC7C,eAAO,MAAM,UAAU,QAAS,CAAC;AACjC,eAAO,MAAM,kBAAkB,QAAc,CAAC;AAE9C;;;;;;;;;GASG;AACH,eAAO,MAAM,oBAAoB,QAAmB,CAAC;AAErD,MAAM,WAAW,YAAY;IAC3B,0DAA0D;IAC1D,MAAM,EAAE,MAAM,CAAC;IACf,IAAI,EAAE,MAAM,CAAC;IACb,MAAM,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAAC;CAC9B;AA4BD,0EAA0E;AAC1E,MAAM,WAAW,cAAc;IAC7B,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,IAAI,CAAC,EAAE,MAAM,CAAC;IACd;;;;;;;;;;OAUG;IACH,QAAQ,CAAC,EAAE,MAAM,CAAC;CACnB;AAED,wBAAsB,OAAO,CAC3B,GAAG,EAAE,MAAM,EACX,OAAO,GAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAM,EACpC,OAAO,GAAE,cAAmB,GAC3B,OAAO,CAAC,YAAY,CAAC,CAwBvB;AAED,4EAA4E;AAC5E,wBAAsB,WAAW,CAC/B,GAAG,EAAE,MAAM,EACX,OAAO,GAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAM,GACnC,OAAO,CAAC;IAAE,MAAM,EAAE,MAAM,CAAC;IAAC,IAAI,EAAE,OAAO,CAAA;CAAE,CAAC,CAQ5C"}
@@ -11,6 +11,17 @@
11
11
  export const USER_AGENT = "grim-indexer-bot";
12
12
  export const TIMEOUT_MS = 30_000;
13
13
  export const MAX_RESPONSE_BYTES = 1024 * 1024;
14
+ /**
15
+ * The cap for the two calls that legitimately read more than a small JSON
16
+ * document: the enrichment checkpoint, which is the whole sidecar tree, and a
17
+ * ratings page, which carries every thread's full body — including bodies this
18
+ * bot did not write.
19
+ *
20
+ * The same ceiling `enrich` already accepts from one `grim` subprocess
21
+ * (`MAX_OUTPUT_BYTES`), so the largest thing this toolchain holds in memory is
22
+ * one number rather than three.
23
+ */
24
+ export const LARGE_RESPONSE_BYTES = 16 * 1024 * 1024;
14
25
  async function readCapped(response, cap) {
15
26
  if (!response.body)
16
27
  return "";
@@ -51,7 +62,7 @@ export async function request(url, headers = {}, options = {}) {
51
62
  redirect: "manual",
52
63
  signal: AbortSignal.timeout(TIMEOUT_MS),
53
64
  });
54
- const body = await readCapped(response, MAX_RESPONSE_BYTES);
65
+ const body = await readCapped(response, options.maxBytes ?? MAX_RESPONSE_BYTES);
55
66
  if (body === null)
56
67
  return none;
57
68
  return {
@@ -1 +1 @@
1
- {"version":3,"file":"http.js","sourceRoot":"","sources":["../../../src/validate/adapters/http.ts"],"names":[],"mappings":"AAAA,sCAAsC;AACtC,sCAAsC;AAEtC;;;;;;;GAOG;AAEH,MAAM,CAAC,MAAM,UAAU,GAAG,kBAAkB,CAAC;AAC7C,MAAM,CAAC,MAAM,UAAU,GAAG,MAAM,CAAC;AACjC,MAAM,CAAC,MAAM,kBAAkB,GAAG,IAAI,GAAG,IAAI,CAAC;AAS9C,KAAK,UAAU,UAAU,CACvB,QAAkB,EAClB,GAAW;IAEX,IAAI,CAAC,QAAQ,CAAC,IAAI;QAAE,OAAO,EAAE,CAAC;IAC9B,MAAM,MAAM,GAAG,QAAQ,CAAC,IAAI,CAAC,SAAS,EAAE,CAAC;IACzC,MAAM,MAAM,GAAiB,EAAE,CAAC;IAChC,IAAI,IAAI,GAAG,CAAC,CAAC;IACb,IAAI,CAAC;QACH,SAAS,CAAC;YACR,MAAM,EAAE,IAAI,EAAE,KAAK,EAAE,GAAG,MAAM,MAAM,CAAC,IAAI,EAAE,CAAC;YAC5C,IAAI,IAAI;gBAAE,MAAM;YAChB,IAAI,CAAC,KAAK;gBAAE,SAAS;YACrB,IAAI,IAAI,KAAK,CAAC,UAAU,CAAC;YACzB,IAAI,IAAI,GAAG,GAAG,EAAE,CAAC;gBACf,MAAM,MAAM,CAAC,MAAM,EAAE,CAAC;gBACtB,OAAO,IAAI,CAAC;YACd,CAAC;YACD,MAAM,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;QACrB,CAAC;IACH,CAAC;YAAS,CAAC;QACT,MAAM,CAAC,WAAW,EAAE,CAAC;IACvB,CAAC;IACD,OAAO,MAAM,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC,QAAQ,CAAC,MAAM,CAAC,CAAC;AAChD,CAAC;AAQD,MAAM,CAAC,KAAK,UAAU,OAAO,CAC3B,GAAW,EACX,UAAkC,EAAE,EACpC,UAA0B,EAAE;IAE5B,MAAM,IAAI,GAAiB,EAAE,MAAM,EAAE,CAAC,EAAE,IAAI,EAAE,EAAE,EAAE,MAAM,EAAE,GAAG,EAAE,CAAC,EAAE,EAAE,CAAC;IACrE,IAAI,CAAC;QACH,MAAM,QAAQ,GAAG,MAAM,KAAK,CAAC,GAAG,EAAE;YAChC,OAAO,EAAE,EAAE,YAAY,EAAE,UAAU,EAAE,GAAG,OAAO,EAAE;YACjD,uEAAuE;YACvE,sEAAsE;YACtE,MAAM,EAAE,OAAO,CAAC,MAAM;YACtB,IAAI,EAAE,OAAO,CAAC,IAAI;YAClB,yEAAyE;YACzE,wEAAwE;YACxE,QAAQ,EAAE,QAAQ;YAClB,MAAM,EAAE,WAAW,CAAC,OAAO,CAAC,UAAU,CAAC;SACxC,CAAC,CAAC;QACH,MAAM,IAAI,GAAG,MAAM,UAAU,CAAC,QAAQ,EAAE,kBAAkB,CAAC,CAAC;QAC5D,IAAI,IAAI,KAAK,IAAI;YAAE,OAAO,IAAI,CAAC;QAC/B,OAAO;YACL,MAAM,EAAE,QAAQ,CAAC,MAAM;YACvB,IAAI;YACJ,MAAM,EAAE,CAAC,IAAI,EAAE,EAAE,CAAC,QAAQ,CAAC,OAAO,CAAC,GAAG,CAAC,IAAI,CAAC,IAAI,EAAE;SACnD,CAAC;IACJ,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,IAAI,CAAC;IACd,CAAC;AACH,CAAC;AAED,4EAA4E;AAC5E,MAAM,CAAC,KAAK,UAAU,WAAW,CAC/B,GAAW,EACX,UAAkC,EAAE;IAEpC,MAAM,QAAQ,GAAG,MAAM,OAAO,CAAC,GAAG,EAAE,OAAO,CAAC,CAAC;IAC7C,IAAI,QAAQ,CAAC,IAAI,KAAK,EAAE;QAAE,OAAO,EAAE,MAAM,EAAE,QAAQ,CAAC,MAAM,EAAE,IAAI,EAAE,IAAI,EAAE,CAAC;IACzE,IAAI,CAAC;QACH,OAAO,EAAE,MAAM,EAAE,QAAQ,CAAC,MAAM,EAAE,IAAI,EAAE,IAAI,CAAC,KAAK,CAAC,QAAQ,CAAC,IAAI,CAAC,EAAE,CAAC;IACtE,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,EAAE,MAAM,EAAE,QAAQ,CAAC,MAAM,EAAE,IAAI,EAAE,IAAI,EAAE,CAAC;IACjD,CAAC;AACH,CAAC"}
1
+ {"version":3,"file":"http.js","sourceRoot":"","sources":["../../../src/validate/adapters/http.ts"],"names":[],"mappings":"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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@grimoire-rs/indexer",
3
- "version": "0.4.0",
3
+ "version": "0.4.2",
4
4
  "description": "CLI + Astro integration for running your own grim package index",
5
5
  "type": "module",
6
6
  "license": "Apache-2.0",