import type { StandardSchemaV1 } from "corvos/deps/standard_schema.ts"; import { type } from "arktype"; import { PipelineStageError } from "corvos"; import { Severity } from "corvos/core/error.ts"; import { strapiCollectionResponseSchema, strapiItemResponseSchema } from "./schema.ts"; async function* loadPaginated(url: URL | string, init?: RequestInit) { let pageSize: number | undefined; let pages = 1; for (let i = 0; i < pages; ++i) { const pageUrl = new URL(url); if (pageSize) { pageUrl.searchParams.set("pagination[page]", `${i + 1}`); pageUrl.searchParams.set("pagination[pageSize]", `${pageSize}`); } const res = await fetch(pageUrl, init); const json = await res.json(); const dec = strapiCollectionResponseSchema(json); if (dec instanceof type.errors) { throw new Error(dec.summary); } pages = dec.meta.pagination.pageCount; pageSize = dec.meta.pagination.pageSize; for (const item of dec.data) { yield item; } } } export class StrapiLoader { init: RequestInit; constructor( readonly host: string | URL, token: string, ) { this.init = { headers: { Authorization: `Bearer ${token}`, }, keepalive: true, }; } async loadSingle( collection: string, schema: StandardSchemaV1, query: Record = {}, ) { const url = new URL(`/api/${collection}`, this.host); Object.entries(query).forEach(([key, value]) => { url.searchParams.append(key, value); }); const res = await fetch(url, this.init); const json = await res.json(); const dec = strapiItemResponseSchema(json); if (dec instanceof type.errors) { throw new Error(`Schema mismatch in ${url}:\n${dec.summary}`); } const dec2 = await schema["~standard"].validate(dec.data); if (dec2.issues) { throw new Error(`Schema mismatch in ${url}:\n${dec2.issues.join("\n")}`); } return { documentId: dec.data.documentId, publishedAt: dec.data.publishedAt, updatedAt: dec.data.updatedAt, ...dec2.value, }; } async *loadCollection( collection: string, schema: StandardSchemaV1, query: Record = {}, ) { const url = new URL(`/api/${collection}`, this.host); Object.entries(query).forEach(([key, value]) => { url.searchParams.append(key, value); }); for await (const item of loadPaginated(url, this.init)) { const dec = await schema["~standard"].validate(item); if (dec.issues) { yield new PipelineStageError( Severity.ERROR, `Schema mismatch in ${url}`, dec.issues.join("\n"), ); } else { yield { documentId: item.documentId, publishedAt: item.publishedAt, updatedAt: item.updatedAt, ...dec.value, }; } } } }