chadtmiller.com/pds.js
An atproto PDS in JavaScript, for Cloudflare Workers and Node
0
0
/**
* Reading and updating the dev.pdsjs.npm.package record value.
*/
import { NPM_PACKAGE_COLLECTION } from './lexicon.js';
/**
* @typedef {Object} BlobRef
* @property {'blob'} $type
* @property {{$link: string}} ref
* @property {string} mimeType
* @property {number} size
*/
/**
* @typedef {Object} VersionEntry
* @property {string} version
* @property {BlobRef} tarball
* @property {BlobRef} manifest
* @property {string} integrity
* @property {string} [shasum]
* @property {string} [publishedAt]
*/
/**
* @typedef {Object} NpmPackageRecord
* @property {string} name
* @property {Array<{tag: string, version: string}>} distTags
* @property {VersionEntry[]} versions
* @property {string} createdAt
* @property {string} [updatedAt]
*/
/**
* @param {string} cid
* @param {string} mimeType
* @param {number} size
* @returns {BlobRef}
*/
export function blobRef(cid, mimeType, size) {
return { $type: 'blob', ref: { $link: cid }, mimeType, size };
}
/**
* @param {unknown} value
* @returns {BlobRef|null}
*/
function asBlobRef(value) {
if (!value || typeof value !== 'object') return null;
const ref =
/** @type {{ref?: {$link?: unknown}, mimeType?: unknown, size?: unknown}} */ (
value
);
if (typeof ref.ref?.$link !== 'string') return null;
return {
$type: 'blob',
ref: { $link: ref.ref.$link },
mimeType:
typeof ref.mimeType === 'string'
? ref.mimeType
: 'application/octet-stream',
size: typeof ref.size === 'number' ? ref.size : 0,
};
}
/**
* @param {unknown} value - record value from getRecord
* @returns {NpmPackageRecord|null}
*/
export function parsePackageRecord(value) {
if (!value || typeof value !== 'object') return null;
const record = /** @type {Record<string, unknown>} */ (value);
if (typeof record.name !== 'string') return null;
/** @type {Array<{tag: string, version: string}>} */
const distTags = [];
for (const entry of Array.isArray(record.distTags) ? record.distTags : []) {
if (
entry &&
typeof entry.tag === 'string' &&
typeof entry.version === 'string'
) {
distTags.push({ tag: entry.tag, version: entry.version });
}
}
/** @type {VersionEntry[]} */
const versions = [];
for (const entry of Array.isArray(record.versions) ? record.versions : []) {
const tarball = asBlobRef(entry?.tarball);
const manifest = asBlobRef(entry?.manifest);
if (
entry &&
typeof entry.version === 'string' &&
typeof entry.integrity === 'string' &&
tarball &&
manifest
) {
versions.push({
version: entry.version,
tarball,
manifest,
integrity: entry.integrity,
shasum: typeof entry.shasum === 'string' ? entry.shasum : undefined,
publishedAt:
typeof entry.publishedAt === 'string' ? entry.publishedAt : undefined,
});
}
}
return {
name: record.name,
distTags,
versions,
createdAt:
typeof record.createdAt === 'string'
? record.createdAt
: new Date(0).toISOString(),
updatedAt:
typeof record.updatedAt === 'string' ? record.updatedAt : undefined,
};
}
/**
* The record value to store, with `$type` set and empty updatedAt dropped.
* @param {NpmPackageRecord} record
* @returns {Record<string, unknown>}
*/
export function serializePackageRecord(record) {
return {
$type: NPM_PACKAGE_COLLECTION,
name: record.name,
distTags: record.distTags,
versions: record.versions.map((entry) => ({
version: entry.version,
tarball: entry.tarball,
manifest: entry.manifest,
integrity: entry.integrity,
...(entry.shasum ? { shasum: entry.shasum } : {}),
...(entry.publishedAt ? { publishedAt: entry.publishedAt } : {}),
})),
createdAt: record.createdAt,
...(record.updatedAt ? { updatedAt: record.updatedAt } : {}),
};
}
/**
* Every blob CID the record references, for the link table.
* @param {NpmPackageRecord} record
* @returns {string[]}
*/
export function recordBlobCids(record) {
const cids = new Set();
for (const entry of record.versions) {
cids.add(entry.tarball.ref.$link);
cids.add(entry.manifest.ref.$link);
}
return [...cids];
}