fimo 0.7.0 → 0.8.0-staging.1
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.
- package/assets/agent-templates/seo-audit/GOAL.md +7 -3
- package/assets/content-templates/hooks-template.eta +42 -605
- package/assets/skills/fimo/references/content.md +1 -1
- package/assets/skills/fimo-studio/SKILL.md +5 -4
- package/assets/skills/fimo-studio/references/cms.md +30 -14
- package/dist/cli/bundle.json +1 -1
- package/dist/cli/index.js +106 -100
- package/dist/scripts/sync-schemas.js +148 -62
- package/package.json +1 -1
- package/release.json +2 -2
- package/templates/react-router/package.json +1 -1
|
@@ -1,620 +1,63 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
%>
|
|
11
|
-
// AUTO-GENERATED ⚠️ – do not edit by hand
|
|
12
|
-
import { <% if (!model.isSingleton) { %>useInfiniteQuery, <% } %>useSuspenseQuery, useQuery, useMutation, useQueryClient } from "@tanstack/react-query";
|
|
13
|
-
<% if (!model.isSingleton) { -%>
|
|
14
|
-
import type { InfiniteData, UseInfiniteQueryResult, UseQueryResult, UseSuspenseQueryResult } from "@tanstack/react-query";
|
|
1
|
+
// AUTO-GENERATED by `fimo` from src/schemas/<%= model.uid %>.json - DO NOT EDIT.
|
|
2
|
+
// Change content fields in the schema, then run `fimo validate`.
|
|
3
|
+
// Runtime implementation lives in the project-local fimo/ui package.
|
|
4
|
+
<% if (model.isSingleton) { -%>
|
|
5
|
+
import { createSingletonClient } from "fimo/ui";
|
|
6
|
+
import type { SingletonContentClient } from "fimo/ui";
|
|
7
|
+
<% } else { -%>
|
|
8
|
+
import { createContentClient } from "fimo/ui";
|
|
9
|
+
import type { CollectionContentClient } from "fimo/ui";
|
|
15
10
|
<% } -%>
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
import fimoConfig from "virtual:fimo-config";
|
|
19
|
-
import type { <%= model.pascal %>, <%= model.pascal %>Input, <%= model.pascal %>Populate, <%= model.pascal %>Result<% if (!model.isSingleton) { %>, <%= model.pascal %>Fields, <%= model.pascal %>Query<% } %> } from "./<%= model.pascal %>.types";
|
|
11
|
+
|
|
12
|
+
import schema from "./<%= model.uid %>.json";
|
|
20
13
|
<%
|
|
21
14
|
if (model.referenceImports && model.referenceImports.length > 0) {
|
|
22
15
|
model.referenceImports.forEach((referenceImport) => {
|
|
23
16
|
-%>
|
|
24
|
-
import
|
|
17
|
+
import * as <%= referenceImport.pascal %> from "./<%= referenceImport.pascal %>";
|
|
25
18
|
<%
|
|
26
19
|
});
|
|
27
20
|
}
|
|
21
|
+
if (model.isSingleton) {
|
|
28
22
|
-%>
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
/*───────────────────────────────────────────────────────────────*/
|
|
32
|
-
/* ──────────── 🎁 Source wrapping ──────────── */
|
|
33
|
-
/*───────────────────────────────────────────────────────────────*/
|
|
34
|
-
|
|
35
|
-
/**
|
|
36
|
-
* Wraps raw API data with Fimo types for source tracking
|
|
37
|
-
*/
|
|
38
|
-
export function wrapWithSource(entry: Record<string, any>): <%= model.pascal %> {
|
|
39
|
-
const id = entry.id;
|
|
40
|
-
const entryId = entry.entryId;
|
|
41
|
-
const documentId = entry.documentId ?? id;
|
|
42
|
-
const locale = entry.locale ?? defaultLocale();
|
|
43
|
-
const sourcePrefix = `<%= model.pascal %>.id:${id}:${encodeURIComponent(locale)}`;
|
|
44
|
-
const entryFimo = entry.__fimo && typeof entry.__fimo === "object" ? entry.__fimo : {};
|
|
45
|
-
|
|
46
|
-
return {
|
|
47
|
-
id,
|
|
48
|
-
entryId,
|
|
49
|
-
documentId,
|
|
50
|
-
slug: entry.slug != null ? new FimoString(entry.slug, `${sourcePrefix}.slug`) : entry.slug,
|
|
51
|
-
<% if (!model.isSingleton || model.localized) { -%>
|
|
52
|
-
locale,
|
|
53
|
-
<% } else { -%>
|
|
54
|
-
...(entry.locale ? { locale } : {}),
|
|
55
|
-
<% } -%>
|
|
56
|
-
createdAt: entry.createdAt,
|
|
57
|
-
updatedAt: entry.updatedAt,
|
|
58
|
-
__fimo: {
|
|
59
|
-
contentType: typeof entryFimo.contentType === "string" ? entryFimo.contentType : "<%= model.uid %>",
|
|
60
|
-
},
|
|
61
|
-
<%
|
|
62
|
-
if (model.fields) {
|
|
63
|
-
Object.entries(model.fields).forEach(([key, field]) => {
|
|
64
|
-
const fieldSource = `\${sourcePrefix}.${key}`;
|
|
65
|
-
switch(field.type) {
|
|
66
|
-
case 'string':
|
|
67
|
-
-%>
|
|
68
|
-
<%= key %>: entry.<%= key %> != null ? new FimoString(entry.<%= key %>, `<%= fieldSource %>`) : entry.<%= key %>,
|
|
69
|
-
<%
|
|
70
|
-
break;
|
|
71
|
-
case 'richtext':
|
|
72
|
-
-%>
|
|
73
|
-
<%= key %>: entry.<%= key %> != null ? new FimoRichText(entry.<%= key %>, `<%= fieldSource %>`) : entry.<%= key %>,
|
|
74
|
-
<%
|
|
75
|
-
break;
|
|
76
|
-
case 'boolean':
|
|
77
|
-
-%>
|
|
78
|
-
<%= key %>: entry.<%= key %> != null ? new FimoBoolean(entry.<%= key %>, `<%= fieldSource %>`) : entry.<%= key %>,
|
|
79
|
-
<%
|
|
80
|
-
break;
|
|
81
|
-
case 'media':
|
|
82
|
-
if (field.multiple) {
|
|
83
|
-
-%>
|
|
84
|
-
<%= key %>: entry.<%= key %>?.map((m: any, i: number) => new FimoMedia(m, `<%= fieldSource %>[${i}]`)) ?? entry.<%= key %>,
|
|
85
|
-
<%
|
|
86
|
-
} else {
|
|
87
|
-
-%>
|
|
88
|
-
<%= key %>: entry.<%= key %> != null ? new FimoMedia(entry.<%= key %>, `<%= fieldSource %>`) : entry.<%= key %>,
|
|
89
|
-
<%
|
|
90
|
-
}
|
|
91
|
-
break;
|
|
92
|
-
case 'reference': {
|
|
93
|
-
if (Array.isArray(field.targetRefs) && field.targetRefs.length > 0) {
|
|
94
|
-
const wrapperFor = (ref) => ref.uid === model.uid ? 'wrapWithSource' : `wrap${ref.pascal}WithSource`;
|
|
95
|
-
const wrappers = `{ ${field.targetRefs.map((ref) => `"${ref.uid}": ${wrapperFor(ref)}`).join(', ')} }`;
|
|
96
|
-
if (field.many) {
|
|
97
|
-
-%>
|
|
98
|
-
<%= key %>: Array.isArray(entry.<%= key %>) ? entry.<%= key %>.map((value: any) => wrapPolymorphic(value, <%= wrappers %>)) : entry.<%= key %>,
|
|
99
|
-
<%
|
|
100
|
-
} else {
|
|
101
|
-
-%>
|
|
102
|
-
<%= key %>: wrapPolymorphic(entry.<%= key %>, <%= wrappers %>),
|
|
103
|
-
<%
|
|
104
|
-
}
|
|
105
|
-
break;
|
|
106
|
-
}
|
|
107
|
-
const wrapper = field.target === model.uid ? 'wrapWithSource' : `wrap${field.targetPascal}WithSource`;
|
|
108
|
-
if (field.many) {
|
|
109
|
-
-%>
|
|
110
|
-
<%= key %>: Array.isArray(entry.<%= key %>) ? entry.<%= key %>.map((value: any) => isPopulatedEntry(value) ? <%= wrapper %>(value) : value) : entry.<%= key %>,
|
|
111
|
-
<%
|
|
112
|
-
} else {
|
|
113
|
-
-%>
|
|
114
|
-
<%= key %>: isPopulatedEntry(entry.<%= key %>) ? <%= wrapper %>(entry.<%= key %>) : entry.<%= key %>,
|
|
115
|
-
<%
|
|
116
|
-
}
|
|
117
|
-
break;
|
|
118
|
-
}
|
|
119
|
-
default:
|
|
120
|
-
-%>
|
|
121
|
-
<%= key %>: entry.<%= key %>,
|
|
122
|
-
<%
|
|
123
|
-
}
|
|
124
|
-
});
|
|
125
|
-
}
|
|
126
|
-
-%>
|
|
127
|
-
} as <%= model.pascal %>;
|
|
128
|
-
}
|
|
129
|
-
|
|
130
|
-
interface PopulatedEntryRecord extends Record<string, unknown> {
|
|
131
|
-
id: string;
|
|
132
|
-
__fimo: { contentType: string };
|
|
133
|
-
}
|
|
134
|
-
|
|
135
|
-
function isPopulatedEntry(value: unknown): value is PopulatedEntryRecord {
|
|
136
|
-
if (value == null || typeof value !== "object") {
|
|
137
|
-
return false;
|
|
138
|
-
}
|
|
139
|
-
const entry = value as Record<string, unknown>;
|
|
140
|
-
const fimo = entry.__fimo;
|
|
141
|
-
return typeof entry.id === "string"
|
|
142
|
-
&& fimo != null
|
|
143
|
-
&& typeof fimo === "object"
|
|
144
|
-
&& typeof (fimo as Record<string, unknown>).contentType === "string";
|
|
145
|
-
}
|
|
146
|
-
|
|
147
|
-
<%
|
|
148
|
-
const hasPolymorphicReferences = model.fields && Object.values(model.fields).some((field) => field.type === 'reference' && Array.isArray(field.targetRefs) && field.targetRefs.length > 0);
|
|
149
|
-
if (hasPolymorphicReferences) {
|
|
150
|
-
-%>
|
|
151
|
-
/**
|
|
152
|
-
* Wraps one polymorphic reference value. Populated entries carry their type
|
|
153
|
-
* marker; the wrapper picks the right per-type wrapping and stamps a
|
|
154
|
-
* top-level $type so the generated union narrows in a switch. Raw
|
|
155
|
-
* (unpopulated) values pass through untouched.
|
|
156
|
-
*/
|
|
157
|
-
function wrapPolymorphic(value: any, wrappers: Record<string, (entry: Record<string, any>) => any>): any {
|
|
158
|
-
if (!isPopulatedEntry(value)) {
|
|
159
|
-
return value;
|
|
160
|
-
}
|
|
161
|
-
const contentType = value.__fimo && typeof value.__fimo.contentType === "string" ? value.__fimo.contentType : null;
|
|
162
|
-
const wrapper = contentType ? wrappers[contentType] : undefined;
|
|
163
|
-
if (!wrapper) return value;
|
|
164
|
-
const wrapped = wrapper(value) as Record<string, unknown>;
|
|
165
|
-
wrapped.$type = contentType;
|
|
166
|
-
return wrapped;
|
|
167
|
-
}
|
|
168
|
-
|
|
169
|
-
<%
|
|
170
|
-
}
|
|
171
|
-
-%>
|
|
172
|
-
/*───────────────────────────────────────────────────────────────*/
|
|
173
|
-
/* ──────────── 🔌 Low-level fetch helpers ──────────── */
|
|
174
|
-
/*───────────────────────────────────────────────────────────────*/
|
|
175
|
-
// @ts-ignore - meta.env is a vite feature
|
|
176
|
-
const base = import.meta.env.VITE_API_URL;
|
|
177
|
-
|
|
178
|
-
function defaultLocale(): string {
|
|
179
|
-
return (fimoConfig as { i18n?: { defaultLocale?: string } }).i18n?.defaultLocale ?? "en";
|
|
180
|
-
}
|
|
181
|
-
|
|
182
|
-
function useQueryLocale(locale?: string): string {
|
|
183
|
-
const activeLocale = useLocale().locale;
|
|
184
|
-
return locale ?? activeLocale ?? defaultLocale();
|
|
185
|
-
}
|
|
186
|
-
|
|
187
|
-
function withMutationLocale<T extends object>(payload: T, activeLocale: string): T & { locale: string } {
|
|
188
|
-
const locale = (payload as { locale?: string }).locale;
|
|
189
|
-
return { ...payload, locale: locale ?? activeLocale };
|
|
190
|
-
}
|
|
191
|
-
|
|
192
|
-
const LOCALIZED_CONTENT = <%= model.localized ? 'true' : 'false' %>;
|
|
193
|
-
const LOCALIZED_REFERENCE_FIELDS = new Set<string>(<%= JSON.stringify(model.localizedReferenceFields) %>);
|
|
194
|
-
|
|
195
|
-
function populateFields(populate: <%= model.pascal %>Populate | undefined): string[] {
|
|
196
|
-
if (populate == null) return [];
|
|
197
|
-
return typeof populate === "string" ? [populate] : [...populate];
|
|
198
|
-
}
|
|
199
|
-
|
|
200
|
-
function readLocale(
|
|
201
|
-
options: { locale?: string; populate?: <%= model.pascal %>Populate },
|
|
202
|
-
ownerUsesLocale: boolean,
|
|
203
|
-
fallbackLocale = defaultLocale()
|
|
204
|
-
): string | undefined {
|
|
205
|
-
<% if (!model.isSingleton) { -%>
|
|
206
|
-
return options.locale ?? fallbackLocale;
|
|
23
|
+
import type { <%= model.pascal %>, <%= model.pascal %>Input } from "./<%= model.pascal %>.types";
|
|
207
24
|
<% } else { -%>
|
|
208
|
-
|
|
209
|
-
return ownerUsesLocale || populatesLocalizedTarget ? options.locale ?? fallbackLocale : undefined;
|
|
25
|
+
import type { <%= model.pascal %>, <%= model.pascal %>Fields, <%= model.pascal %>Input, <%= model.pascal %>Populate, <%= model.pascal %>Query } from "./<%= model.pascal %>.types";
|
|
210
26
|
<% } -%>
|
|
211
|
-
}
|
|
212
|
-
|
|
213
|
-
function fimoHeaders(extra?: Record<string, string>): Record<string, string> {
|
|
214
|
-
const h: Record<string, string> = { ...extra };
|
|
215
|
-
// @ts-ignore
|
|
216
|
-
const branch = import.meta.env.VITE_FIMO_BRANCH;
|
|
217
|
-
if (branch) h["X-Fimo-Branch"] = branch;
|
|
218
|
-
return h;
|
|
219
|
-
}
|
|
220
|
-
|
|
221
|
-
function withReadOptions<TPopulate extends <%= model.pascal %>Populate | undefined>(
|
|
222
|
-
url: string,
|
|
223
|
-
options: { locale?: string; populate?: TPopulate; localeFallbacks?: boolean } = {},
|
|
224
|
-
ownerUsesLocale = LOCALIZED_CONTENT
|
|
225
|
-
): string {
|
|
226
|
-
const u = new URL(url, "http://fimo.local");
|
|
227
|
-
const locale = readLocale(options, ownerUsesLocale);
|
|
228
|
-
if (locale) u.searchParams.set("locale", locale);
|
|
229
|
-
if (options.populate != null) {
|
|
230
|
-
u.searchParams.set("populate", typeof options.populate === "string" ? options.populate : options.populate.join(","));
|
|
231
|
-
}
|
|
232
|
-
if (options.localeFallbacks) u.searchParams.set("localeFallbacks", "true");
|
|
233
|
-
return `${u.pathname}${u.search}`;
|
|
234
|
-
}
|
|
235
|
-
|
|
236
|
-
function splitWritePayload(payload: Partial<<%= model.pascal %>Input>): Record<string, unknown> {
|
|
237
|
-
const {
|
|
238
|
-
id,
|
|
239
|
-
entryId,
|
|
240
|
-
documentId,
|
|
241
|
-
locale,
|
|
242
|
-
createdAt,
|
|
243
|
-
updatedAt,
|
|
244
|
-
...data
|
|
245
|
-
} = payload as Record<string, unknown>;
|
|
246
|
-
delete data.__fimo;
|
|
247
|
-
|
|
248
|
-
return {
|
|
249
|
-
data,
|
|
250
|
-
...(typeof locale === "string" && locale.length > 0 ? { locale } : {}),
|
|
251
|
-
...(typeof documentId === "string" && documentId.length > 0 ? { documentId } : {}),
|
|
252
|
-
};
|
|
253
|
-
}
|
|
254
27
|
|
|
28
|
+
export type { <%= model.pascal %>, <%= model.pascal %>Fields, <%= model.pascal %>FimoMeta, <%= model.pascal %>Input, <%= model.pascal %>Populate, <%= model.pascal %>Query, <%= model.pascal %>References, <%= model.pascal %>Result, <%= model.pascal %>Sort, <%= model.pascal %>Where } from "./<%= model.pascal %>.types";
|
|
255
29
|
<% if (!model.isSingleton) { -%>
|
|
256
|
-
export
|
|
257
|
-
readonly limit: number;
|
|
258
|
-
readonly offset: number;
|
|
259
|
-
readonly total: number;
|
|
260
|
-
readonly hasMore: boolean;
|
|
261
|
-
readonly nextOffset: number | null;
|
|
262
|
-
}
|
|
263
|
-
|
|
264
|
-
export interface CollectionPage<TItem> {
|
|
265
|
-
readonly items: TItem[];
|
|
266
|
-
readonly pageInfo: CollectionPageInfo;
|
|
267
|
-
}
|
|
268
|
-
|
|
30
|
+
export type { CollectionPage, CollectionPageInfo, InfiniteListResult } from "fimo/ui";
|
|
269
31
|
export type InfiniteListQuery<
|
|
270
32
|
TFields extends <%= model.pascal %>Fields | undefined = undefined,
|
|
271
33
|
TPopulate extends <%= model.pascal %>Populate | undefined = undefined,
|
|
272
34
|
> = Omit<<%= model.pascal %>Query<TFields, TPopulate>, "offset">;
|
|
273
|
-
|
|
274
|
-
export type InfiniteListResult<TItem> = UseInfiniteQueryResult<
|
|
275
|
-
InfiniteData<CollectionPage<TItem>>,
|
|
276
|
-
Error
|
|
277
|
-
> & {
|
|
278
|
-
readonly items: TItem[];
|
|
279
|
-
readonly total: number;
|
|
280
|
-
readonly hasMore: boolean;
|
|
281
|
-
readonly loadMore: () => ReturnType<
|
|
282
|
-
UseInfiniteQueryResult<InfiniteData<CollectionPage<TItem>>, Error>["fetchNextPage"]
|
|
283
|
-
>;
|
|
284
|
-
readonly isLoadingMore: boolean;
|
|
285
|
-
};
|
|
286
|
-
|
|
287
|
-
export async function getById<TPopulate extends <%= model.pascal %>Populate | undefined = undefined>(
|
|
288
|
-
id: string,
|
|
289
|
-
options: { locale?: string; populate?: TPopulate; localeFallbacks?: boolean } = {}
|
|
290
|
-
): Promise<<%= model.pascal %>Result<undefined, TPopulate>> {
|
|
291
|
-
const res = await fetch(`${base}${withReadOptions(`/entries/<%= model.uid %>/${id}`, options)}`, { headers: fimoHeaders() });
|
|
292
|
-
if (!res.ok) throw new Error(await res.text());
|
|
293
|
-
const data = await res.json();
|
|
294
|
-
return wrapWithSource(data.data) as <%= model.pascal %>Result<undefined, TPopulate>;
|
|
295
|
-
}
|
|
296
|
-
|
|
297
|
-
export async function getBySlug<TPopulate extends <%= model.pascal %>Populate | undefined = undefined>(
|
|
298
|
-
slug: string,
|
|
299
|
-
options: { locale?: string; populate?: TPopulate; localeFallbacks?: boolean } = {}
|
|
300
|
-
): Promise<<%= model.pascal %>Result<undefined, TPopulate> | null> {
|
|
301
|
-
const res = await fetch(`${base}${withReadOptions(`/entries/<%= model.uid %>/slug/${encodeURIComponent(slug)}`, options)}`, { headers: fimoHeaders() });
|
|
302
|
-
if (res.status === 404) return null;
|
|
303
|
-
if (!res.ok) throw new Error(await res.text());
|
|
304
|
-
const data = await res.json();
|
|
305
|
-
return wrapWithSource(data.data) as <%= model.pascal %>Result<undefined, TPopulate>;
|
|
306
|
-
}
|
|
307
|
-
|
|
308
|
-
export async function getByField<TPopulate extends <%= model.pascal %>Populate | undefined = undefined>(
|
|
309
|
-
fieldName: string,
|
|
310
|
-
value: string,
|
|
311
|
-
options: { locale?: string; populate?: TPopulate; localeFallbacks?: boolean } = {}
|
|
312
|
-
): Promise<<%= model.pascal %>Result<undefined, TPopulate> | null> {
|
|
313
|
-
const res = await fetch(`${base}${withReadOptions(`/entries/<%= model.uid %>/field/${encodeURIComponent(fieldName)}/${encodeURIComponent(value)}`, options)}`, { headers: fimoHeaders() });
|
|
314
|
-
if (res.status === 404) return null;
|
|
315
|
-
if (!res.ok) throw new Error(await res.text());
|
|
316
|
-
const data = await res.json();
|
|
317
|
-
return wrapWithSource(data.data) as <%= model.pascal %>Result<undefined, TPopulate>;
|
|
318
|
-
}
|
|
319
|
-
|
|
320
|
-
function toQueryString<
|
|
321
|
-
TFields extends <%= model.pascal %>Fields | undefined = undefined,
|
|
322
|
-
TPopulate extends <%= model.pascal %>Populate | undefined = undefined,
|
|
323
|
-
>(params: <%= model.pascal %>Query<TFields, TPopulate> = {} as <%= model.pascal %>Query<TFields, TPopulate>): string {
|
|
324
|
-
const { where, sort, fields, populate, locale: requestedLocale, ...flat } = params;
|
|
325
|
-
const query = new URLSearchParams();
|
|
326
|
-
|
|
327
|
-
for (const [key, value] of Object.entries(flat)) {
|
|
328
|
-
if (value == null) continue;
|
|
329
|
-
query.set(key, String(value));
|
|
330
|
-
}
|
|
331
|
-
|
|
332
|
-
if (sort != null) {
|
|
333
|
-
query.set("sort", Array.isArray(sort) ? sort.join(",") : sort);
|
|
334
|
-
}
|
|
335
|
-
|
|
336
|
-
if (fields != null) {
|
|
337
|
-
query.set("fields", typeof fields === "string" ? fields : fields.join(","));
|
|
338
|
-
}
|
|
339
|
-
|
|
340
|
-
if (populate != null) {
|
|
341
|
-
query.set("populate", typeof populate === "string" ? populate : populate.join(","));
|
|
342
|
-
}
|
|
343
|
-
|
|
344
|
-
if (where && Object.keys(where).length > 0) {
|
|
345
|
-
query.set("where", JSON.stringify(where));
|
|
346
|
-
}
|
|
347
|
-
|
|
348
|
-
const locale = readLocale({ locale: requestedLocale, populate }, LOCALIZED_CONTENT);
|
|
349
|
-
if (locale) query.set("locale", locale);
|
|
350
|
-
|
|
351
|
-
return query.toString();
|
|
352
|
-
}
|
|
353
|
-
|
|
354
|
-
export async function list<
|
|
355
|
-
TFields extends <%= model.pascal %>Fields | undefined = undefined,
|
|
356
|
-
TPopulate extends <%= model.pascal %>Populate | undefined = undefined,
|
|
357
|
-
>(params: <%= model.pascal %>Query<TFields, TPopulate> = {} as <%= model.pascal %>Query<TFields, TPopulate>): Promise<CollectionPage<<%= model.pascal %>Result<TFields, TPopulate>>> {
|
|
358
|
-
const qs = toQueryString(params);
|
|
359
|
-
const res = await fetch(`${base}/entries/<%= model.uid %>${qs ? "?" + qs : ""}`, { headers: fimoHeaders() });
|
|
360
|
-
if (!res.ok) throw new Error(await res.text());
|
|
361
|
-
const data = parseCollectionResponse(await res.json());
|
|
362
|
-
const items = data.data.map(wrapWithSource) as <%= model.pascal %>Result<TFields, TPopulate>[];
|
|
363
|
-
const pageInfo: CollectionPageInfo = {
|
|
364
|
-
limit: data.meta.limit,
|
|
365
|
-
offset: data.meta.offset,
|
|
366
|
-
total: data.meta.total,
|
|
367
|
-
hasMore: data.meta.hasMore,
|
|
368
|
-
nextOffset: data.meta.hasMore ? data.meta.offset + data.meta.limit : null,
|
|
369
|
-
};
|
|
370
|
-
return { items, pageInfo };
|
|
371
|
-
}
|
|
372
|
-
|
|
373
|
-
export async function get<
|
|
374
|
-
TFields extends <%= model.pascal %>Fields | undefined = undefined,
|
|
375
|
-
TPopulate extends <%= model.pascal %>Populate | undefined = undefined,
|
|
376
|
-
>(params: <%= model.pascal %>Query<TFields, TPopulate> = {} as <%= model.pascal %>Query<TFields, TPopulate>): Promise<<%= model.pascal %>Result<TFields, TPopulate>[]> {
|
|
377
|
-
return (await list(params)).items;
|
|
378
|
-
}
|
|
379
|
-
|
|
380
|
-
export async function create(payload: <%= model.pascal %>Input): Promise<<%= model.pascal %>> {
|
|
381
|
-
const res = await fetch(`${base}/entries/<%= model.uid %>`, {
|
|
382
|
-
method: "POST",
|
|
383
|
-
headers: fimoHeaders({ "Content-Type": "application/json" }),
|
|
384
|
-
body: JSON.stringify(splitWritePayload(payload))
|
|
385
|
-
});
|
|
386
|
-
if (!res.ok) throw new Error(await res.text());
|
|
387
|
-
const data = await res.json();
|
|
388
|
-
return wrapWithSource(data.data);
|
|
389
|
-
}
|
|
390
|
-
|
|
391
|
-
export async function update(id: string, payload: Partial<<%= model.pascal %>Input>): Promise<<%= model.pascal %>> {
|
|
392
|
-
const write = splitWritePayload(payload) as { data: Record<string, unknown>; locale?: string };
|
|
393
|
-
const url = withReadOptions(`/entries/<%= model.uid %>/${id}`, { locale: write.locale });
|
|
394
|
-
const res = await fetch(`${base}${url}`, {
|
|
395
|
-
method: "PUT",
|
|
396
|
-
headers: fimoHeaders({ "Content-Type": "application/json" }),
|
|
397
|
-
body: JSON.stringify({ data: write.data })
|
|
398
|
-
});
|
|
399
|
-
if (!res.ok) throw new Error(await res.text());
|
|
400
|
-
const data = await res.json();
|
|
401
|
-
return wrapWithSource(data.data);
|
|
402
|
-
}
|
|
403
|
-
|
|
404
|
-
export async function remove(id: string, options: { locale?: string } = {}): Promise<void> {
|
|
405
|
-
const url = withReadOptions(`/entries/<%= model.uid %>/${id}`, options);
|
|
406
|
-
const res = await fetch(`${base}${url}`, { method: "DELETE", headers: fimoHeaders() });
|
|
407
|
-
if (!res.ok) throw new Error(await res.text());
|
|
408
|
-
}
|
|
409
|
-
<% } else { -%>
|
|
410
|
-
export async function get<TPopulate extends <%= model.pascal %>Populate | undefined = undefined>(
|
|
411
|
-
options: { locale?: string; populate?: TPopulate; localeFallbacks?: boolean } = {}
|
|
412
|
-
): Promise<<%= model.pascal %>Result<undefined, TPopulate>> {
|
|
413
|
-
const res = await fetch(`${base}${withReadOptions(`/documents/<%= model.uid %>`, options)}`, { headers: fimoHeaders() });
|
|
414
|
-
if (!res.ok) throw new Error(await res.text());
|
|
415
|
-
const data = await res.json();
|
|
416
|
-
return wrapWithSource(data.data) as <%= model.pascal %>Result<undefined, TPopulate>;
|
|
417
|
-
}
|
|
418
|
-
|
|
419
|
-
export async function update(payload: Partial<<%= model.pascal %>Input>): Promise<<%= model.pascal %>> {
|
|
420
|
-
const write = splitWritePayload(payload) as { data: Record<string, unknown>; locale?: string };
|
|
421
|
-
const url = withReadOptions(`/documents/<%= model.uid %>`, { locale: write.locale });
|
|
422
|
-
const res = await fetch(`${base}${url}`, {
|
|
423
|
-
method: "PUT",
|
|
424
|
-
headers: fimoHeaders({ "Content-Type": "application/json" }),
|
|
425
|
-
body: JSON.stringify({ data: write.data })
|
|
426
|
-
});
|
|
427
|
-
if (!res.ok) throw new Error(await res.text());
|
|
428
|
-
const data = await res.json();
|
|
429
|
-
return wrapWithSource(data.data);
|
|
430
|
-
}
|
|
431
35
|
<% } -%>
|
|
432
36
|
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
root : ["<%= model.pascal %>"] as const,
|
|
440
|
-
byId : <TPopulate extends <%= model.pascal %>Populate | undefined = undefined>(id: string, options: { locale?: string; populate?: TPopulate; localeFallbacks?: boolean } = {}) => ["<%= model.pascal %>", "id", id, options.locale, options.populate] as const,
|
|
441
|
-
bySlug : <TPopulate extends <%= model.pascal %>Populate | undefined = undefined>(slug: string, options: { locale?: string; populate?: TPopulate; localeFallbacks?: boolean } = {}) => ["<%= model.pascal %>", "slug", slug, options.locale, options.populate] as const,
|
|
442
|
-
byField : <TPopulate extends <%= model.pascal %>Populate | undefined = undefined>(field: string, value: string, options: { locale?: string; populate?: TPopulate; localeFallbacks?: boolean } = {}) => ["<%= model.pascal %>", field, value, options.locale, options.populate] as const,
|
|
443
|
-
list : (params: <%= model.pascal %>Query) => ["<%= model.pascal %>", params] as const,
|
|
444
|
-
infiniteList: <
|
|
445
|
-
TFields extends <%= model.pascal %>Fields | undefined = undefined,
|
|
446
|
-
TPopulate extends <%= model.pascal %>Populate | undefined = undefined,
|
|
447
|
-
>(params: InfiniteListQuery<TFields, TPopulate>) => ["<%= model.pascal %>", "infinite", params] as const
|
|
448
|
-
};
|
|
449
|
-
|
|
450
|
-
export function useGetById<TPopulate extends <%= model.pascal %>Populate | undefined = undefined>(id: string, options: { locale?: string; populate?: TPopulate; localeFallbacks?: boolean } = {}): UseQueryResult<<%= model.pascal %>Result<undefined, TPopulate>, Error> {
|
|
451
|
-
const locale = useQueryLocale(options.locale);
|
|
452
|
-
const localizedOptions = { ...options, locale };
|
|
453
|
-
return useQuery<<%= model.pascal %>Result<undefined, TPopulate>, Error>({ queryKey: qk.byId(id, localizedOptions), queryFn: () => getById(id, localizedOptions) });
|
|
454
|
-
}
|
|
455
|
-
|
|
456
|
-
export function useGetBySlug<TPopulate extends <%= model.pascal %>Populate | undefined = undefined>(slug: string, options: { locale?: string; populate?: TPopulate; localeFallbacks?: boolean } = {}) {
|
|
457
|
-
const locale = readLocale(options, LOCALIZED_CONTENT, useQueryLocale(options.locale));
|
|
458
|
-
const localizedOptions = { ...options, locale };
|
|
459
|
-
return useQuery<<%= model.pascal %>Result<undefined, TPopulate> | null, Error>({ queryKey: qk.bySlug(slug, localizedOptions), queryFn: () => getBySlug(slug, localizedOptions), enabled: !!slug });
|
|
460
|
-
}
|
|
461
|
-
|
|
462
|
-
export function useGetByField<TPopulate extends <%= model.pascal %>Populate | undefined = undefined>(fieldName: string, value: string, options: { locale?: string; populate?: TPopulate; localeFallbacks?: boolean } = {}) {
|
|
463
|
-
const locale = readLocale(options, LOCALIZED_CONTENT, useQueryLocale(options.locale));
|
|
464
|
-
const localizedOptions = { ...options, locale };
|
|
465
|
-
return useQuery<<%= model.pascal %>Result<undefined, TPopulate> | null, Error>({ queryKey: qk.byField(fieldName, value, localizedOptions), queryFn: () => getByField(fieldName, value, localizedOptions), enabled: !!value });
|
|
466
|
-
}
|
|
467
|
-
|
|
468
|
-
export function useGet<
|
|
469
|
-
TFields extends <%= model.pascal %>Fields | undefined = undefined,
|
|
470
|
-
TPopulate extends <%= model.pascal %>Populate | undefined = undefined,
|
|
471
|
-
>(params: <%= model.pascal %>Query<TFields, TPopulate> = {} as <%= model.pascal %>Query<TFields, TPopulate>) {
|
|
472
|
-
const locale = readLocale(params, LOCALIZED_CONTENT, useQueryLocale(params.locale));
|
|
473
|
-
const localizedParams = { ...params, locale };
|
|
474
|
-
return useQuery<CollectionPage<<%= model.pascal %>Result<TFields, TPopulate>>, Error, <%= model.pascal %>Result<TFields, TPopulate>[]>({
|
|
475
|
-
queryKey: qk.list(localizedParams),
|
|
476
|
-
queryFn: () => list(localizedParams),
|
|
477
|
-
select: (page) => page.items,
|
|
478
|
-
});
|
|
479
|
-
}
|
|
480
|
-
|
|
481
|
-
export function useList<
|
|
482
|
-
TFields extends <%= model.pascal %>Fields | undefined = undefined,
|
|
483
|
-
TPopulate extends <%= model.pascal %>Populate | undefined = undefined,
|
|
484
|
-
>(params: <%= model.pascal %>Query<TFields, TPopulate> = {} as <%= model.pascal %>Query<TFields, TPopulate>): UseQueryResult<CollectionPage<<%= model.pascal %>Result<TFields, TPopulate>>, Error> {
|
|
485
|
-
const locale = readLocale(params, LOCALIZED_CONTENT, useQueryLocale(params.locale));
|
|
486
|
-
const localizedParams = { ...params, locale };
|
|
487
|
-
return useQuery<CollectionPage<<%= model.pascal %>Result<TFields, TPopulate>>, Error>({
|
|
488
|
-
queryKey: qk.list(localizedParams),
|
|
489
|
-
queryFn: () => list(localizedParams),
|
|
490
|
-
});
|
|
491
|
-
}
|
|
492
|
-
|
|
493
|
-
export function useInfiniteList<
|
|
494
|
-
TFields extends <%= model.pascal %>Fields | undefined = undefined,
|
|
495
|
-
TPopulate extends <%= model.pascal %>Populate | undefined = undefined,
|
|
496
|
-
>(params: InfiniteListQuery<TFields, TPopulate> = {} as InfiniteListQuery<TFields, TPopulate>): InfiniteListResult<<%= model.pascal %>Result<TFields, TPopulate>> {
|
|
497
|
-
const locale = readLocale(params, LOCALIZED_CONTENT, useQueryLocale(params.locale));
|
|
498
|
-
const localizedParams = { ...params, locale };
|
|
499
|
-
const query = useInfiniteQuery({
|
|
500
|
-
queryKey: qk.infiniteList(localizedParams),
|
|
501
|
-
queryFn: ({ pageParam }) => list({ ...localizedParams, offset: pageParam }),
|
|
502
|
-
initialPageParam: 0,
|
|
503
|
-
getNextPageParam: (lastPage) => lastPage.pageInfo.nextOffset ?? undefined,
|
|
504
|
-
});
|
|
505
|
-
|
|
506
|
-
return {
|
|
507
|
-
...query,
|
|
508
|
-
items: query.data?.pages.flatMap((page) => page.items) ?? [],
|
|
509
|
-
total: query.data?.pages[0]?.pageInfo.total ?? 0,
|
|
510
|
-
hasMore: query.hasNextPage,
|
|
511
|
-
loadMore: () => query.fetchNextPage(),
|
|
512
|
-
isLoadingMore: query.isFetchingNextPage,
|
|
513
|
-
};
|
|
514
|
-
}
|
|
515
|
-
|
|
516
|
-
export function useSuspenseGetById<TPopulate extends <%= model.pascal %>Populate | undefined = undefined>(id: string, options: { locale?: string; populate?: TPopulate; localeFallbacks?: boolean } = {}): UseSuspenseQueryResult<<%= model.pascal %>Result<undefined, TPopulate>, Error> {
|
|
517
|
-
const locale = useQueryLocale(options.locale);
|
|
518
|
-
const localizedOptions = { ...options, locale };
|
|
519
|
-
return useSuspenseQuery<<%= model.pascal %>Result<undefined, TPopulate>, Error>({ queryKey: qk.byId(id, localizedOptions), queryFn: () => getById(id, localizedOptions) });
|
|
520
|
-
}
|
|
521
|
-
|
|
522
|
-
export function useSuspenseGetBySlug<TPopulate extends <%= model.pascal %>Populate | undefined = undefined>(slug: string, options: { locale?: string; populate?: TPopulate; localeFallbacks?: boolean } = {}) {
|
|
523
|
-
const locale = readLocale(options, LOCALIZED_CONTENT, useQueryLocale(options.locale));
|
|
524
|
-
const localizedOptions = { ...options, locale };
|
|
525
|
-
return useSuspenseQuery<<%= model.pascal %>Result<undefined, TPopulate> | null, Error>({ queryKey: qk.bySlug(slug, localizedOptions), queryFn: () => getBySlug(slug, localizedOptions) });
|
|
526
|
-
}
|
|
527
|
-
|
|
528
|
-
export function useSuspenseGetByField<TPopulate extends <%= model.pascal %>Populate | undefined = undefined>(fieldName: string, value: string, options: { locale?: string; populate?: TPopulate; localeFallbacks?: boolean } = {}) {
|
|
529
|
-
const locale = readLocale(options, LOCALIZED_CONTENT, useQueryLocale(options.locale));
|
|
530
|
-
const localizedOptions = { ...options, locale };
|
|
531
|
-
return useSuspenseQuery<<%= model.pascal %>Result<undefined, TPopulate> | null, Error>({ queryKey: qk.byField(fieldName, value, localizedOptions), queryFn: () => getByField(fieldName, value, localizedOptions) });
|
|
532
|
-
}
|
|
533
|
-
|
|
534
|
-
export function useSuspenseGet<
|
|
535
|
-
TFields extends <%= model.pascal %>Fields | undefined = undefined,
|
|
536
|
-
TPopulate extends <%= model.pascal %>Populate | undefined = undefined,
|
|
537
|
-
>(params: <%= model.pascal %>Query<TFields, TPopulate> = {} as <%= model.pascal %>Query<TFields, TPopulate>) {
|
|
538
|
-
const locale = readLocale(params, LOCALIZED_CONTENT, useQueryLocale(params.locale));
|
|
539
|
-
const localizedParams = { ...params, locale };
|
|
540
|
-
return useSuspenseQuery<CollectionPage<<%= model.pascal %>Result<TFields, TPopulate>>, Error, <%= model.pascal %>Result<TFields, TPopulate>[]>({
|
|
541
|
-
queryKey: qk.list(localizedParams),
|
|
542
|
-
queryFn: () => list(localizedParams),
|
|
543
|
-
select: (page) => page.items,
|
|
544
|
-
});
|
|
545
|
-
}
|
|
546
|
-
|
|
547
|
-
export function useCreate() {
|
|
548
|
-
const qc = useQueryClient();
|
|
549
|
-
const activeLocale = useQueryLocale();
|
|
550
|
-
return useMutation<<%= model.pascal %>, Error, <%= model.pascal %>Input>({
|
|
551
|
-
mutationFn : (payload) => create(withMutationLocale(payload, activeLocale)),
|
|
552
|
-
onSuccess : () => qc.invalidateQueries({ queryKey: qk.root })
|
|
553
|
-
});
|
|
554
|
-
}
|
|
555
|
-
|
|
556
|
-
export function useUpdate() {
|
|
557
|
-
const qc = useQueryClient();
|
|
558
|
-
const activeLocale = useQueryLocale();
|
|
559
|
-
return useMutation<<%= model.pascal %>, Error, { id: string; locale?: string } & Partial<<%= model.pascal %>Input>>({
|
|
560
|
-
mutationFn : ({ id, ...patch }) => update(id, withMutationLocale(patch, activeLocale)),
|
|
561
|
-
onSuccess : (_data, vars) => {
|
|
562
|
-
qc.invalidateQueries({ queryKey: qk.root });
|
|
563
|
-
qc.invalidateQueries({ queryKey: qk.byId(vars.id, { locale: vars.locale ?? activeLocale }) });
|
|
564
|
-
}
|
|
565
|
-
});
|
|
566
|
-
}
|
|
567
|
-
|
|
568
|
-
export function useRemove() {
|
|
569
|
-
const qc = useQueryClient();
|
|
570
|
-
const activeLocale = useQueryLocale();
|
|
571
|
-
return useMutation<void, Error, string | { id: string; locale?: string }>({
|
|
572
|
-
mutationFn : (value) => {
|
|
573
|
-
const { id, locale } = typeof value === "string" ? { id: value, locale: activeLocale } : { ...value, locale: value.locale ?? activeLocale };
|
|
574
|
-
return remove(id, { locale });
|
|
575
|
-
},
|
|
576
|
-
onSuccess : (_data, value) => {
|
|
577
|
-
const { id, locale } = typeof value === "string" ? { id: value, locale: activeLocale } : { ...value, locale: value.locale ?? activeLocale };
|
|
578
|
-
qc.invalidateQueries({ queryKey: qk.root });
|
|
579
|
-
qc.removeQueries({ queryKey: qk.byId(id, { locale }), exact: false });
|
|
580
|
-
}
|
|
581
|
-
});
|
|
582
|
-
}
|
|
37
|
+
<% if (model.isSingleton) { -%>
|
|
38
|
+
const client: SingletonContentClient<<%= model.pascal %>, <%= model.pascal %>Input> = createSingletonClient<
|
|
39
|
+
<%= model.pascal %>,
|
|
40
|
+
<%= model.pascal %>Input
|
|
41
|
+
>({
|
|
42
|
+
schema,
|
|
583
43
|
<% } else { -%>
|
|
584
|
-
const
|
|
585
|
-
|
|
586
|
-
|
|
587
|
-
|
|
588
|
-
|
|
589
|
-
export function useGet<TPopulate extends <%= model.pascal %>Populate | undefined = undefined>(options: { locale?: string; populate?: TPopulate; localeFallbacks?: boolean } = {}) {
|
|
590
|
-
const locale = readLocale(options, LOCALIZED_CONTENT, useQueryLocale(options.locale));
|
|
591
|
-
const localizedOptions = { ...options, locale };
|
|
592
|
-
return useQuery<<%= model.pascal %>Result<undefined, TPopulate>, Error>({ queryKey: qk.document(localizedOptions), queryFn: () => get(localizedOptions) });
|
|
593
|
-
}
|
|
594
|
-
|
|
595
|
-
export function useSuspenseGet<TPopulate extends <%= model.pascal %>Populate | undefined = undefined>(options: { locale?: string; populate?: TPopulate; localeFallbacks?: boolean } = {}) {
|
|
596
|
-
const locale = readLocale(options, LOCALIZED_CONTENT, useQueryLocale(options.locale));
|
|
597
|
-
const localizedOptions = { ...options, locale };
|
|
598
|
-
return useSuspenseQuery<<%= model.pascal %>Result<undefined, TPopulate>, Error>({ queryKey: qk.document(localizedOptions), queryFn: () => get(localizedOptions) });
|
|
599
|
-
}
|
|
600
|
-
|
|
601
|
-
export function useUpdate() {
|
|
602
|
-
const qc = useQueryClient();
|
|
603
|
-
const activeLocale = useQueryLocale();
|
|
604
|
-
return useMutation<<%= model.pascal %>, Error, Partial<<%= model.pascal %>Input>>({
|
|
605
|
-
mutationFn: (payload) => update(withMutationLocale(payload, activeLocale)),
|
|
606
|
-
onSuccess: () => qc.invalidateQueries({ queryKey: qk.root })
|
|
607
|
-
});
|
|
608
|
-
}
|
|
44
|
+
const client: CollectionContentClient<<%= model.pascal %>, <%= model.pascal %>Input> = createContentClient<
|
|
45
|
+
<%= model.pascal %>,
|
|
46
|
+
<%= model.pascal %>Input
|
|
47
|
+
>({
|
|
48
|
+
schema,
|
|
609
49
|
<% } -%>
|
|
50
|
+
<% if (model.referenceImports && model.referenceImports.length > 0) { -%>
|
|
51
|
+
references: { <%= model.referenceImports.map((referenceImport) => referenceImport.pascal).join(", ") %> },
|
|
52
|
+
<% } -%>
|
|
53
|
+
});
|
|
610
54
|
|
|
611
|
-
|
|
612
|
-
|
|
613
|
-
|
|
614
|
-
|
|
615
|
-
|
|
616
|
-
|
|
617
|
-
/* async helpers */
|
|
55
|
+
<% if (model.isSingleton) { -%>
|
|
56
|
+
export const { get, update, useGet, useSuspenseGet, useUpdate, wrapWithSource, meta } = client;
|
|
57
|
+
<% } else { -%>
|
|
58
|
+
export const {
|
|
59
|
+
wrapWithSource,
|
|
60
|
+
meta,
|
|
618
61
|
getById,
|
|
619
62
|
getBySlug,
|
|
620
63
|
getByField,
|
|
@@ -623,8 +66,6 @@ export default {
|
|
|
623
66
|
create,
|
|
624
67
|
update,
|
|
625
68
|
remove,
|
|
626
|
-
|
|
627
|
-
/* hooks */
|
|
628
69
|
useGetById,
|
|
629
70
|
useGetBySlug,
|
|
630
71
|
useGetByField,
|
|
@@ -637,12 +78,8 @@ export default {
|
|
|
637
78
|
useSuspenseGetBySlug,
|
|
638
79
|
useCreate,
|
|
639
80
|
useUpdate,
|
|
640
|
-
useRemove
|
|
641
|
-
|
|
642
|
-
get,
|
|
643
|
-
update,
|
|
644
|
-
useGet,
|
|
645
|
-
useSuspenseGet,
|
|
646
|
-
useUpdate
|
|
81
|
+
useRemove,
|
|
82
|
+
} = client;
|
|
647
83
|
<% } -%>
|
|
648
|
-
|
|
84
|
+
|
|
85
|
+
export default client;
|