@grimoire-rs/indexer 0.3.3 → 0.4.0
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 +137 -2
- 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/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 +8 -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/enrich/index.d.ts.map +1 -1
- package/dist/enrich/index.js +74 -1
- 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/provider.d.ts +108 -0
- package/dist/ratings/provider.d.ts.map +1 -0
- package/dist/ratings/provider.js +202 -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 +134 -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 +152 -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 +64 -15
- package/dist/renderer/astro/components/Catalog.tsx +104 -17
- package/dist/renderer/astro/layouts/Base.astro +7 -0
- 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 +146 -3
- 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 +6 -1
- package/dist/validate/adapters/http.d.ts.map +1 -1
- package/dist/validate/adapters/http.js +5 -1
- package/dist/validate/adapters/http.js.map +1 -1
- package/package.json +1 -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-ratings-seed.sh +83 -0
- package/templates/ci/gitlab-ratings.yml +37 -0
- package/templates/gitignore +8 -0
|
@@ -0,0 +1,160 @@
|
|
|
1
|
+
// SPDX-License-Identifier: Apache-2.0
|
|
2
|
+
// Copyright 2026 The Grimoire Authors
|
|
3
|
+
// `grim-indexer ratings` — the tally job. Reads the forge's own upvote
|
|
4
|
+
// counters and writes `.stats.json` beside the checkout for `build` to join.
|
|
5
|
+
//
|
|
6
|
+
// Thin by design, like `enrich`: resolve the configuration, construct the
|
|
7
|
+
// provider, run the reconcile, write the file, print the one log line. Every
|
|
8
|
+
// rule lives in `src/ratings/`.
|
|
9
|
+
import fs from "node:fs";
|
|
10
|
+
import path from "node:path";
|
|
11
|
+
import { CliError, EXIT } from "./exit.js";
|
|
12
|
+
import { parseRef } from "../validate/core/metadata.js";
|
|
13
|
+
/** An environment variable, treating the empty string as unset (CI sets both). */
|
|
14
|
+
function env(name) {
|
|
15
|
+
const value = process.env[name];
|
|
16
|
+
return value === undefined || value === "" ? undefined : value;
|
|
17
|
+
}
|
|
18
|
+
/**
|
|
19
|
+
* Where the bot talks to the forge, who it is, and what it may do.
|
|
20
|
+
*
|
|
21
|
+
* Read from the CI-predefined variables rather than `index.config.json`, so a
|
|
22
|
+
* GHES or self-managed GitLab index needs no extra configuration — and so the
|
|
23
|
+
* credential is never a committed file. `CI_JOB_TOKEN` is deliberately absent:
|
|
24
|
+
* it inherits the triggering user's role, which would satisfy R-1 clause 2 with
|
|
25
|
+
* a human's identity.
|
|
26
|
+
*/
|
|
27
|
+
function forgeEnv(provider) {
|
|
28
|
+
const [api, project, token] = provider === "github"
|
|
29
|
+
? [
|
|
30
|
+
env("GITHUB_GRAPHQL_URL") ?? "https://api.github.com/graphql",
|
|
31
|
+
env("GITHUB_REPOSITORY"),
|
|
32
|
+
env("GRIM_RATINGS_TOKEN") ?? env("GITHUB_TOKEN"),
|
|
33
|
+
]
|
|
34
|
+
: [
|
|
35
|
+
env("CI_API_GRAPHQL_URL") ??
|
|
36
|
+
(env("CI_SERVER_URL") !== undefined
|
|
37
|
+
? `${env("CI_SERVER_URL")}/api/graphql`
|
|
38
|
+
: "https://gitlab.com/api/graphql"),
|
|
39
|
+
env("CI_PROJECT_PATH"),
|
|
40
|
+
env("GRIM_RATINGS_TOKEN"),
|
|
41
|
+
];
|
|
42
|
+
if (project === undefined) {
|
|
43
|
+
throw new CliError(`ratings: cannot tell which ${provider === "github" ? "repository" : "project"} holds the ` +
|
|
44
|
+
`threads — set ${provider === "github" ? "GITHUB_REPOSITORY" : "CI_PROJECT_PATH"}`, EXIT.data);
|
|
45
|
+
}
|
|
46
|
+
if (token === undefined) {
|
|
47
|
+
throw new CliError(`ratings: no credential — set ${provider === "github"
|
|
48
|
+
? "GITHUB_TOKEN (permissions: discussions: write) or GRIM_RATINGS_TOKEN"
|
|
49
|
+
: "GRIM_RATINGS_TOKEN to a project access token with `api` scope (CI_JOB_TOKEN cannot write reactions)"}`, EXIT.data);
|
|
50
|
+
}
|
|
51
|
+
return { api: api ?? "", project, token };
|
|
52
|
+
}
|
|
53
|
+
/** Every ref in `index/`. The tally runs before `build`, so `all.json` does not exist yet. */
|
|
54
|
+
function desiredRefs(root, findMetadataFiles) {
|
|
55
|
+
const refs = [];
|
|
56
|
+
for (const file of findMetadataFiles(path.join(root, "index"))) {
|
|
57
|
+
let meta;
|
|
58
|
+
try {
|
|
59
|
+
meta = JSON.parse(fs.readFileSync(file, "utf8"));
|
|
60
|
+
}
|
|
61
|
+
catch (err) {
|
|
62
|
+
throw new CliError(`${file}: ${err.message}`, EXIT.data);
|
|
63
|
+
}
|
|
64
|
+
const ref = meta.ref;
|
|
65
|
+
if (typeof ref !== "string" || ref === "")
|
|
66
|
+
continue;
|
|
67
|
+
// The ref is interpolated into a thread body that R-1 later parses back
|
|
68
|
+
// out of that same body. R-1's four clauses constrain *who wrote* the
|
|
69
|
+
// body and *where the thread lives* — none of them constrains what is in
|
|
70
|
+
// it — so an unvalidated ref carrying a newline injects a second, bare,
|
|
71
|
+
// column-0 marker line that the bot itself then authors. Every clause
|
|
72
|
+
// passes, and the forged binding either zeroes a popular package's rating
|
|
73
|
+
// (two claimants) or points every vote for it at the attacker's thread.
|
|
74
|
+
// `parseRef`'s anchored host/repository grammars have no newline in them.
|
|
75
|
+
const parsed = parseRef(ref);
|
|
76
|
+
if (!parsed.ok) {
|
|
77
|
+
throw new CliError(`${file}: ${parsed.reason}`, EXIT.data);
|
|
78
|
+
}
|
|
79
|
+
refs.push(ref);
|
|
80
|
+
}
|
|
81
|
+
return refs;
|
|
82
|
+
}
|
|
83
|
+
export async function ratings(root) {
|
|
84
|
+
const rootDir = path.resolve(root);
|
|
85
|
+
const { loadRatingsConfig } = await import("../ratings/config.js");
|
|
86
|
+
const cfg = await loadRatingsConfig(rootDir);
|
|
87
|
+
if (!cfg) {
|
|
88
|
+
// The block being absent is how ratings are off. Nothing is written, so a
|
|
89
|
+
// published sidecar keeps serving until the operator deletes it (S-016).
|
|
90
|
+
console.log("ratings: no `ratings` block in index.config.json — nothing to tally");
|
|
91
|
+
return EXIT.ok;
|
|
92
|
+
}
|
|
93
|
+
const { loadConfig } = await import("../config.js");
|
|
94
|
+
const site = (await loadConfig(rootDir)).site;
|
|
95
|
+
if (site === undefined || !site.startsWith("https://")) {
|
|
96
|
+
// Not defaulted. `DEFAULT_CONFIG.site` names the first-party index, and
|
|
97
|
+
// seeding from *someone else's* published stats would merge their ratings
|
|
98
|
+
// into this one's sidecar. https because the seed decides every published
|
|
99
|
+
// rating — and refused *here*, before a single thread is created, because
|
|
100
|
+
// the generated deploy job enforces the same rule far too late to help:
|
|
101
|
+
// by then the tally has already written to the forge.
|
|
102
|
+
throw new CliError("ratings: index.config.json needs an explicit https `site` — the seed is read from `<site>/stats.json`", EXIT.data);
|
|
103
|
+
}
|
|
104
|
+
const { trustedBots } = readPolicy(rootDir);
|
|
105
|
+
if (!trustedBots.some((bot) => typeof bot !== "string" && bot.id !== undefined)) {
|
|
106
|
+
// R-1 clause 2 has an account id and no login in hand, so a login-only
|
|
107
|
+
// entry authorizes nothing — every thread would be dropped and the run
|
|
108
|
+
// would publish an empty tally that looked like a healthy one.
|
|
109
|
+
throw new CliError("ratings: index-policy.json has no `trustedBots` entry with an `id` — R-1 would authorize nothing", EXIT.data);
|
|
110
|
+
}
|
|
111
|
+
const forge = forgeEnv(cfg.provider);
|
|
112
|
+
const { findMetadataFiles } = await import("../data/index.js");
|
|
113
|
+
const desired = desiredRefs(rootDir, findMetadataFiles);
|
|
114
|
+
const { loadSeed, mergeStats, statsDocument, STATS_FILE } = await import("../ratings/seed.js");
|
|
115
|
+
// Read before the forge work: it fails the run on anything but a genuine 404,
|
|
116
|
+
// and failing before a single thread is created is the cheaper order.
|
|
117
|
+
const seed = await loadSeed(`${site.replace(/\/+$/, "")}/stats.json`);
|
|
118
|
+
const { createRatingProvider } = await import("../ratings/provider.js");
|
|
119
|
+
const { conflictWarning, logLine, reconcile } = await import("../ratings/reconcile.js");
|
|
120
|
+
const result = await reconcile({
|
|
121
|
+
provider: createRatingProvider(cfg.provider, { ...forge, container: cfg.container, lockThreads: cfg.lockThreads, trustedBots }),
|
|
122
|
+
desired,
|
|
123
|
+
budget: cfg.createBudget,
|
|
124
|
+
seed,
|
|
125
|
+
});
|
|
126
|
+
if (result.starved) {
|
|
127
|
+
throw new CliError("ratings: rate limited before anything could be observed — nothing to publish, " +
|
|
128
|
+
"leaving the previous sidecar in place", EXIT.unavailable);
|
|
129
|
+
}
|
|
130
|
+
for (const conflict of result.conflicts)
|
|
131
|
+
console.error(conflictWarning(conflict));
|
|
132
|
+
const doc = statsDocument(mergeStats(seed, "rating", cfg.provider, result.fresh));
|
|
133
|
+
fs.writeFileSync(path.join(rootDir, STATS_FILE), JSON.stringify(doc, null, 2) + "\n");
|
|
134
|
+
console.log(logLine(result));
|
|
135
|
+
return EXIT.ok;
|
|
136
|
+
}
|
|
137
|
+
/** `index-policy.json`'s `trustedBots` — the same file the contribution gate reads. */
|
|
138
|
+
function readPolicy(root) {
|
|
139
|
+
const file = path.join(root, "index-policy.json");
|
|
140
|
+
let text;
|
|
141
|
+
try {
|
|
142
|
+
text = fs.readFileSync(file, "utf8");
|
|
143
|
+
}
|
|
144
|
+
catch {
|
|
145
|
+
throw new CliError(`ratings: ${file} is required — R-1 reads the bot allowlist out of its \`trustedBots\``, EXIT.data);
|
|
146
|
+
}
|
|
147
|
+
let parsed;
|
|
148
|
+
try {
|
|
149
|
+
parsed = JSON.parse(text);
|
|
150
|
+
}
|
|
151
|
+
catch (err) {
|
|
152
|
+
throw new CliError(`${file}: ${err.message}`, EXIT.data);
|
|
153
|
+
}
|
|
154
|
+
const bots = parsed?.trustedBots;
|
|
155
|
+
if (bots !== undefined && !Array.isArray(bots)) {
|
|
156
|
+
throw new CliError(`${file}: trustedBots must be an array`, EXIT.data);
|
|
157
|
+
}
|
|
158
|
+
return { trustedBots: (bots ?? []) };
|
|
159
|
+
}
|
|
160
|
+
//# sourceMappingURL=ratings.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ratings.js","sourceRoot":"","sources":["../../src/cli/ratings.ts"],"names":[],"mappings":"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"}
|
package/dist/config.d.ts
CHANGED
|
@@ -100,6 +100,25 @@ export declare const DEFAULT_CONFIG: ResolvedSiteConfig;
|
|
|
100
100
|
export declare class SiteConfigError extends Error {
|
|
101
101
|
constructor(message: string);
|
|
102
102
|
}
|
|
103
|
+
/**
|
|
104
|
+
* `site` is the strictest of the URL keys, deliberately: it is the only one that
|
|
105
|
+
* is *fetched* as well as published. `<site>/stats.json` is the ratings seed —
|
|
106
|
+
* read by `grim-indexer ratings`, and from the same checkout by the generated
|
|
107
|
+
* deploy job — and whatever that fetch returns is the merge base for every
|
|
108
|
+
* published rating. One policy, here, at load: the verbs, the renderer and the
|
|
109
|
+
* generated shell must not disagree about the same key.
|
|
110
|
+
*
|
|
111
|
+
* No userinfo, because `https://real.example@evil.example/` reads as the real
|
|
112
|
+
* host and fetches from the other. No query or fragment, because
|
|
113
|
+
* `<site>/stats.json` appends to them and quietly fetches something else.
|
|
114
|
+
* Lowercase scheme, because the generated guard matches that literal prefix.
|
|
115
|
+
*
|
|
116
|
+
* TLS is *not* required here: `init --quick` writes `http://localhost:4321` as
|
|
117
|
+
* its deliberate placeholder, and a site with no ratings never fetches
|
|
118
|
+
* anything. `grim-indexer ratings` requires https before it writes to the
|
|
119
|
+
* forge, which is the earliest point that requirement exists.
|
|
120
|
+
*/
|
|
121
|
+
export declare function validateSite(value: unknown): void;
|
|
103
122
|
/**
|
|
104
123
|
* Read `index.config.json` from `root`. A missing file is not an
|
|
105
124
|
* error — an index repo that wants the defaults ships no config at all.
|
package/dist/config.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"config.d.ts","sourceRoot":"","sources":["../src/config.ts"],"names":[],"mappings":"AAUA,+DAA+D;AAC/D,eAAO,MAAM,WAAW,sBAAsB,CAAC;AAE/C,2EAA2E;AAC3E,MAAM,WAAW,cAAc;IAC7B,oDAAoD;IACpD,EAAE,EAAE,MAAM,CAAC;IACX,0EAA0E;IAC1E,OAAO,EAAE,MAAM,CAAC;CACjB;AAED;;;;GAIG;AACH,MAAM,WAAW,YAAY;IAC3B,kEAAkE;IAClE,KAAK,EAAE,MAAM,CAAC;IACd,mDAAmD;IACnD,KAAK,EAAE,MAAM,CAAC;CACf;AAED;;;;GAIG;AACH,MAAM,WAAW,UAAU;IACzB,4EAA4E;IAC5E,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,2DAA2D;IAC3D,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,8EAA8E;IAC9E,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,uDAAuD;IACvD,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,uCAAuC;IACvC,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,2CAA2C;IAC3C,OAAO,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IACxB,4EAA4E;IAC5E,cAAc,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAC/B,6CAA6C;IAC7C,OAAO,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IACxB,sEAAsE;IACtE,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB;;;;;;;OAOG;IACH,IAAI,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IACrB,8DAA8D;IAC9D,OAAO,CAAC,EAAE,cAAc,EAAE,CAAC;IAC3B;;;OAGG;IACH,eAAe,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAChC,0DAA0D;IAC1D,QAAQ,CAAC,EAAE,YAAY,GAAG,IAAI,CAAC;IAC/B;;;;OAIG;IACH,UAAU,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAC3B;;;;;;;;;OASG;IACH,WAAW,CAAC,EAAE,KAAK,CAAC;QAAE,KAAK,EAAE,MAAM,CAAC;QAAC,IAAI,EAAE,MAAM,CAAA;KAAE,CAAC,CAAC;IACrD;;;;;OAKG;IACH,WAAW,CAAC,EAAE,OAAO,CAAC;IACtB;;;OAGG;IACH,SAAS,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;CAC3B;AAED,+EAA+E;AAC/E,MAAM,MAAM,kBAAkB,GAAG,QAAQ,CAAC,UAAU,CAAC,CAAC;AAEtD,eAAO,MAAM,cAAc,EAAE,kBAwC5B,CAAC;AAEF,2EAA2E;AAC3E,qBAAa,eAAgB,SAAQ,KAAK;gBAC5B,OAAO,EAAE,MAAM;CAI5B;
|
|
1
|
+
{"version":3,"file":"config.d.ts","sourceRoot":"","sources":["../src/config.ts"],"names":[],"mappings":"AAUA,+DAA+D;AAC/D,eAAO,MAAM,WAAW,sBAAsB,CAAC;AAE/C,2EAA2E;AAC3E,MAAM,WAAW,cAAc;IAC7B,oDAAoD;IACpD,EAAE,EAAE,MAAM,CAAC;IACX,0EAA0E;IAC1E,OAAO,EAAE,MAAM,CAAC;CACjB;AAED;;;;GAIG;AACH,MAAM,WAAW,YAAY;IAC3B,kEAAkE;IAClE,KAAK,EAAE,MAAM,CAAC;IACd,mDAAmD;IACnD,KAAK,EAAE,MAAM,CAAC;CACf;AAED;;;;GAIG;AACH,MAAM,WAAW,UAAU;IACzB,4EAA4E;IAC5E,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,2DAA2D;IAC3D,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,8EAA8E;IAC9E,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,uDAAuD;IACvD,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,uCAAuC;IACvC,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,2CAA2C;IAC3C,OAAO,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IACxB,4EAA4E;IAC5E,cAAc,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAC/B,6CAA6C;IAC7C,OAAO,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IACxB,sEAAsE;IACtE,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB;;;;;;;OAOG;IACH,IAAI,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IACrB,8DAA8D;IAC9D,OAAO,CAAC,EAAE,cAAc,EAAE,CAAC;IAC3B;;;OAGG;IACH,eAAe,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAChC,0DAA0D;IAC1D,QAAQ,CAAC,EAAE,YAAY,GAAG,IAAI,CAAC;IAC/B;;;;OAIG;IACH,UAAU,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAC3B;;;;;;;;;OASG;IACH,WAAW,CAAC,EAAE,KAAK,CAAC;QAAE,KAAK,EAAE,MAAM,CAAC;QAAC,IAAI,EAAE,MAAM,CAAA;KAAE,CAAC,CAAC;IACrD;;;;;OAKG;IACH,WAAW,CAAC,EAAE,OAAO,CAAC;IACtB;;;OAGG;IACH,SAAS,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;CAC3B;AAED,+EAA+E;AAC/E,MAAM,MAAM,kBAAkB,GAAG,QAAQ,CAAC,UAAU,CAAC,CAAC;AAEtD,eAAO,MAAM,cAAc,EAAE,kBAwC5B,CAAC;AAEF,2EAA2E;AAC3E,qBAAa,eAAgB,SAAQ,KAAK;gBAC5B,OAAO,EAAE,MAAM;CAI5B;AAwBD;;;;;;;;;;;;;;;;;GAiBG;AACH,wBAAgB,YAAY,CAAC,KAAK,EAAE,OAAO,GAAG,IAAI,CAuBjD;AA+ED;;;GAGG;AACH,wBAAsB,UAAU,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,UAAU,CAAC,CAelE;AAED,wDAAwD;AACxD,wBAAgB,aAAa,CAAC,MAAM,EAAE,UAAU,GAAG,kBAAkB,CAWpE"}
|
package/dist/config.js
CHANGED
|
@@ -73,6 +73,51 @@ function optionalUrl(raw, key) {
|
|
|
73
73
|
fail(`${key} must be an http(s) URL`);
|
|
74
74
|
}
|
|
75
75
|
}
|
|
76
|
+
/**
|
|
77
|
+
* `site` is the strictest of the URL keys, deliberately: it is the only one that
|
|
78
|
+
* is *fetched* as well as published. `<site>/stats.json` is the ratings seed —
|
|
79
|
+
* read by `grim-indexer ratings`, and from the same checkout by the generated
|
|
80
|
+
* deploy job — and whatever that fetch returns is the merge base for every
|
|
81
|
+
* published rating. One policy, here, at load: the verbs, the renderer and the
|
|
82
|
+
* generated shell must not disagree about the same key.
|
|
83
|
+
*
|
|
84
|
+
* No userinfo, because `https://real.example@evil.example/` reads as the real
|
|
85
|
+
* host and fetches from the other. No query or fragment, because
|
|
86
|
+
* `<site>/stats.json` appends to them and quietly fetches something else.
|
|
87
|
+
* Lowercase scheme, because the generated guard matches that literal prefix.
|
|
88
|
+
*
|
|
89
|
+
* TLS is *not* required here: `init --quick` writes `http://localhost:4321` as
|
|
90
|
+
* its deliberate placeholder, and a site with no ratings never fetches
|
|
91
|
+
* anything. `grim-indexer ratings` requires https before it writes to the
|
|
92
|
+
* forge, which is the earliest point that requirement exists.
|
|
93
|
+
*/
|
|
94
|
+
export function validateSite(value) {
|
|
95
|
+
if (value === undefined)
|
|
96
|
+
return;
|
|
97
|
+
if (typeof value !== "string")
|
|
98
|
+
fail("site must be a string");
|
|
99
|
+
// WHATWG `new URL` strips embedded tabs and newlines, so a control character
|
|
100
|
+
// survives into the raw value every consumer uses while the parsed URL looks
|
|
101
|
+
// clean — and a newline in a generated `::error::` line is a workflow command.
|
|
102
|
+
// No `i` flag: one spelling, so the generated shell can match the literal
|
|
103
|
+
// prefix and `grim-indexer ratings` can compare without a case fold.
|
|
104
|
+
if (!/^https?:\/\//.test(value) || /\s/.test(value)) {
|
|
105
|
+
fail("site must be an http(s) URL — lowercase scheme, no whitespace");
|
|
106
|
+
}
|
|
107
|
+
let url;
|
|
108
|
+
try {
|
|
109
|
+
url = new URL(value);
|
|
110
|
+
}
|
|
111
|
+
catch {
|
|
112
|
+
fail("site must be an absolute URL, e.g. https://index.example.com");
|
|
113
|
+
}
|
|
114
|
+
if (url.username !== "" || url.password !== "") {
|
|
115
|
+
fail("site must not carry userinfo — https://user@host reads as one host and fetches another");
|
|
116
|
+
}
|
|
117
|
+
if (url.search !== "" || url.hash !== "") {
|
|
118
|
+
fail("site must be a bare base URL — no query, no fragment");
|
|
119
|
+
}
|
|
120
|
+
}
|
|
76
121
|
function validate(raw) {
|
|
77
122
|
if (raw === null || typeof raw !== "object" || Array.isArray(raw)) {
|
|
78
123
|
fail("must contain a JSON object");
|
|
@@ -81,9 +126,10 @@ function validate(raw) {
|
|
|
81
126
|
for (const key of ["brand", "brandMark", "description", "tagline", "favicon", "footerNote", "customCss"]) {
|
|
82
127
|
optionalString(cfg, key);
|
|
83
128
|
}
|
|
84
|
-
for (const key of ["
|
|
129
|
+
for (const key of ["docsUrl", "installDocsUrl", "repoUrl"]) {
|
|
85
130
|
optionalUrl(cfg, key);
|
|
86
131
|
}
|
|
132
|
+
validateSite(cfg.site);
|
|
87
133
|
if (cfg.attribution !== undefined && typeof cfg.attribution !== "boolean") {
|
|
88
134
|
fail("attribution must be a boolean");
|
|
89
135
|
}
|
package/dist/config.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"config.js","sourceRoot":"","sources":["../src/config.ts"],"names":[],"mappings":"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
|
|
1
|
+
{"version":3,"file":"config.js","sourceRoot":"","sources":["../src/config.ts"],"names":[],"mappings":"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"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/enrich/index.ts"],"names":[],"mappings":"AAuCA,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;
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/enrich/index.ts"],"names":[],"mappings":"AAuCA,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;AAkQD,wBAAsB,WAAW,CAAC,IAAI,EAAE,aAAa,GAAG,OAAO,CAAC,YAAY,CAAC,CAsB5E"}
|
package/dist/enrich/index.js
CHANGED
|
@@ -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
|
|
@@ -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;AA0BtC,MAAM,UAAU,SAAS,CAAC,GAAG,GAAG,MAAM;IACpC,OAAO,KAAK,EAAE,IAAI,EAAE,EAAE;QACpB,MAAM,EAAE,MAAM,EAAE,GAAG,MAAM,aAAa,CAAC,GAAG,EAAE,CAAC,GAAG,IAAI,EAAE,UAAU,EAAE,MAAM,CAAC,EAAE;YACzE,OAAO,EAAE,UAAU;YACnB,SAAS,EAAE,gBAAgB;YAC3B,QAAQ,EAAE,MAAM;SACjB,CAAC,CAAC;QACH,OAAO,IAAI,CAAC,KAAK,CAAC,MAAM,CAAC,CAAC;IAC5B,CAAC,CAAC;AACJ,CAAC;AAED
|
|
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"}
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Forges a rating can be collected on. The same two names as CI's [`Forge`],
|
|
3
|
+
* and deliberately a separate type: an index hosted on one forge could tally
|
|
4
|
+
* on the other, and nothing here needs them to move together.
|
|
5
|
+
*/
|
|
6
|
+
export type RatingProviderKind = "github" | "gitlab";
|
|
7
|
+
export declare const RATING_PROVIDERS: readonly RatingProviderKind[];
|
|
8
|
+
/** Per-run thread-creation budget when the config names none. */
|
|
9
|
+
export declare const DEFAULT_CREATE_BUDGET = 400;
|
|
10
|
+
/**
|
|
11
|
+
* The `ratings` block, with every gap filled. The block being absent means
|
|
12
|
+
* ratings are off — that is the *absence* of this value, not a variant of it,
|
|
13
|
+
* so every reader gets `undefined` and has nothing to branch on twice.
|
|
14
|
+
*/
|
|
15
|
+
export interface RatingsConfig {
|
|
16
|
+
/** Which forge holds the threads. Required — there is no sensible default. */
|
|
17
|
+
provider: RatingProviderKind;
|
|
18
|
+
/**
|
|
19
|
+
* Where threads live: a GitHub Discussions **category** name, or a GitLab
|
|
20
|
+
* **work item type**. Required, and no default is offered — "Ratings" is a
|
|
21
|
+
* category an operator creates, while GitLab's work item types are a closed
|
|
22
|
+
* set, so a default correct on one forge would be wrong on the other.
|
|
23
|
+
*/
|
|
24
|
+
container: string;
|
|
25
|
+
/**
|
|
26
|
+
* Threads created per run. Default {@link DEFAULT_CREATE_BUDGET}, which sits
|
|
27
|
+
* under GitHub's 500-per-hour content-creation cap with room for the retries
|
|
28
|
+
* a run may spend. A partial run creates *fewer threads*, never corrupt
|
|
29
|
+
* state, so the only cost of a small budget is more runs to converge.
|
|
30
|
+
*/
|
|
31
|
+
createBudget: number;
|
|
32
|
+
/**
|
|
33
|
+
* Lock every thread on creation. **Default `true`**: votes still count,
|
|
34
|
+
* replies are refused.
|
|
35
|
+
*
|
|
36
|
+
* Two things at once. It is the low-moderation default — an operator gets a
|
|
37
|
+
* rating signal without also running a comment forum they have to moderate.
|
|
38
|
+
* And it independently hardens R-1 clause 1: a locked thread cannot receive
|
|
39
|
+
* the forged-marker reply that clause exists to reject, so the marker rule
|
|
40
|
+
* and the lock have to fail together before a stranger's text is counted.
|
|
41
|
+
*/
|
|
42
|
+
lockThreads: boolean;
|
|
43
|
+
}
|
|
44
|
+
/**
|
|
45
|
+
* Validate the `ratings` block of an already-parsed config object.
|
|
46
|
+
*
|
|
47
|
+
* Absent (or `null`) ⇒ `undefined` ⇒ ratings off. Unknown keys are ignored,
|
|
48
|
+
* the same forward-compatibility rule `stats.json` itself follows.
|
|
49
|
+
*/
|
|
50
|
+
export declare function validateRatings(raw: unknown): RatingsConfig | undefined;
|
|
51
|
+
/**
|
|
52
|
+
* Read the `ratings` block out of `index.config.json`. A missing file, or a
|
|
53
|
+
* file with no `ratings` block, means ratings are off.
|
|
54
|
+
*/
|
|
55
|
+
export declare function loadRatingsConfig(root: string): Promise<RatingsConfig | undefined>;
|
|
56
|
+
//# sourceMappingURL=config.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"config.d.ts","sourceRoot":"","sources":["../../src/ratings/config.ts"],"names":[],"mappings":"AAiBA;;;;GAIG;AACH,MAAM,MAAM,kBAAkB,GAAG,QAAQ,GAAG,QAAQ,CAAC;AAErD,eAAO,MAAM,gBAAgB,EAAE,SAAS,kBAAkB,EAAyB,CAAC;AAEpF,iEAAiE;AACjE,eAAO,MAAM,qBAAqB,MAAM,CAAC;AAEzC;;;;GAIG;AACH,MAAM,WAAW,aAAa;IAC5B,8EAA8E;IAC9E,QAAQ,EAAE,kBAAkB,CAAC;IAC7B;;;;;OAKG;IACH,SAAS,EAAE,MAAM,CAAC;IAClB;;;;;OAKG;IACH,YAAY,EAAE,MAAM,CAAC;IACrB;;;;;;;;;OASG;IACH,WAAW,EAAE,OAAO,CAAC;CACtB;AAMD;;;;;GAKG;AACH,wBAAgB,eAAe,CAAC,GAAG,EAAE,OAAO,GAAG,aAAa,GAAG,SAAS,CAgCvE;AAED;;;GAGG;AACH,wBAAsB,iBAAiB,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,aAAa,GAAG,SAAS,CAAC,CAkBxF"}
|
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
// SPDX-License-Identifier: Apache-2.0
|
|
2
|
+
// Copyright 2026 The Grimoire Authors
|
|
3
|
+
// The `ratings` block of `index.config.json` — the third independent reader of
|
|
4
|
+
// that file, beside `config.ts` (the site) and `ci.ts` (the pipeline). One
|
|
5
|
+
// loader for all three would couple the renderer, the CI generator and the
|
|
6
|
+
// tally to each other's validation for the sake of one `readFile`; each stays
|
|
7
|
+
// able to fail on its own block and say so in its own words.
|
|
8
|
+
//
|
|
9
|
+
// There is deliberately no `botIds` key here. The author allowlist is
|
|
10
|
+
// `index-policy.json`'s `trustedBots[].id` and nothing else — a second copy of
|
|
11
|
+
// the same ids in a second file is a consistency hazard, not a convenience.
|
|
12
|
+
import fs from "node:fs/promises";
|
|
13
|
+
import path from "node:path";
|
|
14
|
+
import { CONFIG_FILE, SiteConfigError } from "../config.js";
|
|
15
|
+
export const RATING_PROVIDERS = ["github", "gitlab"];
|
|
16
|
+
/** Per-run thread-creation budget when the config names none. */
|
|
17
|
+
export const DEFAULT_CREATE_BUDGET = 400;
|
|
18
|
+
function fail(msg) {
|
|
19
|
+
throw new SiteConfigError(`${CONFIG_FILE}: ratings.${msg}`);
|
|
20
|
+
}
|
|
21
|
+
/**
|
|
22
|
+
* Validate the `ratings` block of an already-parsed config object.
|
|
23
|
+
*
|
|
24
|
+
* Absent (or `null`) ⇒ `undefined` ⇒ ratings off. Unknown keys are ignored,
|
|
25
|
+
* the same forward-compatibility rule `stats.json` itself follows.
|
|
26
|
+
*/
|
|
27
|
+
export function validateRatings(raw) {
|
|
28
|
+
if (raw === undefined || raw === null)
|
|
29
|
+
return undefined;
|
|
30
|
+
if (typeof raw !== "object" || Array.isArray(raw)) {
|
|
31
|
+
throw new SiteConfigError(`${CONFIG_FILE}: ratings must be an object`);
|
|
32
|
+
}
|
|
33
|
+
const ratings = raw;
|
|
34
|
+
if (!RATING_PROVIDERS.includes(ratings.provider)) {
|
|
35
|
+
fail(`provider must be one of ${RATING_PROVIDERS.join(", ")}`);
|
|
36
|
+
}
|
|
37
|
+
if (typeof ratings.container !== "string" || ratings.container.trim() === "") {
|
|
38
|
+
fail("container must be a non-empty string - a GitHub Discussions category, or a GitLab work item type");
|
|
39
|
+
}
|
|
40
|
+
if (ratings.createBudget !== undefined) {
|
|
41
|
+
if (typeof ratings.createBudget !== "number" || !Number.isInteger(ratings.createBudget)) {
|
|
42
|
+
fail("createBudget must be a whole number");
|
|
43
|
+
}
|
|
44
|
+
if (ratings.createBudget < 0)
|
|
45
|
+
fail("createBudget must not be negative");
|
|
46
|
+
}
|
|
47
|
+
if (ratings.lockThreads !== undefined && typeof ratings.lockThreads !== "boolean") {
|
|
48
|
+
fail("lockThreads must be a boolean");
|
|
49
|
+
}
|
|
50
|
+
// Built key by key rather than spread: the returned value is the four
|
|
51
|
+
// documented keys and nothing else, so an unknown key is ignored in the
|
|
52
|
+
// strong sense — it cannot reach a consumer that happens to look for it.
|
|
53
|
+
return {
|
|
54
|
+
provider: ratings.provider,
|
|
55
|
+
container: ratings.container,
|
|
56
|
+
createBudget: ratings.createBudget ?? DEFAULT_CREATE_BUDGET,
|
|
57
|
+
lockThreads: ratings.lockThreads ?? true,
|
|
58
|
+
};
|
|
59
|
+
}
|
|
60
|
+
/**
|
|
61
|
+
* Read the `ratings` block out of `index.config.json`. A missing file, or a
|
|
62
|
+
* file with no `ratings` block, means ratings are off.
|
|
63
|
+
*/
|
|
64
|
+
export async function loadRatingsConfig(root) {
|
|
65
|
+
let text;
|
|
66
|
+
try {
|
|
67
|
+
text = await fs.readFile(path.join(root, CONFIG_FILE), "utf8");
|
|
68
|
+
}
|
|
69
|
+
catch (err) {
|
|
70
|
+
if (err.code === "ENOENT")
|
|
71
|
+
return undefined;
|
|
72
|
+
throw err;
|
|
73
|
+
}
|
|
74
|
+
let parsed;
|
|
75
|
+
try {
|
|
76
|
+
parsed = JSON.parse(text);
|
|
77
|
+
}
|
|
78
|
+
catch (err) {
|
|
79
|
+
throw new SiteConfigError(`${CONFIG_FILE}: ${err.message}`);
|
|
80
|
+
}
|
|
81
|
+
if (parsed === null || typeof parsed !== "object" || Array.isArray(parsed)) {
|
|
82
|
+
throw new SiteConfigError(`${CONFIG_FILE}: must contain a JSON object`);
|
|
83
|
+
}
|
|
84
|
+
return validateRatings(parsed.ratings);
|
|
85
|
+
}
|
|
86
|
+
//# sourceMappingURL=config.js.map
|