chadtmiller.com/pds.js
An atproto PDS in JavaScript, for Cloudflare Workers and Node
0
0
/**
* Per-repository secrets, read from the file `PDSJS_CI_SECRETS` names.
*
* A step sees a secret only when both sides name it: the file grants it to
* the repository, and the workflow declares it in `secrets`. The names are
* public in the workflow file; the values exist only on the runner host.
*/
/** Where the file format is described, for error messages. */
const FILE_LABEL = 'secrets file';
/**
* A grant maps `<handle-or-did>/<repo>` to the secrets that repository may
* request. The authority resolves to a DID the same way a watch spec does,
* and the name to the record key a run names the repository by.
* @typedef {Record<string, Record<string, string>>} SecretsGrants
*/
/**
* What one run receives: the secrets this repository is granted and its
* workflow declares.
* @typedef {(did: string, repoRkey: string, names: string[]) => Record<string, string>} SecretsResolver
*/
/**
* @param {string} source - file contents
* @returns {SecretsGrants}
*/
export function parseSecretsFile(source) {
/** @type {unknown} */
let parsed;
try {
parsed = JSON.parse(source);
} catch (err) {
throw new Error(
`${FILE_LABEL}: invalid JSON (${err instanceof Error ? err.message : err})`,
);
}
if (!parsed || typeof parsed !== 'object' || Array.isArray(parsed)) {
throw new Error(`${FILE_LABEL}: must be a JSON object keyed by repository`);
}
/** @type {SecretsGrants} */
const grants = {};
for (const [repo, entry] of Object.entries(parsed)) {
if (!entry || typeof entry !== 'object' || Array.isArray(entry)) {
throw new Error(`${FILE_LABEL}: ${repo} must be an object of secrets`);
}
/** @type {Record<string, string>} */
const values = {};
for (const [name, value] of Object.entries(entry)) {
if (typeof value !== 'string') {
throw new Error(`${FILE_LABEL}: ${repo}.${name} must be a string`);
}
values[name] = value;
}
grants[repo] = values;
}
return grants;
}
/**
* The intersection of what the repository is granted and what the workflow
* declares. A declared name with no grant is absent, and the workflow's own
* guard reports it; the run does not fail here.
* @param {Record<string, string>|undefined} granted - this repository's grants
* @param {string[]} names - what the workflow declares
* @returns {Record<string, string>}
*/
export function grantedSecrets(granted, names) {
/** @type {Record<string, string>} */
const env = {};
if (!granted) return env;
for (const name of names) {
if (name in granted) env[name] = granted[name];
}
return env;
}