@flightdev/cms 0.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,206 @@
1
+ // src/adapters/sanity.ts
2
+ function sanity(config) {
3
+ const {
4
+ projectId,
5
+ dataset = "production",
6
+ token,
7
+ apiVersion = "v2024-01-01",
8
+ useCdn = true,
9
+ preview = false,
10
+ timeout = 3e4,
11
+ fetch: customFetch = globalThis.fetch
12
+ } = config;
13
+ const host = useCdn && !preview && !token ? `${projectId}.apicdn.sanity.io` : `${projectId}.api.sanity.io`;
14
+ const baseUrl = `https://${host}/${apiVersion}/data`;
15
+ async function query(groq2, params = {}) {
16
+ const controller = new AbortController();
17
+ const timeoutId = setTimeout(() => controller.abort(), timeout);
18
+ const searchParams = new URLSearchParams();
19
+ searchParams.append("query", groq2);
20
+ for (const [key, value] of Object.entries(params)) {
21
+ searchParams.append(`$${key}`, JSON.stringify(value));
22
+ }
23
+ const headers = {};
24
+ if (token) {
25
+ headers["Authorization"] = `Bearer ${token}`;
26
+ }
27
+ try {
28
+ const response = await customFetch(
29
+ `${baseUrl}/query/${dataset}?${searchParams.toString()}`,
30
+ { headers, signal: controller.signal }
31
+ );
32
+ if (!response.ok) {
33
+ const error = await response.json().catch(() => ({}));
34
+ throw new Error(
35
+ `Sanity error: ${response.status} - ${error.message || response.statusText}`
36
+ );
37
+ }
38
+ const data = await response.json();
39
+ return data.result;
40
+ } finally {
41
+ clearTimeout(timeoutId);
42
+ }
43
+ }
44
+ async function mutate(mutations) {
45
+ if (!token) {
46
+ throw new Error("Sanity token required for mutations");
47
+ }
48
+ const controller = new AbortController();
49
+ const timeoutId = setTimeout(() => controller.abort(), timeout);
50
+ try {
51
+ const response = await customFetch(`${baseUrl}/mutate/${dataset}`, {
52
+ method: "POST",
53
+ headers: {
54
+ "Authorization": `Bearer ${token}`,
55
+ "Content-Type": "application/json"
56
+ },
57
+ body: JSON.stringify({ mutations }),
58
+ signal: controller.signal
59
+ });
60
+ if (!response.ok) {
61
+ const error = await response.json().catch(() => ({}));
62
+ throw new Error(
63
+ `Sanity mutation error: ${response.status} - ${error.message || response.statusText}`
64
+ );
65
+ }
66
+ return response.json();
67
+ } finally {
68
+ clearTimeout(timeoutId);
69
+ }
70
+ }
71
+ function buildGroq(collection, options, single = false) {
72
+ const params = {};
73
+ const filters = [`_type == "${collection}"`];
74
+ if (!preview) {
75
+ filters.push('!(_id in path("drafts.**"))');
76
+ }
77
+ if (options?.where) {
78
+ for (const [key, value] of Object.entries(options.where)) {
79
+ if (value !== void 0 && value !== null) {
80
+ const paramName = `${key.replace(/\./g, "_")}Param`;
81
+ params[paramName] = value;
82
+ if (key === "slug") {
83
+ filters.push(`slug.current == $${paramName}`);
84
+ } else if (key === "id" || key === "_id") {
85
+ filters.push(`_id == $${paramName}`);
86
+ } else {
87
+ filters.push(`${key} == $${paramName}`);
88
+ }
89
+ }
90
+ }
91
+ }
92
+ let projection = "";
93
+ if (options?.fields) {
94
+ projection = `{ ${options.fields.join(", ")} }`;
95
+ } else if (options?.populate) {
96
+ const populateFields = Array.isArray(options.populate) ? options.populate.map((field) => `"${field}": ${field}->`) : [];
97
+ projection = populateFields.length > 0 ? `{ ..., ${populateFields.join(", ")} }` : "";
98
+ }
99
+ let groq2 = `*[${filters.join(" && ")}]`;
100
+ if (options?.sort) {
101
+ if (Array.isArray(options.sort)) {
102
+ groq2 += ` | order(${options.sort.join(", ")})`;
103
+ } else {
104
+ const sortParts = Object.entries(options.sort).map(([field, order]) => `${field} ${order}`).join(", ");
105
+ groq2 += ` | order(${sortParts})`;
106
+ }
107
+ }
108
+ if (single) {
109
+ groq2 += "[0]";
110
+ } else {
111
+ const start = options?.offset ?? ((options?.page ?? 1) - 1) * (options?.pageSize ?? options?.limit ?? 10);
112
+ const end = start + (options?.limit ?? options?.pageSize ?? 10);
113
+ groq2 += `[${start}...${end}]`;
114
+ }
115
+ if (projection) {
116
+ groq2 += ` ${projection}`;
117
+ }
118
+ return { groq: groq2, params };
119
+ }
120
+ function transformDocument(doc) {
121
+ const { _id, _type, _createdAt, _updatedAt, _rev, ...rest } = doc;
122
+ return {
123
+ id: _id,
124
+ type: _type,
125
+ createdAt: _createdAt,
126
+ updatedAt: _updatedAt,
127
+ ...rest
128
+ };
129
+ }
130
+ return {
131
+ name: "sanity",
132
+ async findOne(collection, options) {
133
+ const { groq: groq2, params } = buildGroq(collection, options, true);
134
+ const result = await query(groq2, params);
135
+ if (!result) {
136
+ return null;
137
+ }
138
+ return transformDocument(result);
139
+ },
140
+ async findMany(collection, options) {
141
+ const { groq: groq2, params } = buildGroq(collection, options);
142
+ const documents = await query(groq2, params);
143
+ const countGroq = `count(*[_type == "${collection}" && !(_id in path("drafts.**"))])`;
144
+ const total = await query(countGroq);
145
+ const limit = options?.limit ?? options?.pageSize ?? 10;
146
+ const offset = options?.offset ?? ((options?.page ?? 1) - 1) * limit;
147
+ return {
148
+ data: documents.map(transformDocument),
149
+ meta: {
150
+ total,
151
+ page: Math.floor(offset / limit) + 1,
152
+ pageSize: limit,
153
+ pageCount: Math.ceil(total / limit)
154
+ }
155
+ };
156
+ },
157
+ async findById(collection, id, options) {
158
+ return this.findOne(collection, { ...options, where: { _id: String(id) } });
159
+ },
160
+ async create(collection, data) {
161
+ const doc = {
162
+ _type: collection,
163
+ ...data
164
+ };
165
+ const response = await mutate([{ create: doc }]);
166
+ const id = response.results[0]?.id;
167
+ if (!id) {
168
+ throw new Error("Failed to create document");
169
+ }
170
+ const created = await query(
171
+ `*[_id == $id][0]`,
172
+ { id }
173
+ );
174
+ return transformDocument(created);
175
+ },
176
+ async update(collection, id, data) {
177
+ await mutate([{
178
+ patch: {
179
+ id: String(id),
180
+ set: data
181
+ }
182
+ }]);
183
+ const updated = await query(
184
+ `*[_id == $id][0]`,
185
+ { id: String(id) }
186
+ );
187
+ return transformDocument(updated);
188
+ },
189
+ async delete(collection, id) {
190
+ await mutate([{ delete: { id: String(id) } }]);
191
+ },
192
+ getClient() {
193
+ return { query, mutate, baseUrl };
194
+ }
195
+ };
196
+ }
197
+ function groq(strings, ...values) {
198
+ return strings.reduce((acc, str, i) => {
199
+ const value = i < values.length ? String(values[i]) : "";
200
+ return acc + str + value;
201
+ }, "");
202
+ }
203
+
204
+ export { groq, sanity };
205
+ //# sourceMappingURL=sanity.js.map
206
+ //# sourceMappingURL=sanity.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../src/adapters/sanity.ts"],"names":["groq"],"mappings":"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","file":"sanity.js","sourcesContent":["/**\r\n * Sanity CMS Adapter\r\n * \r\n * Integration with Sanity.io using GROQ queries.\r\n * \r\n * @example\r\n * ```typescript\r\n * import { createCMS } from '@flightdev/cms';\r\n * import { sanity } from '@flightdev/cms/sanity';\r\n * \r\n * const cms = createCMS(sanity({\r\n * projectId: process.env.SANITY_PROJECT_ID,\r\n * dataset: 'production',\r\n * token: process.env.SANITY_TOKEN, // Optional for public datasets\r\n * }));\r\n * \r\n * const posts = await cms.findMany('post', {\r\n * limit: 10,\r\n * sort: { publishedAt: 'desc' },\r\n * });\r\n * ```\r\n */\r\n\r\nimport type { CMSAdapter, CMSResult, FindOneOptions, FindManyOptions } from '../index';\r\n\r\n// =============================================================================\r\n// Types\r\n// =============================================================================\r\n\r\nexport interface SanityConfig {\r\n /** Sanity Project ID */\r\n projectId: string;\r\n /** Dataset name (default: production) */\r\n dataset?: string;\r\n /** API Token (for private datasets or mutations) */\r\n token?: string;\r\n /** API version (default: v2024-01-01) */\r\n apiVersion?: string;\r\n /** Use CDN for faster reads (default: true) */\r\n useCdn?: boolean;\r\n /** Enable draft preview */\r\n preview?: boolean;\r\n /** Request timeout in ms */\r\n timeout?: number;\r\n /** Custom fetch function */\r\n fetch?: typeof fetch;\r\n}\r\n\r\ninterface SanityDocument {\r\n _id: string;\r\n _type: string;\r\n _createdAt?: string;\r\n _updatedAt?: string;\r\n _rev?: string;\r\n [key: string]: unknown;\r\n}\r\n\r\ninterface SanityQueryResponse<T> {\r\n result: T;\r\n ms: number;\r\n}\r\n\r\ninterface SanityMutationResponse {\r\n transactionId: string;\r\n results: Array<{\r\n id: string;\r\n operation: string;\r\n }>;\r\n}\r\n\r\n// =============================================================================\r\n// Adapter Implementation\r\n// =============================================================================\r\n\r\n/**\r\n * Create a Sanity CMS adapter.\r\n * \r\n * @param config - Sanity configuration\r\n * @returns CMS adapter instance\r\n */\r\nexport function sanity(config: SanityConfig): CMSAdapter {\r\n const {\r\n projectId,\r\n dataset = 'production',\r\n token,\r\n apiVersion = 'v2024-01-01',\r\n useCdn = true,\r\n preview = false,\r\n timeout = 30000,\r\n fetch: customFetch = globalThis.fetch,\r\n } = config;\r\n\r\n // Use CDN for reads, API for mutations or preview\r\n const host = useCdn && !preview && !token\r\n ? `${projectId}.apicdn.sanity.io`\r\n : `${projectId}.api.sanity.io`;\r\n\r\n const baseUrl = `https://${host}/${apiVersion}/data`;\r\n\r\n /**\r\n * Execute a GROQ query.\r\n */\r\n async function query<T>(groq: string, params: Record<string, unknown> = {}): Promise<T> {\r\n const controller = new AbortController();\r\n const timeoutId = setTimeout(() => controller.abort(), timeout);\r\n\r\n const searchParams = new URLSearchParams();\r\n searchParams.append('query', groq);\r\n\r\n for (const [key, value] of Object.entries(params)) {\r\n searchParams.append(`$${key}`, JSON.stringify(value));\r\n }\r\n\r\n const headers: Record<string, string> = {};\r\n if (token) {\r\n headers['Authorization'] = `Bearer ${token}`;\r\n }\r\n\r\n try {\r\n const response = await customFetch(\r\n `${baseUrl}/query/${dataset}?${searchParams.toString()}`,\r\n { headers, signal: controller.signal }\r\n );\r\n\r\n if (!response.ok) {\r\n const error = await response.json().catch(() => ({}));\r\n throw new Error(\r\n `Sanity error: ${response.status} - ${error.message || response.statusText}`\r\n );\r\n }\r\n\r\n const data: SanityQueryResponse<T> = await response.json();\r\n return data.result;\r\n } finally {\r\n clearTimeout(timeoutId);\r\n }\r\n }\r\n\r\n /**\r\n * Execute a mutation.\r\n */\r\n async function mutate(mutations: unknown[]): Promise<SanityMutationResponse> {\r\n if (!token) {\r\n throw new Error('Sanity token required for mutations');\r\n }\r\n\r\n const controller = new AbortController();\r\n const timeoutId = setTimeout(() => controller.abort(), timeout);\r\n\r\n try {\r\n const response = await customFetch(`${baseUrl}/mutate/${dataset}`, {\r\n method: 'POST',\r\n headers: {\r\n 'Authorization': `Bearer ${token}`,\r\n 'Content-Type': 'application/json',\r\n },\r\n body: JSON.stringify({ mutations }),\r\n signal: controller.signal,\r\n });\r\n\r\n if (!response.ok) {\r\n const error = await response.json().catch(() => ({}));\r\n throw new Error(\r\n `Sanity mutation error: ${response.status} - ${error.message || response.statusText}`\r\n );\r\n }\r\n\r\n return response.json();\r\n } finally {\r\n clearTimeout(timeoutId);\r\n }\r\n }\r\n\r\n /**\r\n * Build GROQ query from options.\r\n */\r\n function buildGroq(\r\n collection: string,\r\n options?: FindOneOptions & FindManyOptions,\r\n single: boolean = false\r\n ): { groq: string; params: Record<string, unknown> } {\r\n const params: Record<string, unknown> = {};\r\n const filters: string[] = [`_type == \"${collection}\"`];\r\n\r\n // Add draft filter for preview mode\r\n if (!preview) {\r\n filters.push('!(_id in path(\"drafts.**\"))');\r\n }\r\n\r\n // Where conditions\r\n if (options?.where) {\r\n for (const [key, value] of Object.entries(options.where)) {\r\n if (value !== undefined && value !== null) {\r\n const paramName = `${key.replace(/\\./g, '_')}Param`;\r\n params[paramName] = value;\r\n\r\n // Handle special cases\r\n if (key === 'slug') {\r\n filters.push(`slug.current == $${paramName}`);\r\n } else if (key === 'id' || key === '_id') {\r\n filters.push(`_id == $${paramName}`);\r\n } else {\r\n filters.push(`${key} == $${paramName}`);\r\n }\r\n }\r\n }\r\n }\r\n\r\n // Build projection\r\n let projection = '';\r\n if (options?.fields) {\r\n projection = `{ ${options.fields.join(', ')} }`;\r\n } else if (options?.populate) {\r\n // Resolve references\r\n const populateFields = Array.isArray(options.populate)\r\n ? options.populate.map(field => `\"${field}\": ${field}->`)\r\n : [];\r\n projection = populateFields.length > 0\r\n ? `{ ..., ${populateFields.join(', ')} }`\r\n : '';\r\n }\r\n\r\n // Build query parts\r\n let groq = `*[${filters.join(' && ')}]`;\r\n\r\n // Sort\r\n if (options?.sort) {\r\n if (Array.isArray(options.sort)) {\r\n groq += ` | order(${options.sort.join(', ')})`;\r\n } else {\r\n const sortParts = Object.entries(options.sort)\r\n .map(([field, order]) => `${field} ${order}`)\r\n .join(', ');\r\n groq += ` | order(${sortParts})`;\r\n }\r\n }\r\n\r\n // Pagination\r\n if (single) {\r\n groq += '[0]';\r\n } else {\r\n const start = options?.offset ?? ((options?.page ?? 1) - 1) * (options?.pageSize ?? options?.limit ?? 10);\r\n const end = start + (options?.limit ?? options?.pageSize ?? 10);\r\n groq += `[${start}...${end}]`;\r\n }\r\n\r\n // Add projection\r\n if (projection) {\r\n groq += ` ${projection}`;\r\n }\r\n\r\n return { groq, params };\r\n }\r\n\r\n /**\r\n * Transform Sanity document to normalized format.\r\n */\r\n function transformDocument<T>(doc: SanityDocument): T {\r\n const { _id, _type, _createdAt, _updatedAt, _rev, ...rest } = doc;\r\n\r\n return {\r\n id: _id,\r\n type: _type,\r\n createdAt: _createdAt,\r\n updatedAt: _updatedAt,\r\n ...rest,\r\n } as T;\r\n }\r\n\r\n return {\r\n name: 'sanity',\r\n\r\n async findOne<T>(collection: string, options?: FindOneOptions): Promise<T | null> {\r\n const { groq, params } = buildGroq(collection, options, true);\r\n const result = await query<SanityDocument | null>(groq, params);\r\n\r\n if (!result) {\r\n return null;\r\n }\r\n\r\n return transformDocument<T>(result);\r\n },\r\n\r\n async findMany<T>(collection: string, options?: FindManyOptions): Promise<CMSResult<T>> {\r\n // Get documents\r\n const { groq, params } = buildGroq(collection, options);\r\n const documents = await query<SanityDocument[]>(groq, params);\r\n\r\n // Get total count\r\n const countGroq = `count(*[_type == \"${collection}\" && !(_id in path(\"drafts.**\"))])`;\r\n const total = await query<number>(countGroq);\r\n\r\n const limit = options?.limit ?? options?.pageSize ?? 10;\r\n const offset = options?.offset ?? ((options?.page ?? 1) - 1) * limit;\r\n\r\n return {\r\n data: documents.map(transformDocument<T>),\r\n meta: {\r\n total,\r\n page: Math.floor(offset / limit) + 1,\r\n pageSize: limit,\r\n pageCount: Math.ceil(total / limit),\r\n },\r\n };\r\n },\r\n\r\n async findById<T>(\r\n collection: string,\r\n id: string | number,\r\n options?: Omit<FindOneOptions, 'where'>\r\n ): Promise<T | null> {\r\n return this.findOne<T>(collection, { ...options, where: { _id: String(id) } });\r\n },\r\n\r\n async create<T>(collection: string, data: Partial<T>): Promise<T> {\r\n const doc = {\r\n _type: collection,\r\n ...data,\r\n };\r\n\r\n const response = await mutate([{ create: doc }]);\r\n const id = response.results[0]?.id;\r\n\r\n if (!id) {\r\n throw new Error('Failed to create document');\r\n }\r\n\r\n // Fetch the created document\r\n const created = await query<SanityDocument>(\r\n `*[_id == $id][0]`,\r\n { id }\r\n );\r\n\r\n return transformDocument<T>(created);\r\n },\r\n\r\n async update<T>(collection: string, id: string | number, data: Partial<T>): Promise<T> {\r\n await mutate([{\r\n patch: {\r\n id: String(id),\r\n set: data,\r\n },\r\n }]);\r\n\r\n // Fetch the updated document\r\n const updated = await query<SanityDocument>(\r\n `*[_id == $id][0]`,\r\n { id: String(id) }\r\n );\r\n\r\n return transformDocument<T>(updated);\r\n },\r\n\r\n async delete(collection: string, id: string | number): Promise<void> {\r\n await mutate([{ delete: { id: String(id) } }]);\r\n },\r\n\r\n getClient() {\r\n return { query, mutate, baseUrl };\r\n },\r\n };\r\n}\r\n\r\n// =============================================================================\r\n// GROQ Helpers\r\n// =============================================================================\r\n\r\n/**\r\n * Helper to build GROQ queries manually.\r\n * Use when you need more control than the standard adapter methods.\r\n * \r\n * @example\r\n * ```typescript\r\n * import { sanity, groq } from '@flightdev/cms/sanity';\r\n * \r\n * const cms = createCMS(sanity({ projectId: '...' }));\r\n * const client = cms.getClient() as { query: Function };\r\n * \r\n * const posts = await client.query(groq`\r\n * *[_type == \"post\" && publishedAt < now()] | order(publishedAt desc) {\r\n * title,\r\n * slug,\r\n * \"author\": author->name\r\n * }\r\n * `);\r\n * ```\r\n */\r\nexport function groq(\r\n strings: TemplateStringsArray,\r\n ...values: unknown[]\r\n): string {\r\n return strings.reduce((acc, str, i) => {\r\n const value = i < values.length ? String(values[i]) : '';\r\n return acc + str + value;\r\n }, '');\r\n}\r\n"]}
@@ -0,0 +1,47 @@
1
+ import { Adapter as CMSAdapter } from '../index.js';
2
+
3
+ /**
4
+ * Strapi CMS Adapter
5
+ *
6
+ * Integration with Strapi v4/v5 REST API.
7
+ *
8
+ * @example
9
+ * ```typescript
10
+ * import { createCMS } from '@flightdev/cms';
11
+ * import { strapi } from '@flightdev/cms/strapi';
12
+ *
13
+ * const cms = createCMS(strapi({
14
+ * url: 'http://localhost:1337',
15
+ * token: process.env.STRAPI_TOKEN,
16
+ * }));
17
+ *
18
+ * const posts = await cms.findMany('posts', {
19
+ * populate: ['author', 'cover'],
20
+ * sort: { publishedAt: 'desc' },
21
+ * });
22
+ * ```
23
+ */
24
+
25
+ interface StrapiConfig {
26
+ /** Strapi URL (e.g., http://localhost:1337) */
27
+ url: string;
28
+ /** API token for authentication */
29
+ token?: string;
30
+ /** API prefix (default: /api) */
31
+ apiPrefix?: string;
32
+ /** Request timeout in ms */
33
+ timeout?: number;
34
+ /** Enable draft preview mode */
35
+ preview?: boolean;
36
+ /** Custom fetch function */
37
+ fetch?: typeof fetch;
38
+ }
39
+ /**
40
+ * Create a Strapi CMS adapter.
41
+ *
42
+ * @param config - Strapi configuration
43
+ * @returns CMS adapter instance
44
+ */
45
+ declare function strapi(config: StrapiConfig): CMSAdapter;
46
+
47
+ export { type StrapiConfig, strapi };
@@ -0,0 +1,178 @@
1
+ // src/adapters/strapi.ts
2
+ function strapi(config) {
3
+ const {
4
+ url,
5
+ token,
6
+ apiPrefix = "/api",
7
+ timeout = 3e4,
8
+ preview = false,
9
+ fetch: customFetch = globalThis.fetch
10
+ } = config;
11
+ async function request(path, options = {}) {
12
+ const controller = new AbortController();
13
+ const timeoutId = setTimeout(() => controller.abort(), timeout);
14
+ const headers = {
15
+ "Content-Type": "application/json",
16
+ ...options.headers
17
+ };
18
+ if (token) {
19
+ headers["Authorization"] = `Bearer ${token}`;
20
+ }
21
+ try {
22
+ const response = await customFetch(`${url}${apiPrefix}${path}`, {
23
+ ...options,
24
+ headers,
25
+ signal: controller.signal
26
+ });
27
+ if (!response.ok) {
28
+ const error = await response.json().catch(() => ({}));
29
+ throw new Error(
30
+ `Strapi error: ${response.status} - ${error.error?.message || response.statusText}`
31
+ );
32
+ }
33
+ return response.json();
34
+ } finally {
35
+ clearTimeout(timeoutId);
36
+ }
37
+ }
38
+ function buildQuery(options) {
39
+ if (!options) return "";
40
+ const params = new URLSearchParams();
41
+ if (options.where) {
42
+ for (const [key, value] of Object.entries(options.where)) {
43
+ if (value !== void 0 && value !== null) {
44
+ params.append(`filters[${key}][$eq]`, String(value));
45
+ }
46
+ }
47
+ }
48
+ if (options.populate) {
49
+ if (Array.isArray(options.populate)) {
50
+ options.populate.forEach((field, index) => {
51
+ params.append(`populate[${index}]`, field);
52
+ });
53
+ } else {
54
+ params.append("populate", "*");
55
+ }
56
+ }
57
+ if (options.limit !== void 0) {
58
+ params.append("pagination[pageSize]", String(options.limit));
59
+ }
60
+ if (options.offset !== void 0) {
61
+ const page = Math.floor(options.offset / (options.limit || 25)) + 1;
62
+ params.append("pagination[page]", String(page));
63
+ }
64
+ if (options.page !== void 0) {
65
+ params.append("pagination[page]", String(options.page));
66
+ }
67
+ if (options.pageSize !== void 0) {
68
+ params.append("pagination[pageSize]", String(options.pageSize));
69
+ }
70
+ if (options.sort) {
71
+ if (Array.isArray(options.sort)) {
72
+ options.sort.forEach((field, index) => {
73
+ params.append(`sort[${index}]`, field);
74
+ });
75
+ } else {
76
+ Object.entries(options.sort).forEach(([field, order]) => {
77
+ params.append("sort", `${field}:${order}`);
78
+ });
79
+ }
80
+ }
81
+ if (options.fields) {
82
+ options.fields.forEach((field, index) => {
83
+ params.append(`fields[${index}]`, field);
84
+ });
85
+ }
86
+ if (options.locale) {
87
+ params.append("locale", options.locale);
88
+ }
89
+ if (options.preview || preview) {
90
+ params.append("publicationState", "preview");
91
+ }
92
+ const queryString = params.toString();
93
+ return queryString ? `?${queryString}` : "";
94
+ }
95
+ function transformEntity(entity) {
96
+ const { id, attributes } = entity;
97
+ const transformed = { id };
98
+ for (const [key, value] of Object.entries(attributes)) {
99
+ if (value && typeof value === "object" && "data" in value) {
100
+ const data = value.data;
101
+ if (Array.isArray(data)) {
102
+ transformed[key] = data.map(transformEntity);
103
+ } else if (data) {
104
+ transformed[key] = transformEntity(data);
105
+ } else {
106
+ transformed[key] = null;
107
+ }
108
+ } else {
109
+ transformed[key] = value;
110
+ }
111
+ }
112
+ return transformed;
113
+ }
114
+ return {
115
+ name: "strapi",
116
+ async findOne(collection, options) {
117
+ const query = buildQuery({ ...options, limit: 1 });
118
+ const response = await request(`/${collection}${query}`);
119
+ if (!response.data || response.data.length === 0) {
120
+ return null;
121
+ }
122
+ return transformEntity(response.data[0]);
123
+ },
124
+ async findMany(collection, options) {
125
+ const query = buildQuery(options);
126
+ const response = await request(`/${collection}${query}`);
127
+ const pagination = response.meta?.pagination;
128
+ return {
129
+ data: (response.data || []).map(transformEntity),
130
+ meta: {
131
+ total: pagination?.total ?? response.data?.length ?? 0,
132
+ page: pagination?.page ?? 1,
133
+ pageSize: pagination?.pageSize ?? response.data?.length ?? 0,
134
+ pageCount: pagination?.pageCount ?? 1
135
+ }
136
+ };
137
+ },
138
+ async findById(collection, id, options) {
139
+ const query = buildQuery(options);
140
+ try {
141
+ const response = await request(`/${collection}/${id}${query}`);
142
+ if (!response.data) {
143
+ return null;
144
+ }
145
+ return transformEntity(response.data);
146
+ } catch (error) {
147
+ if (error.message.includes("404")) {
148
+ return null;
149
+ }
150
+ throw error;
151
+ }
152
+ },
153
+ async create(collection, data) {
154
+ const response = await request(`/${collection}`, {
155
+ method: "POST",
156
+ body: JSON.stringify({ data })
157
+ });
158
+ return transformEntity(response.data);
159
+ },
160
+ async update(collection, id, data) {
161
+ const response = await request(`/${collection}/${id}`, {
162
+ method: "PUT",
163
+ body: JSON.stringify({ data })
164
+ });
165
+ return transformEntity(response.data);
166
+ },
167
+ async delete(collection, id) {
168
+ await request(`/${collection}/${id}`, { method: "DELETE" });
169
+ },
170
+ getClient() {
171
+ return { request, buildQuery };
172
+ }
173
+ };
174
+ }
175
+
176
+ export { strapi };
177
+ //# sourceMappingURL=strapi.js.map
178
+ //# sourceMappingURL=strapi.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../src/adapters/strapi.ts"],"names":[],"mappings":"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","file":"strapi.js","sourcesContent":["/**\r\n * Strapi CMS Adapter\r\n * \r\n * Integration with Strapi v4/v5 REST API.\r\n * \r\n * @example\r\n * ```typescript\r\n * import { createCMS } from '@flightdev/cms';\r\n * import { strapi } from '@flightdev/cms/strapi';\r\n * \r\n * const cms = createCMS(strapi({\r\n * url: 'http://localhost:1337',\r\n * token: process.env.STRAPI_TOKEN,\r\n * }));\r\n * \r\n * const posts = await cms.findMany('posts', {\r\n * populate: ['author', 'cover'],\r\n * sort: { publishedAt: 'desc' },\r\n * });\r\n * ```\r\n */\r\n\r\nimport type { CMSAdapter, CMSResult, FindOneOptions, FindManyOptions } from '../index';\r\n\r\n// =============================================================================\r\n// Types\r\n// =============================================================================\r\n\r\nexport interface StrapiConfig {\r\n /** Strapi URL (e.g., http://localhost:1337) */\r\n url: string;\r\n /** API token for authentication */\r\n token?: string;\r\n /** API prefix (default: /api) */\r\n apiPrefix?: string;\r\n /** Request timeout in ms */\r\n timeout?: number;\r\n /** Enable draft preview mode */\r\n preview?: boolean;\r\n /** Custom fetch function */\r\n fetch?: typeof fetch;\r\n}\r\n\r\ninterface StrapiResponse<T> {\r\n data: T;\r\n meta?: {\r\n pagination?: {\r\n page: number;\r\n pageSize: number;\r\n pageCount: number;\r\n total: number;\r\n };\r\n };\r\n}\r\n\r\ninterface StrapiEntity {\r\n id: number;\r\n attributes: Record<string, unknown>;\r\n}\r\n\r\n// =============================================================================\r\n// Adapter Implementation\r\n// =============================================================================\r\n\r\n/**\r\n * Create a Strapi CMS adapter.\r\n * \r\n * @param config - Strapi configuration\r\n * @returns CMS adapter instance\r\n */\r\nexport function strapi(config: StrapiConfig): CMSAdapter {\r\n const {\r\n url,\r\n token,\r\n apiPrefix = '/api',\r\n timeout = 30000,\r\n preview = false,\r\n fetch: customFetch = globalThis.fetch,\r\n } = config;\r\n\r\n /**\r\n * Make a request to Strapi API.\r\n */\r\n async function request<T>(\r\n path: string,\r\n options: RequestInit = {}\r\n ): Promise<T> {\r\n const controller = new AbortController();\r\n const timeoutId = setTimeout(() => controller.abort(), timeout);\r\n\r\n const headers: Record<string, string> = {\r\n 'Content-Type': 'application/json',\r\n ...options.headers as Record<string, string>,\r\n };\r\n\r\n if (token) {\r\n headers['Authorization'] = `Bearer ${token}`;\r\n }\r\n\r\n try {\r\n const response = await customFetch(`${url}${apiPrefix}${path}`, {\r\n ...options,\r\n headers,\r\n signal: controller.signal,\r\n });\r\n\r\n if (!response.ok) {\r\n const error = await response.json().catch(() => ({}));\r\n throw new Error(\r\n `Strapi error: ${response.status} - ${error.error?.message || response.statusText}`\r\n );\r\n }\r\n\r\n return response.json();\r\n } finally {\r\n clearTimeout(timeoutId);\r\n }\r\n }\r\n\r\n /**\r\n * Build query string from options.\r\n */\r\n function buildQuery(options?: FindOneOptions & FindManyOptions): string {\r\n if (!options) return '';\r\n\r\n const params = new URLSearchParams();\r\n\r\n // Filters\r\n if (options.where) {\r\n for (const [key, value] of Object.entries(options.where)) {\r\n if (value !== undefined && value !== null) {\r\n params.append(`filters[${key}][$eq]`, String(value));\r\n }\r\n }\r\n }\r\n\r\n // Populate\r\n if (options.populate) {\r\n if (Array.isArray(options.populate)) {\r\n options.populate.forEach((field, index) => {\r\n params.append(`populate[${index}]`, field);\r\n });\r\n } else {\r\n params.append('populate', '*');\r\n }\r\n }\r\n\r\n // Pagination\r\n if (options.limit !== undefined) {\r\n params.append('pagination[pageSize]', String(options.limit));\r\n }\r\n if (options.offset !== undefined) {\r\n const page = Math.floor(options.offset / (options.limit || 25)) + 1;\r\n params.append('pagination[page]', String(page));\r\n }\r\n if (options.page !== undefined) {\r\n params.append('pagination[page]', String(options.page));\r\n }\r\n if (options.pageSize !== undefined) {\r\n params.append('pagination[pageSize]', String(options.pageSize));\r\n }\r\n\r\n // Sort\r\n if (options.sort) {\r\n if (Array.isArray(options.sort)) {\r\n options.sort.forEach((field, index) => {\r\n params.append(`sort[${index}]`, field);\r\n });\r\n } else {\r\n Object.entries(options.sort).forEach(([field, order]) => {\r\n params.append('sort', `${field}:${order}`);\r\n });\r\n }\r\n }\r\n\r\n // Fields\r\n if (options.fields) {\r\n options.fields.forEach((field, index) => {\r\n params.append(`fields[${index}]`, field);\r\n });\r\n }\r\n\r\n // Locale\r\n if (options.locale) {\r\n params.append('locale', options.locale);\r\n }\r\n\r\n // Draft preview\r\n if (options.preview || preview) {\r\n params.append('publicationState', 'preview');\r\n }\r\n\r\n const queryString = params.toString();\r\n return queryString ? `?${queryString}` : '';\r\n }\r\n\r\n /**\r\n * Transform Strapi entity to normalized format.\r\n */\r\n function transformEntity<T>(entity: StrapiEntity): T {\r\n const { id, attributes } = entity;\r\n\r\n // Recursively transform nested relations\r\n const transformed: Record<string, unknown> = { id };\r\n\r\n for (const [key, value] of Object.entries(attributes)) {\r\n if (value && typeof value === 'object' && 'data' in (value as any)) {\r\n const data = (value as any).data;\r\n if (Array.isArray(data)) {\r\n transformed[key] = data.map(transformEntity);\r\n } else if (data) {\r\n transformed[key] = transformEntity(data);\r\n } else {\r\n transformed[key] = null;\r\n }\r\n } else {\r\n transformed[key] = value;\r\n }\r\n }\r\n\r\n return transformed as T;\r\n }\r\n\r\n return {\r\n name: 'strapi',\r\n\r\n async findOne<T>(collection: string, options?: FindOneOptions): Promise<T | null> {\r\n const query = buildQuery({ ...options, limit: 1 });\r\n const response = await request<StrapiResponse<StrapiEntity[]>>(`/${collection}${query}`);\r\n\r\n if (!response.data || response.data.length === 0) {\r\n return null;\r\n }\r\n\r\n return transformEntity<T>(response.data[0]);\r\n },\r\n\r\n async findMany<T>(collection: string, options?: FindManyOptions): Promise<CMSResult<T>> {\r\n const query = buildQuery(options);\r\n const response = await request<StrapiResponse<StrapiEntity[]>>(`/${collection}${query}`);\r\n\r\n const pagination = response.meta?.pagination;\r\n\r\n return {\r\n data: (response.data || []).map(transformEntity<T>),\r\n meta: {\r\n total: pagination?.total ?? response.data?.length ?? 0,\r\n page: pagination?.page ?? 1,\r\n pageSize: pagination?.pageSize ?? response.data?.length ?? 0,\r\n pageCount: pagination?.pageCount ?? 1,\r\n },\r\n };\r\n },\r\n\r\n async findById<T>(\r\n collection: string,\r\n id: string | number,\r\n options?: Omit<FindOneOptions, 'where'>\r\n ): Promise<T | null> {\r\n const query = buildQuery(options);\r\n\r\n try {\r\n const response = await request<StrapiResponse<StrapiEntity>>(`/${collection}/${id}${query}`);\r\n\r\n if (!response.data) {\r\n return null;\r\n }\r\n\r\n return transformEntity<T>(response.data);\r\n } catch (error) {\r\n if ((error as Error).message.includes('404')) {\r\n return null;\r\n }\r\n throw error;\r\n }\r\n },\r\n\r\n async create<T>(collection: string, data: Partial<T>): Promise<T> {\r\n const response = await request<StrapiResponse<StrapiEntity>>(`/${collection}`, {\r\n method: 'POST',\r\n body: JSON.stringify({ data }),\r\n });\r\n\r\n return transformEntity<T>(response.data);\r\n },\r\n\r\n async update<T>(collection: string, id: string | number, data: Partial<T>): Promise<T> {\r\n const response = await request<StrapiResponse<StrapiEntity>>(`/${collection}/${id}`, {\r\n method: 'PUT',\r\n body: JSON.stringify({ data }),\r\n });\r\n\r\n return transformEntity<T>(response.data);\r\n },\r\n\r\n async delete(collection: string, id: string | number): Promise<void> {\r\n await request(`/${collection}/${id}`, { method: 'DELETE' });\r\n },\r\n\r\n getClient() {\r\n return { request, buildQuery };\r\n },\r\n };\r\n}\r\n"]}