@mark1russell7/site-pages 0.2.13 → 0.2.18
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 +14 -2
- package/build/chunks/build.js +79 -8
- package/build/chunks/build.js.map +1 -1
- package/build/chunks/freshness.d.ts +2 -0
- package/build/chunks/freshness.d.ts.map +1 -1
- package/build/chunks/freshness.js +14 -4
- package/build/chunks/freshness.js.map +1 -1
- package/build/index.d.ts +47 -1
- package/build/index.d.ts.map +1 -1
- package/build/index.js +3 -3
- package/build/vite.d.ts +13 -0
- package/build/vite.d.ts.map +1 -1
- package/build/vite.js +23 -1
- package/build/vite.js.map +1 -1
- package/package.json +1 -1
|
@@ -129,6 +129,8 @@ interface KpiMeasure {
|
|
|
129
129
|
unit?: string;
|
|
130
130
|
/** The ISO time of the data of this measure in UTC. Without it, the measure has the time of the file. */
|
|
131
131
|
at?: string;
|
|
132
|
+
/** The things that this count does not read, in plain words. So a 0 does not seem to tell more than the count measured. */
|
|
133
|
+
notCounted?: string;
|
|
132
134
|
}
|
|
133
135
|
/** One engine that made the data of `kpi.json`. */
|
|
134
136
|
interface KpiEngine {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"freshness.d.ts","names":[],"sources":["../../src/data/types.ts","../../src/data/kpi.ts","../../src/check/freshness.ts"],"mappings":";;;;;UAMiB;EACf;EACA;;;;;;KAWU;;UAGK;;EAEf;;EAEA;;EAEA;;;UAIe;EACf;;EAEA;EACA,MAAM;;EAEN,SAAS;;EAET,OAAO;;;UAIQ;EACf;EACA;;EAEA;;EAEA;EACA;EACA;;;KAIU;EAAmB;EAAiB,UAAU;;EAAsB;EAAkB;;;UAGjF;EACf;EACA;EACA;;;UAIe;EACf,OAAO;EACP;;;KAQU;;UAGK;EACf;EACA;;EAEA;EACA,aAAa;EACb;EACA;;EAEA;;EAEA;;EAEA;EACA,OAAO;;;KAIG;;UAGK;EACf;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA,QAAQ;;;UAIO;EACf;EACA;EACA;EACA;EACA;EACA;EACA;;;UAIe;EACf;EACA;;;KAIU;EACN;EAAiB,cAAc;EAAoB,QAAQ;EAAc,QAAQ;;EACjF;EAAkB;;;UAGP;;EAEf;EACA;EACA;;EAEA;;;UAIe;;EAEf;EACA;;;UAIe;EACf;;EAEA;;EAEA;;EAEA;EACA,UAAU;;EAEV,UAAU;;;UAIK;;EAEf;;EAEA;;EAEA;IAAU;IAAwB;;EAClC,OAAO;EACP,cAAc;EACd,QAAQ;;EAER;IAAU;IAAoB;IAAiB,SAAS;;;EAExD,MAAM;;;
|
|
1
|
+
{"version":3,"file":"freshness.d.ts","names":[],"sources":["../../src/data/types.ts","../../src/data/kpi.ts","../../src/check/freshness.ts"],"mappings":";;;;;UAMiB;EACf;EACA;;;;;;KAWU;;UAGK;;EAEf;;EAEA;;EAEA;;;UAIe;EACf;;EAEA;EACA,MAAM;;EAEN,SAAS;;EAET,OAAO;;;UAIQ;EACf;EACA;;EAEA;;EAEA;EACA;EACA;;;KAIU;EAAmB;EAAiB,UAAU;;EAAsB;EAAkB;;;UAGjF;EACf;EACA;EACA;;;UAIe;EACf,OAAO;EACP;;;KAQU;;UAGK;EACf;EACA;;EAEA;EACA,aAAa;EACb;EACA;;EAEA;;EAEA;;EAEA;EACA,OAAO;;;KAIG;;UAGK;EACf;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA,QAAQ;;;UAIO;EACf;EACA;EACA;EACA;EACA;EACA;EACA;;;UAIe;EACf;EACA;;;KAIU;EACN;EAAiB,cAAc;EAAoB,QAAQ;EAAc,QAAQ;;EACjF;EAAkB;;;UAGP;;EAEf;EACA;EACA;;EAEA;;EAEA;;;UAIe;;EAEf;EACA;;;UAIe;EACf;;EAEA;;EAEA;;EAEA;EACA,UAAU;;EAEV,UAAU;;;UAIK;;EAEf;;EAEA;;EAEA;IAAU;IAAwB;;EAClC,OAAO;EACP,cAAc;EACd,QAAQ;;EAER;IAAU;IAAoB;IAAiB,SAAS;;;EAExD,MAAM;;;cCzKK;;cAGA;;KAGD,WAAW,KAAK;;cAef,iBAAiB;WACnB;EAET,YAAY;;;;;;;;iBA6CE,YAAY,gBAAgB;;;;;;iBAgC5B,QAAQ,OAAO,UAAU,eAAe;;iBASxC,QAAQ,MAAM;;;;;;iBASd,QAAQ,cAAc,eAAe;;;;;;iBAkBrC,SAAS,aAAa,OAAO,UAAU;;cCpI1C;;cAGA;;cAGA;;cAGA;;UAKI;WACN;WACA;;WAEA;;WAEA,SAAS,SAAS;;;UAIZ;;WAEN;;WAEA;;WAEA;;WAEA;;;UAIM;WACN;;WAEA,eAAe;;WAEf,WAAW;;WAEX;;WAEA,QAAQ;;;UAIF;WACN,eAAe;WACf,gBAAgB;;;KAIf;WAA6B;WAAmB,QAAQ;;WAA0B;WAAoB;;;UAGjG;;WAEN;;WAEA,gBAAgB;;WAEhB;;WAEA;;WAEA,YAAY,SAAS;;;UAIf;;WAEN;;WAEA;;;;;;iBAsDK,gBAAgB,eAAe;;iBAwB/B,aAAa,cAAc;;iBAS3B,SAAS,gBAAgB,kBAAkB,cAAc;;UAqDxD;;WAEN;;WAEA;;;;;;iBAOK,UAAU,mBAAmB;;;;;;;;;;iBAsD7B,eAAe,QAAQ,YAAY,SAAS,mBAAmB"}
|
|
@@ -186,6 +186,14 @@ function headingDate(repoDir, version, file) {
|
|
|
186
186
|
const dates = (git(repoDir, "log", "--format=%cI", `-S## ${version}`, "--", file) ?? "").trim().split(/\r?\n/).filter(Boolean);
|
|
187
187
|
return dates[dates.length - 1] ?? null;
|
|
188
188
|
}
|
|
189
|
+
/**
|
|
190
|
+
* This function gives the ISO time of the last commit that changed the `version` line of a `package.json`, or null. The
|
|
191
|
+
* path of the file starts at `repoDir`. A shallow clone can give a later commit, so use the full history. A change of
|
|
192
|
+
* only the comma of that line also counts as a change of the line.
|
|
193
|
+
*/
|
|
194
|
+
function versionCommitTime(repoDir, file) {
|
|
195
|
+
return git(repoDir, "log", "-1", "--format=%cI", "-G^\\s*\"version\":", "HEAD", "--", file)?.trim() || null;
|
|
196
|
+
}
|
|
189
197
|
/** This function gives the full SHA of `HEAD`, or null. */
|
|
190
198
|
function headCommit(repoDir) {
|
|
191
199
|
const sha = git(repoDir, "rev-parse", "HEAD")?.trim();
|
|
@@ -236,7 +244,7 @@ function timeProblems(at, name, now) {
|
|
|
236
244
|
/** This function gives the faults of one measure. */
|
|
237
245
|
function measureProblems(value, index, seen, now) {
|
|
238
246
|
if (!isRecord(value)) return [`Measure ${index} is not an object.`];
|
|
239
|
-
const { id, value: amount, unit, at } = value;
|
|
247
|
+
const { id, value: amount, unit, at, notCounted } = value;
|
|
240
248
|
const problems = [];
|
|
241
249
|
if (typeof id !== "string" || !SLUG.test(id)) problems.push(`Measure ${index} has no id of lowercase words with single hyphens.`);
|
|
242
250
|
else if (seen.has(id)) problems.push(`The id ${id} occurs two times.`);
|
|
@@ -244,6 +252,7 @@ function measureProblems(value, index, seen, now) {
|
|
|
244
252
|
if (typeof amount !== "number" || !Number.isFinite(amount)) problems.push(`Measure ${index} has no finite number as its value.`);
|
|
245
253
|
if (unit !== void 0 && !isText(unit)) problems.push(`Measure ${index} has a unit that is not a text.`);
|
|
246
254
|
if (at !== void 0) problems.push(...timeProblems(at, `The field at of measure ${index}`, now));
|
|
255
|
+
if (notCounted !== void 0 && !isText(notCounted)) problems.push(`Measure ${index} has a field notCounted that is not a text.`);
|
|
247
256
|
return problems;
|
|
248
257
|
}
|
|
249
258
|
/** This function gives the faults of the list of engines. */
|
|
@@ -275,12 +284,13 @@ function kpiProblems(value, now = Date.now()) {
|
|
|
275
284
|
return problems;
|
|
276
285
|
}
|
|
277
286
|
/** This function gives a copy of a measure with only its known fields. */
|
|
278
|
-
function cleanMeasure({ id, value, unit, at }) {
|
|
287
|
+
function cleanMeasure({ id, value, unit, at, notCounted }) {
|
|
279
288
|
return {
|
|
280
289
|
id,
|
|
281
290
|
value,
|
|
282
291
|
...unit === void 0 ? {} : { unit },
|
|
283
|
-
...at === void 0 ? {} : { at }
|
|
292
|
+
...at === void 0 ? {} : { at },
|
|
293
|
+
...notCounted === void 0 ? {} : { notCounted }
|
|
284
294
|
};
|
|
285
295
|
}
|
|
286
296
|
/** This function gives a copy of an engine with only its known fields. */
|
|
@@ -754,6 +764,6 @@ async function runFreshnessCli(argv, io, fetcher) {
|
|
|
754
764
|
io.stdout(`The data stamps of ${parsed.stamps.pages.length} pages pass.\n`);
|
|
755
765
|
return 0;
|
|
756
766
|
}
|
|
757
|
-
export { originRepository as A, readKpi as C, headCommit as D, gitTagDates as E,
|
|
767
|
+
export { originRepository as A, readKpi as C, headCommit as D, gitTagDates as E, kindOf as F, parseVersion as I, versionOfTag as L, NPM_REGISTRY as M, npmPublishTimes as N, headingDate as O, compareVersions as P, kpiText as S, git as T, KPI_FILE as _, checkFreshness as a, kpiFile as b, minorsBehind as c, escapeHtml as d, inlineMarkdown as f, safeUrl as g, plural as h, MAX_MINORS_BEHIND as i, versionCommitTime as j, isShallow as k, parseDataStamps as l, listText as m, MAX_DATA_AGE_DAYS as n, cronLines as o, link as p, MAX_LIMIT_DAYS_WITHOUT_RULING as r, latestAt as s, DATA_STAMP_SCHEMA as t, runFreshnessCli as u, KPI_FORMAT as v, writeKpi as w, kpiProblems as x, KpiError as y };
|
|
758
768
|
|
|
759
769
|
//# sourceMappingURL=freshness.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"freshness.js","names":[],"sources":["../../src/data/versions.ts","../../src/data/npm.ts","../../src/data/git.ts","../../src/data/kpi.ts","../../src/render/html.ts","../../src/check/freshness.ts"],"sourcesContent":["/**\n * Versions: the SemVer parser, the sort, the kind of a version and the version of a git tag.\n */\n\nimport type { ReleaseKind } from \"./types.ts\";\n\n/** The parts of a SemVer 2.0.0 version. */\nexport interface ParsedVersion {\n major: number;\n minor: number;\n patch: number;\n prerelease: string[];\n}\n\nconst SEMVER = /^(0|[1-9]\\d*)\\.(0|[1-9]\\d*)\\.(0|[1-9]\\d*)(?:-([0-9A-Za-z-]+(?:\\.[0-9A-Za-z-]+)*))?(?:\\+[0-9A-Za-z-]+(?:\\.[0-9A-Za-z-]+)*)?$/;\n\n/** This function gives the parts of a version, or null when the text is not a SemVer version. */\nexport function parseVersion(version: string): ParsedVersion | null {\n const match = SEMVER.exec(version);\n if (!match) return null;\n return {\n major: Number(match[1]),\n minor: Number(match[2]),\n patch: Number(match[3]),\n prerelease: match[4] ? match[4].split(\".\") : [],\n };\n}\n\n/**\n * This function compares two versions by the SemVer precedence. It gives a negative number when `a`\n * is older, a positive number when `a` is newer, and 0 for the same version. A text that is not a\n * version comes before each version.\n */\nexport function compareVersions(a: string, b: string): number {\n const x = parseVersion(a);\n const y = parseVersion(b);\n if (!x || !y) return x ? 1 : y ? -1 : a.localeCompare(b);\n const core = x.major - y.major || x.minor - y.minor || x.patch - y.patch;\n if (core !== 0) return core;\n // A prerelease comes before its release: 1.0.0-rc.1 < 1.0.0\n if (x.prerelease.length === 0 || y.prerelease.length === 0) return y.prerelease.length - x.prerelease.length;\n for (let index = 0; index < Math.max(x.prerelease.length, y.prerelease.length); index++) {\n const p = x.prerelease[index];\n const q = y.prerelease[index];\n if (p === undefined) return -1;\n if (q === undefined) return 1;\n if (p === q) continue;\n const pNumber = /^\\d+$/.test(p);\n const qNumber = /^\\d+$/.test(q);\n if (pNumber && qNumber) return Number(p) - Number(q);\n if (pNumber !== qNumber) return pNumber ? -1 : 1;\n return p < q ? -1 : 1;\n }\n return 0;\n}\n\n/**\n * This function gives the kind of a version. The oldest version of the list is \"first\". The caller\n * tells if the version is the oldest.\n */\nexport function kindOf(version: string, oldest: boolean): ReleaseKind {\n if (oldest) return \"first\";\n const parsed = parseVersion(version);\n if (!parsed) return \"patch\";\n if (parsed.major > 0 && parsed.minor === 0 && parsed.patch === 0) return \"major\";\n return parsed.patch === 0 ? \"minor\" : \"patch\";\n}\n\n/** The version of a git tag, and the package that the tag names (null for a tag like `v1.2.3`). */\nexport interface TagVersion {\n version: string;\n package: string | null;\n}\n\n/**\n * This function reads the version of a git tag. It knows `1.2.3`, `v1.2.3` and the tags of Changesets:\n * `name@1.2.3` and `@scope/name@1.2.3`. It gives null for another tag.\n */\nexport function versionOfTag(tag: string): TagVersion | null {\n const match = /^(?:(.+)@)?v?(\\d+\\.\\d+\\.\\d+(?:[-+][0-9A-Za-z.+-]*)?)$/.exec(tag.trim());\n if (!match || match[2] === undefined || !parseVersion(match[2])) return null;\n return { version: match[2], package: match[1] ?? null };\n}\n","/**\n * The npm source: the publication time of each version of some packages. The release data uses\n * these times when git and GitHub give no date for a version.\n */\n\nimport type { NpmPublishTimes, NpmVersionTime } from \"./types.ts\";\n\n/** The public npm registry. */\nexport const NPM_REGISTRY = \"https://registry.npmjs.org\";\n\n/** The options of `npmPublishTimes`. */\nexport interface NpmOptions {\n /** The npm names of the packages, or a scope (for example `@procedures`) for all the packages of the scope. */\n packages: readonly string[];\n /** The `fetch` function. The default is the global `fetch`. */\n fetch?: typeof fetch;\n registry?: string;\n /** The time limit of each request, in milliseconds. The default is 15000. */\n timeoutMs?: number;\n}\n\ntype Json = Record<string, unknown>;\nconst object = (value: unknown): Json => (value !== null && typeof value === \"object\" ? (value as Json) : {});\n\nfunction message(error: unknown): string {\n if (object(error)[\"name\"] === \"TimeoutError\") return \"npm did not answer in time\";\n return error instanceof Error ? error.message : String(error);\n}\n\n/**\n * This function reads the publication time of each version of the packages from the npm registry.\n * A scope (`@name`) gives each package of the scope. It does not throw. Each failure gives a\n * message in `problems`.\n */\nexport async function npmPublishTimes(options: NpmOptions): Promise<NpmPublishTimes> {\n const { registry = NPM_REGISTRY, timeoutMs = 15_000 } = options;\n const fetcher = options.fetch ?? globalThis.fetch;\n const problems: string[] = [];\n const times: NpmVersionTime[] = [];\n if (typeof fetcher !== \"function\") return { times, problems: [\"this Node has no fetch\"] };\n const get = async (url: string): Promise<unknown> => {\n const response = await fetcher(url, { headers: { accept: \"application/json\" }, signal: AbortSignal.timeout(timeoutMs) });\n if (!response.ok) throw new Error(`HTTP ${response.status}`);\n return response.json();\n };\n\n const names = new Set<string>();\n for (const entry of options.packages.map((name) => name.trim()).filter(Boolean)) {\n if (!/^@[^/]+$/.test(entry)) {\n names.add(entry);\n continue;\n }\n try {\n const found = object(await get(`${registry}/-/v1/search?text=scope:${encodeURIComponent(entry.slice(1))}&size=250`));\n const objects = Array.isArray(found[\"objects\"]) ? found[\"objects\"] : [];\n const before = names.size;\n for (const item of objects) {\n const name = object(object(item)[\"package\"])[\"name\"];\n if (typeof name === \"string\" && name.startsWith(`${entry}/`)) names.add(name);\n }\n if (names.size === before) problems.push(`npm has no package in the scope ${entry}`);\n } catch (error) {\n problems.push(`npm did not give the packages of ${entry}: ${message(error)}`);\n }\n }\n\n // Four requests at a time\n const queue = [...names].sort();\n const worker = async (): Promise<void> => {\n for (let name = queue.shift(); name !== undefined; name = queue.shift()) {\n try {\n const packument = object(await get(`${registry}/${name.replaceAll(\"/\", \"%2f\")}`));\n const published = object(packument[\"versions\"]);\n for (const [version, date] of Object.entries(object(packument[\"time\"]))) {\n if (version === \"created\" || version === \"modified\" || typeof date !== \"string\") continue;\n if (!(version in published)) continue;\n times.push({ name, version, date });\n }\n } catch (error) {\n problems.push(`npm did not give the versions of ${name}: ${message(error)}`);\n }\n }\n };\n await Promise.all(Array.from({ length: 4 }, worker));\n times.sort((a, b) => a.name.localeCompare(b.name) || a.date.localeCompare(b.date));\n return { times, problems };\n}\n","/**\n * The git source: the tags with their dates, the commit of the build and the GitHub remote. Each\n * function gives an empty result when git or the repository is not available.\n */\n\nimport { execFileSync } from \"node:child_process\";\n\n/** A git tag and the ISO date of its creation. */\nexport interface TagDate {\n tag: string;\n date: string;\n}\n\n/** This function starts git in a folder and gives its output, or null when git fails. */\nexport function git(repoDir: string, ...args: string[]): string | null {\n try {\n return execFileSync(\"git\", args, { cwd: repoDir, encoding: \"utf8\", stdio: [\"ignore\", \"pipe\", \"ignore\"], windowsHide: true });\n } catch {\n return null;\n }\n}\n\n/** This function tells if the clone is shallow. A shallow clone has not all the tags and commits. */\nexport function isShallow(repoDir: string): boolean {\n return git(repoDir, \"rev-parse\", \"--is-shallow-repository\")?.trim() === \"true\";\n}\n\n/** This function gives each tag of the repository with its date. An annotated tag has its own date. */\nexport function gitTagDates(repoDir: string): TagDate[] {\n const output = git(repoDir, \"for-each-ref\", \"--format=%(refname:strip=2) %(creatordate:iso-strict)\", \"refs/tags\");\n if (!output) return [];\n const tags: TagDate[] = [];\n for (const line of output.split(/\\r?\\n/)) {\n const match = /^(\\S+) (\\S+)$/.exec(line.trim());\n if (match?.[1] && match[2]) tags.push({ tag: match[1], date: match[2] });\n }\n return tags;\n}\n\n/**\n * This function gives the date of the oldest commit that added a version heading to a changelog,\n * or null. The path of the changelog starts at `repoDir`.\n */\nexport function headingDate(repoDir: string, version: string, file: string): string | null {\n const output = git(repoDir, \"log\", \"--format=%cI\", `-S## ${version}`, \"--\", file);\n const dates = (output ?? \"\").trim().split(/\\r?\\n/).filter(Boolean);\n return dates[dates.length - 1] ?? null;\n}\n\n/** This function gives the full SHA of `HEAD`, or null. */\nexport function headCommit(repoDir: string): string | null {\n const sha = git(repoDir, \"rev-parse\", \"HEAD\")?.trim();\n return sha && /^[0-9a-f]{40}$/.test(sha) ? sha : null;\n}\n\n/** This function gives \"owner/name\" of the GitHub remote \"origin\", or null. */\nexport function originRepository(repoDir: string): string | null {\n const url = git(repoDir, \"remote\", \"get-url\", \"origin\")?.trim() ?? \"\";\n const match = /github\\.com[:/]([^/]+)\\/(.+?)(?:\\.git)?\\/?$/.exec(url);\n return match ? `${match[1]}/${match[2]}` : null;\n}\n","/**\n * The file `kpi.json` at the root of a site, format `kpi/1`. A reader outside the site takes the measures of the project\n * from this file. Thus a number on a progress page comes from a record and not from a person.\n *\n * The field `at` is the time of the data and not the time of the build. Thus the writer has no default for it.\n * A measure can have its own `at` when its data is from another time than the rest of the file.\n */\n\nimport { mkdirSync, writeFileSync } from \"node:fs\";\nimport { join } from \"node:path\";\nimport type { KpiEngine, KpiFile, KpiMeasure } from \"./types.ts\";\n\n/** The name of the format. A new format gets a new name, so that an old reader refuses it. */\nexport const KPI_FORMAT = \"kpi/1\";\n\n/** The name of the file at the root of the built site. */\nexport const KPI_FILE = \"kpi.json\";\n\n/** The data of `kpi.json`, without the name of the format. */\nexport type KpiInput = Omit<KpiFile, \"format\">;\n\n/** The form of a project and a measure. A double hyphen separates fields in the file name of a reading. */\nconst SLUG = /^[a-z0-9]+(?:-[a-z0-9]+)*$/;\n\n/** The form of a time in UTC, with or without seconds. */\nconst UTC_TIME = /^\\d{4}-\\d{2}-\\d{2}T\\d{2}:\\d{2}(?::\\d{2}(?:\\.\\d+)?)?Z$/;\n\n/** The form of a full commit. */\nconst COMMIT = /^[0-9a-f]{40}$/;\n\n/** The clock difference between two computers that the check accepts, in milliseconds. */\nconst CLOCK_MARGIN = 5 * 60 * 1000;\n\n/** An error in the data of `kpi.json`. The field `problems` has one line for each fault. */\nexport class KpiError extends Error {\n readonly problems: readonly string[];\n\n constructor(problems: readonly string[]) {\n super(`${KPI_FILE} is not correct: ${problems.join(\" \")}`);\n this.name = \"KpiError\";\n this.problems = problems;\n }\n}\n\nconst isRecord = (value: unknown): value is Record<string, unknown> => typeof value === \"object\" && value !== null && !Array.isArray(value);\n\nconst isText = (value: unknown): value is string => typeof value === \"string\" && value.trim() !== \"\";\n\n/** This function gives the fault of a time of the data, or an empty list. The name tells which field it is. */\nfunction timeProblems(at: unknown, name: string, now: number): string[] {\n if (typeof at !== \"string\" || !UTC_TIME.test(at) || Number.isNaN(Date.parse(at))) return [`${name} is not a time in UTC.`];\n return Date.parse(at) > now + CLOCK_MARGIN ? [`The time of the data ${at} is after the time of the check.`] : [];\n}\n\n/** This function gives the faults of one measure. */\nfunction measureProblems(value: unknown, index: number, seen: Set<string>, now: number): string[] {\n if (!isRecord(value)) return [`Measure ${index} is not an object.`];\n const { id, value: amount, unit, at } = value;\n const problems: string[] = [];\n if (typeof id !== \"string\" || !SLUG.test(id)) problems.push(`Measure ${index} has no id of lowercase words with single hyphens.`);\n else if (seen.has(id)) problems.push(`The id ${id} occurs two times.`);\n else seen.add(id);\n if (typeof amount !== \"number\" || !Number.isFinite(amount)) problems.push(`Measure ${index} has no finite number as its value.`);\n if (unit !== undefined && !isText(unit)) problems.push(`Measure ${index} has a unit that is not a text.`);\n if (at !== undefined) problems.push(...timeProblems(at, `The field at of measure ${index}`, now));\n return problems;\n}\n\n/** This function gives the faults of the list of engines. */\nfunction engineProblems(value: unknown): string[] {\n if (value === undefined) return [];\n if (!Array.isArray(value)) return [\"The field engines is not a list.\"];\n return value.flatMap((engine: unknown, index) => (isRecord(engine) && isText(engine[\"name\"]) && isText(engine[\"version\"]) ? [] : [`Engine ${index} has no name and version.`]));\n}\n\n/**\n * This function gives the faults of a parsed `kpi.json`, or an empty list.\n *\n * @param value - The parsed file.\n * @param now - The time of the check, in milliseconds. A time of the data after this time is a fault.\n */\nexport function kpiProblems(value: unknown, now: number = Date.now()): string[] {\n if (!isRecord(value)) return [\"The file is not an object.\"];\n const problems: string[] = [];\n if (value[\"format\"] !== KPI_FORMAT) problems.push(`The format is not ${KPI_FORMAT}.`);\n const { project, at, commit, measures } = value;\n if (typeof project !== \"string\" || !SLUG.test(project)) problems.push(\"The project is not lowercase words with single hyphens.\");\n problems.push(...timeProblems(at, \"The field at\", now));\n if (typeof commit !== \"string\" || !COMMIT.test(commit)) problems.push(\"The commit is not a full commit.\");\n if (!Array.isArray(measures) || measures.length === 0) problems.push(\"The file has no measures.\");\n else {\n const seen = new Set<string>();\n problems.push(...measures.flatMap((measure: unknown, index) => measureProblems(measure, index, seen, now)));\n }\n problems.push(...engineProblems(value[\"engines\"]));\n return problems;\n}\n\n/** This function gives a copy of a measure with only its known fields. */\nfunction cleanMeasure({ id, value, unit, at }: KpiMeasure): KpiMeasure {\n return { id, value, ...(unit === undefined ? {} : { unit }), ...(at === undefined ? {} : { at }) };\n}\n\n/** This function gives a copy of an engine with only its known fields. */\nfunction cleanEngine({ name, version }: KpiEngine): KpiEngine {\n return { name, version };\n}\n\n/**\n * This function gives the checked content of `kpi.json`. The file has only its known fields, in a fixed order.\n *\n * @throws KpiError when the data is not correct\n */\nexport function kpiFile(input: KpiInput, now?: number): KpiFile {\n const problems = kpiProblems({ format: KPI_FORMAT, ...input }, now);\n if (problems.length > 0) throw new KpiError(problems);\n const { project, at, commit, measures, engines } = input;\n const file: KpiFile = { format: KPI_FORMAT, project, at, commit, measures: measures.map(cleanMeasure) };\n return engines === undefined ? file : { ...file, engines: engines.map(cleanEngine) };\n}\n\n/** This function gives the text of `kpi.json`. */\nexport function kpiText(file: KpiFile): string {\n return `${JSON.stringify(file, null, 2)}\\n`;\n}\n\n/**\n * This function reads the text of a `kpi.json`.\n *\n * @throws KpiError when the text is not correct\n */\nexport function readKpi(text: string, now?: number): KpiFile {\n let value: unknown;\n try {\n value = JSON.parse(text);\n } catch (error) {\n throw new KpiError([`The file is not JSON: ${error instanceof Error ? error.message : String(error)}`]);\n }\n const problems = kpiProblems(value, now);\n if (problems.length > 0) throw new KpiError(problems);\n const { project, at, commit, measures, engines } = value as KpiFile;\n return kpiFile(engines === undefined ? { project, at, commit, measures } : { project, at, commit, measures, engines }, now);\n}\n\n/**\n * This function writes `kpi.json` into the built output folder of a site, and gives the path of the file.\n *\n * @throws KpiError when the data is not correct\n */\nexport function writeKpi(out: string, input: KpiInput, now?: number): string {\n const text = kpiText(kpiFile(input, now));\n mkdirSync(out, { recursive: true });\n const file = join(out, KPI_FILE);\n writeFileSync(file, text);\n return file;\n}\n","/**\n * The HTML helpers of the pages: the escape functions and a small inline Markdown renderer.\n */\n\nconst ESCAPES: Record<string, string> = { \"&\": \"&\", \"<\": \"<\", \">\": \">\", '\"': \""\", \"'\": \"'\" };\n\n/** This function escapes a text for HTML content and for an attribute value in double quotes. */\nexport function escapeHtml(text: string): string {\n return String(text).replace(/[&<>\"']/g, (char) => ESCAPES[char] ?? char);\n}\n\n/**\n * This function removes the control characters and the spaces (U+0000 to U+0020) at the two ends\n * of a text. A loop does it, because the regular expression `[\\u0000- ]+$` backtracks. For a long\n * run of these characters, its time grows with the square of the length.\n */\nfunction trimControl(text: string): string {\n let start = 0;\n let end = text.length;\n while (start < end && text.charCodeAt(start) <= 0x20) start++;\n while (end > start && text.charCodeAt(end - 1) <= 0x20) end--;\n return text.slice(start, end);\n}\n\n/**\n * This function gives a URL that a link can hold, or null. It accepts `http:`, `https:` and\n * `mailto:` URLs, and relative URLs. It refuses each other scheme, for example `javascript:`.\n *\n * The URL parser of a browser removes each tab and line break, and the control characters and\n * spaces at the two ends. This function removes them before the scheme check, as the browser does.\n * Thus a control character before `javascript:` does not hide the scheme.\n */\nexport function safeUrl(url: string): string | null {\n const trimmed = trimControl(url.trim().replace(/[\\t\\n\\r]/g, \"\"));\n if (/^(?:https?:|mailto:)/i.test(trimmed)) return trimmed;\n // A browser reads \"\\\" as \"/\" in a URL of the page, so \"\\\\host\" and \"/\\host\" go to a different host, as \"//host\" does\n if (/^[a-z][a-z0-9+.-]*:/i.test(trimmed) || /^[/\\\\]{2}/.test(trimmed)) return null;\n return trimmed;\n}\n\n/** This function gives an `<a>` element, or the escaped text when the URL is not safe. */\nexport function link(url: string, html: string, attributes = \"\"): string {\n const href = safeUrl(url);\n return href === null ? html : `<a href=\"${escapeHtml(href)}\"${attributes ? ` ${attributes}` : \"\"}>${html}</a>`;\n}\n\n/** The emphasis of a text without code spans or links. The text is already escaped. */\nfunction emphasis(escaped: string): string {\n return escaped\n .replace(/\\*\\*([^*]+?)\\*\\*/g, \"<strong>$1</strong>\")\n .replace(/(^|[^\\w*])\\*([^*\\s][^*]*?)\\*(?![\\w*])/g, \"$1<em>$2</em>\")\n .replace(/(^|[^\\w])_([^_\\s][^_]*?)_(?![\\w])/g, \"$1<em>$2</em>\");\n}\n\nconst INLINE = \"`([^`]+)`|\\\\[((?:[^\\\\]`]|`[^`]*`)+)\\\\]\\\\(([^)\\\\s]+)\\\\)\";\n\n/**\n * This function renders inline Markdown as HTML: code spans, links, strong text and emphasis. It\n * escapes all other text, so the result is safe.\n */\nexport function inlineMarkdown(markdown: string): string {\n const text = String(markdown ?? \"\");\n // A new expression for each call: the text of a link is rendered with a recursive call\n const pattern = new RegExp(INLINE, \"g\");\n let html = \"\";\n let last = 0;\n for (let match = pattern.exec(text); match; match = pattern.exec(text)) {\n html += emphasis(escapeHtml(text.slice(last, match.index)));\n if (match[1] !== undefined) html += `<code>${escapeHtml(match[1])}</code>`;\n else html += link(match[3] ?? \"\", inlineMarkdown(match[2] ?? \"\"));\n last = match.index + match[0].length;\n }\n return html + emphasis(escapeHtml(text.slice(last)));\n}\n\n/** This function gives \"1 item\" or \"2 items\". */\nexport function plural(count: number, word: string, many = `${word}s`): string {\n return `${count} ${count === 1 ? word : many}`;\n}\n\n/** This function gives \"a\", \"a and b\" or \"a, b and c\". */\nexport function listText(parts: readonly string[]): string {\n if (parts.length <= 1) return parts.join(\"\");\n return `${parts.slice(0, -1).join(\", \")} and ${parts[parts.length - 1]}`;\n}\n","/**\n * The freshness gate of a measured page. A site keeps a file of data stamps.\n * Each stamp names the page, its runs, their dates and their engine versions.\n * The gate fails on a page with no versions or no run date, and on old data with no newer run in flight.\n * The gate also fails on a run that started behind the engines on npm.\n * A fixed study has no age limit. A page with a longer limit names the reason and the scheduled workflow that meets the limit.\n * This file imports only Node.js and the folder `data`, so `reusable-pages.yml` starts it with no npm release of the package.\n */\n\nimport { readFileSync } from \"node:fs\";\nimport { isAbsolute, join } from \"node:path\";\nimport { parseArgs } from \"node:util\";\nimport { npmPublishTimes } from \"../data/npm.ts\";\nimport type { NpmVersionTime } from \"../data/types.ts\";\nimport { compareVersions, parseVersion } from \"../data/versions.ts\";\nimport type { CheckIo } from \"./check.ts\";\n\n/** The schema of a file of data stamps. */\nexport const DATA_STAMP_SCHEMA = \"data-stamp/1\";\n\n/** The largest age of the data of a page, in days, when no newer run is in flight. */\nexport const MAX_DATA_AGE_DAYS = 3;\n\n/** The most minor versions that an engine of a run can be behind npm at the start of the run. */\nexport const MAX_MINORS_BEHIND = 1;\n\n/** The longest limit of a page, in days, with no ruling for that site. */\nexport const MAX_LIMIT_DAYS_WITHOUT_RULING = 14;\n\nconst DAY_MS = 86_400_000;\n\n/** One run that gives the data of a page. */\nexport interface DataRun {\n readonly id: string;\n readonly url: string;\n /** The ISO time of the start of the run. */\n readonly date: string;\n /** The version of each engine of the run, by its npm name. */\n readonly engines: Readonly<Record<string, string>>;\n}\n\n/** The age limit of one page, in place of `MAX_DATA_AGE_DAYS`. */\nexport interface DataLimit {\n /** The largest age of the data, in days. */\n readonly days: number;\n /** The reason for the limit, as one sentence. The page shows it. */\n readonly reason: string;\n /** The path of the workflow from the root of the repository. Its schedule must meet the limit. */\n readonly workflow: string;\n /** The link to the public ruling. A limit over `MAX_LIMIT_DAYS_WITHOUT_RULING` needs it. */\n readonly ruling?: string;\n}\n\n/** The stamp of one measured page. */\nexport interface PageStamp {\n readonly page: string;\n /** Each run that gives data to the page. */\n readonly runs: readonly DataRun[];\n /** The run on newer engines that is in progress. */\n readonly inFlight?: DataRun;\n /** The page shows a fixed study of the versions of its runs, so its data has no age limit. It makes no claim about a newer release. */\n readonly study?: true;\n /** The age limit of this page, in place of the default. */\n readonly limit?: DataLimit;\n}\n\n/** The content of a file of data stamps. */\nexport interface DataStamps {\n readonly schema: typeof DATA_STAMP_SCHEMA;\n readonly pages: readonly PageStamp[];\n}\n\n/** The result of `parseDataStamps`. */\nexport type DataStampsParse = { readonly ok: true; readonly stamps: DataStamps } | { readonly ok: false; readonly faults: readonly string[] };\n\n/** The options of `checkFreshness`. */\nexport interface FreshnessOptions {\n /** The ISO time of the check. */\n readonly now: string;\n /** The publication times of the engines on npm. */\n readonly times: readonly NpmVersionTime[];\n /** The limit of the age. The default is `MAX_DATA_AGE_DAYS`. */\n readonly maxAgeDays?: number;\n /** The limit of the minor versions. The default is `MAX_MINORS_BEHIND`. */\n readonly maxMinorsBehind?: number;\n /** The schedule of each workflow of a limit. A number is the largest gap between its starts in days. A text tells why the gate cannot read it. */\n readonly schedules?: Readonly<Record<string, number | string>>;\n}\n\n/** The result of `checkFreshness`. */\nexport interface FreshnessCheck {\n /** Each break of a rule. One fault fails the gate. */\n readonly faults: readonly string[];\n /** Each rule that the gate could not run, with the reason. */\n readonly notRun: readonly string[];\n}\n\nconst record = (value: unknown): Record<string, unknown> | null => (value !== null && typeof value === \"object\" && !Array.isArray(value) ? (value as Record<string, unknown>) : null);\n\n/** This function gives the faults of one run of a stamp. */\nfunction runFaults(raw: unknown, at: string): string[] {\n const run = record(raw);\n if (run === null) return [`${at}: a run is not an object.`];\n const faults: string[] = [];\n if (typeof run[\"id\"] !== \"string\" || run[\"id\"] === \"\") faults.push(`${at}: a run has no id.`);\n const label = `${at}, run ${String(run[\"id\"])}`;\n if (typeof run[\"url\"] !== \"string\" || !run[\"url\"].startsWith(\"https://\")) faults.push(`${label}: the run has no https link.`);\n if (typeof run[\"date\"] !== \"string\" || !Number.isFinite(Date.parse(run[\"date\"]))) faults.push(`${label}: the run has no date.`);\n const engines = record(run[\"engines\"]);\n if (engines === null || Object.keys(engines).length === 0) return [...faults, `${label}: the run names no engine versions.`];\n for (const [name, version] of Object.entries(engines)) {\n if (typeof version !== \"string\" || parseVersion(version) === null) faults.push(`${label}: the version of ${name} is not a version.`);\n }\n return faults;\n}\n\n/** This function gives the faults of the age limit of one page. */\nfunction limitFaults(raw: unknown, at: string): string[] {\n const limit = record(raw);\n if (limit === null) return [`${at}: the limit is not an object.`];\n const faults: string[] = [];\n const days = limit[\"days\"];\n if (typeof days !== \"number\" || !Number.isFinite(days) || days <= 0) faults.push(`${at}: the limit has no count of days.`);\n if (typeof limit[\"reason\"] !== \"string\" || limit[\"reason\"].trim() === \"\") faults.push(`${at}: the limit has no reason.`);\n if (typeof limit[\"workflow\"] !== \"string\" || limit[\"workflow\"] === \"\") faults.push(`${at}: the limit names no workflow.`);\n const ruling = limit[\"ruling\"];\n if (ruling !== undefined && (typeof ruling !== \"string\" || !ruling.startsWith(\"https://\"))) faults.push(`${at}: the ruling of the limit has no https link.`);\n if (typeof days === \"number\" && days > MAX_LIMIT_DAYS_WITHOUT_RULING && ruling === undefined) {\n faults.push(`${at}: a limit over ${MAX_LIMIT_DAYS_WITHOUT_RULING} days needs a ruling for this site.`);\n }\n return faults;\n}\n\n/** This function gives the faults of the fields of a page that change the age rule. */\nfunction ageRuleFaults(page: Record<string, unknown>, at: string): string[] {\n const faults: string[] = [];\n const study = page[\"study\"];\n if (study !== undefined && study !== true) faults.push(`${at}: the field study is not true.`);\n if (study === true && page[\"limit\"] !== undefined) faults.push(`${at}: a fixed study has no limit.`);\n if (study === true && page[\"inFlight\"] !== undefined) faults.push(`${at}: a fixed study has no run in flight.`);\n if (page[\"limit\"] !== undefined) faults.push(...limitFaults(page[\"limit\"], at));\n return faults;\n}\n\n/**\n * This function reads the content of a file of data stamps. It gives each fault of the content, so a page with no versions\n * or no run date fails here.\n */\nexport function parseDataStamps(raw: unknown): DataStampsParse {\n const file = record(raw);\n if (file === null) return { ok: false, faults: [\"The file is not an object.\"] };\n if (file[\"schema\"] !== DATA_STAMP_SCHEMA) return { ok: false, faults: [`The schema is not ${DATA_STAMP_SCHEMA}.`] };\n const pages = file[\"pages\"];\n if (!Array.isArray(pages) || pages.length === 0) return { ok: false, faults: [\"The file has no pages.\"] };\n const faults: string[] = [];\n for (const item of pages) {\n const page = record(item);\n if (page === null || typeof page[\"page\"] !== \"string\" || page[\"page\"] === \"\") {\n faults.push(\"A stamp has no page.\");\n continue;\n }\n const at = page[\"page\"];\n const runs = page[\"runs\"];\n if (!Array.isArray(runs) || runs.length === 0) faults.push(`${at}: the page names no run.`);\n else for (const run of runs) faults.push(...runFaults(run, at));\n if (page[\"inFlight\"] !== undefined) faults.push(...runFaults(page[\"inFlight\"], `${at}, in flight`));\n faults.push(...ageRuleFaults(page, at));\n }\n return faults.length > 0 ? { ok: false, faults } : { ok: true, stamps: raw as DataStamps };\n}\n\n/** This function gives the count of minor versions from `used` to `latest`. A newer major version counts as no limit. */\nexport function minorsBehind(used: string, latest: string): number {\n const a = parseVersion(used);\n const b = parseVersion(latest);\n if (a === null || b === null) return Number.POSITIVE_INFINITY;\n if (a.major !== b.major) return b.major > a.major ? Number.POSITIVE_INFINITY : 0;\n return Math.max(0, b.minor - a.minor);\n}\n\n/** This function gives the newest stable version of a package on npm at a time, or undefined when npm has none. */\nexport function latestAt(times: readonly NpmVersionTime[], name: string, date: string): string | undefined {\n const limit = Date.parse(date);\n return times\n .filter((t) => t.name === name && Date.parse(t.date) <= limit && parseVersion(t.version)?.prerelease.length === 0)\n .map((t) => t.version)\n .sort(compareVersions)\n .at(-1);\n}\n\nconst ageInDays = (date: string, now: number): number => (now - Date.parse(date)) / DAY_MS;\n\n/** This function gives the values of one field of a cron line, or undefined for a form that it cannot read. */\nfunction cronValues(field: string, low: number, high: number): Set<number> | undefined {\n const values = new Set<number>();\n for (const part of field.split(\",\")) {\n const match = /^(\\*|\\d+(?:-\\d+)?)(?:\\/(\\d+))?$/.exec(part);\n if (match === null) return undefined;\n const [, range = \"\", step = \"1\"] = match;\n const [from = low, to = from] = range === \"*\" ? [low, high] : range.split(\"-\").map(Number);\n const by = Number(step);\n if (by <= 0 || from < low || to > high || from > to) return undefined;\n for (let value = from; value <= to; value += by) values.add(value);\n }\n return values;\n}\n\n/** This function gives the start of each day of a cron line, in a span of two years. It gives undefined for a line that it cannot read. */\nfunction cronDays(line: string, from: number, days: number): number[] | undefined {\n const fields = line.trim().split(/\\s+/);\n if (fields.length !== 5) return undefined;\n const [minute = \"\", hour = \"\", dayOfMonth = \"\", month = \"\", dayOfWeek = \"\"] = fields;\n const minutes = cronValues(minute, 0, 59);\n const hours = cronValues(hour, 0, 23);\n const monthDays = cronValues(dayOfMonth, 1, 31);\n const months = cronValues(month, 1, 12);\n const weekDays = cronValues(dayOfWeek, 0, 6);\n if (minutes === undefined || hours === undefined || monthDays === undefined || months === undefined || weekDays === undefined) return undefined;\n const firstTime = (Math.min(...hours) * 60 + Math.min(...minutes)) * 60_000;\n const starts: number[] = [];\n for (let day = 0; day < days; day += 1) {\n const time = from + day * DAY_MS;\n const date = new Date(time);\n if (!months.has(date.getUTCMonth() + 1)) continue;\n const onMonthDay = monthDays.has(date.getUTCDate());\n const onWeekDay = weekDays.has(date.getUTCDay());\n // A cron line that sets both day fields starts on a day that matches one of them.\n const both = dayOfMonth !== \"*\" && dayOfWeek !== \"*\";\n if (both ? onMonthDay || onWeekDay : onMonthDay && onWeekDay) starts.push(time + firstTime);\n }\n return starts;\n}\n\n/** One schedule of a workflow. */\nexport interface CronLine {\n /** The number of the line in the file, from 1. */\n readonly line: number;\n /** The cron text, with no quotes and no comment. */\n readonly cron: string;\n}\n\n/**\n * This function gives each line `cron:` of a workflow. It is the one reader of these lines: the freshness gate and the rule `runner-budget` of conventions use it.\n * It skips a comment line. It reads a quoted key, a list item, a quoted value and a comment at the end of the line.\n */\nexport function cronLines(workflow: string): CronLine[] {\n return workflow.split(/\\r?\\n/).flatMap((raw, index) => {\n const text = raw.trim().replace(/^- */, \"\");\n const key = [\"cron:\", '\"cron\":', \"'cron':\"].find((form) => text.startsWith(form));\n if (key === undefined) return [];\n // YAML starts a comment at a \"#\" after a space or a tab.\n const rest = text.slice(key.length);\n const comment = rest.search(/\\s#/);\n const value = (comment >= 0 ? rest.slice(0, comment) : rest).trim();\n const quote = value.charAt(0);\n const quoted = value.length >= 2 && (quote === '\"' || quote === \"'\") && value.endsWith(quote);\n return [{ line: index + 1, cron: quoted ? value.slice(1, -1).trim() : value }];\n });\n}\n\n/**\n * This function gives the largest gap between the scheduled starts of a workflow, in days. The gap counts from the first start of a day.\n * It gives a text in place of the gap for a workflow with no schedule, and for a line that it cannot read. The text names the line, and the gate fails on it.\n */\nexport function scheduleGapDays(workflow: string): number | string {\n const lines = cronLines(workflow).map((item) => item.cron);\n if (lines.length === 0) return \"the workflow has no line `cron:`\";\n const from = Date.UTC(2027, 0, 1);\n const span = 731;\n const starts: number[] = [];\n for (const line of lines) {\n const days = cronDays(line, from, span);\n if (days === undefined) return `the gate cannot read the line \\`cron: ${line}\\``;\n starts.push(...days);\n }\n if (starts.length < 2) return `the lines \\`cron: ${lines.join(\"`, `cron: \")}\\` give fewer than two starts in two years`;\n starts.sort((a, b) => a - b);\n let largest = 0;\n for (let index = 1; index < starts.length; index += 1) largest = Math.max(largest, ((starts[index] ?? 0) - (starts[index - 1] ?? 0)) / DAY_MS);\n return largest;\n}\n\n/** This function tells if a run uses a newer version of an engine than another run. */\nfunction hasNewerEngine(run: DataRun, than: DataRun): boolean {\n return Object.entries(run.engines).some(([name, version]) => {\n const old = than.engines[name];\n return old !== undefined && compareVersions(version, old) > 0;\n });\n}\n\n/**\n * This function applies the rules of the gate to the stamps.\n *\n * - The data of each page is at most `maxAgeDays` old, or a run on newer engines is in flight.\n * - The engines of the newest run are at most `maxMinorsBehind` behind npm at the start of that run.\n * - A run in flight has the same age limit, so an old stamp does not hold the gate open.\n * - A page with its own limit uses that limit, and the schedule of its workflow meets the limit.\n * - A fixed study has no age limit and no limit of the engines, because it tells about the versions that it names.\n */\nexport function checkFreshness(stamps: DataStamps, options: FreshnessOptions): FreshnessCheck {\n const { times, maxAgeDays: defaultAge = MAX_DATA_AGE_DAYS, maxMinorsBehind = MAX_MINORS_BEHIND, schedules = {} } = options;\n const now = Date.parse(options.now);\n const faults: string[] = [];\n const notRun: string[] = [];\n for (const stamp of stamps.pages) {\n for (const run of [...stamp.runs, ...(stamp.inFlight === undefined ? [] : [stamp.inFlight])]) {\n if (ageInDays(run.date, now) < 0) faults.push(`${stamp.page}: the run ${run.id} has a date after the check.`);\n }\n if (stamp.study === true) continue;\n const maxAgeDays = stamp.limit?.days ?? defaultAge;\n if (stamp.limit !== undefined) {\n const gap = schedules[stamp.limit.workflow];\n if (gap === undefined || typeof gap === \"string\") faults.push(`${stamp.page}: the limit of ${maxAgeDays} days has no schedule that the gate can read in ${stamp.limit.workflow}: ${gap ?? \"the check has no read of this workflow\"}.`);\n else if (gap > maxAgeDays) faults.push(`${stamp.page}: the schedule of ${stamp.limit.workflow} has a gap of ${gap.toFixed(1)} days, over the limit of ${maxAgeDays}.`);\n }\n const flight = stamp.inFlight;\n const flying = flight !== undefined && ageInDays(flight.date, now) <= maxAgeDays;\n if (flight !== undefined && !flying) faults.push(`${stamp.page}: the run ${flight.id} is in flight for more than ${maxAgeDays} days.`);\n for (const run of stamp.runs) {\n const age = ageInDays(run.date, now);\n if (age > maxAgeDays && !(flying && hasNewerEngine(flight, run))) {\n faults.push(`${stamp.page}: the run ${run.id} is ${age.toFixed(1)} days old, over the limit of ${maxAgeDays}, and no run on newer engines is in flight.`);\n }\n }\n const newest = flight ?? [...stamp.runs].sort((a, b) => Date.parse(a.date) - Date.parse(b.date)).at(-1);\n if (newest === undefined) continue;\n for (const [name, version] of Object.entries(newest.engines)) {\n const latest = latestAt(times, name, newest.date);\n if (latest === undefined) {\n notRun.push(`${stamp.page}: npm gives no version of ${name} at the start of the run ${newest.id}.`);\n } else if (minorsBehind(version, latest) > maxMinorsBehind) {\n faults.push(`${stamp.page}: the run ${newest.id} used ${name} ${version}, and npm had ${latest} at its start. The limit is ${maxMinorsBehind} minor version behind.`);\n }\n }\n }\n return { faults, notRun };\n}\n\n/** The help of the command `site-pages freshness`. */\nexport const FRESHNESS_HELP: string = `Usage: site-pages freshness --stamps <file> [options]\n\nExamine the data stamps of the measured pages of a site.\n\n --stamps <file> The file of data stamps, schema ${DATA_STAMP_SCHEMA}. (necessary)\n --now <time> The ISO time of the check. The default is the time of the call.\n --help Show this text.\n\nThe exit code is 0 when the stamps pass, 1 for a fault, and 2 for an incorrect command line.\n`;\n\n/** This function gives the largest gap of the schedule in a workflow file. It gives a text for a file that it cannot read. */\nfunction readSchedule(path: string): number | string {\n try {\n return scheduleGapDays(readFileSync(path, \"utf8\"));\n } catch (error) {\n return `the file cannot be read: ${error instanceof Error ? error.message : String(error)}`;\n }\n}\n\n/** This function is the command `site-pages freshness`. The `fetch` option is for a test. */\nexport async function runFreshnessCli(argv: readonly string[], io: CheckIo, fetcher?: typeof fetch): Promise<number> {\n let values: { stamps?: string | undefined; now?: string | undefined; help?: boolean | undefined };\n try {\n ({ values } = parseArgs({ args: [...argv], allowPositionals: false, options: { stamps: { type: \"string\" }, now: { type: \"string\" }, help: { type: \"boolean\" } } }));\n } catch (error) {\n io.stderr(`site-pages freshness: ${error instanceof Error ? error.message : String(error)}\\n\\n${FRESHNESS_HELP}`);\n return 2;\n }\n if (values.help === true) {\n io.stdout(FRESHNESS_HELP);\n return 0;\n }\n if (values.stamps === undefined) {\n io.stderr(`site-pages freshness: --stamps is necessary.\\n\\n${FRESHNESS_HELP}`);\n return 2;\n }\n const path = isAbsolute(values.stamps) ? values.stamps : join(io.cwd, values.stamps);\n let raw: unknown;\n try {\n raw = JSON.parse(readFileSync(path, \"utf8\"));\n } catch (error) {\n io.stderr(`site-pages freshness: the file ${values.stamps} cannot be read: ${error instanceof Error ? error.message : String(error)}\\n`);\n return 1;\n }\n const parsed = parseDataStamps(raw);\n if (!parsed.ok) {\n for (const fault of parsed.faults) io.stderr(`${fault}\\n`);\n return 1;\n }\n const engines = parsed.stamps.pages\n .filter((p) => p.study !== true)\n .flatMap((p) => [...p.runs, ...(p.inFlight === undefined ? [] : [p.inFlight])])\n .flatMap((r) => Object.keys(r.engines));\n const schedules: Record<string, number | string> = {};\n for (const page of parsed.stamps.pages) {\n if (page.limit !== undefined) schedules[page.limit.workflow] = readSchedule(join(io.cwd, page.limit.workflow));\n }\n const npm = await npmPublishTimes({ packages: [...new Set(engines)], ...(fetcher === undefined ? {} : { fetch: fetcher }) });\n const result = checkFreshness(parsed.stamps, { now: values.now ?? new Date().toISOString(), times: npm.times, schedules });\n for (const problem of [...npm.problems, ...result.notRun]) io.stdout(`Not run: ${problem}\\n`);\n for (const fault of result.faults) io.stderr(`${fault}\\n`);\n if (result.faults.length > 0) return 1;\n io.stdout(`The data stamps of ${parsed.stamps.pages.length} pages pass.\\n`);\n return 0;\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"freshness.js","names":[],"sources":["../../src/data/versions.ts","../../src/data/npm.ts","../../src/data/git.ts","../../src/data/kpi.ts","../../src/render/html.ts","../../src/check/freshness.ts"],"sourcesContent":["/**\n * Versions: the SemVer parser, the sort, the kind of a version and the version of a git tag.\n */\n\nimport type { ReleaseKind } from \"./types.ts\";\n\n/** The parts of a SemVer 2.0.0 version. */\nexport interface ParsedVersion {\n major: number;\n minor: number;\n patch: number;\n prerelease: string[];\n}\n\nconst SEMVER = /^(0|[1-9]\\d*)\\.(0|[1-9]\\d*)\\.(0|[1-9]\\d*)(?:-([0-9A-Za-z-]+(?:\\.[0-9A-Za-z-]+)*))?(?:\\+[0-9A-Za-z-]+(?:\\.[0-9A-Za-z-]+)*)?$/;\n\n/** This function gives the parts of a version, or null when the text is not a SemVer version. */\nexport function parseVersion(version: string): ParsedVersion | null {\n const match = SEMVER.exec(version);\n if (!match) return null;\n return {\n major: Number(match[1]),\n minor: Number(match[2]),\n patch: Number(match[3]),\n prerelease: match[4] ? match[4].split(\".\") : [],\n };\n}\n\n/**\n * This function compares two versions by the SemVer precedence. It gives a negative number when `a`\n * is older, a positive number when `a` is newer, and 0 for the same version. A text that is not a\n * version comes before each version.\n */\nexport function compareVersions(a: string, b: string): number {\n const x = parseVersion(a);\n const y = parseVersion(b);\n if (!x || !y) return x ? 1 : y ? -1 : a.localeCompare(b);\n const core = x.major - y.major || x.minor - y.minor || x.patch - y.patch;\n if (core !== 0) return core;\n // A prerelease comes before its release: 1.0.0-rc.1 < 1.0.0\n if (x.prerelease.length === 0 || y.prerelease.length === 0) return y.prerelease.length - x.prerelease.length;\n for (let index = 0; index < Math.max(x.prerelease.length, y.prerelease.length); index++) {\n const p = x.prerelease[index];\n const q = y.prerelease[index];\n if (p === undefined) return -1;\n if (q === undefined) return 1;\n if (p === q) continue;\n const pNumber = /^\\d+$/.test(p);\n const qNumber = /^\\d+$/.test(q);\n if (pNumber && qNumber) return Number(p) - Number(q);\n if (pNumber !== qNumber) return pNumber ? -1 : 1;\n return p < q ? -1 : 1;\n }\n return 0;\n}\n\n/**\n * This function gives the kind of a version. The oldest version of the list is \"first\". The caller\n * tells if the version is the oldest.\n */\nexport function kindOf(version: string, oldest: boolean): ReleaseKind {\n if (oldest) return \"first\";\n const parsed = parseVersion(version);\n if (!parsed) return \"patch\";\n if (parsed.major > 0 && parsed.minor === 0 && parsed.patch === 0) return \"major\";\n return parsed.patch === 0 ? \"minor\" : \"patch\";\n}\n\n/** The version of a git tag, and the package that the tag names (null for a tag like `v1.2.3`). */\nexport interface TagVersion {\n version: string;\n package: string | null;\n}\n\n/**\n * This function reads the version of a git tag. It knows `1.2.3`, `v1.2.3` and the tags of Changesets:\n * `name@1.2.3` and `@scope/name@1.2.3`. It gives null for another tag.\n */\nexport function versionOfTag(tag: string): TagVersion | null {\n const match = /^(?:(.+)@)?v?(\\d+\\.\\d+\\.\\d+(?:[-+][0-9A-Za-z.+-]*)?)$/.exec(tag.trim());\n if (!match || match[2] === undefined || !parseVersion(match[2])) return null;\n return { version: match[2], package: match[1] ?? null };\n}\n","/**\n * The npm source: the publication time of each version of some packages. The release data uses\n * these times when git and GitHub give no date for a version.\n */\n\nimport type { NpmPublishTimes, NpmVersionTime } from \"./types.ts\";\n\n/** The public npm registry. */\nexport const NPM_REGISTRY = \"https://registry.npmjs.org\";\n\n/** The options of `npmPublishTimes`. */\nexport interface NpmOptions {\n /** The npm names of the packages, or a scope (for example `@procedures`) for all the packages of the scope. */\n packages: readonly string[];\n /** The `fetch` function. The default is the global `fetch`. */\n fetch?: typeof fetch;\n registry?: string;\n /** The time limit of each request, in milliseconds. The default is 15000. */\n timeoutMs?: number;\n}\n\ntype Json = Record<string, unknown>;\nconst object = (value: unknown): Json => (value !== null && typeof value === \"object\" ? (value as Json) : {});\n\nfunction message(error: unknown): string {\n if (object(error)[\"name\"] === \"TimeoutError\") return \"npm did not answer in time\";\n return error instanceof Error ? error.message : String(error);\n}\n\n/**\n * This function reads the publication time of each version of the packages from the npm registry.\n * A scope (`@name`) gives each package of the scope. It does not throw. Each failure gives a\n * message in `problems`.\n */\nexport async function npmPublishTimes(options: NpmOptions): Promise<NpmPublishTimes> {\n const { registry = NPM_REGISTRY, timeoutMs = 15_000 } = options;\n const fetcher = options.fetch ?? globalThis.fetch;\n const problems: string[] = [];\n const times: NpmVersionTime[] = [];\n if (typeof fetcher !== \"function\") return { times, problems: [\"this Node has no fetch\"] };\n const get = async (url: string): Promise<unknown> => {\n const response = await fetcher(url, { headers: { accept: \"application/json\" }, signal: AbortSignal.timeout(timeoutMs) });\n if (!response.ok) throw new Error(`HTTP ${response.status}`);\n return response.json();\n };\n\n const names = new Set<string>();\n for (const entry of options.packages.map((name) => name.trim()).filter(Boolean)) {\n if (!/^@[^/]+$/.test(entry)) {\n names.add(entry);\n continue;\n }\n try {\n const found = object(await get(`${registry}/-/v1/search?text=scope:${encodeURIComponent(entry.slice(1))}&size=250`));\n const objects = Array.isArray(found[\"objects\"]) ? found[\"objects\"] : [];\n const before = names.size;\n for (const item of objects) {\n const name = object(object(item)[\"package\"])[\"name\"];\n if (typeof name === \"string\" && name.startsWith(`${entry}/`)) names.add(name);\n }\n if (names.size === before) problems.push(`npm has no package in the scope ${entry}`);\n } catch (error) {\n problems.push(`npm did not give the packages of ${entry}: ${message(error)}`);\n }\n }\n\n // Four requests at a time\n const queue = [...names].sort();\n const worker = async (): Promise<void> => {\n for (let name = queue.shift(); name !== undefined; name = queue.shift()) {\n try {\n const packument = object(await get(`${registry}/${name.replaceAll(\"/\", \"%2f\")}`));\n const published = object(packument[\"versions\"]);\n for (const [version, date] of Object.entries(object(packument[\"time\"]))) {\n if (version === \"created\" || version === \"modified\" || typeof date !== \"string\") continue;\n if (!(version in published)) continue;\n times.push({ name, version, date });\n }\n } catch (error) {\n problems.push(`npm did not give the versions of ${name}: ${message(error)}`);\n }\n }\n };\n await Promise.all(Array.from({ length: 4 }, worker));\n times.sort((a, b) => a.name.localeCompare(b.name) || a.date.localeCompare(b.date));\n return { times, problems };\n}\n","/**\n * The git source: the tags with their dates, the commit of the build and the GitHub remote. Each\n * function gives an empty result when git or the repository is not available.\n */\n\nimport { execFileSync } from \"node:child_process\";\n\n/** A git tag and the ISO date of its creation. */\nexport interface TagDate {\n tag: string;\n date: string;\n}\n\n/** This function starts git in a folder and gives its output, or null when git fails. */\nexport function git(repoDir: string, ...args: string[]): string | null {\n try {\n return execFileSync(\"git\", args, { cwd: repoDir, encoding: \"utf8\", stdio: [\"ignore\", \"pipe\", \"ignore\"], windowsHide: true });\n } catch {\n return null;\n }\n}\n\n/** This function tells if the clone is shallow. A shallow clone has not all the tags and commits. */\nexport function isShallow(repoDir: string): boolean {\n return git(repoDir, \"rev-parse\", \"--is-shallow-repository\")?.trim() === \"true\";\n}\n\n/** This function gives each tag of the repository with its date. An annotated tag has its own date. */\nexport function gitTagDates(repoDir: string): TagDate[] {\n const output = git(repoDir, \"for-each-ref\", \"--format=%(refname:strip=2) %(creatordate:iso-strict)\", \"refs/tags\");\n if (!output) return [];\n const tags: TagDate[] = [];\n for (const line of output.split(/\\r?\\n/)) {\n const match = /^(\\S+) (\\S+)$/.exec(line.trim());\n if (match?.[1] && match[2]) tags.push({ tag: match[1], date: match[2] });\n }\n return tags;\n}\n\n/**\n * This function gives the date of the oldest commit that added a version heading to a changelog,\n * or null. The path of the changelog starts at `repoDir`.\n */\nexport function headingDate(repoDir: string, version: string, file: string): string | null {\n const output = git(repoDir, \"log\", \"--format=%cI\", `-S## ${version}`, \"--\", file);\n const dates = (output ?? \"\").trim().split(/\\r?\\n/).filter(Boolean);\n return dates[dates.length - 1] ?? null;\n}\n\n/**\n * This function gives the ISO time of the last commit that changed the `version` line of a `package.json`, or null. The\n * path of the file starts at `repoDir`. A shallow clone can give a later commit, so use the full history. A change of\n * only the comma of that line also counts as a change of the line.\n */\nexport function versionCommitTime(repoDir: string, file: string): string | null {\n return git(repoDir, \"log\", \"-1\", \"--format=%cI\", '-G^\\\\s*\"version\":', \"HEAD\", \"--\", file)?.trim() || null;\n}\n\n/** This function gives the full SHA of `HEAD`, or null. */\nexport function headCommit(repoDir: string): string | null {\n const sha = git(repoDir, \"rev-parse\", \"HEAD\")?.trim();\n return sha && /^[0-9a-f]{40}$/.test(sha) ? sha : null;\n}\n\n/** This function gives \"owner/name\" of the GitHub remote \"origin\", or null. */\nexport function originRepository(repoDir: string): string | null {\n const url = git(repoDir, \"remote\", \"get-url\", \"origin\")?.trim() ?? \"\";\n const match = /github\\.com[:/]([^/]+)\\/(.+?)(?:\\.git)?\\/?$/.exec(url);\n return match ? `${match[1]}/${match[2]}` : null;\n}\n","/**\n * The file `kpi.json` at the root of a site, format `kpi/1`. A reader outside the site takes the measures of the project\n * from this file. Thus a number on a progress page comes from a record and not from a person.\n *\n * The field `at` is the time of the data and not the time of the build. Thus the writer has no default for it.\n * A measure can have its own `at` when its data is from another time than the rest of the file.\n */\n\nimport { mkdirSync, writeFileSync } from \"node:fs\";\nimport { join } from \"node:path\";\nimport type { KpiEngine, KpiFile, KpiMeasure } from \"./types.ts\";\n\n/** The name of the format. A new format gets a new name, so that an old reader refuses it. */\nexport const KPI_FORMAT = \"kpi/1\";\n\n/** The name of the file at the root of the built site. */\nexport const KPI_FILE = \"kpi.json\";\n\n/** The data of `kpi.json`, without the name of the format. */\nexport type KpiInput = Omit<KpiFile, \"format\">;\n\n/** The form of a project and a measure. A double hyphen separates fields in the file name of a reading. */\nconst SLUG = /^[a-z0-9]+(?:-[a-z0-9]+)*$/;\n\n/** The form of a time in UTC, with or without seconds. */\nconst UTC_TIME = /^\\d{4}-\\d{2}-\\d{2}T\\d{2}:\\d{2}(?::\\d{2}(?:\\.\\d+)?)?Z$/;\n\n/** The form of a full commit. */\nconst COMMIT = /^[0-9a-f]{40}$/;\n\n/** The clock difference between two computers that the check accepts, in milliseconds. */\nconst CLOCK_MARGIN = 5 * 60 * 1000;\n\n/** An error in the data of `kpi.json`. The field `problems` has one line for each fault. */\nexport class KpiError extends Error {\n readonly problems: readonly string[];\n\n constructor(problems: readonly string[]) {\n super(`${KPI_FILE} is not correct: ${problems.join(\" \")}`);\n this.name = \"KpiError\";\n this.problems = problems;\n }\n}\n\nconst isRecord = (value: unknown): value is Record<string, unknown> => typeof value === \"object\" && value !== null && !Array.isArray(value);\n\nconst isText = (value: unknown): value is string => typeof value === \"string\" && value.trim() !== \"\";\n\n/** This function gives the fault of a time of the data, or an empty list. The name tells which field it is. */\nfunction timeProblems(at: unknown, name: string, now: number): string[] {\n if (typeof at !== \"string\" || !UTC_TIME.test(at) || Number.isNaN(Date.parse(at))) return [`${name} is not a time in UTC.`];\n return Date.parse(at) > now + CLOCK_MARGIN ? [`The time of the data ${at} is after the time of the check.`] : [];\n}\n\n/** This function gives the faults of one measure. */\nfunction measureProblems(value: unknown, index: number, seen: Set<string>, now: number): string[] {\n if (!isRecord(value)) return [`Measure ${index} is not an object.`];\n const { id, value: amount, unit, at, notCounted } = value;\n const problems: string[] = [];\n if (typeof id !== \"string\" || !SLUG.test(id)) problems.push(`Measure ${index} has no id of lowercase words with single hyphens.`);\n else if (seen.has(id)) problems.push(`The id ${id} occurs two times.`);\n else seen.add(id);\n if (typeof amount !== \"number\" || !Number.isFinite(amount)) problems.push(`Measure ${index} has no finite number as its value.`);\n if (unit !== undefined && !isText(unit)) problems.push(`Measure ${index} has a unit that is not a text.`);\n if (at !== undefined) problems.push(...timeProblems(at, `The field at of measure ${index}`, now));\n if (notCounted !== undefined && !isText(notCounted)) problems.push(`Measure ${index} has a field notCounted that is not a text.`);\n return problems;\n}\n\n/** This function gives the faults of the list of engines. */\nfunction engineProblems(value: unknown): string[] {\n if (value === undefined) return [];\n if (!Array.isArray(value)) return [\"The field engines is not a list.\"];\n return value.flatMap((engine: unknown, index) => (isRecord(engine) && isText(engine[\"name\"]) && isText(engine[\"version\"]) ? [] : [`Engine ${index} has no name and version.`]));\n}\n\n/**\n * This function gives the faults of a parsed `kpi.json`, or an empty list.\n *\n * @param value - The parsed file.\n * @param now - The time of the check, in milliseconds. A time of the data after this time is a fault.\n */\nexport function kpiProblems(value: unknown, now: number = Date.now()): string[] {\n if (!isRecord(value)) return [\"The file is not an object.\"];\n const problems: string[] = [];\n if (value[\"format\"] !== KPI_FORMAT) problems.push(`The format is not ${KPI_FORMAT}.`);\n const { project, at, commit, measures } = value;\n if (typeof project !== \"string\" || !SLUG.test(project)) problems.push(\"The project is not lowercase words with single hyphens.\");\n problems.push(...timeProblems(at, \"The field at\", now));\n if (typeof commit !== \"string\" || !COMMIT.test(commit)) problems.push(\"The commit is not a full commit.\");\n if (!Array.isArray(measures) || measures.length === 0) problems.push(\"The file has no measures.\");\n else {\n const seen = new Set<string>();\n problems.push(...measures.flatMap((measure: unknown, index) => measureProblems(measure, index, seen, now)));\n }\n problems.push(...engineProblems(value[\"engines\"]));\n return problems;\n}\n\n/** This function gives a copy of a measure with only its known fields. */\nfunction cleanMeasure({ id, value, unit, at, notCounted }: KpiMeasure): KpiMeasure {\n return { id, value, ...(unit === undefined ? {} : { unit }), ...(at === undefined ? {} : { at }), ...(notCounted === undefined ? {} : { notCounted }) };\n}\n\n/** This function gives a copy of an engine with only its known fields. */\nfunction cleanEngine({ name, version }: KpiEngine): KpiEngine {\n return { name, version };\n}\n\n/**\n * This function gives the checked content of `kpi.json`. The file has only its known fields, in a fixed order.\n *\n * @throws KpiError when the data is not correct\n */\nexport function kpiFile(input: KpiInput, now?: number): KpiFile {\n const problems = kpiProblems({ format: KPI_FORMAT, ...input }, now);\n if (problems.length > 0) throw new KpiError(problems);\n const { project, at, commit, measures, engines } = input;\n const file: KpiFile = { format: KPI_FORMAT, project, at, commit, measures: measures.map(cleanMeasure) };\n return engines === undefined ? file : { ...file, engines: engines.map(cleanEngine) };\n}\n\n/** This function gives the text of `kpi.json`. */\nexport function kpiText(file: KpiFile): string {\n return `${JSON.stringify(file, null, 2)}\\n`;\n}\n\n/**\n * This function reads the text of a `kpi.json`.\n *\n * @throws KpiError when the text is not correct\n */\nexport function readKpi(text: string, now?: number): KpiFile {\n let value: unknown;\n try {\n value = JSON.parse(text);\n } catch (error) {\n throw new KpiError([`The file is not JSON: ${error instanceof Error ? error.message : String(error)}`]);\n }\n const problems = kpiProblems(value, now);\n if (problems.length > 0) throw new KpiError(problems);\n const { project, at, commit, measures, engines } = value as KpiFile;\n return kpiFile(engines === undefined ? { project, at, commit, measures } : { project, at, commit, measures, engines }, now);\n}\n\n/**\n * This function writes `kpi.json` into the built output folder of a site, and gives the path of the file.\n *\n * @throws KpiError when the data is not correct\n */\nexport function writeKpi(out: string, input: KpiInput, now?: number): string {\n const text = kpiText(kpiFile(input, now));\n mkdirSync(out, { recursive: true });\n const file = join(out, KPI_FILE);\n writeFileSync(file, text);\n return file;\n}\n","/**\n * The HTML helpers of the pages: the escape functions and a small inline Markdown renderer.\n */\n\nconst ESCAPES: Record<string, string> = { \"&\": \"&\", \"<\": \"<\", \">\": \">\", '\"': \""\", \"'\": \"'\" };\n\n/** This function escapes a text for HTML content and for an attribute value in double quotes. */\nexport function escapeHtml(text: string): string {\n return String(text).replace(/[&<>\"']/g, (char) => ESCAPES[char] ?? char);\n}\n\n/**\n * This function removes the control characters and the spaces (U+0000 to U+0020) at the two ends\n * of a text. A loop does it, because the regular expression `[\\u0000- ]+$` backtracks. For a long\n * run of these characters, its time grows with the square of the length.\n */\nfunction trimControl(text: string): string {\n let start = 0;\n let end = text.length;\n while (start < end && text.charCodeAt(start) <= 0x20) start++;\n while (end > start && text.charCodeAt(end - 1) <= 0x20) end--;\n return text.slice(start, end);\n}\n\n/**\n * This function gives a URL that a link can hold, or null. It accepts `http:`, `https:` and\n * `mailto:` URLs, and relative URLs. It refuses each other scheme, for example `javascript:`.\n *\n * The URL parser of a browser removes each tab and line break, and the control characters and\n * spaces at the two ends. This function removes them before the scheme check, as the browser does.\n * Thus a control character before `javascript:` does not hide the scheme.\n */\nexport function safeUrl(url: string): string | null {\n const trimmed = trimControl(url.trim().replace(/[\\t\\n\\r]/g, \"\"));\n if (/^(?:https?:|mailto:)/i.test(trimmed)) return trimmed;\n // A browser reads \"\\\" as \"/\" in a URL of the page, so \"\\\\host\" and \"/\\host\" go to a different host, as \"//host\" does\n if (/^[a-z][a-z0-9+.-]*:/i.test(trimmed) || /^[/\\\\]{2}/.test(trimmed)) return null;\n return trimmed;\n}\n\n/** This function gives an `<a>` element, or the escaped text when the URL is not safe. */\nexport function link(url: string, html: string, attributes = \"\"): string {\n const href = safeUrl(url);\n return href === null ? html : `<a href=\"${escapeHtml(href)}\"${attributes ? ` ${attributes}` : \"\"}>${html}</a>`;\n}\n\n/** The emphasis of a text without code spans or links. The text is already escaped. */\nfunction emphasis(escaped: string): string {\n return escaped\n .replace(/\\*\\*([^*]+?)\\*\\*/g, \"<strong>$1</strong>\")\n .replace(/(^|[^\\w*])\\*([^*\\s][^*]*?)\\*(?![\\w*])/g, \"$1<em>$2</em>\")\n .replace(/(^|[^\\w])_([^_\\s][^_]*?)_(?![\\w])/g, \"$1<em>$2</em>\");\n}\n\nconst INLINE = \"`([^`]+)`|\\\\[((?:[^\\\\]`]|`[^`]*`)+)\\\\]\\\\(([^)\\\\s]+)\\\\)\";\n\n/**\n * This function renders inline Markdown as HTML: code spans, links, strong text and emphasis. It\n * escapes all other text, so the result is safe.\n */\nexport function inlineMarkdown(markdown: string): string {\n const text = String(markdown ?? \"\");\n // A new expression for each call: the text of a link is rendered with a recursive call\n const pattern = new RegExp(INLINE, \"g\");\n let html = \"\";\n let last = 0;\n for (let match = pattern.exec(text); match; match = pattern.exec(text)) {\n html += emphasis(escapeHtml(text.slice(last, match.index)));\n if (match[1] !== undefined) html += `<code>${escapeHtml(match[1])}</code>`;\n else html += link(match[3] ?? \"\", inlineMarkdown(match[2] ?? \"\"));\n last = match.index + match[0].length;\n }\n return html + emphasis(escapeHtml(text.slice(last)));\n}\n\n/** This function gives \"1 item\" or \"2 items\". */\nexport function plural(count: number, word: string, many = `${word}s`): string {\n return `${count} ${count === 1 ? word : many}`;\n}\n\n/** This function gives \"a\", \"a and b\" or \"a, b and c\". */\nexport function listText(parts: readonly string[]): string {\n if (parts.length <= 1) return parts.join(\"\");\n return `${parts.slice(0, -1).join(\", \")} and ${parts[parts.length - 1]}`;\n}\n","/**\n * The freshness gate of a measured page. A site keeps a file of data stamps.\n * Each stamp names the page, its runs, their dates and their engine versions.\n * The gate fails on a page with no versions or no run date, and on old data with no newer run in flight.\n * The gate also fails on a run that started behind the engines on npm.\n * A fixed study has no age limit. A page with a longer limit names the reason and the scheduled workflow that meets the limit.\n * This file imports only Node.js and the folder `data`, so `reusable-pages.yml` starts it with no npm release of the package.\n */\n\nimport { readFileSync } from \"node:fs\";\nimport { isAbsolute, join } from \"node:path\";\nimport { parseArgs } from \"node:util\";\nimport { npmPublishTimes } from \"../data/npm.ts\";\nimport type { NpmVersionTime } from \"../data/types.ts\";\nimport { compareVersions, parseVersion } from \"../data/versions.ts\";\nimport type { CheckIo } from \"./check.ts\";\n\n/** The schema of a file of data stamps. */\nexport const DATA_STAMP_SCHEMA = \"data-stamp/1\";\n\n/** The largest age of the data of a page, in days, when no newer run is in flight. */\nexport const MAX_DATA_AGE_DAYS = 3;\n\n/** The most minor versions that an engine of a run can be behind npm at the start of the run. */\nexport const MAX_MINORS_BEHIND = 1;\n\n/** The longest limit of a page, in days, with no ruling for that site. */\nexport const MAX_LIMIT_DAYS_WITHOUT_RULING = 14;\n\nconst DAY_MS = 86_400_000;\n\n/** One run that gives the data of a page. */\nexport interface DataRun {\n readonly id: string;\n readonly url: string;\n /** The ISO time of the start of the run. */\n readonly date: string;\n /** The version of each engine of the run, by its npm name. */\n readonly engines: Readonly<Record<string, string>>;\n}\n\n/** The age limit of one page, in place of `MAX_DATA_AGE_DAYS`. */\nexport interface DataLimit {\n /** The largest age of the data, in days. */\n readonly days: number;\n /** The reason for the limit, as one sentence. The page shows it. */\n readonly reason: string;\n /** The path of the workflow from the root of the repository. Its schedule must meet the limit. */\n readonly workflow: string;\n /** The link to the public ruling. A limit over `MAX_LIMIT_DAYS_WITHOUT_RULING` needs it. */\n readonly ruling?: string;\n}\n\n/** The stamp of one measured page. */\nexport interface PageStamp {\n readonly page: string;\n /** Each run that gives data to the page. */\n readonly runs: readonly DataRun[];\n /** The run on newer engines that is in progress. */\n readonly inFlight?: DataRun;\n /** The page shows a fixed study of the versions of its runs, so its data has no age limit. It makes no claim about a newer release. */\n readonly study?: true;\n /** The age limit of this page, in place of the default. */\n readonly limit?: DataLimit;\n}\n\n/** The content of a file of data stamps. */\nexport interface DataStamps {\n readonly schema: typeof DATA_STAMP_SCHEMA;\n readonly pages: readonly PageStamp[];\n}\n\n/** The result of `parseDataStamps`. */\nexport type DataStampsParse = { readonly ok: true; readonly stamps: DataStamps } | { readonly ok: false; readonly faults: readonly string[] };\n\n/** The options of `checkFreshness`. */\nexport interface FreshnessOptions {\n /** The ISO time of the check. */\n readonly now: string;\n /** The publication times of the engines on npm. */\n readonly times: readonly NpmVersionTime[];\n /** The limit of the age. The default is `MAX_DATA_AGE_DAYS`. */\n readonly maxAgeDays?: number;\n /** The limit of the minor versions. The default is `MAX_MINORS_BEHIND`. */\n readonly maxMinorsBehind?: number;\n /** The schedule of each workflow of a limit. A number is the largest gap between its starts in days. A text tells why the gate cannot read it. */\n readonly schedules?: Readonly<Record<string, number | string>>;\n}\n\n/** The result of `checkFreshness`. */\nexport interface FreshnessCheck {\n /** Each break of a rule. One fault fails the gate. */\n readonly faults: readonly string[];\n /** Each rule that the gate could not run, with the reason. */\n readonly notRun: readonly string[];\n}\n\nconst record = (value: unknown): Record<string, unknown> | null => (value !== null && typeof value === \"object\" && !Array.isArray(value) ? (value as Record<string, unknown>) : null);\n\n/** This function gives the faults of one run of a stamp. */\nfunction runFaults(raw: unknown, at: string): string[] {\n const run = record(raw);\n if (run === null) return [`${at}: a run is not an object.`];\n const faults: string[] = [];\n if (typeof run[\"id\"] !== \"string\" || run[\"id\"] === \"\") faults.push(`${at}: a run has no id.`);\n const label = `${at}, run ${String(run[\"id\"])}`;\n if (typeof run[\"url\"] !== \"string\" || !run[\"url\"].startsWith(\"https://\")) faults.push(`${label}: the run has no https link.`);\n if (typeof run[\"date\"] !== \"string\" || !Number.isFinite(Date.parse(run[\"date\"]))) faults.push(`${label}: the run has no date.`);\n const engines = record(run[\"engines\"]);\n if (engines === null || Object.keys(engines).length === 0) return [...faults, `${label}: the run names no engine versions.`];\n for (const [name, version] of Object.entries(engines)) {\n if (typeof version !== \"string\" || parseVersion(version) === null) faults.push(`${label}: the version of ${name} is not a version.`);\n }\n return faults;\n}\n\n/** This function gives the faults of the age limit of one page. */\nfunction limitFaults(raw: unknown, at: string): string[] {\n const limit = record(raw);\n if (limit === null) return [`${at}: the limit is not an object.`];\n const faults: string[] = [];\n const days = limit[\"days\"];\n if (typeof days !== \"number\" || !Number.isFinite(days) || days <= 0) faults.push(`${at}: the limit has no count of days.`);\n if (typeof limit[\"reason\"] !== \"string\" || limit[\"reason\"].trim() === \"\") faults.push(`${at}: the limit has no reason.`);\n if (typeof limit[\"workflow\"] !== \"string\" || limit[\"workflow\"] === \"\") faults.push(`${at}: the limit names no workflow.`);\n const ruling = limit[\"ruling\"];\n if (ruling !== undefined && (typeof ruling !== \"string\" || !ruling.startsWith(\"https://\"))) faults.push(`${at}: the ruling of the limit has no https link.`);\n if (typeof days === \"number\" && days > MAX_LIMIT_DAYS_WITHOUT_RULING && ruling === undefined) {\n faults.push(`${at}: a limit over ${MAX_LIMIT_DAYS_WITHOUT_RULING} days needs a ruling for this site.`);\n }\n return faults;\n}\n\n/** This function gives the faults of the fields of a page that change the age rule. */\nfunction ageRuleFaults(page: Record<string, unknown>, at: string): string[] {\n const faults: string[] = [];\n const study = page[\"study\"];\n if (study !== undefined && study !== true) faults.push(`${at}: the field study is not true.`);\n if (study === true && page[\"limit\"] !== undefined) faults.push(`${at}: a fixed study has no limit.`);\n if (study === true && page[\"inFlight\"] !== undefined) faults.push(`${at}: a fixed study has no run in flight.`);\n if (page[\"limit\"] !== undefined) faults.push(...limitFaults(page[\"limit\"], at));\n return faults;\n}\n\n/**\n * This function reads the content of a file of data stamps. It gives each fault of the content, so a page with no versions\n * or no run date fails here.\n */\nexport function parseDataStamps(raw: unknown): DataStampsParse {\n const file = record(raw);\n if (file === null) return { ok: false, faults: [\"The file is not an object.\"] };\n if (file[\"schema\"] !== DATA_STAMP_SCHEMA) return { ok: false, faults: [`The schema is not ${DATA_STAMP_SCHEMA}.`] };\n const pages = file[\"pages\"];\n if (!Array.isArray(pages) || pages.length === 0) return { ok: false, faults: [\"The file has no pages.\"] };\n const faults: string[] = [];\n for (const item of pages) {\n const page = record(item);\n if (page === null || typeof page[\"page\"] !== \"string\" || page[\"page\"] === \"\") {\n faults.push(\"A stamp has no page.\");\n continue;\n }\n const at = page[\"page\"];\n const runs = page[\"runs\"];\n if (!Array.isArray(runs) || runs.length === 0) faults.push(`${at}: the page names no run.`);\n else for (const run of runs) faults.push(...runFaults(run, at));\n if (page[\"inFlight\"] !== undefined) faults.push(...runFaults(page[\"inFlight\"], `${at}, in flight`));\n faults.push(...ageRuleFaults(page, at));\n }\n return faults.length > 0 ? { ok: false, faults } : { ok: true, stamps: raw as DataStamps };\n}\n\n/** This function gives the count of minor versions from `used` to `latest`. A newer major version counts as no limit. */\nexport function minorsBehind(used: string, latest: string): number {\n const a = parseVersion(used);\n const b = parseVersion(latest);\n if (a === null || b === null) return Number.POSITIVE_INFINITY;\n if (a.major !== b.major) return b.major > a.major ? Number.POSITIVE_INFINITY : 0;\n return Math.max(0, b.minor - a.minor);\n}\n\n/** This function gives the newest stable version of a package on npm at a time, or undefined when npm has none. */\nexport function latestAt(times: readonly NpmVersionTime[], name: string, date: string): string | undefined {\n const limit = Date.parse(date);\n return times\n .filter((t) => t.name === name && Date.parse(t.date) <= limit && parseVersion(t.version)?.prerelease.length === 0)\n .map((t) => t.version)\n .sort(compareVersions)\n .at(-1);\n}\n\nconst ageInDays = (date: string, now: number): number => (now - Date.parse(date)) / DAY_MS;\n\n/** This function gives the values of one field of a cron line, or undefined for a form that it cannot read. */\nfunction cronValues(field: string, low: number, high: number): Set<number> | undefined {\n const values = new Set<number>();\n for (const part of field.split(\",\")) {\n const match = /^(\\*|\\d+(?:-\\d+)?)(?:\\/(\\d+))?$/.exec(part);\n if (match === null) return undefined;\n const [, range = \"\", step = \"1\"] = match;\n const [from = low, to = from] = range === \"*\" ? [low, high] : range.split(\"-\").map(Number);\n const by = Number(step);\n if (by <= 0 || from < low || to > high || from > to) return undefined;\n for (let value = from; value <= to; value += by) values.add(value);\n }\n return values;\n}\n\n/** This function gives the start of each day of a cron line, in a span of two years. It gives undefined for a line that it cannot read. */\nfunction cronDays(line: string, from: number, days: number): number[] | undefined {\n const fields = line.trim().split(/\\s+/);\n if (fields.length !== 5) return undefined;\n const [minute = \"\", hour = \"\", dayOfMonth = \"\", month = \"\", dayOfWeek = \"\"] = fields;\n const minutes = cronValues(minute, 0, 59);\n const hours = cronValues(hour, 0, 23);\n const monthDays = cronValues(dayOfMonth, 1, 31);\n const months = cronValues(month, 1, 12);\n const weekDays = cronValues(dayOfWeek, 0, 6);\n if (minutes === undefined || hours === undefined || monthDays === undefined || months === undefined || weekDays === undefined) return undefined;\n const firstTime = (Math.min(...hours) * 60 + Math.min(...minutes)) * 60_000;\n const starts: number[] = [];\n for (let day = 0; day < days; day += 1) {\n const time = from + day * DAY_MS;\n const date = new Date(time);\n if (!months.has(date.getUTCMonth() + 1)) continue;\n const onMonthDay = monthDays.has(date.getUTCDate());\n const onWeekDay = weekDays.has(date.getUTCDay());\n // A cron line that sets both day fields starts on a day that matches one of them.\n const both = dayOfMonth !== \"*\" && dayOfWeek !== \"*\";\n if (both ? onMonthDay || onWeekDay : onMonthDay && onWeekDay) starts.push(time + firstTime);\n }\n return starts;\n}\n\n/** One schedule of a workflow. */\nexport interface CronLine {\n /** The number of the line in the file, from 1. */\n readonly line: number;\n /** The cron text, with no quotes and no comment. */\n readonly cron: string;\n}\n\n/**\n * This function gives each line `cron:` of a workflow. It is the one reader of these lines: the freshness gate and the rule `runner-budget` of conventions use it.\n * It skips a comment line. It reads a quoted key, a list item, a quoted value and a comment at the end of the line.\n */\nexport function cronLines(workflow: string): CronLine[] {\n return workflow.split(/\\r?\\n/).flatMap((raw, index) => {\n const text = raw.trim().replace(/^- */, \"\");\n const key = [\"cron:\", '\"cron\":', \"'cron':\"].find((form) => text.startsWith(form));\n if (key === undefined) return [];\n // YAML starts a comment at a \"#\" after a space or a tab.\n const rest = text.slice(key.length);\n const comment = rest.search(/\\s#/);\n const value = (comment >= 0 ? rest.slice(0, comment) : rest).trim();\n const quote = value.charAt(0);\n const quoted = value.length >= 2 && (quote === '\"' || quote === \"'\") && value.endsWith(quote);\n return [{ line: index + 1, cron: quoted ? value.slice(1, -1).trim() : value }];\n });\n}\n\n/**\n * This function gives the largest gap between the scheduled starts of a workflow, in days. The gap counts from the first start of a day.\n * It gives a text in place of the gap for a workflow with no schedule, and for a line that it cannot read. The text names the line, and the gate fails on it.\n */\nexport function scheduleGapDays(workflow: string): number | string {\n const lines = cronLines(workflow).map((item) => item.cron);\n if (lines.length === 0) return \"the workflow has no line `cron:`\";\n const from = Date.UTC(2027, 0, 1);\n const span = 731;\n const starts: number[] = [];\n for (const line of lines) {\n const days = cronDays(line, from, span);\n if (days === undefined) return `the gate cannot read the line \\`cron: ${line}\\``;\n starts.push(...days);\n }\n if (starts.length < 2) return `the lines \\`cron: ${lines.join(\"`, `cron: \")}\\` give fewer than two starts in two years`;\n starts.sort((a, b) => a - b);\n let largest = 0;\n for (let index = 1; index < starts.length; index += 1) largest = Math.max(largest, ((starts[index] ?? 0) - (starts[index - 1] ?? 0)) / DAY_MS);\n return largest;\n}\n\n/** This function tells if a run uses a newer version of an engine than another run. */\nfunction hasNewerEngine(run: DataRun, than: DataRun): boolean {\n return Object.entries(run.engines).some(([name, version]) => {\n const old = than.engines[name];\n return old !== undefined && compareVersions(version, old) > 0;\n });\n}\n\n/**\n * This function applies the rules of the gate to the stamps.\n *\n * - The data of each page is at most `maxAgeDays` old, or a run on newer engines is in flight.\n * - The engines of the newest run are at most `maxMinorsBehind` behind npm at the start of that run.\n * - A run in flight has the same age limit, so an old stamp does not hold the gate open.\n * - A page with its own limit uses that limit, and the schedule of its workflow meets the limit.\n * - A fixed study has no age limit and no limit of the engines, because it tells about the versions that it names.\n */\nexport function checkFreshness(stamps: DataStamps, options: FreshnessOptions): FreshnessCheck {\n const { times, maxAgeDays: defaultAge = MAX_DATA_AGE_DAYS, maxMinorsBehind = MAX_MINORS_BEHIND, schedules = {} } = options;\n const now = Date.parse(options.now);\n const faults: string[] = [];\n const notRun: string[] = [];\n for (const stamp of stamps.pages) {\n for (const run of [...stamp.runs, ...(stamp.inFlight === undefined ? [] : [stamp.inFlight])]) {\n if (ageInDays(run.date, now) < 0) faults.push(`${stamp.page}: the run ${run.id} has a date after the check.`);\n }\n if (stamp.study === true) continue;\n const maxAgeDays = stamp.limit?.days ?? defaultAge;\n if (stamp.limit !== undefined) {\n const gap = schedules[stamp.limit.workflow];\n if (gap === undefined || typeof gap === \"string\") faults.push(`${stamp.page}: the limit of ${maxAgeDays} days has no schedule that the gate can read in ${stamp.limit.workflow}: ${gap ?? \"the check has no read of this workflow\"}.`);\n else if (gap > maxAgeDays) faults.push(`${stamp.page}: the schedule of ${stamp.limit.workflow} has a gap of ${gap.toFixed(1)} days, over the limit of ${maxAgeDays}.`);\n }\n const flight = stamp.inFlight;\n const flying = flight !== undefined && ageInDays(flight.date, now) <= maxAgeDays;\n if (flight !== undefined && !flying) faults.push(`${stamp.page}: the run ${flight.id} is in flight for more than ${maxAgeDays} days.`);\n for (const run of stamp.runs) {\n const age = ageInDays(run.date, now);\n if (age > maxAgeDays && !(flying && hasNewerEngine(flight, run))) {\n faults.push(`${stamp.page}: the run ${run.id} is ${age.toFixed(1)} days old, over the limit of ${maxAgeDays}, and no run on newer engines is in flight.`);\n }\n }\n const newest = flight ?? [...stamp.runs].sort((a, b) => Date.parse(a.date) - Date.parse(b.date)).at(-1);\n if (newest === undefined) continue;\n for (const [name, version] of Object.entries(newest.engines)) {\n const latest = latestAt(times, name, newest.date);\n if (latest === undefined) {\n notRun.push(`${stamp.page}: npm gives no version of ${name} at the start of the run ${newest.id}.`);\n } else if (minorsBehind(version, latest) > maxMinorsBehind) {\n faults.push(`${stamp.page}: the run ${newest.id} used ${name} ${version}, and npm had ${latest} at its start. The limit is ${maxMinorsBehind} minor version behind.`);\n }\n }\n }\n return { faults, notRun };\n}\n\n/** The help of the command `site-pages freshness`. */\nexport const FRESHNESS_HELP: string = `Usage: site-pages freshness --stamps <file> [options]\n\nExamine the data stamps of the measured pages of a site.\n\n --stamps <file> The file of data stamps, schema ${DATA_STAMP_SCHEMA}. (necessary)\n --now <time> The ISO time of the check. The default is the time of the call.\n --help Show this text.\n\nThe exit code is 0 when the stamps pass, 1 for a fault, and 2 for an incorrect command line.\n`;\n\n/** This function gives the largest gap of the schedule in a workflow file. It gives a text for a file that it cannot read. */\nfunction readSchedule(path: string): number | string {\n try {\n return scheduleGapDays(readFileSync(path, \"utf8\"));\n } catch (error) {\n return `the file cannot be read: ${error instanceof Error ? error.message : String(error)}`;\n }\n}\n\n/** This function is the command `site-pages freshness`. The `fetch` option is for a test. */\nexport async function runFreshnessCli(argv: readonly string[], io: CheckIo, fetcher?: typeof fetch): Promise<number> {\n let values: { stamps?: string | undefined; now?: string | undefined; help?: boolean | undefined };\n try {\n ({ values } = parseArgs({ args: [...argv], allowPositionals: false, options: { stamps: { type: \"string\" }, now: { type: \"string\" }, help: { type: \"boolean\" } } }));\n } catch (error) {\n io.stderr(`site-pages freshness: ${error instanceof Error ? error.message : String(error)}\\n\\n${FRESHNESS_HELP}`);\n return 2;\n }\n if (values.help === true) {\n io.stdout(FRESHNESS_HELP);\n return 0;\n }\n if (values.stamps === undefined) {\n io.stderr(`site-pages freshness: --stamps is necessary.\\n\\n${FRESHNESS_HELP}`);\n return 2;\n }\n const path = isAbsolute(values.stamps) ? values.stamps : join(io.cwd, values.stamps);\n let raw: unknown;\n try {\n raw = JSON.parse(readFileSync(path, \"utf8\"));\n } catch (error) {\n io.stderr(`site-pages freshness: the file ${values.stamps} cannot be read: ${error instanceof Error ? error.message : String(error)}\\n`);\n return 1;\n }\n const parsed = parseDataStamps(raw);\n if (!parsed.ok) {\n for (const fault of parsed.faults) io.stderr(`${fault}\\n`);\n return 1;\n }\n const engines = parsed.stamps.pages\n .filter((p) => p.study !== true)\n .flatMap((p) => [...p.runs, ...(p.inFlight === undefined ? [] : [p.inFlight])])\n .flatMap((r) => Object.keys(r.engines));\n const schedules: Record<string, number | string> = {};\n for (const page of parsed.stamps.pages) {\n if (page.limit !== undefined) schedules[page.limit.workflow] = readSchedule(join(io.cwd, page.limit.workflow));\n }\n const npm = await npmPublishTimes({ packages: [...new Set(engines)], ...(fetcher === undefined ? {} : { fetch: fetcher }) });\n const result = checkFreshness(parsed.stamps, { now: values.now ?? new Date().toISOString(), times: npm.times, schedules });\n for (const problem of [...npm.problems, ...result.notRun]) io.stdout(`Not run: ${problem}\\n`);\n for (const fault of result.faults) io.stderr(`${fault}\\n`);\n if (result.faults.length > 0) return 1;\n io.stdout(`The data stamps of ${parsed.stamps.pages.length} pages pass.\\n`);\n return 0;\n}\n"],"mappings":"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"}
|
package/build/index.d.ts
CHANGED
|
@@ -73,6 +73,8 @@ export declare function releaseBodyEntries(body: string): ChangelogEntry[];
|
|
|
73
73
|
* for each space. The anchor of "0.13.2" is "0132".
|
|
74
74
|
*/
|
|
75
75
|
export declare function headingAnchor(heading: string): string;
|
|
76
|
+
/** This function gives the versions of the headings of a changelog, in the order of the file. Another heading is not a version. */
|
|
77
|
+
export declare function changelogVersions(markdown: string): string[];
|
|
76
78
|
/** The GraphQL endpoint of GitHub. */
|
|
77
79
|
export declare const GITHUB_GRAPHQL = "https://api.github.com/graphql";
|
|
78
80
|
/** The REST endpoint of GitHub. */
|
|
@@ -144,6 +146,12 @@ export declare function gitTagDates(repoDir: string): TagDate[];
|
|
|
144
146
|
* or null. The path of the changelog starts at `repoDir`.
|
|
145
147
|
*/
|
|
146
148
|
export declare function headingDate(repoDir: string, version: string, file: string): string | null;
|
|
149
|
+
/**
|
|
150
|
+
* This function gives the ISO time of the last commit that changed the `version` line of a `package.json`, or null. The
|
|
151
|
+
* path of the file starts at `repoDir`. A shallow clone can give a later commit, so use the full history. A change of
|
|
152
|
+
* only the comma of that line also counts as a change of the line.
|
|
153
|
+
*/
|
|
154
|
+
export declare function versionCommitTime(repoDir: string, file: string): string | null;
|
|
147
155
|
/** This function gives the full SHA of `HEAD`, or null. */
|
|
148
156
|
export declare function headCommit(repoDir: string): string | null;
|
|
149
157
|
/** This function gives "owner/name" of the GitHub remote "origin", or null. */
|
|
@@ -188,13 +196,51 @@ interface ReleaseSources {
|
|
|
188
196
|
* This function merges the release sources into one entry for each version, the newest first.
|
|
189
197
|
*
|
|
190
198
|
* - The versions: the changelog of `group`, or else each changelog version and each GitHub release.
|
|
199
|
+
* - The versions also include each stable npm version of the group, or of the only npm package. A publication with no changelog heading is still a release.
|
|
191
200
|
* - The date: the git tag, else the GitHub release, else npm, else the commit of the changelog heading.
|
|
192
201
|
* - The changes: the changelog entries, else the list items of the GitHub release text.
|
|
193
202
|
* - Without a changelog version and without a GitHub release, each npm version of each package is a release.
|
|
203
|
+
* - With `group`, a version of another package that the group does not have is a release with the name of its package.
|
|
194
204
|
*
|
|
195
205
|
* @throws ReleaseDataError when `group` has no changelog and other changelogs exist
|
|
196
206
|
*/
|
|
197
207
|
export declare function mergeReleases(sources: ReleaseSources): Release[];
|
|
208
|
+
/** The sources that a release list must cover. Each source is optional. */
|
|
209
|
+
interface ReleaseListSources {
|
|
210
|
+
/** The versions of the changelog, from `changelogVersions`. */
|
|
211
|
+
changelog?: readonly string[] | undefined;
|
|
212
|
+
/** The version of the package at the build. */
|
|
213
|
+
current?: string | undefined;
|
|
214
|
+
/** The versions on npm. The check ignores a prerelease, because a prerelease is not a release. */
|
|
215
|
+
npm?: readonly string[] | undefined;
|
|
216
|
+
/** The versions that have a tag. A release makes its tag at its end. */
|
|
217
|
+
tags?: readonly string[] | undefined;
|
|
218
|
+
/** The ISO time of the commit that set `current`, from `versionCommitTime`. */
|
|
219
|
+
versionAt?: string | undefined;
|
|
220
|
+
}
|
|
221
|
+
/** A version that a release list does not show, and the sources that name it. */
|
|
222
|
+
interface ReleaseGap {
|
|
223
|
+
version: string;
|
|
224
|
+
sources: Array<"changelog" | "current" | "npm">;
|
|
225
|
+
}
|
|
226
|
+
/** The result of the check of a release list. */
|
|
227
|
+
interface ReleaseReading {
|
|
228
|
+
/** The versions that the list does not show, the newest first. */
|
|
229
|
+
gaps: ReleaseGap[];
|
|
230
|
+
/** The current version when it has no tag and its commit is less than 1 day old. */
|
|
231
|
+
inFlight?: string;
|
|
232
|
+
}
|
|
233
|
+
/**
|
|
234
|
+
* This function reads a release list against its sources. An old list and a list with a missing middle version both give
|
|
235
|
+
* a gap. A version in flight gives no gap, because its release has not ended. After 1 day it gives a gap, so a failed
|
|
236
|
+
* release shows. The check covers one line of versions, so give the plain versions of one package. It does not read a
|
|
237
|
+
* page: a page that does not show its list has no gap here.
|
|
238
|
+
*
|
|
239
|
+
* @param listed - The versions of the list.
|
|
240
|
+
* @param sources - The sources that name the versions.
|
|
241
|
+
* @param now - The time of the check, in milliseconds.
|
|
242
|
+
*/
|
|
243
|
+
export declare function releaseReading(listed: readonly string[], sources: ReleaseListSources, now?: number): ReleaseReading;
|
|
198
244
|
/** The default patterns of the changelog files: the first pattern that finds a file wins. */
|
|
199
245
|
export declare const DEFAULT_CHANGELOGS: readonly string[];
|
|
200
246
|
/**
|
|
@@ -586,5 +632,5 @@ export declare function parseBuildArgs(argv: readonly string[], io: Pick<CliIo,
|
|
|
586
632
|
* source is not correct, and 2 for an incorrect command line.
|
|
587
633
|
*/
|
|
588
634
|
export declare function runCli(argv: readonly string[], io: CliIo): Promise<number>;
|
|
589
|
-
export { type BoardIssue, type BoardItem, type BoardPullRequest, type BoardSection, type BuildOptions, type BuildResult, type ChangelogEntry, type ChangelogSection, type ChangelogSource, type CheckState, type CliIo, type CronLine, DATA_STAMP_SCHEMA, type DataLimit, type DataRun, type DataStamps, type DataStampsParse, type FreshnessCheck, type FreshnessOptions, type GitHubOpenWork, type GitHubOptions, type GitHubRelease, type GitHubReleases, type ItemGroup, KPI_FILE, KPI_FORMAT, type KpiEngine, KpiError, type KpiFile, type KpiInput, type KpiMeasure, type Link, type LinkOptions, MAX_DATA_AGE_DAYS, MAX_LIMIT_DAYS_WITHOUT_RULING, MAX_MINORS_BEHIND, type MarkdownTable, type NpmOptions, type NpmPublishTimes, type NpmVersionTime, type OpenWorkTotals, type PageOptions, type PageParts, type PageStamp, type ParsedVersion, type ReadReleasesOptions, type ReadStatusBoardOptions, type Release, type ReleaseChange, type ReleaseKind, type ReleasePoint, type ReleaseSources, type ReleaseSummary, type StatusBoardData, type StatusBoardInput, type StatusClass, type TagDate, type TagVersion, type TimelineBand, type TimelineLayout, type TimelineMark, type TimelineOptions, checkFreshness, cronLines, kpiFile, kpiProblems, kpiText, latestAt, minorsBehind, parseDataStamps, readKpi, writeKpi };
|
|
635
|
+
export { type BoardIssue, type BoardItem, type BoardPullRequest, type BoardSection, type BuildOptions, type BuildResult, type ChangelogEntry, type ChangelogSection, type ChangelogSource, type CheckState, type CliIo, type CronLine, DATA_STAMP_SCHEMA, type DataLimit, type DataRun, type DataStamps, type DataStampsParse, type FreshnessCheck, type FreshnessOptions, type GitHubOpenWork, type GitHubOptions, type GitHubRelease, type GitHubReleases, type ItemGroup, KPI_FILE, KPI_FORMAT, type KpiEngine, KpiError, type KpiFile, type KpiInput, type KpiMeasure, type Link, type LinkOptions, MAX_DATA_AGE_DAYS, MAX_LIMIT_DAYS_WITHOUT_RULING, MAX_MINORS_BEHIND, type MarkdownTable, type NpmOptions, type NpmPublishTimes, type NpmVersionTime, type OpenWorkTotals, type PageOptions, type PageParts, type PageStamp, type ParsedVersion, type ReadReleasesOptions, type ReadStatusBoardOptions, type Release, type ReleaseChange, type ReleaseGap, type ReleaseKind, type ReleaseListSources, type ReleasePoint, type ReleaseReading, type ReleaseSources, type ReleaseSummary, type StatusBoardData, type StatusBoardInput, type StatusClass, type TagDate, type TagVersion, type TimelineBand, type TimelineLayout, type TimelineMark, type TimelineOptions, checkFreshness, cronLines, kpiFile, kpiProblems, kpiText, latestAt, minorsBehind, parseDataStamps, readKpi, writeKpi };
|
|
590
636
|
//# sourceMappingURL=index.d.ts.map
|
package/build/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","names":[],"sources":["../src/data/versions.ts","../src/data/changelog.ts","../src/data/github.ts","../src/data/npm.ts","../src/data/git.ts","../src/data/glob.ts","../src/data/releases.ts","../src/data/status.ts","../src/render/html.ts","../src/render/dates.ts","../src/render/theme.ts","../src/render/timeline.ts","../src/render/releases.ts","../src/render/status.ts","../src/check/probe.ts","../src/render/data-stamp.ts","../src/build.ts"],"mappings":";;UAOiB;EACf;EACA;EACA;EACA;;;wBAMc,aAAa,kBAAkB;;;;;;wBAgB/B,gBAAgB,WAAW;;;;;wBA2B3B,OAAO,iBAAiB,kBAAkB;;UASzC;EACf;EACA;;;;;;wBAOc,aAAa,cAAc;;;;;;;UCvE1B;;EAEf;;EAEA;;;UAIe;EACf;;EAEA;EACA,SAAS;;;;;;;;;wBAqBK,cAAc;;;;;;;wBA0Cd,eAAe,mBAAmB;;;;;wBAwClC,mBAAmB,eAAe;;;;;wBAqClC,cAAc;;
|
|
1
|
+
{"version":3,"file":"index.d.ts","names":[],"sources":["../src/data/versions.ts","../src/data/changelog.ts","../src/data/github.ts","../src/data/npm.ts","../src/data/git.ts","../src/data/glob.ts","../src/data/releases.ts","../src/data/status.ts","../src/render/html.ts","../src/render/dates.ts","../src/render/theme.ts","../src/render/timeline.ts","../src/render/releases.ts","../src/render/status.ts","../src/check/probe.ts","../src/render/data-stamp.ts","../src/build.ts"],"mappings":"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"}
|
package/build/index.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { A as originRepository, C as readKpi, D as headCommit, E as gitTagDates, F as
|
|
2
|
-
import { $ as
|
|
1
|
+
import { A as originRepository, C as readKpi, D as headCommit, E as gitTagDates, F as kindOf, I as parseVersion, L as versionOfTag, M as NPM_REGISTRY, N as npmPublishTimes, O as headingDate, P as compareVersions, S as kpiText, _ as KPI_FILE, a as checkFreshness, b as kpiFile, c as minorsBehind, d as escapeHtml, f as inlineMarkdown, g as safeUrl, i as MAX_MINORS_BEHIND, j as versionCommitTime, k as isShallow, l as parseDataStamps, n as MAX_DATA_AGE_DAYS, o as cronLines, r as MAX_LIMIT_DAYS_WITHOUT_RULING, s as latestAt, t as DATA_STAMP_SCHEMA, v as KPI_FORMAT, w as writeKpi, x as kpiProblems, y as KpiError } from "./chunks/freshness.js";
|
|
2
|
+
import { $ as firstSentence, A as STATUS_CLASSES, B as DEFAULT_CHANGELOGS, C as pageShell, D as utcDay, E as shortDate, F as plainText, G as releaseReading, H as mergeReleases, I as readStatusBoard, J as GITHUB_GRAPHQL, K as globFiles, L as roadmapItems, M as linksOf, N as markdownTables, O as OPEN_CLASSES, P as parseRoadmap, Q as changelogVersions, R as splitRow, S as BASE_CSS, T as longDate, U as readChangelogs, V as ReleaseDataError, W as readReleases, X as githubOpenWork, Y as checkStateOf, Z as githubReleases, _ as renderReleasesPage, a as runCli, b as releaseSummary, c as STATUS_SCRIPT, d as renderStatusPage, et as headingAnchor, f as sectionsOf, g as releasesLead, h as RELEASES_SCRIPT, i as parseBuildArgs, j as buildStatusBoard, k as RoadmapParseError, l as classCounts, m as statusLead, n as UsageError, nt as releaseBodyEntries, o as TYPESCRIPT_PROBE, p as slugOf, q as GITHUB_API, r as build, s as probeScript, t as HELP, tt as parseChangelog, u as openGroups, v as laneCenter, w as dayRange, x as timelineLayout, y as oldestFirst, z as statusClassOf } from "./chunks/build.js";
|
|
3
3
|
import { t as renderDataStamp } from "./chunks/data-stamp.js";
|
|
4
|
-
export { BASE_CSS, DATA_STAMP_SCHEMA, DEFAULT_CHANGELOGS, GITHUB_API, GITHUB_GRAPHQL, HELP, KPI_FILE, KPI_FORMAT, KpiError, MAX_DATA_AGE_DAYS, MAX_LIMIT_DAYS_WITHOUT_RULING, MAX_MINORS_BEHIND, NPM_REGISTRY, OPEN_CLASSES, RELEASES_SCRIPT, ReleaseDataError, RoadmapParseError, STATUS_CLASSES, STATUS_SCRIPT, TYPESCRIPT_PROBE, UsageError, build, buildStatusBoard, checkFreshness, checkStateOf, classCounts, compareVersions, cronLines, dayRange, escapeHtml, firstSentence, gitTagDates, githubOpenWork, githubReleases, globFiles, headCommit, headingAnchor, headingDate, inlineMarkdown, isShallow, kindOf, kpiFile, kpiProblems, kpiText, laneCenter, latestAt, linksOf, longDate, markdownTables, mergeReleases, minorsBehind, npmPublishTimes, oldestFirst, openGroups, originRepository, pageShell, parseBuildArgs, parseChangelog, parseDataStamps, parseRoadmap, parseVersion, plainText, probeScript, readChangelogs, readKpi, readReleases, readStatusBoard, releaseBodyEntries, releaseSummary, releasesLead, renderDataStamp, renderReleasesPage, renderStatusPage, roadmapItems, runCli, safeUrl, sectionsOf, shortDate, slugOf, splitRow, statusClassOf, statusLead, timelineLayout, utcDay, versionOfTag, writeKpi };
|
|
4
|
+
export { BASE_CSS, DATA_STAMP_SCHEMA, DEFAULT_CHANGELOGS, GITHUB_API, GITHUB_GRAPHQL, HELP, KPI_FILE, KPI_FORMAT, KpiError, MAX_DATA_AGE_DAYS, MAX_LIMIT_DAYS_WITHOUT_RULING, MAX_MINORS_BEHIND, NPM_REGISTRY, OPEN_CLASSES, RELEASES_SCRIPT, ReleaseDataError, RoadmapParseError, STATUS_CLASSES, STATUS_SCRIPT, TYPESCRIPT_PROBE, UsageError, build, buildStatusBoard, changelogVersions, checkFreshness, checkStateOf, classCounts, compareVersions, cronLines, dayRange, escapeHtml, firstSentence, gitTagDates, githubOpenWork, githubReleases, globFiles, headCommit, headingAnchor, headingDate, inlineMarkdown, isShallow, kindOf, kpiFile, kpiProblems, kpiText, laneCenter, latestAt, linksOf, longDate, markdownTables, mergeReleases, minorsBehind, npmPublishTimes, oldestFirst, openGroups, originRepository, pageShell, parseBuildArgs, parseChangelog, parseDataStamps, parseRoadmap, parseVersion, plainText, probeScript, readChangelogs, readKpi, readReleases, readStatusBoard, releaseBodyEntries, releaseReading, releaseSummary, releasesLead, renderDataStamp, renderReleasesPage, renderStatusPage, roadmapItems, runCli, safeUrl, sectionsOf, shortDate, slugOf, splitRow, statusClassOf, statusLead, timelineLayout, utcDay, versionCommitTime, versionOfTag, writeKpi };
|
package/build/vite.d.ts
CHANGED
|
@@ -117,6 +117,19 @@ export declare function siteBase(env?: SiteEnv): string;
|
|
|
117
117
|
* @throws When git cannot read the commit. A file with no time of its data is a fault.
|
|
118
118
|
*/
|
|
119
119
|
export declare function commitTime(env?: SiteEnv, repoDir?: string): string;
|
|
120
|
+
/** A node of an HTML tree, with only the fields that `rehypeScrollCue` reads. */
|
|
121
|
+
export interface RehypeNode {
|
|
122
|
+
readonly type: string;
|
|
123
|
+
readonly tagName?: string;
|
|
124
|
+
properties?: Record<string, unknown>;
|
|
125
|
+
readonly children?: readonly RehypeNode[];
|
|
126
|
+
}
|
|
127
|
+
/**
|
|
128
|
+
* This function gives the transformer of a rehype plugin. The transformer gives the class `scroll-cue` to each table of a Markdown page.
|
|
129
|
+
* The table also gets a place in the tab order and a label, so a keyboard can scroll it. A site that uses this plugin imports `scroll-cue.css`.
|
|
130
|
+
* Markdown has no way to set the class of a table, so a page of a docs theme needs this plugin.
|
|
131
|
+
*/
|
|
132
|
+
export declare function rehypeScrollCue(): (tree: RehypeNode) => void;
|
|
120
133
|
/** This function gives the data of a build. Outside CI, `GITHUB_SHA` is not set, and `sha` is `local`. */
|
|
121
134
|
export declare function buildInfo(version?: string, env?: SiteEnv, now?: Date): BuildInfo;
|
|
122
135
|
/**
|
package/build/vite.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"vite.d.ts","names":[],"sources":["../src/vite.ts"],"mappings":";;;YAmBY,UAAU,SAAS;;iBAGd;;WAEN;;;;;WAKA;;;YAIC;;iBAGK;WACN;WACA;WACA;aAAmB,SAAS;;;;iBAItB;WACN;WACA;aAAkB,QAAQ;;WAC1B,QAAQ,SAAS;;;iBAIX;WACN;WACA;WACA;;;iBAIM;WACN;EACT,OAAO,QAAQ,uBAAuB;EACtC,sBAAsB;;;YAIZ,eAAe,KAAK;WAAiC;;;iBAGhD;EACf,SAAS;IAAQ;IAAe;IAAkB;;;;iBAInC;WACN;WACA;EACT,eAAA,MAAqB;;;;;;;;wBASP,QAAQ,OAAO,sBAAsB,eAAe,MAAK,UAAwB;;qBAapF;;qBAGA;;iBAGI;;WAEN;;;iBAIM;WACN;WACA;WACA;aAA+B;IAAc,QAAQ,cAAc,MAAM;;EAClF,eAAA,MAAqB;;;;;;;;;;wBAWP,WAAW,OAAO,oBAAoB,cAAc;;;;;;;;wBA2CpD,YAAY;;;;;wBASZ,SAAS,MAAK;;;;;;;;;wBAad,WAAW,MAAK,SAAuB;;
|
|
1
|
+
{"version":3,"file":"vite.d.ts","names":[],"sources":["../src/vite.ts"],"mappings":";;;YAmBY,UAAU,SAAS;;iBAGd;;WAEN;;;;;WAKA;;;YAIC;;iBAGK;WACN;WACA;WACA;aAAmB,SAAS;;;;iBAItB;WACN;WACA;aAAkB,QAAQ;;WAC1B,QAAQ,SAAS;;;iBAIX;WACN;WACA;WACA;;;iBAIM;WACN;EACT,OAAO,QAAQ,uBAAuB;EACtC,sBAAsB;;;YAIZ,eAAe,KAAK;WAAiC;;;iBAGhD;EACf,SAAS;IAAQ;IAAe;IAAkB;;;;iBAInC;WACN;WACA;EACT,eAAA,MAAqB;;;;;;;;wBASP,QAAQ,OAAO,sBAAsB,eAAe,MAAK,UAAwB;;qBAapF;;qBAGA;;iBAGI;;WAEN;;;iBAIM;WACN;WACA;WACA;aAA+B;IAAc,QAAQ,cAAc,MAAM;;EAClF,eAAA,MAAqB;;;;;;;;;;wBAWP,WAAW,OAAO,oBAAoB,cAAc;;;;;;;;wBA2CpD,YAAY;;;;;wBASZ,SAAS,MAAK;;;;;;;;;wBAad,WAAW,MAAK,SAAuB;;iBAQtC;WACN;WACA;EACT,aAAa;WACJ,oBAAoB;;;;;;;wBAQf,oBAAoB,MAAM;;wBAc1B,UAAU,kBAAkB,MAAK,SAAuB,MAAK,OAAoB;;;;;;;;;;;;wBAoBjF,UAAU,SAAS,mBAAmB"}
|
package/build/vite.js
CHANGED
|
@@ -115,6 +115,28 @@ function commitTime(env = process.env, repoDir = process.cwd()) {
|
|
|
115
115
|
if (!time) throw new Error(`git cannot read the commit ${ref}, so the data has no time.`);
|
|
116
116
|
return new Date(time).toISOString();
|
|
117
117
|
}
|
|
118
|
+
/**
|
|
119
|
+
* This function gives the transformer of a rehype plugin. The transformer gives the class `scroll-cue` to each table of a Markdown page.
|
|
120
|
+
* The table also gets a place in the tab order and a label, so a keyboard can scroll it. A site that uses this plugin imports `scroll-cue.css`.
|
|
121
|
+
* Markdown has no way to set the class of a table, so a page of a docs theme needs this plugin.
|
|
122
|
+
*/
|
|
123
|
+
function rehypeScrollCue() {
|
|
124
|
+
const visit = (node) => {
|
|
125
|
+
if (node.type === "element" && node.tagName === "table") {
|
|
126
|
+
const properties = node.properties ?? {};
|
|
127
|
+
const names = properties["className"];
|
|
128
|
+
const list = Array.isArray(names) ? names : typeof names === "string" ? [names] : [];
|
|
129
|
+
node.properties = {
|
|
130
|
+
...properties,
|
|
131
|
+
className: [...list, "scroll-cue"],
|
|
132
|
+
tabIndex: 0,
|
|
133
|
+
ariaLabel: "Table. It scrolls sideways on a narrow screen."
|
|
134
|
+
};
|
|
135
|
+
}
|
|
136
|
+
for (const child of node.children ?? []) visit(child);
|
|
137
|
+
};
|
|
138
|
+
return visit;
|
|
139
|
+
}
|
|
118
140
|
/** This function gives the data of a build. Outside CI, `GITHUB_SHA` is not set, and `sha` is `local`. */
|
|
119
141
|
function buildInfo(version, env = process.env, now = /* @__PURE__ */ new Date()) {
|
|
120
142
|
return {
|
|
@@ -159,6 +181,6 @@ function siteShell(options) {
|
|
|
159
181
|
}
|
|
160
182
|
};
|
|
161
183
|
}
|
|
162
|
-
export { STAMPS_FILE, STAMP_MARK, buildInfo, commitTime, siteBase, siteKpi, siteShell, siteStamps, themeScript };
|
|
184
|
+
export { STAMPS_FILE, STAMP_MARK, buildInfo, commitTime, rehypeScrollCue, siteBase, siteKpi, siteShell, siteStamps, themeScript };
|
|
163
185
|
|
|
164
186
|
//# sourceMappingURL=vite.js.map
|