@grimoire-rs/indexer 0.3.3 → 0.4.1
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 +220 -9
- package/dist/ci.d.ts +6 -1
- package/dist/ci.d.ts.map +1 -1
- package/dist/ci.js +32 -1
- package/dist/ci.js.map +1 -1
- package/dist/cli/build.d.ts.map +1 -1
- package/dist/cli/build.js +3 -1
- package/dist/cli/build.js.map +1 -1
- package/dist/cli/ci.d.ts.map +1 -1
- package/dist/cli/ci.js +5 -1
- package/dist/cli/ci.js.map +1 -1
- package/dist/cli/enrich.d.ts +1 -0
- package/dist/cli/enrich.d.ts.map +1 -1
- package/dist/cli/enrich.js +38 -0
- package/dist/cli/enrich.js.map +1 -1
- package/dist/cli/init.d.ts.map +1 -1
- package/dist/cli/init.js +17 -3
- package/dist/cli/init.js.map +1 -1
- package/dist/cli/main.d.ts.map +1 -1
- package/dist/cli/main.js +9 -0
- package/dist/cli/main.js.map +1 -1
- package/dist/cli/ratings.d.ts +3 -0
- package/dist/cli/ratings.d.ts.map +1 -0
- package/dist/cli/ratings.js +160 -0
- package/dist/cli/ratings.js.map +1 -0
- package/dist/config.d.ts +19 -0
- package/dist/config.d.ts.map +1 -1
- package/dist/config.js +47 -1
- package/dist/config.js.map +1 -1
- package/dist/data/index.d.ts.map +1 -1
- package/dist/data/index.js +13 -0
- package/dist/data/index.js.map +1 -1
- package/dist/enrich/checkpoint.d.ts +53 -0
- package/dist/enrich/checkpoint.d.ts.map +1 -0
- package/dist/enrich/checkpoint.js +296 -0
- package/dist/enrich/checkpoint.js.map +1 -0
- package/dist/enrich/index.d.ts +14 -0
- package/dist/enrich/index.d.ts.map +1 -1
- package/dist/enrich/index.js +77 -4
- package/dist/enrich/index.js.map +1 -1
- package/dist/ratings/budget.d.ts +16 -0
- package/dist/ratings/budget.d.ts.map +1 -0
- package/dist/ratings/budget.js +7 -0
- package/dist/ratings/budget.js.map +1 -0
- package/dist/ratings/config.d.ts +56 -0
- package/dist/ratings/config.d.ts.map +1 -0
- package/dist/ratings/config.js +86 -0
- package/dist/ratings/config.js.map +1 -0
- package/dist/ratings/marker.d.ts +105 -0
- package/dist/ratings/marker.d.ts.map +1 -0
- package/dist/ratings/marker.js +141 -0
- package/dist/ratings/marker.js.map +1 -0
- package/dist/ratings/paging.d.ts +16 -0
- package/dist/ratings/paging.d.ts.map +1 -0
- package/dist/ratings/paging.js +36 -0
- package/dist/ratings/paging.js.map +1 -0
- package/dist/ratings/provider.d.ts +100 -0
- package/dist/ratings/provider.d.ts.map +1 -0
- package/dist/ratings/provider.js +203 -0
- package/dist/ratings/provider.js.map +1 -0
- package/dist/ratings/provider_github.d.ts +3 -0
- package/dist/ratings/provider_github.d.ts.map +1 -0
- package/dist/ratings/provider_github.js +135 -0
- package/dist/ratings/provider_github.js.map +1 -0
- package/dist/ratings/provider_gitlab.d.ts +3 -0
- package/dist/ratings/provider_gitlab.d.ts.map +1 -0
- package/dist/ratings/provider_gitlab.js +153 -0
- package/dist/ratings/provider_gitlab.js.map +1 -0
- package/dist/ratings/provider_memory.d.ts +28 -0
- package/dist/ratings/provider_memory.d.ts.map +1 -0
- package/dist/ratings/provider_memory.js +49 -0
- package/dist/ratings/provider_memory.js.map +1 -0
- package/dist/ratings/reconcile.d.ts +47 -0
- package/dist/ratings/reconcile.d.ts.map +1 -0
- package/dist/ratings/reconcile.js +133 -0
- package/dist/ratings/reconcile.js.map +1 -0
- package/dist/ratings/seed.d.ts +60 -0
- package/dist/ratings/seed.d.ts.map +1 -0
- package/dist/ratings/seed.js +132 -0
- package/dist/ratings/seed.js.map +1 -0
- package/dist/renderer/astro/components/Catalog.d.ts +2 -0
- package/dist/renderer/astro/components/Catalog.js +65 -16
- package/dist/renderer/astro/components/Catalog.tsx +119 -26
- package/dist/renderer/astro/components/CommandField.astro +1 -0
- package/dist/renderer/astro/components/VersionMenu.astro +31 -23
- package/dist/renderer/astro/layouts/Base.astro +218 -263
- package/dist/renderer/astro/lib/catalog.d.ts +25 -0
- package/dist/renderer/astro/lib/catalog.js +46 -0
- package/dist/renderer/astro/lib/catalog.ts +49 -0
- package/dist/renderer/astro/pages/p/[...slug].astro +238 -85
- package/dist/renderer/astro/styles/tokens.css +157 -0
- package/dist/renderer/index.d.ts.map +1 -1
- package/dist/renderer/index.js +101 -2
- package/dist/renderer/index.js.map +1 -1
- package/dist/renderer/types.d.ts +41 -0
- package/dist/renderer/types.d.ts.map +1 -1
- package/dist/validate/adapters/http.d.ts +29 -1
- package/dist/validate/adapters/http.d.ts.map +1 -1
- package/dist/validate/adapters/http.js +17 -2
- package/dist/validate/adapters/http.js.map +1 -1
- package/package.json +1 -1
- package/templates/ci/github-enrich.yml +9 -1
- package/templates/ci/github-pages.yml +11 -5
- package/templates/ci/github-ratings-seed.yml +88 -0
- package/templates/ci/github-ratings.yml +51 -0
- package/templates/ci/gitlab-ci.yml +3 -3
- package/templates/ci/gitlab-enrich.sh +10 -1
- package/templates/ci/gitlab-ratings-seed.sh +83 -0
- package/templates/ci/gitlab-ratings.yml +37 -0
- package/templates/gitignore +8 -0
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
/** Published name, and the path `--seed` appends to `site`. */
|
|
2
|
+
export declare const CHECKPOINT_FILE = "enrich.json";
|
|
3
|
+
/**
|
|
4
|
+
* The document version.
|
|
5
|
+
*
|
|
6
|
+
* Worth carrying even though nothing outside this package reads it: the reader
|
|
7
|
+
* is always a *different build of this package than the writer* — last week's
|
|
8
|
+
* deploy wrote the file this week's run seeds from — so a format change really
|
|
9
|
+
* does cross a version boundary. An unrecognised version seeds nothing rather
|
|
10
|
+
* than guessing.
|
|
11
|
+
*/
|
|
12
|
+
export declare const SCHEMA_VERSION = 1;
|
|
13
|
+
/** What `compileIndex` hands over — it has already walked the tree. */
|
|
14
|
+
export interface CheckpointEntry {
|
|
15
|
+
namespace: string;
|
|
16
|
+
name: string;
|
|
17
|
+
/** `enrich/<namespace>/<name>`, which holds `data.json` and its companions. */
|
|
18
|
+
dir: string;
|
|
19
|
+
}
|
|
20
|
+
/**
|
|
21
|
+
* Publish the sidecar tree as one document.
|
|
22
|
+
*
|
|
23
|
+
* Called from `compileIndex`, which has already resolved every namespace and
|
|
24
|
+
* sidecar directory — re-walking the tree here would be a second derivation of
|
|
25
|
+
* the same answer.
|
|
26
|
+
*
|
|
27
|
+
* Base64 for every member, markdown included. `decode()` in `./index.ts`
|
|
28
|
+
* already treats a companion member as arbitrary bytes off a registry, so
|
|
29
|
+
* inlining the markdown as UTF-8 would save a fifth of the file in exchange
|
|
30
|
+
* for a validity branch on data that is not guaranteed to be text.
|
|
31
|
+
*/
|
|
32
|
+
export declare function packCheckpoint(outDir: string, entries: readonly CheckpointEntry[]): void;
|
|
33
|
+
export interface SeedOptions {
|
|
34
|
+
/** Index repo root — holds `index/`, gains `enrich/`. */
|
|
35
|
+
root: string;
|
|
36
|
+
/** Full URL of the published checkpoint, `<site>/enrich.json`. */
|
|
37
|
+
url: string;
|
|
38
|
+
}
|
|
39
|
+
/**
|
|
40
|
+
* Restore `enrich/` from the published checkpoint, and report how many
|
|
41
|
+
* packages it seeded.
|
|
42
|
+
*
|
|
43
|
+
* Never throws. Every failure path warns and returns 0, which leaves the run
|
|
44
|
+
* doing exactly what it did before this existed.
|
|
45
|
+
*
|
|
46
|
+
* The iteration is over the **index**, never the document's keys: a package
|
|
47
|
+
* the index does not list is not seeded at all, so a key naming a path outside
|
|
48
|
+
* the tree describes a package that is simply never looked up. A sidecar that
|
|
49
|
+
* already exists on disk always wins — the checkpoint fills gaps, it does not
|
|
50
|
+
* overwrite a committed or freshly-enriched tree.
|
|
51
|
+
*/
|
|
52
|
+
export declare function seedFromCheckpoint(opts: SeedOptions): Promise<number>;
|
|
53
|
+
//# sourceMappingURL=checkpoint.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"checkpoint.d.ts","sourceRoot":"","sources":["../../src/enrich/checkpoint.ts"],"names":[],"mappings":"AAuCA,+DAA+D;AAC/D,eAAO,MAAM,eAAe,gBAAgB,CAAC;AAE7C;;;;;;;;GAQG;AACH,eAAO,MAAM,cAAc,IAAI,CAAC;AAwBhC,uEAAuE;AACvE,MAAM,WAAW,eAAe;IAC9B,SAAS,EAAE,MAAM,CAAC;IAClB,IAAI,EAAE,MAAM,CAAC;IACb,+EAA+E;IAC/E,GAAG,EAAE,MAAM,CAAC;CACb;AAoBD;;;;;;;;;;;GAWG;AACH,wBAAgB,cAAc,CAAC,MAAM,EAAE,MAAM,EAAE,OAAO,EAAE,SAAS,eAAe,EAAE,GAAG,IAAI,CA8CxF;AAED,MAAM,WAAW,WAAW;IAC1B,yDAAyD;IACzD,IAAI,EAAE,MAAM,CAAC;IACb,kEAAkE;IAClE,GAAG,EAAE,MAAM,CAAC;CACb;AAiID;;;;;;;;;;;;GAYG;AACH,wBAAsB,kBAAkB,CAAC,IAAI,EAAE,WAAW,GAAG,OAAO,CAAC,MAAM,CAAC,CAwC3E"}
|
|
@@ -0,0 +1,296 @@
|
|
|
1
|
+
// SPDX-License-Identifier: Apache-2.0
|
|
2
|
+
// Copyright 2026 The Grimoire Authors
|
|
3
|
+
/**
|
|
4
|
+
* The enrichment checkpoint — `enrich.json`, published beside `all.json` and
|
|
5
|
+
* read back by `grim-indexer enrich --seed`.
|
|
6
|
+
*
|
|
7
|
+
* `enrich` skips work by digest, and every one of those comparisons reads
|
|
8
|
+
* `enrich/<ns>/<name>/data.json` off disk. The scaffolded CI never has that
|
|
9
|
+
* file: the sidecars live only in the deploy job's workspace and are committed
|
|
10
|
+
* nowhere, so every deploy re-downloaded every README, changelog, logo and
|
|
11
|
+
* contents payload, and — worse — re-stamped `updated` on every artifact that
|
|
12
|
+
* carries no `created` of its own.
|
|
13
|
+
*
|
|
14
|
+
* So the live site is the checkpoint, exactly as it already is for ratings
|
|
15
|
+
* (`../ratings/seed.ts`): the build publishes the sidecar tree, and the next
|
|
16
|
+
* run seeds itself from what it published last time. `site` in
|
|
17
|
+
* `index.config.json` is the same validated base URL both read.
|
|
18
|
+
*
|
|
19
|
+
* **This document is not a read contract.** Unlike `stats.json`, nothing
|
|
20
|
+
* outside this package reads it and nothing should code against it.
|
|
21
|
+
*
|
|
22
|
+
* Failure is never fatal here. An unreachable, oversized, unparseable or
|
|
23
|
+
* unrecognised checkpoint warns and seeds nothing, which degrades to exactly
|
|
24
|
+
* the behaviour that shipped before it existed — a full re-enrich. `loadSeed`
|
|
25
|
+
* takes the opposite line because an empty ratings seed *wipes* every
|
|
26
|
+
* published rating; a missing enrich seed only costs bandwidth.
|
|
27
|
+
*/
|
|
28
|
+
import fs from "node:fs";
|
|
29
|
+
import path from "node:path";
|
|
30
|
+
// The cycle is deliberate and benign, the same shape `ratings/provider.ts`
|
|
31
|
+
// documents: `compileIndex` writes the checkpoint, and the seed reads the
|
|
32
|
+
// index tree back to decide what it is allowed to hydrate. Everything crossing
|
|
33
|
+
// is a hoisted function declaration, referenced only from inside a closure.
|
|
34
|
+
import { findMetadataFiles, namespaceOf } from "../data/index.js";
|
|
35
|
+
import { LOGO_EXT, clearCompanions, clearContents } from "./index.js";
|
|
36
|
+
import { LARGE_RESPONSE_BYTES, request } from "../validate/adapters/http.js";
|
|
37
|
+
/** Published name, and the path `--seed` appends to `site`. */
|
|
38
|
+
export const CHECKPOINT_FILE = "enrich.json";
|
|
39
|
+
/**
|
|
40
|
+
* The document version.
|
|
41
|
+
*
|
|
42
|
+
* Worth carrying even though nothing outside this package reads it: the reader
|
|
43
|
+
* is always a *different build of this package than the writer* — last week's
|
|
44
|
+
* deploy wrote the file this week's run seeds from — so a format change really
|
|
45
|
+
* does cross a version boundary. An unrecognised version seeds nothing rather
|
|
46
|
+
* than guessing.
|
|
47
|
+
*/
|
|
48
|
+
export const SCHEMA_VERSION = 1;
|
|
49
|
+
/**
|
|
50
|
+
* Every sidecar file the checkpoint carries, and the only names hydration will
|
|
51
|
+
* write. The set is the one `clearCompanions` and `clearContents` already
|
|
52
|
+
* enforce; a logo is handled separately because its extension comes from the
|
|
53
|
+
* registry.
|
|
54
|
+
*/
|
|
55
|
+
const COMPANION_FILES = ["readme.md", "changelog.md", "contents.md", "contents.json"];
|
|
56
|
+
/**
|
|
57
|
+
* The `logo.<ext>` in a sidecar directory, if there is exactly one whose
|
|
58
|
+
* extension is acceptable.
|
|
59
|
+
*
|
|
60
|
+
* "Exactly one" is load-bearing on the read side: two logos mean the directory
|
|
61
|
+
* disagrees with itself about which one `data.logo` names, and a checkpoint
|
|
62
|
+
* that cannot say which file it meant has not earned the digest that would
|
|
63
|
+
* stop the companion being re-fetched.
|
|
64
|
+
*/
|
|
65
|
+
function soleLogo(dir) {
|
|
66
|
+
if (!fs.existsSync(dir))
|
|
67
|
+
return undefined;
|
|
68
|
+
const found = fs
|
|
69
|
+
.readdirSync(dir)
|
|
70
|
+
.filter((entry) => entry.toLowerCase().startsWith("logo."))
|
|
71
|
+
.filter((entry) => LOGO_EXT.test(entry.slice(entry.indexOf(".") + 1)));
|
|
72
|
+
return found.length === 1 ? found[0] : undefined;
|
|
73
|
+
}
|
|
74
|
+
/**
|
|
75
|
+
* Publish the sidecar tree as one document.
|
|
76
|
+
*
|
|
77
|
+
* Called from `compileIndex`, which has already resolved every namespace and
|
|
78
|
+
* sidecar directory — re-walking the tree here would be a second derivation of
|
|
79
|
+
* the same answer.
|
|
80
|
+
*
|
|
81
|
+
* Base64 for every member, markdown included. `decode()` in `./index.ts`
|
|
82
|
+
* already treats a companion member as arbitrary bytes off a registry, so
|
|
83
|
+
* inlining the markdown as UTF-8 would save a fifth of the file in exchange
|
|
84
|
+
* for a validity branch on data that is not guaranteed to be text.
|
|
85
|
+
*/
|
|
86
|
+
export function packCheckpoint(outDir, entries) {
|
|
87
|
+
const packages = {};
|
|
88
|
+
for (const { namespace, name, dir } of entries) {
|
|
89
|
+
const dataFile = path.join(dir, "data.json");
|
|
90
|
+
if (!fs.existsSync(dataFile))
|
|
91
|
+
continue;
|
|
92
|
+
let data;
|
|
93
|
+
try {
|
|
94
|
+
data = JSON.parse(fs.readFileSync(dataFile, "utf8"));
|
|
95
|
+
}
|
|
96
|
+
catch {
|
|
97
|
+
// A sidecar this build could not read is a sidecar the next run should
|
|
98
|
+
// fetch again, not one to publish half of.
|
|
99
|
+
continue;
|
|
100
|
+
}
|
|
101
|
+
const files = {};
|
|
102
|
+
for (const member of COMPANION_FILES) {
|
|
103
|
+
const file = path.join(dir, member);
|
|
104
|
+
if (fs.existsSync(file))
|
|
105
|
+
files[member] = fs.readFileSync(file).toString("base64");
|
|
106
|
+
}
|
|
107
|
+
const logo = soleLogo(dir);
|
|
108
|
+
if (logo)
|
|
109
|
+
files[logo.toLowerCase()] = fs.readFileSync(path.join(dir, logo)).toString("base64");
|
|
110
|
+
const entry = { data };
|
|
111
|
+
if (Object.keys(files).length > 0)
|
|
112
|
+
entry.files = files;
|
|
113
|
+
packages[`${namespace}/${name}`] = entry;
|
|
114
|
+
}
|
|
115
|
+
// Nothing enriched yet. An absent document and an empty one behave
|
|
116
|
+
// identically to the reader, and not writing keeps `dist/` honest about
|
|
117
|
+
// what this build actually had.
|
|
118
|
+
if (Object.keys(packages).length === 0)
|
|
119
|
+
return;
|
|
120
|
+
const document = { schema_version: SCHEMA_VERSION, packages };
|
|
121
|
+
const body = JSON.stringify(document) + "\n";
|
|
122
|
+
if (Buffer.byteLength(body) > LARGE_RESPONSE_BYTES) {
|
|
123
|
+
// Silence here would be a permanent, invisible regression: the file
|
|
124
|
+
// publishes fine and every future `--seed` refuses to read it, so the
|
|
125
|
+
// index quietly goes back to re-downloading everything for ever.
|
|
126
|
+
console.warn(`warn: ${CHECKPOINT_FILE} is larger than the ${LARGE_RESPONSE_BYTES} byte cap \`enrich --seed\` reads — ` +
|
|
127
|
+
`it will be published but not read back`);
|
|
128
|
+
}
|
|
129
|
+
fs.writeFileSync(path.join(outDir, CHECKPOINT_FILE), body);
|
|
130
|
+
}
|
|
131
|
+
/** A parsed document, or `null` with the reason already reported. */
|
|
132
|
+
async function loadCheckpoint(url) {
|
|
133
|
+
const response = await request(url, {}, { maxBytes: LARGE_RESPONSE_BYTES });
|
|
134
|
+
if (response.status === 404) {
|
|
135
|
+
// The first deploy, and the ordinary case for a young index.
|
|
136
|
+
console.log(`no ${CHECKPOINT_FILE} published yet — enriching everything`);
|
|
137
|
+
return null;
|
|
138
|
+
}
|
|
139
|
+
if (response.status < 200 || response.status >= 300) {
|
|
140
|
+
// `status: 0` lands here too: it is the transport catch and the over-cap
|
|
141
|
+
// read. So does a 3xx — `request()` refuses to follow redirects, so a site
|
|
142
|
+
// whose canonical host redirects pays for the checkpoint on every deploy.
|
|
143
|
+
// Deliberately not guarded with a cross-check against the forge's own
|
|
144
|
+
// Pages URL the way the ratings seed is: a wrong ratings seed empties
|
|
145
|
+
// every published rating, where a missed enrich seed only re-downloads.
|
|
146
|
+
console.warn(`warn: could not read ${url} (HTTP ${response.status}) — enriching everything`);
|
|
147
|
+
return null;
|
|
148
|
+
}
|
|
149
|
+
let parsed;
|
|
150
|
+
try {
|
|
151
|
+
parsed = JSON.parse(response.body);
|
|
152
|
+
}
|
|
153
|
+
catch (err) {
|
|
154
|
+
console.warn(`warn: ${url} did not parse (${err.message}) — enriching everything`);
|
|
155
|
+
return null;
|
|
156
|
+
}
|
|
157
|
+
if (parsed === null || typeof parsed !== "object" || Array.isArray(parsed)) {
|
|
158
|
+
console.warn(`warn: ${url} is not a JSON object — enriching everything`);
|
|
159
|
+
return null;
|
|
160
|
+
}
|
|
161
|
+
const doc = parsed;
|
|
162
|
+
if (doc.schema_version !== SCHEMA_VERSION) {
|
|
163
|
+
console.warn(`warn: ${url} declares schema_version ${JSON.stringify(doc.schema_version)}, ` +
|
|
164
|
+
`this build reads ${SCHEMA_VERSION} — enriching everything`);
|
|
165
|
+
return null;
|
|
166
|
+
}
|
|
167
|
+
const packages = doc.packages;
|
|
168
|
+
if (packages === null || typeof packages !== "object" || Array.isArray(packages)) {
|
|
169
|
+
console.warn(`warn: ${url} carries no packages object — enriching everything`);
|
|
170
|
+
return null;
|
|
171
|
+
}
|
|
172
|
+
return { schema_version: SCHEMA_VERSION, packages: packages };
|
|
173
|
+
}
|
|
174
|
+
/**
|
|
175
|
+
* Write one package's files, and return the names that actually landed.
|
|
176
|
+
*
|
|
177
|
+
* Filenames are chosen from [`COMPANION_FILES`] and `logo.<ext>` gated on
|
|
178
|
+
* [`LOGO_EXT`] — never echoed from the document — and the directory comes from
|
|
179
|
+
* the index's own tree, so no part of a path here is attacker-controlled.
|
|
180
|
+
*/
|
|
181
|
+
function hydrateFiles(dir, files) {
|
|
182
|
+
const written = new Set();
|
|
183
|
+
fs.mkdirSync(dir, { recursive: true });
|
|
184
|
+
clearCompanions(dir);
|
|
185
|
+
clearContents(dir);
|
|
186
|
+
for (const [name, content] of Object.entries(files)) {
|
|
187
|
+
if (typeof content !== "string")
|
|
188
|
+
continue;
|
|
189
|
+
const low = name.toLowerCase();
|
|
190
|
+
const logo = low.startsWith("logo.") && LOGO_EXT.test(low.slice(low.indexOf(".") + 1));
|
|
191
|
+
if (!COMPANION_FILES.includes(low) && !logo)
|
|
192
|
+
continue;
|
|
193
|
+
fs.writeFileSync(path.join(dir, low), Buffer.from(content, "base64"));
|
|
194
|
+
written.add(low);
|
|
195
|
+
}
|
|
196
|
+
return written;
|
|
197
|
+
}
|
|
198
|
+
/**
|
|
199
|
+
* Bring a hydrated `data.json` back into agreement with what is on disk, and
|
|
200
|
+
* drop the digest that guards anything it got wrong.
|
|
201
|
+
*
|
|
202
|
+
* Recomputing the flags alone is not enough, and this is the case worth
|
|
203
|
+
* catching: a document that claims `hasReadme: true`, carries no `readme.md`
|
|
204
|
+
* and carries a `descDigest` that still matches upstream would leave the site
|
|
205
|
+
* *self-consistent* — no README, no flag saying there is one — while
|
|
206
|
+
* `enrichOne`'s digest comparison keeps skipping the download. The README
|
|
207
|
+
* would never come back until the artifact happened to move on its own.
|
|
208
|
+
*
|
|
209
|
+
* Dropping the digest costs nothing and needs no new machinery: an absent
|
|
210
|
+
* `descDigest` makes the probe comparison false and takes the download branch,
|
|
211
|
+
* and an absent `contentDigest` makes `moved` true, re-stamping `updated` —
|
|
212
|
+
* which is right, because an entry that misdescribed its own files has not
|
|
213
|
+
* earned trust in its date either. A healthy package pays nothing.
|
|
214
|
+
*/
|
|
215
|
+
function reconcile(data, dir, namespace, name, written) {
|
|
216
|
+
const logoFile = soleLogo(dir);
|
|
217
|
+
const logo = logoFile
|
|
218
|
+
? `/logos/${namespace}/${name}.${logoFile.slice(logoFile.indexOf(".") + 1)}`
|
|
219
|
+
: undefined;
|
|
220
|
+
const actual = {
|
|
221
|
+
hasReadme: written.has("readme.md"),
|
|
222
|
+
hasChangelog: written.has("changelog.md"),
|
|
223
|
+
hasContents: written.has("contents.md") || written.has("contents.json"),
|
|
224
|
+
};
|
|
225
|
+
// `=== true` rather than a bare compare: an absent flag means "no such file",
|
|
226
|
+
// which is the same claim as `false`. Treating absence as a disagreement
|
|
227
|
+
// would drop a perfectly good digest on every sidecar written before a flag
|
|
228
|
+
// existed, and re-download the file it was still guarding.
|
|
229
|
+
const companionLied = (data.hasReadme === true) !== actual.hasReadme ||
|
|
230
|
+
(data.hasChangelog === true) !== actual.hasChangelog ||
|
|
231
|
+
(data.logo ?? undefined) !== logo;
|
|
232
|
+
const contentsLied = (data.hasContents === true) !== actual.hasContents;
|
|
233
|
+
data.hasReadme = actual.hasReadme;
|
|
234
|
+
data.hasChangelog = actual.hasChangelog;
|
|
235
|
+
data.hasContents = actual.hasContents;
|
|
236
|
+
if (logo)
|
|
237
|
+
data.logo = logo;
|
|
238
|
+
else
|
|
239
|
+
delete data.logo;
|
|
240
|
+
if (companionLied)
|
|
241
|
+
delete data.descDigest;
|
|
242
|
+
if (contentsLied)
|
|
243
|
+
delete data.contentDigest;
|
|
244
|
+
}
|
|
245
|
+
/**
|
|
246
|
+
* Restore `enrich/` from the published checkpoint, and report how many
|
|
247
|
+
* packages it seeded.
|
|
248
|
+
*
|
|
249
|
+
* Never throws. Every failure path warns and returns 0, which leaves the run
|
|
250
|
+
* doing exactly what it did before this existed.
|
|
251
|
+
*
|
|
252
|
+
* The iteration is over the **index**, never the document's keys: a package
|
|
253
|
+
* the index does not list is not seeded at all, so a key naming a path outside
|
|
254
|
+
* the tree describes a package that is simply never looked up. A sidecar that
|
|
255
|
+
* already exists on disk always wins — the checkpoint fills gaps, it does not
|
|
256
|
+
* overwrite a committed or freshly-enriched tree.
|
|
257
|
+
*/
|
|
258
|
+
export async function seedFromCheckpoint(opts) {
|
|
259
|
+
const indexDir = path.join(opts.root, "index");
|
|
260
|
+
const enrichDir = path.join(opts.root, "enrich");
|
|
261
|
+
const document = await loadCheckpoint(opts.url);
|
|
262
|
+
if (!document)
|
|
263
|
+
return 0;
|
|
264
|
+
let seeded = 0;
|
|
265
|
+
for (const file of findMetadataFiles(indexDir)) {
|
|
266
|
+
let name;
|
|
267
|
+
try {
|
|
268
|
+
({ name } = JSON.parse(fs.readFileSync(file, "utf8")));
|
|
269
|
+
}
|
|
270
|
+
catch {
|
|
271
|
+
continue; // `compileIndex` reports a malformed entry properly; not this.
|
|
272
|
+
}
|
|
273
|
+
if (typeof name !== "string" || name === "")
|
|
274
|
+
continue;
|
|
275
|
+
const namespace = namespaceOf(indexDir, file);
|
|
276
|
+
const dir = path.join(enrichDir, namespace, name);
|
|
277
|
+
if (fs.existsSync(path.join(dir, "data.json")))
|
|
278
|
+
continue;
|
|
279
|
+
const entry = document.packages[`${namespace}/${name}`];
|
|
280
|
+
if (entry === undefined)
|
|
281
|
+
continue;
|
|
282
|
+
const data = entry.data;
|
|
283
|
+
if (data === null || typeof data !== "object" || Array.isArray(data))
|
|
284
|
+
continue;
|
|
285
|
+
const files = entry.files;
|
|
286
|
+
const record = data;
|
|
287
|
+
const written = hydrateFiles(dir, files !== null && typeof files === "object" && !Array.isArray(files)
|
|
288
|
+
? files
|
|
289
|
+
: {});
|
|
290
|
+
reconcile(record, dir, namespace, name, written);
|
|
291
|
+
fs.writeFileSync(path.join(dir, "data.json"), JSON.stringify(record, null, 1) + "\n");
|
|
292
|
+
seeded += 1;
|
|
293
|
+
}
|
|
294
|
+
return seeded;
|
|
295
|
+
}
|
|
296
|
+
//# sourceMappingURL=checkpoint.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"checkpoint.js","sourceRoot":"","sources":["../../src/enrich/checkpoint.ts"],"names":[],"mappings":"AAAA,sCAAsC;AACtC,sCAAsC;AAEtC;;;;;;;;;;;;;;;;;;;;;;;;GAwBG;AACH,OAAO,EAAE,MAAM,SAAS,CAAC;AACzB,OAAO,IAAI,MAAM,WAAW,CAAC;AAE7B,2EAA2E;AAC3E,0EAA0E;AAC1E,+EAA+E;AAC/E,4EAA4E;AAC5E,OAAO,EAAE,iBAAiB,EAAE,WAAW,EAAE,MAAM,kBAAkB,CAAC;AAClE,OAAO,EAAE,QAAQ,EAAE,eAAe,EAAE,aAAa,EAAE,MAAM,YAAY,CAAC;AACtE,OAAO,EAAE,oBAAoB,EAAE,OAAO,EAAE,MAAM,8BAA8B,CAAC;AAE7E,+DAA+D;AAC/D,MAAM,CAAC,MAAM,eAAe,GAAG,aAAa,CAAC;AAE7C;;;;;;;;GAQG;AACH,MAAM,CAAC,MAAM,cAAc,GAAG,CAAC,CAAC;AAEhC;;;;;GAKG;AACH,MAAM,eAAe,GAAG,CAAC,WAAW,EAAE,cAAc,EAAE,aAAa,EAAE,eAAe,CAAC,CAAC;AAwBtF;;;;;;;;GAQG;AACH,SAAS,QAAQ,CAAC,GAAW;IAC3B,IAAI,CAAC,EAAE,CAAC,UAAU,CAAC,GAAG,CAAC;QAAE,OAAO,SAAS,CAAC;IAC1C,MAAM,KAAK,GAAG,EAAE;SACb,WAAW,CAAC,GAAG,CAAC;SAChB,MAAM,CAAC,CAAC,KAAK,EAAE,EAAE,CAAC,KAAK,CAAC,WAAW,EAAE,CAAC,UAAU,CAAC,OAAO,CAAC,CAAC;SAC1D,MAAM,CAAC,CAAC,KAAK,EAAE,EAAE,CAAC,QAAQ,CAAC,IAAI,CAAC,KAAK,CAAC,KAAK,CAAC,KAAK,CAAC,OAAO,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC;IACzE,OAAO,KAAK,CAAC,MAAM,KAAK,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,SAAS,CAAC;AACnD,CAAC;AAED;;;;;;;;;;;GAWG;AACH,MAAM,UAAU,cAAc,CAAC,MAAc,EAAE,OAAmC;IAChF,MAAM,QAAQ,GAAsC,EAAE,CAAC;IAEvD,KAAK,MAAM,EAAE,SAAS,EAAE,IAAI,EAAE,GAAG,EAAE,IAAI,OAAO,EAAE,CAAC;QAC/C,MAAM,QAAQ,GAAG,IAAI,CAAC,IAAI,CAAC,GAAG,EAAE,WAAW,CAAC,CAAC;QAC7C,IAAI,CAAC,EAAE,CAAC,UAAU,CAAC,QAAQ,CAAC;YAAE,SAAS;QAEvC,IAAI,IAA6B,CAAC;QAClC,IAAI,CAAC;YACH,IAAI,GAAG,IAAI,CAAC,KAAK,CAAC,EAAE,CAAC,YAAY,CAAC,QAAQ,EAAE,MAAM,CAAC,CAA4B,CAAC;QAClF,CAAC;QAAC,MAAM,CAAC;YACP,uEAAuE;YACvE,2CAA2C;YAC3C,SAAS;QACX,CAAC;QAED,MAAM,KAAK,GAA2B,EAAE,CAAC;QACzC,KAAK,MAAM,MAAM,IAAI,eAAe,EAAE,CAAC;YACrC,MAAM,IAAI,GAAG,IAAI,CAAC,IAAI,CAAC,GAAG,EAAE,MAAM,CAAC,CAAC;YACpC,IAAI,EAAE,CAAC,UAAU,CAAC,IAAI,CAAC;gBAAE,KAAK,CAAC,MAAM,CAAC,GAAG,EAAE,CAAC,YAAY,CAAC,IAAI,CAAC,CAAC,QAAQ,CAAC,QAAQ,CAAC,CAAC;QACpF,CAAC;QACD,MAAM,IAAI,GAAG,QAAQ,CAAC,GAAG,CAAC,CAAC;QAC3B,IAAI,IAAI;YAAE,KAAK,CAAC,IAAI,CAAC,WAAW,EAAE,CAAC,GAAG,EAAE,CAAC,YAAY,CAAC,IAAI,CAAC,IAAI,CAAC,GAAG,EAAE,IAAI,CAAC,CAAC,CAAC,QAAQ,CAAC,QAAQ,CAAC,CAAC;QAE/F,MAAM,KAAK,GAAsB,EAAE,IAAI,EAAE,CAAC;QAC1C,IAAI,MAAM,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,MAAM,GAAG,CAAC;YAAE,KAAK,CAAC,KAAK,GAAG,KAAK,CAAC;QACvD,QAAQ,CAAC,GAAG,SAAS,IAAI,IAAI,EAAE,CAAC,GAAG,KAAK,CAAC;IAC3C,CAAC;IAED,mEAAmE;IACnE,wEAAwE;IACxE,gCAAgC;IAChC,IAAI,MAAM,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC,MAAM,KAAK,CAAC;QAAE,OAAO;IAE/C,MAAM,QAAQ,GAAuB,EAAE,cAAc,EAAE,cAAc,EAAE,QAAQ,EAAE,CAAC;IAClF,MAAM,IAAI,GAAG,IAAI,CAAC,SAAS,CAAC,QAAQ,CAAC,GAAG,IAAI,CAAC;IAC7C,IAAI,MAAM,CAAC,UAAU,CAAC,IAAI,CAAC,GAAG,oBAAoB,EAAE,CAAC;QACnD,oEAAoE;QACpE,sEAAsE;QACtE,iEAAiE;QACjE,OAAO,CAAC,IAAI,CACV,SAAS,eAAe,uBAAuB,oBAAoB,sCAAsC;YACvG,wCAAwC,CAC3C,CAAC;IACJ,CAAC;IACD,EAAE,CAAC,aAAa,CAAC,IAAI,CAAC,IAAI,CAAC,MAAM,EAAE,eAAe,CAAC,EAAE,IAAI,CAAC,CAAC;AAC7D,CAAC;AASD,qEAAqE;AACrE,KAAK,UAAU,cAAc,CAAC,GAAW;IACvC,MAAM,QAAQ,GAAG,MAAM,OAAO,CAAC,GAAG,EAAE,EAAE,EAAE,EAAE,QAAQ,EAAE,oBAAoB,EAAE,CAAC,CAAC;IAE5E,IAAI,QAAQ,CAAC,MAAM,KAAK,GAAG,EAAE,CAAC;QAC5B,6DAA6D;QAC7D,OAAO,CAAC,GAAG,CAAC,MAAM,eAAe,uCAAuC,CAAC,CAAC;QAC1E,OAAO,IAAI,CAAC;IACd,CAAC;IACD,IAAI,QAAQ,CAAC,MAAM,GAAG,GAAG,IAAI,QAAQ,CAAC,MAAM,IAAI,GAAG,EAAE,CAAC;QACpD,yEAAyE;QACzE,2EAA2E;QAC3E,0EAA0E;QAC1E,sEAAsE;QACtE,sEAAsE;QACtE,wEAAwE;QACxE,OAAO,CAAC,IAAI,CAAC,wBAAwB,GAAG,UAAU,QAAQ,CAAC,MAAM,0BAA0B,CAAC,CAAC;QAC7F,OAAO,IAAI,CAAC;IACd,CAAC;IAED,IAAI,MAAe,CAAC;IACpB,IAAI,CAAC;QACH,MAAM,GAAG,IAAI,CAAC,KAAK,CAAC,QAAQ,CAAC,IAAI,CAAY,CAAC;IAChD,CAAC;IAAC,OAAO,GAAG,EAAE,CAAC;QACb,OAAO,CAAC,IAAI,CAAC,SAAS,GAAG,mBAAoB,GAAa,CAAC,OAAO,0BAA0B,CAAC,CAAC;QAC9F,OAAO,IAAI,CAAC;IACd,CAAC;IACD,IAAI,MAAM,KAAK,IAAI,IAAI,OAAO,MAAM,KAAK,QAAQ,IAAI,KAAK,CAAC,OAAO,CAAC,MAAM,CAAC,EAAE,CAAC;QAC3E,OAAO,CAAC,IAAI,CAAC,SAAS,GAAG,8CAA8C,CAAC,CAAC;QACzE,OAAO,IAAI,CAAC;IACd,CAAC;IAED,MAAM,GAAG,GAAG,MAAiC,CAAC;IAC9C,IAAI,GAAG,CAAC,cAAc,KAAK,cAAc,EAAE,CAAC;QAC1C,OAAO,CAAC,IAAI,CACV,SAAS,GAAG,4BAA4B,IAAI,CAAC,SAAS,CAAC,GAAG,CAAC,cAAc,CAAC,IAAI;YAC5E,oBAAoB,cAAc,yBAAyB,CAC9D,CAAC;QACF,OAAO,IAAI,CAAC;IACd,CAAC;IACD,MAAM,QAAQ,GAAG,GAAG,CAAC,QAAQ,CAAC;IAC9B,IAAI,QAAQ,KAAK,IAAI,IAAI,OAAO,QAAQ,KAAK,QAAQ,IAAI,KAAK,CAAC,OAAO,CAAC,QAAQ,CAAC,EAAE,CAAC;QACjF,OAAO,CAAC,IAAI,CAAC,SAAS,GAAG,oDAAoD,CAAC,CAAC;QAC/E,OAAO,IAAI,CAAC;IACd,CAAC;IAED,OAAO,EAAE,cAAc,EAAE,cAAc,EAAE,QAAQ,EAAE,QAA6C,EAAE,CAAC;AACrG,CAAC;AAED;;;;;;GAMG;AACH,SAAS,YAAY,CAAC,GAAW,EAAE,KAA8B;IAC/D,MAAM,OAAO,GAAG,IAAI,GAAG,EAAU,CAAC;IAClC,EAAE,CAAC,SAAS,CAAC,GAAG,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC;IACvC,eAAe,CAAC,GAAG,CAAC,CAAC;IACrB,aAAa,CAAC,GAAG,CAAC,CAAC;IAEnB,KAAK,MAAM,CAAC,IAAI,EAAE,OAAO,CAAC,IAAI,MAAM,CAAC,OAAO,CAAC,KAAK,CAAC,EAAE,CAAC;QACpD,IAAI,OAAO,OAAO,KAAK,QAAQ;YAAE,SAAS;QAC1C,MAAM,GAAG,GAAG,IAAI,CAAC,WAAW,EAAE,CAAC;QAC/B,MAAM,IAAI,GAAG,GAAG,CAAC,UAAU,CAAC,OAAO,CAAC,IAAI,QAAQ,CAAC,IAAI,CAAC,GAAG,CAAC,KAAK,CAAC,GAAG,CAAC,OAAO,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC;QACvF,IAAI,CAAC,eAAe,CAAC,QAAQ,CAAC,GAAG,CAAC,IAAI,CAAC,IAAI;YAAE,SAAS;QACtD,EAAE,CAAC,aAAa,CAAC,IAAI,CAAC,IAAI,CAAC,GAAG,EAAE,GAAG,CAAC,EAAE,MAAM,CAAC,IAAI,CAAC,OAAO,EAAE,QAAQ,CAAC,CAAC,CAAC;QACtE,OAAO,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC;IACnB,CAAC;IACD,OAAO,OAAO,CAAC;AACjB,CAAC;AAED;;;;;;;;;;;;;;;;GAgBG;AACH,SAAS,SAAS,CAChB,IAA6B,EAC7B,GAAW,EACX,SAAiB,EACjB,IAAY,EACZ,OAAoB;IAEpB,MAAM,QAAQ,GAAG,QAAQ,CAAC,GAAG,CAAC,CAAC;IAC/B,MAAM,IAAI,GAAG,QAAQ;QACnB,CAAC,CAAC,UAAU,SAAS,IAAI,IAAI,IAAI,QAAQ,CAAC,KAAK,CAAC,QAAQ,CAAC,OAAO,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC,EAAE;QAC5E,CAAC,CAAC,SAAS,CAAC;IACd,MAAM,MAAM,GAAG;QACb,SAAS,EAAE,OAAO,CAAC,GAAG,CAAC,WAAW,CAAC;QACnC,YAAY,EAAE,OAAO,CAAC,GAAG,CAAC,cAAc,CAAC;QACzC,WAAW,EAAE,OAAO,CAAC,GAAG,CAAC,aAAa,CAAC,IAAI,OAAO,CAAC,GAAG,CAAC,eAAe,CAAC;KACxE,CAAC;IAEF,8EAA8E;IAC9E,yEAAyE;IACzE,4EAA4E;IAC5E,2DAA2D;IAC3D,MAAM,aAAa,GACjB,CAAC,IAAI,CAAC,SAAS,KAAK,IAAI,CAAC,KAAK,MAAM,CAAC,SAAS;QAC9C,CAAC,IAAI,CAAC,YAAY,KAAK,IAAI,CAAC,KAAK,MAAM,CAAC,YAAY;QACpD,CAAC,IAAI,CAAC,IAAI,IAAI,SAAS,CAAC,KAAK,IAAI,CAAC;IACpC,MAAM,YAAY,GAAG,CAAC,IAAI,CAAC,WAAW,KAAK,IAAI,CAAC,KAAK,MAAM,CAAC,WAAW,CAAC;IAExE,IAAI,CAAC,SAAS,GAAG,MAAM,CAAC,SAAS,CAAC;IAClC,IAAI,CAAC,YAAY,GAAG,MAAM,CAAC,YAAY,CAAC;IACxC,IAAI,CAAC,WAAW,GAAG,MAAM,CAAC,WAAW,CAAC;IACtC,IAAI,IAAI;QAAE,IAAI,CAAC,IAAI,GAAG,IAAI,CAAC;;QACtB,OAAO,IAAI,CAAC,IAAI,CAAC;IAEtB,IAAI,aAAa;QAAE,OAAO,IAAI,CAAC,UAAU,CAAC;IAC1C,IAAI,YAAY;QAAE,OAAO,IAAI,CAAC,aAAa,CAAC;AAC9C,CAAC;AAED;;;;;;;;;;;;GAYG;AACH,MAAM,CAAC,KAAK,UAAU,kBAAkB,CAAC,IAAiB;IACxD,MAAM,QAAQ,GAAG,IAAI,CAAC,IAAI,CAAC,IAAI,CAAC,IAAI,EAAE,OAAO,CAAC,CAAC;IAC/C,MAAM,SAAS,GAAG,IAAI,CAAC,IAAI,CAAC,IAAI,CAAC,IAAI,EAAE,QAAQ,CAAC,CAAC;IAEjD,MAAM,QAAQ,GAAG,MAAM,cAAc,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;IAChD,IAAI,CAAC,QAAQ;QAAE,OAAO,CAAC,CAAC;IAExB,IAAI,MAAM,GAAG,CAAC,CAAC;IACf,KAAK,MAAM,IAAI,IAAI,iBAAiB,CAAC,QAAQ,CAAC,EAAE,CAAC;QAC/C,IAAI,IAAY,CAAC;QACjB,IAAI,CAAC;YACH,CAAC,EAAE,IAAI,EAAE,GAAG,IAAI,CAAC,KAAK,CAAC,EAAE,CAAC,YAAY,CAAC,IAAI,EAAE,MAAM,CAAC,CAAqB,CAAC,CAAC;QAC7E,CAAC;QAAC,MAAM,CAAC;YACP,SAAS,CAAC,+DAA+D;QAC3E,CAAC;QACD,IAAI,OAAO,IAAI,KAAK,QAAQ,IAAI,IAAI,KAAK,EAAE;YAAE,SAAS;QAEtD,MAAM,SAAS,GAAG,WAAW,CAAC,QAAQ,EAAE,IAAI,CAAC,CAAC;QAC9C,MAAM,GAAG,GAAG,IAAI,CAAC,IAAI,CAAC,SAAS,EAAE,SAAS,EAAE,IAAI,CAAC,CAAC;QAClD,IAAI,EAAE,CAAC,UAAU,CAAC,IAAI,CAAC,IAAI,CAAC,GAAG,EAAE,WAAW,CAAC,CAAC;YAAE,SAAS;QAEzD,MAAM,KAAK,GAAG,QAAQ,CAAC,QAAQ,CAAC,GAAG,SAAS,IAAI,IAAI,EAAE,CAAkC,CAAC;QACzF,IAAI,KAAK,KAAK,SAAS;YAAE,SAAS;QAClC,MAAM,IAAI,GAAY,KAAK,CAAC,IAAI,CAAC;QACjC,IAAI,IAAI,KAAK,IAAI,IAAI,OAAO,IAAI,KAAK,QAAQ,IAAI,KAAK,CAAC,OAAO,CAAC,IAAI,CAAC;YAAE,SAAS;QAE/E,MAAM,KAAK,GAAY,KAAK,CAAC,KAAK,CAAC;QACnC,MAAM,MAAM,GAAG,IAA+B,CAAC;QAC/C,MAAM,OAAO,GAAG,YAAY,CAC1B,GAAG,EACH,KAAK,KAAK,IAAI,IAAI,OAAO,KAAK,KAAK,QAAQ,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,KAAK,CAAC;YAClE,CAAC,CAAE,KAAiC;YACpC,CAAC,CAAC,EAAE,CACP,CAAC;QACF,SAAS,CAAC,MAAM,EAAE,GAAG,EAAE,SAAS,EAAE,IAAI,EAAE,OAAO,CAAC,CAAC;QACjD,EAAE,CAAC,aAAa,CAAC,IAAI,CAAC,IAAI,CAAC,GAAG,EAAE,WAAW,CAAC,EAAE,IAAI,CAAC,SAAS,CAAC,MAAM,EAAE,IAAI,EAAE,CAAC,CAAC,GAAG,IAAI,CAAC,CAAC;QACtF,MAAM,IAAI,CAAC,CAAC;IACd,CAAC;IAED,OAAO,MAAM,CAAC;AAChB,CAAC"}
|
package/dist/enrich/index.d.ts
CHANGED
|
@@ -1,3 +1,9 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* A logo filename comes from the registry, which is not ours. Only an
|
|
3
|
+
* alphanumeric extension is ever joined onto a path — `logo.a/b` or
|
|
4
|
+
* `logo.` + traversal must not reach `writeFileSync`.
|
|
5
|
+
*/
|
|
6
|
+
export declare const LOGO_EXT: RegExp;
|
|
1
7
|
/** Runs one `grim` subcommand and parses its JSON. Injected so tests need no binary. */
|
|
2
8
|
export type GrimRunner = (args: string[]) => Promise<unknown>;
|
|
3
9
|
export interface EnrichOptions {
|
|
@@ -13,5 +19,13 @@ export interface EnrichResult {
|
|
|
13
19
|
failures: string[];
|
|
14
20
|
}
|
|
15
21
|
export declare function spawnGrim(bin?: string): GrimRunner;
|
|
22
|
+
/**
|
|
23
|
+
* Drop companion files before rewriting them. Without this, content deleted
|
|
24
|
+
* upstream would render forever — the detail page globs the directory, it does
|
|
25
|
+
* not consult a flag in `data.json`.
|
|
26
|
+
*/
|
|
27
|
+
export declare function clearCompanions(dir: string): void;
|
|
28
|
+
/** Both spellings, so a kind that changes shape upstream cannot leave the old one behind. */
|
|
29
|
+
export declare function clearContents(dir: string): void;
|
|
16
30
|
export declare function enrichIndex(opts: EnrichOptions): Promise<EnrichResult>;
|
|
17
31
|
//# sourceMappingURL=index.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/enrich/index.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/enrich/index.ts"],"names":[],"mappings":"AAgCA;;;;GAIG;AACH,eAAO,MAAM,QAAQ,QAAuB,CAAC;AAE7C,wFAAwF;AACxF,MAAM,MAAM,UAAU,GAAG,CAAC,IAAI,EAAE,MAAM,EAAE,KAAK,OAAO,CAAC,OAAO,CAAC,CAAC;AAE9D,MAAM,WAAW,aAAa;IAC5B,yDAAyD;IACzD,IAAI,EAAE,MAAM,CAAC;IACb,+CAA+C;IAC/C,GAAG,CAAC,EAAE,UAAU,CAAC;CAClB;AAED,MAAM,WAAW,YAAY;IAC3B,KAAK,EAAE,MAAM,CAAC;IACd,QAAQ,EAAE,MAAM,CAAC;IACjB,gEAAgE;IAChE,QAAQ,EAAE,MAAM,EAAE,CAAC;CACpB;AASD,wBAAgB,SAAS,CAAC,GAAG,SAAS,GAAG,UAAU,CASlD;AAgFD;;;;GAIG;AACH,wBAAgB,eAAe,CAAC,GAAG,EAAE,MAAM,GAAG,IAAI,CAQjD;AAWD,6FAA6F;AAC7F,wBAAgB,aAAa,CAAC,GAAG,EAAE,MAAM,GAAG,IAAI,CAI/C;AAqJD,wBAAsB,WAAW,CAAC,IAAI,EAAE,aAAa,GAAG,OAAO,CAAC,YAAY,CAAC,CAsB5E"}
|
package/dist/enrich/index.js
CHANGED
|
@@ -30,7 +30,7 @@ const MAX_OUTPUT_BYTES = 16 * 1024 * 1024;
|
|
|
30
30
|
* alphanumeric extension is ever joined onto a path — `logo.a/b` or
|
|
31
31
|
* `logo.` + traversal must not reach `writeFileSync`.
|
|
32
32
|
*/
|
|
33
|
-
const LOGO_EXT = /^[A-Za-z0-9]{1,8}$/;
|
|
33
|
+
export const LOGO_EXT = /^[A-Za-z0-9]{1,8}$/;
|
|
34
34
|
export function spawnGrim(bin = "grim") {
|
|
35
35
|
return async (args) => {
|
|
36
36
|
const { stdout } = await execFileAsync(bin, [...args, "--format", "json"], {
|
|
@@ -41,6 +41,57 @@ export function spawnGrim(bin = "grim") {
|
|
|
41
41
|
return JSON.parse(stdout);
|
|
42
42
|
};
|
|
43
43
|
}
|
|
44
|
+
/**
|
|
45
|
+
* The curated fields copied straight across from `describe`, in the order
|
|
46
|
+
* they are written to disk. Every one is optional on the wire: a grim older
|
|
47
|
+
* than the annotation work reports the first five and nothing after them,
|
|
48
|
+
* and `compatibility` is null for every kind but a skill. Absent is absent —
|
|
49
|
+
* no placeholder is written for a field the registry did not report.
|
|
50
|
+
*/
|
|
51
|
+
const META_KEYS = [
|
|
52
|
+
"title",
|
|
53
|
+
"summary",
|
|
54
|
+
"version",
|
|
55
|
+
"license",
|
|
56
|
+
"created",
|
|
57
|
+
"revision",
|
|
58
|
+
"authors",
|
|
59
|
+
"vendor",
|
|
60
|
+
"url",
|
|
61
|
+
"documentation",
|
|
62
|
+
"compatibility",
|
|
63
|
+
];
|
|
64
|
+
/**
|
|
65
|
+
* The support channels, in the order `describe` reports them.
|
|
66
|
+
*
|
|
67
|
+
* They come off the *description companion's* manifest rather than the
|
|
68
|
+
* version's, because a maintainer contact belongs to the repository and
|
|
69
|
+
* changes over time. That has no effect on how they are refreshed here:
|
|
70
|
+
* `describe` runs on every pass and is never skipped, so a support link
|
|
71
|
+
* edited without republishing any artifact still lands on the next enrich —
|
|
72
|
+
* it is only the companion *download* and the contents fetch that a digest
|
|
73
|
+
* skips.
|
|
74
|
+
*/
|
|
75
|
+
const SUPPORT_CHANNELS = ["issues", "chat", "contact", "security"];
|
|
76
|
+
/**
|
|
77
|
+
* The `support` block, or nothing.
|
|
78
|
+
*
|
|
79
|
+
* A repository publishing no companion reports four nulls, and four nulls
|
|
80
|
+
* are the *absence* of a support block rather than an empty one — writing
|
|
81
|
+
* them would put a key in every sidecar in the index for the sake of the
|
|
82
|
+
* repositories that have nothing to say.
|
|
83
|
+
*/
|
|
84
|
+
function mapSupport(raw) {
|
|
85
|
+
if (raw === null || typeof raw !== "object" || Array.isArray(raw))
|
|
86
|
+
return undefined;
|
|
87
|
+
const reported = raw;
|
|
88
|
+
const support = {};
|
|
89
|
+
for (const channel of SUPPORT_CHANNELS) {
|
|
90
|
+
if (reported[channel] != null)
|
|
91
|
+
support[channel] = reported[channel];
|
|
92
|
+
}
|
|
93
|
+
return Object.keys(support).length > 0 ? support : undefined;
|
|
94
|
+
}
|
|
44
95
|
/**
|
|
45
96
|
* grim's snake_case `describe` payload -> the camelCase fields the renderer
|
|
46
97
|
* reads. Key insertion order is the on-disk order, and these sidecars are
|
|
@@ -49,10 +100,13 @@ export function spawnGrim(bin = "grim") {
|
|
|
49
100
|
*/
|
|
50
101
|
function mapMeta(desc) {
|
|
51
102
|
const data = {};
|
|
52
|
-
for (const key of
|
|
103
|
+
for (const key of META_KEYS) {
|
|
53
104
|
if (desc[key] != null)
|
|
54
105
|
data[key] = desc[key];
|
|
55
106
|
}
|
|
107
|
+
const support = mapSupport(desc.support);
|
|
108
|
+
if (support)
|
|
109
|
+
data.support = support;
|
|
56
110
|
data.keywords = desc.keywords ?? [];
|
|
57
111
|
data.tags = desc.tags ?? [];
|
|
58
112
|
data.deprecated = desc.deprecated ?? null; // string | null, always present
|
|
@@ -70,7 +124,7 @@ function decode(member) {
|
|
|
70
124
|
* upstream would render forever — the detail page globs the directory, it does
|
|
71
125
|
* not consult a flag in `data.json`.
|
|
72
126
|
*/
|
|
73
|
-
function clearCompanions(dir) {
|
|
127
|
+
export function clearCompanions(dir) {
|
|
74
128
|
if (!fs.existsSync(dir))
|
|
75
129
|
return;
|
|
76
130
|
for (const entry of fs.readdirSync(dir)) {
|
|
@@ -89,7 +143,7 @@ function clearCompanions(dir) {
|
|
|
89
143
|
const MARKDOWN_KINDS = new Set(["skill", "rule", "agent"]);
|
|
90
144
|
const JSON_KINDS = new Set(["bundle", "mcp"]);
|
|
91
145
|
/** Both spellings, so a kind that changes shape upstream cannot leave the old one behind. */
|
|
92
|
-
function clearContents(dir) {
|
|
146
|
+
export function clearContents(dir) {
|
|
93
147
|
for (const name of ["contents.md", "contents.json"]) {
|
|
94
148
|
fs.rmSync(path.join(dir, name), { force: true });
|
|
95
149
|
}
|
|
@@ -203,6 +257,25 @@ async function enrichOne(run, enrichDir, ref, namespace, name) {
|
|
|
203
257
|
data.contentDigest = digest;
|
|
204
258
|
data.hasContents = written;
|
|
205
259
|
}
|
|
260
|
+
// Recency is an indexer-side join, not something grim publishes: `describe`
|
|
261
|
+
// runs here per package on every pass, so nothing upstream has to carry a
|
|
262
|
+
// second date. The artifact's own `created` is it whenever there is one —
|
|
263
|
+
// it is the commit date, so a re-publish of the same commit keeps the same
|
|
264
|
+
// answer. An artifact published outside a repository (or with `--no-git`)
|
|
265
|
+
// has none, and then recency is when *this index* first saw the current
|
|
266
|
+
// digest. The digest bookkeeping above is the only state that can answer
|
|
267
|
+
// "first", so the stamp rides with it and is re-taken only when the
|
|
268
|
+
// artifact actually moved — a fresh stamp every run would churn a
|
|
269
|
+
// committed file and date every package to the last enrich.
|
|
270
|
+
const moved = digest !== null && digest !== existing.contentDigest;
|
|
271
|
+
data.updated =
|
|
272
|
+
typeof data.created === "string"
|
|
273
|
+
? data.created
|
|
274
|
+
: !moved && typeof existing.updated === "string"
|
|
275
|
+
? existing.updated
|
|
276
|
+
// RFC 3339 UTC to the second. Milliseconds in a committed file are
|
|
277
|
+
// noise nobody reads and a wider diff when the stamp does move.
|
|
278
|
+
: new Date().toISOString().replace(/\.\d+Z$/, "Z");
|
|
206
279
|
fs.mkdirSync(out, { recursive: true });
|
|
207
280
|
fs.writeFileSync(dataFile, JSON.stringify(data, null, 1) + "\n");
|
|
208
281
|
}
|
package/dist/enrich/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/enrich/index.ts"],"names":[],"mappings":"AAAA,sCAAsC;AACtC,sCAAsC;AAEtC;;;;;;;;;;;;;;;GAeG;AACH,OAAO,EAAE,QAAQ,EAAE,MAAM,oBAAoB,CAAC;AAC9C,OAAO,EAAE,MAAM,SAAS,CAAC;AACzB,OAAO,IAAI,MAAM,WAAW,CAAC;AAC7B,OAAO,EAAE,SAAS,EAAE,MAAM,WAAW,CAAC;AAEtC,OAAO,EAAE,iBAAiB,EAAE,WAAW,EAAE,MAAM,kBAAkB,CAAC;AAElE,MAAM,aAAa,GAAG,SAAS,CAAC,QAAQ,CAAC,CAAC;AAE1C,8EAA8E;AAC9E,MAAM,UAAU,GAAG,MAAM,CAAC;AAC1B,MAAM,gBAAgB,GAAG,EAAE,GAAG,IAAI,GAAG,IAAI,CAAC;AAE1C;;;;GAIG;AACH,MAAM,QAAQ,GAAG,oBAAoB,CAAC;
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/enrich/index.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The per-run creation cap, and the order it applies in.
|
|
3
|
+
*
|
|
4
|
+
* The sort is load-bearing, not tidiness: it is what lets run *N+1* resume
|
|
5
|
+
* exactly where run *N* stopped with no cursor file anywhere. `actions/stale`
|
|
6
|
+
* keeps a resume cursor as an optimization; correctness there, as here, never
|
|
7
|
+
* depends on it.
|
|
8
|
+
*/
|
|
9
|
+
export interface CreationPlan {
|
|
10
|
+
/** Every ref with no thread yet, sorted. `created=X/Y`'s Y. */
|
|
11
|
+
missing: string[];
|
|
12
|
+
/** The prefix of `missing` this run may create. */
|
|
13
|
+
batch: string[];
|
|
14
|
+
}
|
|
15
|
+
export declare function planCreations(desired: Iterable<string>, observed: ReadonlySet<string>, budget: number): CreationPlan;
|
|
16
|
+
//# sourceMappingURL=budget.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"budget.d.ts","sourceRoot":"","sources":["../../src/ratings/budget.ts"],"names":[],"mappings":"AAGA;;;;;;;GAOG;AAEH,MAAM,WAAW,YAAY;IAC3B,+DAA+D;IAC/D,OAAO,EAAE,MAAM,EAAE,CAAC;IAClB,mDAAmD;IACnD,KAAK,EAAE,MAAM,EAAE,CAAC;CACjB;AAED,wBAAgB,aAAa,CAC3B,OAAO,EAAE,QAAQ,CAAC,MAAM,CAAC,EACzB,QAAQ,EAAE,WAAW,CAAC,MAAM,CAAC,EAC7B,MAAM,EAAE,MAAM,GACb,YAAY,CAGd"}
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
// SPDX-License-Identifier: Apache-2.0
|
|
2
|
+
// Copyright 2026 The Grimoire Authors
|
|
3
|
+
export function planCreations(desired, observed, budget) {
|
|
4
|
+
const missing = [...new Set(desired)].filter((ref) => !observed.has(ref)).sort();
|
|
5
|
+
return { missing, batch: missing.slice(0, Math.max(0, budget)) };
|
|
6
|
+
}
|
|
7
|
+
//# sourceMappingURL=budget.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"budget.js","sourceRoot":"","sources":["../../src/ratings/budget.ts"],"names":[],"mappings":"AAAA,sCAAsC;AACtC,sCAAsC;AAkBtC,MAAM,UAAU,aAAa,CAC3B,OAAyB,EACzB,QAA6B,EAC7B,MAAc;IAEd,MAAM,OAAO,GAAG,CAAC,GAAG,IAAI,GAAG,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,GAAG,EAAE,EAAE,CAAC,CAAC,QAAQ,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC,CAAC,IAAI,EAAE,CAAC;IACjF,OAAO,EAAE,OAAO,EAAE,KAAK,EAAE,OAAO,CAAC,KAAK,CAAC,CAAC,EAAE,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,MAAM,CAAC,CAAC,EAAE,CAAC;AACnE,CAAC"}
|