From 5c899b4a5c2c07398151960b2b86d238b49d5bf4 Mon Sep 17 00:00:00 2001 From: Volpeon Date: Sun, 26 Jul 2026 11:57:13 +0200 Subject: Better Strapi integration --- src/deps/strapi/loader.ts | 116 ++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 116 insertions(+) create mode 100644 src/deps/strapi/loader.ts (limited to 'src/deps/strapi/loader.ts') diff --git a/src/deps/strapi/loader.ts b/src/deps/strapi/loader.ts new file mode 100644 index 0000000..c3b8e85 --- /dev/null +++ b/src/deps/strapi/loader.ts @@ -0,0 +1,116 @@ +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, + }; + } + } + } +} -- cgit v1.3.1