@web3-storage/content-claims
Version:
Implementation of the Content Claims Protocol.
89 lines (88 loc) • 3.77 kB
JavaScript
/* global ReadableStream */
import * as Server from '@ucanto/server';
import * as Delegation from '@ucanto/core/delegation';
import * as Link from 'multiformats/link';
import { sha256 } from 'multiformats/hashes/sha2';
import { createService } from './service/index.js';
/** @typedef {import('@ucanto/interface').ServerView<import('./service/api.js').Service>} Server */
/**
* @param {import('./service/api.js').ServiceContext & {
* id: import('@ucanto/server').Signer
* codec: import('@ucanto/server').InboundCodec
* validateAuthorization: import('@ucanto/interface').RevocationChecker['validateAuthorization']
* errorReporter?: { catch: (err: import('@ucanto/server').HandlerExecutionError) => void }
* }} config
* @returns {Server}
*/
export const createServer = ({ id, codec, errorReporter: errorHandler, validateAuthorization, ...context }) => Server.create({
id,
codec,
service: createService(context),
catch: errorHandler?.catch,
validateAuthorization
});
/**
* Fetch claims for the passed content CID and walk the specified paths,
* returning a stream of content claims.
*
* @param {{ claimFetcher: import('./api.js').ClaimFetcher }} context
* @param {import('multiformats').MultihashDigest} content
* @param {Set<string>} walk
*/
export const walkClaims = (context, content, walk) => {
const queue = [content];
/** @type {ReadableStream<import('carstream/api').Block>} */
const readable = new ReadableStream({
async pull(controller) {
const content = queue.shift();
if (!content)
return controller.close();
const results = await context.claimFetcher.get(content);
if (!results.length)
return;
for (const result of results) {
controller.enqueue({
cid: Link.create(0x0202, await sha256.digest(result.bytes)),
bytes: result.bytes
});
if (walk.size) {
const claim = await Delegation.extract(result.bytes);
if (claim.error) {
console.error(claim.error);
continue;
}
const nb = claim.ok.capabilities[0].nb;
if (!nb)
continue;
/** @param {object} obj */
const walkKeys = obj => {
for (const key of Object.keys(obj).filter(k => k !== 'content')) {
// @ts-expect-error
const content = obj[key];
if (walk.has(key)) {
if (Array.isArray(content)) {
for (const c of content) {
if (Link.isLink(c)) {
queue.push(c.multihash);
}
else if (content && typeof content === 'object') {
walkKeys(content);
}
}
}
else if (Link.isLink(content)) {
queue.push(content.multihash);
}
else if (content && typeof content === 'object') {
walkKeys(content);
}
}
}
};
walkKeys(nb);
}
}
}
});
return readable;
};