summaryrefslogtreecommitdiffstats
path: root/src/deps/strapi/loader.ts
diff options
context:
space:
mode:
authorVolpeon <git@volpeon.ink>2026-07-26 11:57:13 +0200
committerVolpeon <git@volpeon.ink>2026-07-26 11:57:13 +0200
commit5c899b4a5c2c07398151960b2b86d238b49d5bf4 (patch)
treeccc743f27ba234a25396cb24b6124c139d13b027 /src/deps/strapi/loader.ts
parentUse Corvos 0.1.4 (diff)
downloadcorvos-website-5c899b4a5c2c07398151960b2b86d238b49d5bf4.tar.gz
corvos-website-5c899b4a5c2c07398151960b2b86d238b49d5bf4.tar.bz2
corvos-website-5c899b4a5c2c07398151960b2b86d238b49d5bf4.zip
Better Strapi integrationHEADmaster
Diffstat (limited to 'src/deps/strapi/loader.ts')
-rw-r--r--src/deps/strapi/loader.ts116
1 files changed, 116 insertions, 0 deletions
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 @@
1import type { StandardSchemaV1 } from "corvos/deps/standard_schema.ts";
2
3import { type } from "arktype";
4import { PipelineStageError } from "corvos";
5import { Severity } from "corvos/core/error.ts";
6
7import { strapiCollectionResponseSchema, strapiItemResponseSchema } from "./schema.ts";
8
9async function* loadPaginated(url: URL | string, init?: RequestInit) {
10 let pageSize: number | undefined;
11 let pages = 1;
12
13 for (let i = 0; i < pages; ++i) {
14 const pageUrl = new URL(url);
15
16 if (pageSize) {
17 pageUrl.searchParams.set("pagination[page]", `${i + 1}`);
18 pageUrl.searchParams.set("pagination[pageSize]", `${pageSize}`);
19 }
20
21 const res = await fetch(pageUrl, init);
22 const json = await res.json();
23
24 const dec = strapiCollectionResponseSchema(json);
25
26 if (dec instanceof type.errors) {
27 throw new Error(dec.summary);
28 }
29
30 pages = dec.meta.pagination.pageCount;
31 pageSize = dec.meta.pagination.pageSize;
32
33 for (const item of dec.data) {
34 yield item;
35 }
36 }
37}
38
39export class StrapiLoader {
40 init: RequestInit;
41
42 constructor(
43 readonly host: string | URL,
44 token: string,
45 ) {
46 this.init = {
47 headers: {
48 Authorization: `Bearer ${token}`,
49 },
50 keepalive: true,
51 };
52 }
53
54 async loadSingle<T>(
55 collection: string,
56 schema: StandardSchemaV1<unknown, T>,
57 query: Record<string, string> = {},
58 ) {
59 const url = new URL(`/api/${collection}`, this.host);
60 Object.entries(query).forEach(([key, value]) => {
61 url.searchParams.append(key, value);
62 });
63
64 const res = await fetch(url, this.init);
65 const json = await res.json();
66
67 const dec = strapiItemResponseSchema(json);
68
69 if (dec instanceof type.errors) {
70 throw new Error(`Schema mismatch in ${url}:\n${dec.summary}`);
71 }
72
73 const dec2 = await schema["~standard"].validate(dec.data);
74
75 if (dec2.issues) {
76 throw new Error(`Schema mismatch in ${url}:\n${dec2.issues.join("\n")}`);
77 }
78
79 return {
80 documentId: dec.data.documentId,
81 publishedAt: dec.data.publishedAt,
82 updatedAt: dec.data.updatedAt,
83 ...dec2.value,
84 };
85 }
86
87 async *loadCollection<T>(
88 collection: string,
89 schema: StandardSchemaV1<unknown, T>,
90 query: Record<string, string> = {},
91 ) {
92 const url = new URL(`/api/${collection}`, this.host);
93 Object.entries(query).forEach(([key, value]) => {
94 url.searchParams.append(key, value);
95 });
96
97 for await (const item of loadPaginated(url, this.init)) {
98 const dec = await schema["~standard"].validate(item);
99
100 if (dec.issues) {
101 yield new PipelineStageError(
102 Severity.ERROR,
103 `Schema mismatch in ${url}`,
104 dec.issues.join("\n"),
105 );
106 } else {
107 yield {
108 documentId: item.documentId,
109 publishedAt: item.publishedAt,
110 updatedAt: item.updatedAt,
111 ...dec.value,
112 };
113 }
114 }
115 }
116}