fimo 0.6.0 → 0.7.0-experimental.2
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/README.md +1 -1
- package/assets/content-templates/hooks-template.eta +73 -27
- package/assets/content-templates/types-template.eta +4 -1
- package/assets/skills/fimo/references/content.md +46 -42
- package/assets/skills/fimo-cli/references/content.md +14 -14
- package/assets/skills/fimo-cli/references/domains.md +1 -1
- package/assets/skills/fimo-studio/references/custom-domains.md +1 -1
- package/dist/cli/bundle.json +3 -2
- package/dist/cli/index.js +1203 -110450
- package/dist/runtime/content/client/createContentClient.d.ts +5 -0
- package/dist/runtime/content/client/createContentClient.js +300 -0
- package/dist/runtime/content/client/createSingletonClient.d.ts +5 -0
- package/dist/runtime/content/client/createSingletonClient.js +108 -0
- package/dist/runtime/content/client/internal.d.ts +44 -0
- package/dist/runtime/content/client/internal.js +236 -0
- package/dist/runtime/content/client/types.d.ts +136 -0
- package/dist/runtime/content/client/useQueryLocale.d.ts +2 -0
- package/dist/runtime/content/client/useQueryLocale.js +6 -0
- package/dist/runtime/content/utils/retrieve-data-sources.d.ts +1 -0
- package/dist/runtime/content/utils/retrieve-data-sources.js +27 -4
- package/dist/runtime/iframe/runtime/overlay-hmr.js +3 -0
- package/dist/runtime/index.d.ts +3 -0
- package/dist/runtime/index.js +2 -0
- package/dist/runtime/primitives/lib/primitives.d.ts +2 -2
- package/dist/runtime/primitives/lib/query.d.ts +5 -3
- package/package.json +31 -13
- package/release.json +2 -2
- package/templates/react-router/package.json +1 -1
- package/templates/react-router/src/pages/Index.tsx +1 -1
- package/dist/build/vite/index.d.ts.map +0 -1
- package/dist/build/vite/plugins/content-sync.d.ts.map +0 -1
- package/dist/build/vite/plugins/content-sync.test.d.ts +0 -2
- package/dist/build/vite/plugins/content-sync.test.d.ts.map +0 -1
- package/dist/build/vite/plugins/content-sync.test.js +0 -64
- package/dist/build/vite/plugins/data-id.d.ts.map +0 -1
- package/dist/build/vite/plugins/fimo-config.d.ts.map +0 -1
- package/dist/build/vite/plugins/fimo-config.test.d.ts +0 -2
- package/dist/build/vite/plugins/fimo-config.test.d.ts.map +0 -1
- package/dist/build/vite/plugins/fimo-config.test.js +0 -46
- package/dist/build/vite/plugins/overlay.d.ts.map +0 -1
- package/dist/build/vite/plugins/robots.d.ts.map +0 -1
- package/dist/build/vite/plugins/seo.d.ts.map +0 -1
- package/dist/build/vite/plugins/server-config.d.ts.map +0 -1
- package/dist/build/vite/plugins/sitemap.d.ts.map +0 -1
- package/dist/build/vite/plugins/translations.d.ts.map +0 -1
- package/dist/build/vite/plugins/translations.test.d.ts +0 -2
- package/dist/build/vite/plugins/translations.test.d.ts.map +0 -1
- package/dist/build/vite/plugins/translations.test.js +0 -266
- package/dist/runtime/app/FimoProviders.d.ts.map +0 -1
- package/dist/runtime/app/FimoScripts.d.ts.map +0 -1
- package/dist/runtime/app/index.d.ts.map +0 -1
- package/dist/runtime/app/prefetch.d.ts.map +0 -1
- package/dist/runtime/content/DataSourcesProvider.d.ts.map +0 -1
- package/dist/runtime/content/RefreshContentProvider.d.ts.map +0 -1
- package/dist/runtime/content/RefreshContentProvider.test.d.ts +0 -2
- package/dist/runtime/content/RefreshContentProvider.test.d.ts.map +0 -1
- package/dist/runtime/content/RefreshContentProvider.test.js +0 -33
- package/dist/runtime/content/parseCollectionResponse.d.ts.map +0 -1
- package/dist/runtime/content/parseCollectionResponse.test.d.ts +0 -2
- package/dist/runtime/content/parseCollectionResponse.test.d.ts.map +0 -1
- package/dist/runtime/content/parseCollectionResponse.test.js +0 -20
- package/dist/runtime/content/utils/retrieve-data-sources.d.ts.map +0 -1
- package/dist/runtime/core/AppErrorBoundary.d.ts.map +0 -1
- package/dist/runtime/core/ViteLifecycles.d.ts.map +0 -1
- package/dist/runtime/i18n/FimoLocaleContext.d.ts.map +0 -1
- package/dist/runtime/i18n/locale.d.ts.map +0 -1
- package/dist/runtime/i18n/locale.test.d.ts +0 -2
- package/dist/runtime/i18n/locale.test.d.ts.map +0 -1
- package/dist/runtime/i18n/locale.test.js +0 -58
- package/dist/runtime/iframe/ConsoleErrorProvider.d.ts.map +0 -1
- package/dist/runtime/iframe/ScreenshotProvider.d.ts.map +0 -1
- package/dist/runtime/iframe/runtime/overlay-hmr.d.ts.map +0 -1
- package/dist/runtime/iframe/runtime/overlay-runtime.d.ts.map +0 -1
- package/dist/runtime/index.d.ts.map +0 -1
- package/dist/runtime/paths/get-fimo-paths.d.ts.map +0 -1
- package/dist/runtime/paths/get-fimo-paths.test.d.ts +0 -2
- package/dist/runtime/paths/get-fimo-paths.test.d.ts.map +0 -1
- package/dist/runtime/paths/get-fimo-paths.test.js +0 -69
- package/dist/runtime/paths/index.d.ts.map +0 -1
- package/dist/runtime/paths/types.d.ts.map +0 -1
- package/dist/runtime/primitives/components/Boolean.d.ts.map +0 -1
- package/dist/runtime/primitives/components/Date.d.ts.map +0 -1
- package/dist/runtime/primitives/components/Image.d.ts.map +0 -1
- package/dist/runtime/primitives/components/RichText.d.ts.map +0 -1
- package/dist/runtime/primitives/components/StaticImage.d.ts.map +0 -1
- package/dist/runtime/primitives/components/Text.d.ts.map +0 -1
- package/dist/runtime/primitives/components/Video.d.ts.map +0 -1
- package/dist/runtime/primitives/components/index.d.ts.map +0 -1
- package/dist/runtime/primitives/index.d.ts.map +0 -1
- package/dist/runtime/primitives/lib/image-optimization.d.ts.map +0 -1
- package/dist/runtime/primitives/lib/index.d.ts.map +0 -1
- package/dist/runtime/primitives/lib/primitives.d.ts.map +0 -1
- package/dist/runtime/primitives/lib/query.d.ts.map +0 -1
- package/dist/runtime/primitives/lib/template.d.ts.map +0 -1
- package/dist/runtime/primitives/lib/useFimoLiveValue.d.ts.map +0 -1
- package/dist/runtime/primitives/public-surface.test.d.ts +0 -2
- package/dist/runtime/primitives/public-surface.test.d.ts.map +0 -1
- package/dist/runtime/primitives/public-surface.test.js +0 -45
- package/dist/runtime/primitives/translations.d.ts.map +0 -1
- package/dist/runtime/react-router/index.d.ts.map +0 -1
- package/dist/runtime/react-router/navigation-utils.d.ts.map +0 -1
- package/dist/runtime/react-router/navigation.d.ts.map +0 -1
- package/dist/runtime/react-router/navigation.test.d.ts +0 -2
- package/dist/runtime/react-router/navigation.test.d.ts.map +0 -1
- package/dist/runtime/react-router/navigation.test.js +0 -35
- package/dist/runtime/react-router/routes.d.ts.map +0 -1
- package/dist/runtime/react-router/routes.test.d.ts +0 -2
- package/dist/runtime/react-router/routes.test.d.ts.map +0 -1
- package/dist/runtime/react-router/routes.test.js +0 -67
- package/dist/runtime/routing/AppResetScrollOnNavigate.d.ts.map +0 -1
- package/dist/runtime/routing/AppRoutesProvider.d.ts.map +0 -1
- package/dist/runtime/seo/buildSeoForPath.d.ts.map +0 -1
- package/dist/runtime/seo/htmlProps.d.ts.map +0 -1
- package/dist/runtime/seo/index.d.ts.map +0 -1
- package/dist/runtime/seo/resolver.d.ts.map +0 -1
- package/dist/runtime/shared/fimo-config.server.d.ts.map +0 -1
- package/dist/runtime/shared/fimo-config.types.d.ts.map +0 -1
- package/dist/runtime/t/OptionalTracking.d.ts.map +0 -1
- package/dist/runtime/t/OptionalTracking.test.d.ts +0 -2
- package/dist/runtime/t/OptionalTracking.test.d.ts.map +0 -1
- package/dist/runtime/t/OptionalTracking.test.js +0 -11
- package/dist/runtime/t/Pageview.d.ts.map +0 -1
- package/dist/runtime/templates.d.ts.map +0 -1
- package/dist/scripts/generated-types.test.js +0 -212
- package/dist/scripts/lint-translation-keys.test.d.ts +0 -1
- package/dist/scripts/lint-translation-keys.test.js +0 -16
- package/dist/scripts/sync-schemas.test.d.ts +0 -1
- package/dist/scripts/sync-schemas.test.js +0 -124
- package/dist/scripts/validate-route-metadata.test.d.ts +0 -1
- package/dist/scripts/validate-route-metadata.test.js +0 -116
- package/dist/scripts/validate-schemas.test.d.ts +0 -1
- package/dist/scripts/validate-schemas.test.js +0 -263
- package/scripts/add-ext.mjs +0 -47
- package/scripts/bump-version.mjs +0 -58
- package/scripts/bundle-cli.mjs +0 -123
- package/scripts/install-cli.mjs +0 -36
- package/scripts/lib/channels.mjs +0 -70
- package/scripts/lib/dev-release-state.mjs +0 -44
- package/scripts/lib/dev-release-state.test.ts +0 -36
- package/scripts/lib/postinstall-onboarding.test.ts +0 -82
- package/scripts/lib/release-core.mjs +0 -225
- package/scripts/lib/release-core.test.ts +0 -147
- package/scripts/migration-qa-gate.test.ts +0 -155
- package/scripts/publish-npm.mjs +0 -180
- /package/dist/{scripts/generated-types.test.d.ts → runtime/content/client/types.js} +0 -0
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
import type { CollectionContentClient, ContentClientConfig } from './types.js';
|
|
2
|
+
export declare function createContentClient<T extends {
|
|
3
|
+
id: string;
|
|
4
|
+
}, TInput extends object>(config: ContentClientConfig): CollectionContentClient<T, TInput>;
|
|
5
|
+
//# sourceMappingURL=createContentClient.d.ts.map
|
|
@@ -0,0 +1,300 @@
|
|
|
1
|
+
import { useInfiniteQuery, useMutation, useQuery, useQueryClient, useSuspenseQuery } from '@tanstack/react-query';
|
|
2
|
+
import { parseCollectionResponse } from '../parseCollectionResponse.js';
|
|
3
|
+
import { apiBase, buildReadPath, fimoHeaders, interpretSchema, readCollectionLocale, resolveClientHandle, splitWritePayload, validateReferences, withMutationLocale, wrapEntry, } from './internal.js';
|
|
4
|
+
import { useQueryLocale } from './useQueryLocale.js';
|
|
5
|
+
export function createContentClient(config) {
|
|
6
|
+
const references = config.references ?? {};
|
|
7
|
+
const info = interpretSchema(config.schema);
|
|
8
|
+
if (info.isSingleton) {
|
|
9
|
+
throw new Error(`Schema "${info.uid}" is a singleton — use createSingletonClient instead.`);
|
|
10
|
+
}
|
|
11
|
+
validateReferences(info, references);
|
|
12
|
+
const meta = { uid: info.uid, pascal: info.pascal, localized: info.localized };
|
|
13
|
+
const self = { wrapWithSource, meta };
|
|
14
|
+
const resolve = (uid) => uid === info.uid ? self : resolveClientHandle(references[uid]);
|
|
15
|
+
function wrapWithSource(entry) {
|
|
16
|
+
return wrapEntry(info, entry, { alwaysIncludeLocale: true, resolve });
|
|
17
|
+
}
|
|
18
|
+
function readPath(url, options) {
|
|
19
|
+
return buildReadPath(url, readCollectionLocale(options), options.populate, options.localeFallbacks);
|
|
20
|
+
}
|
|
21
|
+
async function getById(id, options = {}) {
|
|
22
|
+
const res = await fetch(`${apiBase()}${readPath(`/entries/${info.uid}/${id}`, options)}`, {
|
|
23
|
+
headers: fimoHeaders(),
|
|
24
|
+
});
|
|
25
|
+
if (!res.ok) {
|
|
26
|
+
throw new Error(await res.text());
|
|
27
|
+
}
|
|
28
|
+
const data = await res.json();
|
|
29
|
+
return wrapWithSource(data.data);
|
|
30
|
+
}
|
|
31
|
+
async function getBySlug(slug, options = {}) {
|
|
32
|
+
const res = await fetch(`${apiBase()}${readPath(`/entries/${info.uid}/slug/${encodeURIComponent(slug)}`, options)}`, { headers: fimoHeaders() });
|
|
33
|
+
if (res.status === 404) {
|
|
34
|
+
return null;
|
|
35
|
+
}
|
|
36
|
+
if (!res.ok) {
|
|
37
|
+
throw new Error(await res.text());
|
|
38
|
+
}
|
|
39
|
+
const data = await res.json();
|
|
40
|
+
return wrapWithSource(data.data);
|
|
41
|
+
}
|
|
42
|
+
async function getByField(fieldName, value, options = {}) {
|
|
43
|
+
const res = await fetch(`${apiBase()}${readPath(`/entries/${info.uid}/field/${encodeURIComponent(fieldName)}/${encodeURIComponent(value)}`, options)}`, { headers: fimoHeaders() });
|
|
44
|
+
if (res.status === 404) {
|
|
45
|
+
return null;
|
|
46
|
+
}
|
|
47
|
+
if (!res.ok) {
|
|
48
|
+
throw new Error(await res.text());
|
|
49
|
+
}
|
|
50
|
+
const data = await res.json();
|
|
51
|
+
return wrapWithSource(data.data);
|
|
52
|
+
}
|
|
53
|
+
function toQueryString(params) {
|
|
54
|
+
const { where, sort, fields, populate, locale: requestedLocale, ...flat } = params;
|
|
55
|
+
const query = new URLSearchParams();
|
|
56
|
+
for (const [key, value] of Object.entries(flat)) {
|
|
57
|
+
if (value == null) {
|
|
58
|
+
continue;
|
|
59
|
+
}
|
|
60
|
+
query.set(key, String(value));
|
|
61
|
+
}
|
|
62
|
+
if (sort != null) {
|
|
63
|
+
query.set('sort', Array.isArray(sort) ? sort.join(',') : String(sort));
|
|
64
|
+
}
|
|
65
|
+
if (fields != null) {
|
|
66
|
+
query.set('fields', typeof fields === 'string' ? fields : fields.join(','));
|
|
67
|
+
}
|
|
68
|
+
if (populate != null) {
|
|
69
|
+
query.set('populate', typeof populate === 'string' ? populate : populate.join(','));
|
|
70
|
+
}
|
|
71
|
+
if (where && Object.keys(where).length > 0) {
|
|
72
|
+
query.set('where', JSON.stringify(where));
|
|
73
|
+
}
|
|
74
|
+
const locale = readCollectionLocale({ locale: requestedLocale });
|
|
75
|
+
if (locale) {
|
|
76
|
+
query.set('locale', locale);
|
|
77
|
+
}
|
|
78
|
+
return query.toString();
|
|
79
|
+
}
|
|
80
|
+
async function list(params = {}) {
|
|
81
|
+
const qs = toQueryString(params);
|
|
82
|
+
const res = await fetch(`${apiBase()}/entries/${info.uid}${qs ? '?' + qs : ''}`, {
|
|
83
|
+
headers: fimoHeaders(),
|
|
84
|
+
});
|
|
85
|
+
if (!res.ok) {
|
|
86
|
+
throw new Error(await res.text());
|
|
87
|
+
}
|
|
88
|
+
const data = parseCollectionResponse(await res.json());
|
|
89
|
+
const items = data.data.map(wrapWithSource);
|
|
90
|
+
const pageInfo = {
|
|
91
|
+
limit: data.meta.limit,
|
|
92
|
+
offset: data.meta.offset,
|
|
93
|
+
total: data.meta.total,
|
|
94
|
+
hasMore: data.meta.hasMore,
|
|
95
|
+
nextOffset: data.meta.hasMore ? data.meta.offset + data.meta.limit : null,
|
|
96
|
+
};
|
|
97
|
+
return { items, pageInfo };
|
|
98
|
+
}
|
|
99
|
+
async function get(params = {}) {
|
|
100
|
+
return (await list(params)).items;
|
|
101
|
+
}
|
|
102
|
+
async function create(payload) {
|
|
103
|
+
const res = await fetch(`${apiBase()}/entries/${info.uid}`, {
|
|
104
|
+
method: 'POST',
|
|
105
|
+
headers: fimoHeaders({ 'Content-Type': 'application/json' }),
|
|
106
|
+
body: JSON.stringify(splitWritePayload(payload)),
|
|
107
|
+
});
|
|
108
|
+
if (!res.ok) {
|
|
109
|
+
throw new Error(await res.text());
|
|
110
|
+
}
|
|
111
|
+
const data = await res.json();
|
|
112
|
+
return wrapWithSource(data.data);
|
|
113
|
+
}
|
|
114
|
+
async function update(id, payload) {
|
|
115
|
+
const write = splitWritePayload(payload);
|
|
116
|
+
const url = buildReadPath(`/entries/${info.uid}/${id}`, readCollectionLocale({ locale: write.locale }), undefined);
|
|
117
|
+
const res = await fetch(`${apiBase()}${url}`, {
|
|
118
|
+
method: 'PUT',
|
|
119
|
+
headers: fimoHeaders({ 'Content-Type': 'application/json' }),
|
|
120
|
+
body: JSON.stringify({ data: write.data }),
|
|
121
|
+
});
|
|
122
|
+
if (!res.ok) {
|
|
123
|
+
throw new Error(await res.text());
|
|
124
|
+
}
|
|
125
|
+
const data = await res.json();
|
|
126
|
+
return wrapWithSource(data.data);
|
|
127
|
+
}
|
|
128
|
+
async function remove(id, options = {}) {
|
|
129
|
+
const url = buildReadPath(`/entries/${info.uid}/${id}`, readCollectionLocale(options), undefined);
|
|
130
|
+
const res = await fetch(`${apiBase()}${url}`, { method: 'DELETE', headers: fimoHeaders() });
|
|
131
|
+
if (!res.ok) {
|
|
132
|
+
throw new Error(await res.text());
|
|
133
|
+
}
|
|
134
|
+
}
|
|
135
|
+
const qk = {
|
|
136
|
+
root: [info.pascal],
|
|
137
|
+
byId: (id, options = {}) => [info.pascal, 'id', id, options.locale, options.populate],
|
|
138
|
+
bySlug: (slug, options = {}) => [info.pascal, 'slug', slug, options.locale, options.populate],
|
|
139
|
+
byField: (field, value, options = {}) => [info.pascal, field, value, options.locale, options.populate],
|
|
140
|
+
list: (params) => [info.pascal, params],
|
|
141
|
+
infiniteList: (params) => [info.pascal, 'infinite', params],
|
|
142
|
+
};
|
|
143
|
+
function useGetById(id, options = {}) {
|
|
144
|
+
const locale = useQueryLocale(options.locale);
|
|
145
|
+
const localizedOptions = { ...options, locale };
|
|
146
|
+
return useQuery({
|
|
147
|
+
queryKey: qk.byId(id, localizedOptions),
|
|
148
|
+
queryFn: () => getById(id, localizedOptions),
|
|
149
|
+
});
|
|
150
|
+
}
|
|
151
|
+
function useGetBySlug(slug, options = {}) {
|
|
152
|
+
const locale = readCollectionLocale(options, useQueryLocale(options.locale));
|
|
153
|
+
const localizedOptions = { ...options, locale };
|
|
154
|
+
return useQuery({
|
|
155
|
+
queryKey: qk.bySlug(slug, localizedOptions),
|
|
156
|
+
queryFn: () => getBySlug(slug, localizedOptions),
|
|
157
|
+
enabled: !!slug,
|
|
158
|
+
});
|
|
159
|
+
}
|
|
160
|
+
function useGetByField(fieldName, value, options = {}) {
|
|
161
|
+
const locale = readCollectionLocale(options, useQueryLocale(options.locale));
|
|
162
|
+
const localizedOptions = { ...options, locale };
|
|
163
|
+
return useQuery({
|
|
164
|
+
queryKey: qk.byField(fieldName, value, localizedOptions),
|
|
165
|
+
queryFn: () => getByField(fieldName, value, localizedOptions),
|
|
166
|
+
enabled: !!value,
|
|
167
|
+
});
|
|
168
|
+
}
|
|
169
|
+
function useGet(params = {}) {
|
|
170
|
+
const locale = readCollectionLocale(params, useQueryLocale(params.locale));
|
|
171
|
+
const localizedParams = { ...params, locale };
|
|
172
|
+
return useQuery({
|
|
173
|
+
queryKey: qk.list(localizedParams),
|
|
174
|
+
queryFn: () => list(localizedParams),
|
|
175
|
+
select: (page) => page.items,
|
|
176
|
+
});
|
|
177
|
+
}
|
|
178
|
+
function useList(params = {}) {
|
|
179
|
+
const locale = readCollectionLocale(params, useQueryLocale(params.locale));
|
|
180
|
+
const localizedParams = { ...params, locale };
|
|
181
|
+
return useQuery({
|
|
182
|
+
queryKey: qk.list(localizedParams),
|
|
183
|
+
queryFn: () => list(localizedParams),
|
|
184
|
+
});
|
|
185
|
+
}
|
|
186
|
+
function useInfiniteList(params = {}) {
|
|
187
|
+
const locale = readCollectionLocale(params, useQueryLocale(params.locale));
|
|
188
|
+
const localizedParams = { ...params, locale };
|
|
189
|
+
const query = useInfiniteQuery({
|
|
190
|
+
queryKey: qk.infiniteList(localizedParams),
|
|
191
|
+
queryFn: ({ pageParam }) => list({ ...localizedParams, offset: pageParam }),
|
|
192
|
+
initialPageParam: 0,
|
|
193
|
+
getNextPageParam: (lastPage) => lastPage.pageInfo.nextOffset ?? undefined,
|
|
194
|
+
});
|
|
195
|
+
return {
|
|
196
|
+
...query,
|
|
197
|
+
items: query.data?.pages.flatMap((page) => page.items) ?? [],
|
|
198
|
+
total: query.data?.pages[0]?.pageInfo.total ?? 0,
|
|
199
|
+
hasMore: query.hasNextPage,
|
|
200
|
+
loadMore: () => query.fetchNextPage(),
|
|
201
|
+
isLoadingMore: query.isFetchingNextPage,
|
|
202
|
+
};
|
|
203
|
+
}
|
|
204
|
+
function useSuspenseGetById(id, options = {}) {
|
|
205
|
+
const locale = useQueryLocale(options.locale);
|
|
206
|
+
const localizedOptions = { ...options, locale };
|
|
207
|
+
return useSuspenseQuery({
|
|
208
|
+
queryKey: qk.byId(id, localizedOptions),
|
|
209
|
+
queryFn: () => getById(id, localizedOptions),
|
|
210
|
+
});
|
|
211
|
+
}
|
|
212
|
+
function useSuspenseGetBySlug(slug, options = {}) {
|
|
213
|
+
const locale = readCollectionLocale(options, useQueryLocale(options.locale));
|
|
214
|
+
const localizedOptions = { ...options, locale };
|
|
215
|
+
return useSuspenseQuery({
|
|
216
|
+
queryKey: qk.bySlug(slug, localizedOptions),
|
|
217
|
+
queryFn: () => getBySlug(slug, localizedOptions),
|
|
218
|
+
});
|
|
219
|
+
}
|
|
220
|
+
function useSuspenseGetByField(fieldName, value, options = {}) {
|
|
221
|
+
const locale = readCollectionLocale(options, useQueryLocale(options.locale));
|
|
222
|
+
const localizedOptions = { ...options, locale };
|
|
223
|
+
return useSuspenseQuery({
|
|
224
|
+
queryKey: qk.byField(fieldName, value, localizedOptions),
|
|
225
|
+
queryFn: () => getByField(fieldName, value, localizedOptions),
|
|
226
|
+
});
|
|
227
|
+
}
|
|
228
|
+
function useSuspenseGet(params = {}) {
|
|
229
|
+
const locale = readCollectionLocale(params, useQueryLocale(params.locale));
|
|
230
|
+
const localizedParams = { ...params, locale };
|
|
231
|
+
return useSuspenseQuery({
|
|
232
|
+
queryKey: qk.list(localizedParams),
|
|
233
|
+
queryFn: () => list(localizedParams),
|
|
234
|
+
select: (page) => page.items,
|
|
235
|
+
});
|
|
236
|
+
}
|
|
237
|
+
function useCreate() {
|
|
238
|
+
const qc = useQueryClient();
|
|
239
|
+
const activeLocale = useQueryLocale();
|
|
240
|
+
return useMutation({
|
|
241
|
+
mutationFn: (payload) => create(withMutationLocale(payload, activeLocale)),
|
|
242
|
+
onSuccess: () => qc.invalidateQueries({ queryKey: qk.root }),
|
|
243
|
+
});
|
|
244
|
+
}
|
|
245
|
+
function useUpdate() {
|
|
246
|
+
const qc = useQueryClient();
|
|
247
|
+
const activeLocale = useQueryLocale();
|
|
248
|
+
return useMutation({
|
|
249
|
+
mutationFn: ({ id, ...patch }) => update(id, withMutationLocale(patch, activeLocale)),
|
|
250
|
+
onSuccess: (_data, vars) => {
|
|
251
|
+
qc.invalidateQueries({ queryKey: qk.root });
|
|
252
|
+
qc.invalidateQueries({ queryKey: qk.byId(vars.id, { locale: vars.locale ?? activeLocale }) });
|
|
253
|
+
},
|
|
254
|
+
});
|
|
255
|
+
}
|
|
256
|
+
function useRemove() {
|
|
257
|
+
const qc = useQueryClient();
|
|
258
|
+
const activeLocale = useQueryLocale();
|
|
259
|
+
return useMutation({
|
|
260
|
+
mutationFn: (value) => {
|
|
261
|
+
const { id, locale } = typeof value === 'string'
|
|
262
|
+
? { id: value, locale: activeLocale }
|
|
263
|
+
: { ...value, locale: value.locale ?? activeLocale };
|
|
264
|
+
return remove(id, { locale });
|
|
265
|
+
},
|
|
266
|
+
onSuccess: (_data, value) => {
|
|
267
|
+
const { id, locale } = typeof value === 'string'
|
|
268
|
+
? { id: value, locale: activeLocale }
|
|
269
|
+
: { ...value, locale: value.locale ?? activeLocale };
|
|
270
|
+
qc.invalidateQueries({ queryKey: qk.root });
|
|
271
|
+
qc.removeQueries({ queryKey: qk.byId(id, { locale }), exact: false });
|
|
272
|
+
},
|
|
273
|
+
});
|
|
274
|
+
}
|
|
275
|
+
return {
|
|
276
|
+
meta,
|
|
277
|
+
wrapWithSource,
|
|
278
|
+
getById,
|
|
279
|
+
getBySlug,
|
|
280
|
+
getByField,
|
|
281
|
+
list,
|
|
282
|
+
get,
|
|
283
|
+
create,
|
|
284
|
+
update,
|
|
285
|
+
remove,
|
|
286
|
+
useGetById,
|
|
287
|
+
useGetBySlug,
|
|
288
|
+
useGetByField,
|
|
289
|
+
useGet,
|
|
290
|
+
useList,
|
|
291
|
+
useInfiniteList,
|
|
292
|
+
useSuspenseGetById,
|
|
293
|
+
useSuspenseGetBySlug,
|
|
294
|
+
useSuspenseGetByField,
|
|
295
|
+
useSuspenseGet,
|
|
296
|
+
useCreate,
|
|
297
|
+
useUpdate,
|
|
298
|
+
useRemove,
|
|
299
|
+
};
|
|
300
|
+
}
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
import type { ContentClientConfig, SingletonContentClient } from './types.js';
|
|
2
|
+
export declare function createSingletonClient<T extends {
|
|
3
|
+
id: string;
|
|
4
|
+
}, TInput extends object>(config: ContentClientConfig): SingletonContentClient<T, TInput>;
|
|
5
|
+
//# sourceMappingURL=createSingletonClient.d.ts.map
|
|
@@ -0,0 +1,108 @@
|
|
|
1
|
+
import { useMutation, useQuery, useQueryClient, useSuspenseQuery } from '@tanstack/react-query';
|
|
2
|
+
import { apiBase, buildReadPath, fimoHeaders, interpretSchema, readSingletonLocale, referenceTargetUids, resolveClientHandle, splitWritePayload, validateReferences, withMutationLocale, wrapEntry, } from './internal.js';
|
|
3
|
+
import { useQueryLocale } from './useQueryLocale.js';
|
|
4
|
+
export function createSingletonClient(config) {
|
|
5
|
+
const references = config.references ?? {};
|
|
6
|
+
const info = interpretSchema(config.schema);
|
|
7
|
+
if (!info.isSingleton) {
|
|
8
|
+
throw new Error(`Schema "${info.uid}" is a collection — use createContentClient instead.`);
|
|
9
|
+
}
|
|
10
|
+
validateReferences(info, references);
|
|
11
|
+
const meta = { uid: info.uid, pascal: info.pascal, localized: info.localized };
|
|
12
|
+
const self = { wrapWithSource, meta };
|
|
13
|
+
const resolve = (uid) => uid === info.uid ? self : resolveClientHandle(references[uid]);
|
|
14
|
+
// Which reference fields point at a localized schema decides whether a
|
|
15
|
+
// non-localized singleton read must still send a locale. Target metas live
|
|
16
|
+
// on other modules in a possibly-cyclic graph, so this is derived lazily on
|
|
17
|
+
// first request, never at module init.
|
|
18
|
+
let localizedReferenceFields = null;
|
|
19
|
+
function getLocalizedReferenceFields() {
|
|
20
|
+
if (localizedReferenceFields) {
|
|
21
|
+
return localizedReferenceFields;
|
|
22
|
+
}
|
|
23
|
+
const fields = new Set();
|
|
24
|
+
for (const [key, field] of info.fields) {
|
|
25
|
+
if (field.type !== 'reference') {
|
|
26
|
+
continue;
|
|
27
|
+
}
|
|
28
|
+
const localizedTarget = referenceTargetUids(field).some((uid) => resolve(uid)?.meta?.localized !== false);
|
|
29
|
+
if (localizedTarget) {
|
|
30
|
+
fields.add(key);
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
localizedReferenceFields = fields;
|
|
34
|
+
return fields;
|
|
35
|
+
}
|
|
36
|
+
function wrapWithSource(entry) {
|
|
37
|
+
return wrapEntry(info, entry, { alwaysIncludeLocale: info.localized, resolve });
|
|
38
|
+
}
|
|
39
|
+
function readLocale(options, fallbackLocale) {
|
|
40
|
+
return fallbackLocale === undefined
|
|
41
|
+
? readSingletonLocale(options, info.localized, getLocalizedReferenceFields())
|
|
42
|
+
: readSingletonLocale(options, info.localized, getLocalizedReferenceFields(), fallbackLocale);
|
|
43
|
+
}
|
|
44
|
+
function readPath(url, options) {
|
|
45
|
+
return buildReadPath(url, readLocale(options), options.populate, options.localeFallbacks);
|
|
46
|
+
}
|
|
47
|
+
async function get(options = {}) {
|
|
48
|
+
const res = await fetch(`${apiBase()}${readPath(`/documents/${info.uid}`, options)}`, {
|
|
49
|
+
headers: fimoHeaders(),
|
|
50
|
+
});
|
|
51
|
+
if (!res.ok) {
|
|
52
|
+
throw new Error(await res.text());
|
|
53
|
+
}
|
|
54
|
+
const data = await res.json();
|
|
55
|
+
return wrapWithSource(data.data);
|
|
56
|
+
}
|
|
57
|
+
async function update(payload) {
|
|
58
|
+
const write = splitWritePayload(payload);
|
|
59
|
+
const url = buildReadPath(`/documents/${info.uid}`, readLocale({ locale: write.locale }), undefined);
|
|
60
|
+
const res = await fetch(`${apiBase()}${url}`, {
|
|
61
|
+
method: 'PUT',
|
|
62
|
+
headers: fimoHeaders({ 'Content-Type': 'application/json' }),
|
|
63
|
+
body: JSON.stringify({ data: write.data }),
|
|
64
|
+
});
|
|
65
|
+
if (!res.ok) {
|
|
66
|
+
throw new Error(await res.text());
|
|
67
|
+
}
|
|
68
|
+
const data = await res.json();
|
|
69
|
+
return wrapWithSource(data.data);
|
|
70
|
+
}
|
|
71
|
+
const qk = {
|
|
72
|
+
root: [info.pascal],
|
|
73
|
+
document: (options = {}) => [info.pascal, 'document', options.locale, options.populate],
|
|
74
|
+
};
|
|
75
|
+
function useGet(options = {}) {
|
|
76
|
+
const locale = readLocale(options, useQueryLocale(options.locale));
|
|
77
|
+
const localizedOptions = { ...options, locale };
|
|
78
|
+
return useQuery({
|
|
79
|
+
queryKey: qk.document(localizedOptions),
|
|
80
|
+
queryFn: () => get(localizedOptions),
|
|
81
|
+
});
|
|
82
|
+
}
|
|
83
|
+
function useSuspenseGet(options = {}) {
|
|
84
|
+
const locale = readLocale(options, useQueryLocale(options.locale));
|
|
85
|
+
const localizedOptions = { ...options, locale };
|
|
86
|
+
return useSuspenseQuery({
|
|
87
|
+
queryKey: qk.document(localizedOptions),
|
|
88
|
+
queryFn: () => get(localizedOptions),
|
|
89
|
+
});
|
|
90
|
+
}
|
|
91
|
+
function useUpdate() {
|
|
92
|
+
const qc = useQueryClient();
|
|
93
|
+
const activeLocale = useQueryLocale();
|
|
94
|
+
return useMutation({
|
|
95
|
+
mutationFn: (payload) => update(withMutationLocale(payload, activeLocale)),
|
|
96
|
+
onSuccess: () => qc.invalidateQueries({ queryKey: qk.root }),
|
|
97
|
+
});
|
|
98
|
+
}
|
|
99
|
+
return {
|
|
100
|
+
meta,
|
|
101
|
+
wrapWithSource,
|
|
102
|
+
get,
|
|
103
|
+
update,
|
|
104
|
+
useGet,
|
|
105
|
+
useSuspenseGet,
|
|
106
|
+
useUpdate,
|
|
107
|
+
};
|
|
108
|
+
}
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
import type { ContentClientHandle, ContentModuleRef, ContentSchema, ContentSchemaField } from './types.js';
|
|
2
|
+
export interface InterpretedSchema {
|
|
3
|
+
uid: string;
|
|
4
|
+
pascal: string;
|
|
5
|
+
localized: boolean;
|
|
6
|
+
isSingleton: boolean;
|
|
7
|
+
fields: ReadonlyArray<readonly [string, ContentSchemaField]>;
|
|
8
|
+
}
|
|
9
|
+
export declare function interpretSchema(schema: ContentSchema): InterpretedSchema;
|
|
10
|
+
export declare function referenceTargetUids(field: ContentSchemaField): readonly string[];
|
|
11
|
+
export declare function validateReferences(info: InterpretedSchema, references: Readonly<Record<string, ContentModuleRef>>): void;
|
|
12
|
+
export declare function apiBase(): string;
|
|
13
|
+
export declare function defaultLocale(): string;
|
|
14
|
+
export declare function fimoHeaders(extra?: Record<string, string>): Record<string, string>;
|
|
15
|
+
interface LocaleReadOptions {
|
|
16
|
+
locale?: string;
|
|
17
|
+
populate?: string | readonly string[];
|
|
18
|
+
}
|
|
19
|
+
export declare function readCollectionLocale(options: LocaleReadOptions, fallbackLocale?: string): string;
|
|
20
|
+
export declare function readSingletonLocale(options: LocaleReadOptions, ownerUsesLocale: boolean, localizedReferenceFields: ReadonlySet<string>, fallbackLocale?: string): string | undefined;
|
|
21
|
+
export declare function buildReadPath(url: string, locale: string | undefined, populate: string | readonly string[] | undefined, localeFallbacks?: boolean): string;
|
|
22
|
+
export declare function splitWritePayload(payload: Record<string, unknown>): {
|
|
23
|
+
data: Record<string, unknown>;
|
|
24
|
+
locale?: string;
|
|
25
|
+
documentId?: string;
|
|
26
|
+
};
|
|
27
|
+
export declare function withMutationLocale<T extends object>(payload: T, activeLocale: string): T & {
|
|
28
|
+
locale: string;
|
|
29
|
+
};
|
|
30
|
+
type ReferenceResolver = (uid: string) => ContentClientHandle | undefined;
|
|
31
|
+
/**
|
|
32
|
+
* A reference is either a client or a module namespace whose default export
|
|
33
|
+
* is the client. Only ever called at request time — unwrapping a namespace's
|
|
34
|
+
* `default` mid-cycle would read an uninitialized binding. A namespace can
|
|
35
|
+
* plausibly carry BOTH a re-exported wrapWithSource and a default client, so
|
|
36
|
+
* prefer whichever shape carries meta — the locale rules depend on it.
|
|
37
|
+
*/
|
|
38
|
+
export declare function resolveClientHandle(ref: ContentModuleRef | undefined): ContentClientHandle | undefined;
|
|
39
|
+
export declare function wrapEntry(info: InterpretedSchema, entry: Record<string, any>, options: {
|
|
40
|
+
alwaysIncludeLocale: boolean;
|
|
41
|
+
resolve: ReferenceResolver;
|
|
42
|
+
}): Record<string, unknown>;
|
|
43
|
+
export {};
|
|
44
|
+
//# sourceMappingURL=internal.d.ts.map
|