fimo 0.9.0-experimental.1 → 0.9.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.
- package/assets/skills/fimo/SKILL.md +17 -54
- package/assets/skills/fimo-cli/SKILL.md +15 -15
- package/assets/skills/fimo-cli/references/agent-setup.md +36 -53
- package/assets/skills/fimo-cli/references/agents.md +4 -4
- package/assets/skills/fimo-cli/references/content.md +2 -2
- package/assets/skills/fimo-cli/references/deploy.md +1 -1
- package/assets/skills/fimo-cli/references/forms.md +1 -1
- package/assets/skills/fimo-cli/references/init.md +16 -24
- package/assets/skills/fimo-cli/references/project-vars-and-secrets.md +14 -11
- package/assets/skills/fimo-cli/references/projects.md +1 -1
- package/assets/skills/fimo-cli/references/support.md +11 -9
- package/assets/skills/fimo-cli/references/webhooks.md +3 -1
- package/assets/skills/fimo-code/SKILL.md +67 -0
- package/assets/skills/fimo-migration/SKILL.md +2 -22
- package/assets/skills/fimo-migration/references/content.md +0 -24
- package/assets/skills/fimo-migration/references/manifest.md +0 -35
- package/assets/skills/fimo-migration/references/qa.md +2 -24
- package/assets/skills/fimo-migration/references/workflow.md +1 -12
- package/assets/skills/fimo-studio/references/visual-editing.md +3 -5
- package/assets/skills/fimo-studio/references/webhooks.md +1 -1
- package/dist/build/vite/plugins/translations.d.ts +8 -2
- package/dist/build/vite/plugins/translations.js +121 -3
- package/dist/cli/bundle.json +2 -2
- package/dist/cli/index.js +311 -1209
- package/dist/runtime/app/FimoProviders.d.ts +11 -1
- package/dist/runtime/app/FimoProviders.js +23 -2
- package/dist/runtime/content/client/createContentClient.js +2 -1
- package/dist/runtime/i18n/FimoLocaleContext.js +0 -1
- package/dist/runtime/primitives/lib/useFimoLiveValue.d.ts +3 -5
- package/dist/runtime/primitives/lib/useFimoLiveValue.js +4 -11
- package/dist/runtime/primitives/translations.d.ts +27 -1
- package/dist/runtime/primitives/translations.js +85 -7
- package/dist/runtime/shared/fimo-config.server.js +0 -1
- package/dist/runtime/shared/fimo-config.types.d.ts +0 -2
- package/dist/scripts/extract-translations.js +11 -16
- package/dist/scripts/lib/validation-report.d.ts +0 -1
- package/dist/scripts/lib/validation-report.js +0 -3
- package/dist/scripts/validate-route-metadata.js +0 -11
- package/package.json +4 -84
- package/release.json +2 -2
- package/scripts/postinstall.mjs +11 -65
- package/templates/react-router/AGENTS.md +44 -0
- package/templates/react-router/CLAUDE.md +5 -0
- package/templates/react-router/package.json +1 -1
- package/assets/agents-starter/AGENTS.md +0 -66
- package/assets/skills/fimo-migration/references/wrap.md +0 -617
- package/dist/runtime/astro/index.d.ts +0 -2
- package/dist/runtime/astro/index.js +0 -3
- package/dist/runtime/astro/server.d.ts +0 -3
- package/dist/runtime/astro/server.js +0 -6
- package/dist/runtime/next/FimoNextProviders.d.ts +0 -24
- package/dist/runtime/next/FimoNextProviders.js +0 -20
- package/dist/runtime/next/FimoNextScripts.d.ts +0 -10
- package/dist/runtime/next/FimoNextScripts.js +0 -19
- package/dist/runtime/next/index.d.ts +0 -14
- package/dist/runtime/next/index.js +0 -14
- package/dist/runtime/next/server.d.ts +0 -2
- package/dist/runtime/next/server.js +0 -4
- package/dist/runtime/nuxt/composables.d.ts +0 -9
- package/dist/runtime/nuxt/composables.js +0 -57
- package/dist/runtime/nuxt/index.d.ts +0 -3
- package/dist/runtime/nuxt/index.js +0 -6
- package/dist/runtime/nuxt/server.d.ts +0 -3
- package/dist/runtime/nuxt/server.js +0 -6
- package/dist/runtime/primitives/labels-hook.d.ts +0 -33
- package/dist/runtime/primitives/labels-hook.js +0 -41
- package/dist/runtime/primitives/labels-store.d.ts +0 -12
- package/dist/runtime/primitives/labels-store.js +0 -60
- package/dist/runtime/react/FimoSpaProviders.d.ts +0 -36
- package/dist/runtime/react/FimoSpaProviders.js +0 -63
- package/dist/runtime/react/index.d.ts +0 -33
- package/dist/runtime/react/index.js +0 -39
- package/dist/runtime/remix/FimoRemixProviders.d.ts +0 -16
- package/dist/runtime/remix/FimoRemixProviders.js +0 -12
- package/dist/runtime/remix/FimoRemixScripts.d.ts +0 -10
- package/dist/runtime/remix/FimoRemixScripts.js +0 -19
- package/dist/runtime/remix/index.d.ts +0 -12
- package/dist/runtime/remix/index.js +0 -11
- package/dist/runtime/remix/server.d.ts +0 -2
- package/dist/runtime/remix/server.js +0 -2
- package/dist/runtime/shared/display-routes.d.ts +0 -14
- package/dist/runtime/shared/display-routes.js +0 -25
- package/dist/runtime/shared/i18n-messages.d.ts +0 -29
- package/dist/runtime/shared/i18n-messages.js +0 -79
- package/dist/runtime/shared/labels-fetch.pure.d.ts +0 -17
- package/dist/runtime/shared/labels-fetch.pure.js +0 -71
- package/dist/runtime/shared/labels-fetch.server.d.ts +0 -7
- package/dist/runtime/shared/labels-fetch.server.js +0 -97
- package/dist/runtime/shared/labels-t.pure.d.ts +0 -2
- package/dist/runtime/shared/labels-t.pure.js +0 -10
- package/dist/runtime/strapi/client.d.ts +0 -30
- package/dist/runtime/strapi/client.js +0 -89
- package/dist/runtime/strapi/index.d.ts +0 -5
- package/dist/runtime/strapi/index.js +0 -6
- package/dist/runtime/strapi/reshape.d.ts +0 -46
- package/dist/runtime/strapi/reshape.js +0 -114
- package/dist/runtime/svelte/index.d.ts +0 -3
- package/dist/runtime/svelte/index.js +0 -7
- package/dist/runtime/svelte/server.d.ts +0 -3
- package/dist/runtime/svelte/server.js +0 -6
- package/dist/runtime/vue/index.d.ts +0 -3
- package/dist/runtime/vue/index.js +0 -5
- package/dist/runtime/vue/server.d.ts +0 -3
- package/dist/runtime/vue/server.js +0 -6
- package/dist/runtime/wrapped/FimoFileLink.d.ts +0 -17
- package/dist/runtime/wrapped/FimoFileLink.js +0 -19
- package/dist/runtime/wrapped/FimoMediaImage.d.ts +0 -14
- package/dist/runtime/wrapped/FimoMediaImage.js +0 -20
- package/dist/runtime/wrapped/FimoMediaVideo.d.ts +0 -14
- package/dist/runtime/wrapped/FimoMediaVideo.js +0 -20
- package/dist/runtime/wrapped/FimoRichTextBlock.d.ts +0 -23
- package/dist/runtime/wrapped/FimoRichTextBlock.js +0 -22
- package/dist/runtime/wrapped/FimoText.d.ts +0 -22
- package/dist/runtime/wrapped/FimoText.js +0 -20
- package/dist/runtime/wrapped/WrappedContentContext.d.ts +0 -28
- package/dist/runtime/wrapped/WrappedContentContext.js +0 -30
- package/dist/runtime/wrapped/WrappedProviders.d.ts +0 -33
- package/dist/runtime/wrapped/WrappedProviders.js +0 -101
- package/dist/runtime/wrapped/dom.d.ts +0 -4
- package/dist/runtime/wrapped/dom.js +0 -23
- package/dist/runtime/wrapped/server.d.ts +0 -77
- package/dist/runtime/wrapped/server.js +0 -237
- package/dist/scripts/apply-wrap-templates.d.ts +0 -61
- package/dist/scripts/apply-wrap-templates.js +0 -132
- package/dist/scripts/apply-wrap.d.ts +0 -46
- package/dist/scripts/apply-wrap.js +0 -278
- package/dist/scripts/cms/discover.d.ts +0 -2
- package/dist/scripts/cms/discover.js +0 -17
- package/dist/scripts/cms/example-content.d.ts +0 -33
- package/dist/scripts/cms/example-content.js +0 -162
- package/dist/scripts/cms/strapi/detect.d.ts +0 -2
- package/dist/scripts/cms/strapi/detect.js +0 -186
- package/dist/scripts/cms/strapi/index.d.ts +0 -3
- package/dist/scripts/cms/strapi/index.js +0 -25
- package/dist/scripts/cms/strapi/list-source.d.ts +0 -17
- package/dist/scripts/cms/strapi/list-source.js +0 -90
- package/dist/scripts/cms/strapi/schema-map.d.ts +0 -9
- package/dist/scripts/cms/strapi/schema-map.js +0 -277
- package/dist/scripts/cms/strapi/shapes.d.ts +0 -23
- package/dist/scripts/cms/strapi/shapes.js +0 -38
- package/dist/scripts/cms/strapi/touchpoints.d.ts +0 -31
- package/dist/scripts/cms/strapi/touchpoints.js +0 -145
- package/dist/scripts/cms/strapi/wire-client.d.ts +0 -15
- package/dist/scripts/cms/strapi/wire-client.js +0 -107
- package/dist/scripts/cms/strapi/wiring.d.ts +0 -17
- package/dist/scripts/cms/strapi/wiring.js +0 -46
- package/dist/scripts/cms/types.d.ts +0 -145
- package/dist/scripts/cms/types.js +0 -11
- package/dist/scripts/detect-framework.d.ts +0 -39
- package/dist/scripts/detect-framework.js +0 -133
- package/dist/scripts/draft-rows.d.ts +0 -19
- package/dist/scripts/draft-rows.js +0 -135
- package/dist/scripts/draft-schemas.d.ts +0 -27
- package/dist/scripts/draft-schemas.js +0 -188
- package/dist/scripts/frameworks/astro/routes.d.ts +0 -3
- package/dist/scripts/frameworks/astro/routes.js +0 -55
- package/dist/scripts/frameworks/astro/template-style.d.ts +0 -7
- package/dist/scripts/frameworks/astro/template-style.js +0 -19
- package/dist/scripts/frameworks/discover.d.ts +0 -70
- package/dist/scripts/frameworks/discover.js +0 -178
- package/dist/scripts/frameworks/next/rewire.d.ts +0 -30
- package/dist/scripts/frameworks/next/rewire.js +0 -258
- package/dist/scripts/frameworks/next/routes.d.ts +0 -17
- package/dist/scripts/frameworks/next/routes.js +0 -134
- package/dist/scripts/frameworks/next/wiring.d.ts +0 -33
- package/dist/scripts/frameworks/next/wiring.js +0 -186
- package/dist/scripts/frameworks/nuxt/routes.d.ts +0 -3
- package/dist/scripts/frameworks/nuxt/routes.js +0 -54
- package/dist/scripts/frameworks/nuxt/template-style.d.ts +0 -9
- package/dist/scripts/frameworks/nuxt/template-style.js +0 -27
- package/dist/scripts/frameworks/remix/routes.d.ts +0 -3
- package/dist/scripts/frameworks/remix/routes.js +0 -50
- package/dist/scripts/frameworks/support.d.ts +0 -75
- package/dist/scripts/frameworks/support.js +0 -165
- package/dist/scripts/frameworks/sveltekit/routes.d.ts +0 -3
- package/dist/scripts/frameworks/sveltekit/routes.js +0 -50
- package/dist/scripts/frameworks/sveltekit/template-style.d.ts +0 -9
- package/dist/scripts/frameworks/sveltekit/template-style.js +0 -54
- package/dist/scripts/frameworks/vite/routes.d.ts +0 -15
- package/dist/scripts/frameworks/vite/routes.js +0 -93
- package/dist/scripts/frameworks/vite/wiring.d.ts +0 -10
- package/dist/scripts/frameworks/vite/wiring.js +0 -99
- package/dist/scripts/i18n-namespace.d.ts +0 -27
- package/dist/scripts/i18n-namespace.js +0 -155
- package/dist/scripts/render-parity/capture.d.ts +0 -56
- package/dist/scripts/render-parity/capture.js +0 -366
- package/dist/scripts/render-parity/compare.d.ts +0 -59
- package/dist/scripts/render-parity/compare.js +0 -0
- package/dist/scripts/render-parity/normalize-html.d.ts +0 -13
- package/dist/scripts/render-parity/normalize-html.js +0 -223
- package/dist/scripts/render-parity/types.d.ts +0 -41
- package/dist/scripts/render-parity/types.js +0 -1
- package/dist/scripts/scan-wrappable.d.ts +0 -196
- package/dist/scripts/scan-wrappable.js +0 -1232
- /package/assets/skills/{fimo → fimo-code}/assets/react-router-locales/navigation.tsx +0 -0
- /package/assets/skills/{fimo → fimo-code}/assets/react-router-locales/routes.ts +0 -0
- /package/assets/skills/{fimo → fimo-code}/references/assets.md +0 -0
- /package/assets/skills/{fimo → fimo-code}/references/content.md +0 -0
- /package/assets/skills/{fimo → fimo-code}/references/design.md +0 -0
- /package/assets/skills/{fimo → fimo-code}/references/forms.md +0 -0
- /package/assets/skills/{fimo → fimo-code}/references/labels.md +0 -0
- /package/assets/skills/{fimo → fimo-code}/references/locales.md +0 -0
- /package/assets/skills/{fimo → fimo-code}/references/setup-plain-vite.md +0 -0
- /package/assets/skills/{fimo → fimo-code}/references/setup-react-router.md +0 -0
- /package/assets/skills/{fimo → fimo-code}/references/ui.md +0 -0
|
@@ -1,25 +0,0 @@
|
|
|
1
|
-
const titleCase = (seg) => seg
|
|
2
|
-
.replace(/[_\-]+/g, ' ')
|
|
3
|
-
.replace(/([a-z])([A-Z])/g, '$1 $2')
|
|
4
|
-
.split(/\s+/)
|
|
5
|
-
.filter(Boolean)
|
|
6
|
-
.map((w) => w.charAt(0).toUpperCase() + w.slice(1).toLowerCase())
|
|
7
|
-
.join(' ');
|
|
8
|
-
const generateRouteName = (path) => {
|
|
9
|
-
if (path === '/') {
|
|
10
|
-
return 'Home';
|
|
11
|
-
}
|
|
12
|
-
const firstSegment = path.replace(/^\//, '').split('/')[0];
|
|
13
|
-
return titleCase(firstSegment);
|
|
14
|
-
};
|
|
15
|
-
export function getDisplayRoutes(config) {
|
|
16
|
-
const configRoutes = config?.routes ?? {};
|
|
17
|
-
return Object.entries(configRoutes)
|
|
18
|
-
.filter(([, route]) => route.path)
|
|
19
|
-
.map(([id, route]) => ({
|
|
20
|
-
id,
|
|
21
|
-
path: route.path,
|
|
22
|
-
name: route.name || route.label || generateRouteName(route.path),
|
|
23
|
-
params: route.params,
|
|
24
|
-
}));
|
|
25
|
-
}
|
|
@@ -1,29 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Lossless bridge between a nested i18n message store (next-intl / i18next
|
|
3
|
-
* style: `{ home: { title: "…" } }`) and Fimo's FLAT per-locale label map
|
|
4
|
-
* (`{ "home.title": "…" }`). Ingestion flattens the store into labels;
|
|
5
|
-
* `loadFimoMessages` un-flattens the labels back so the app's own i18n
|
|
6
|
-
* runtime keeps rendering unchanged, now sourced from Fimo.
|
|
7
|
-
*
|
|
8
|
-
* String leaves and nested objects round-trip byte-for-byte, ICU strings
|
|
9
|
-
* (`{count, plural, …}`, `<tag>…</tag>`) kept opaque. Array/number/boolean
|
|
10
|
-
* leaves are rare in message stores and are NOT flattened — they stay in the
|
|
11
|
-
* caller's static base (deep-merged under the Fimo bundle).
|
|
12
|
-
*/
|
|
13
|
-
/** Flatten a nested message object to dot-keyed string entries. Non-string
|
|
14
|
-
* leaves (arrays, numbers) are skipped — reported so the caller can note
|
|
15
|
-
* them. Keys already containing a dot are preserved verbatim. */
|
|
16
|
-
export declare function flattenMessages(messages: unknown, prefix?: string): {
|
|
17
|
-
flat: Record<string, string>;
|
|
18
|
-
skipped: string[];
|
|
19
|
-
};
|
|
20
|
-
/** Rebuild the nested message object from a flat dot-keyed map. Inverse of
|
|
21
|
-
* flattenMessages for string leaves. A key whose path collides with an
|
|
22
|
-
* already-set string (e.g. both `a` and `a.b` exist) keeps the deeper
|
|
23
|
-
* object and drops the scalar — message stores never have both. */
|
|
24
|
-
export declare function unflattenMessages(flat: Record<string, string>): Record<string, unknown>;
|
|
25
|
-
/** Deep-merge `override` (Fimo bundle) over `base` (static messages) so keys
|
|
26
|
-
* absent from Fimo fall back to the shipped file instead of next-intl's
|
|
27
|
-
* MISSING_MESSAGE. Objects merge recursively; leaves in `override` win. */
|
|
28
|
-
export declare function deepMergeMessages(base: Record<string, unknown>, override: Record<string, unknown>): Record<string, unknown>;
|
|
29
|
-
//# sourceMappingURL=i18n-messages.d.ts.map
|
|
@@ -1,79 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Lossless bridge between a nested i18n message store (next-intl / i18next
|
|
3
|
-
* style: `{ home: { title: "…" } }`) and Fimo's FLAT per-locale label map
|
|
4
|
-
* (`{ "home.title": "…" }`). Ingestion flattens the store into labels;
|
|
5
|
-
* `loadFimoMessages` un-flattens the labels back so the app's own i18n
|
|
6
|
-
* runtime keeps rendering unchanged, now sourced from Fimo.
|
|
7
|
-
*
|
|
8
|
-
* String leaves and nested objects round-trip byte-for-byte, ICU strings
|
|
9
|
-
* (`{count, plural, …}`, `<tag>…</tag>`) kept opaque. Array/number/boolean
|
|
10
|
-
* leaves are rare in message stores and are NOT flattened — they stay in the
|
|
11
|
-
* caller's static base (deep-merged under the Fimo bundle).
|
|
12
|
-
*/
|
|
13
|
-
/** Flatten a nested message object to dot-keyed string entries. Non-string
|
|
14
|
-
* leaves (arrays, numbers) are skipped — reported so the caller can note
|
|
15
|
-
* them. Keys already containing a dot are preserved verbatim. */
|
|
16
|
-
export function flattenMessages(messages, prefix = '') {
|
|
17
|
-
const flat = {};
|
|
18
|
-
const skipped = [];
|
|
19
|
-
if (!messages || typeof messages !== 'object' || Array.isArray(messages)) {
|
|
20
|
-
return { flat, skipped };
|
|
21
|
-
}
|
|
22
|
-
for (const [key, value] of Object.entries(messages)) {
|
|
23
|
-
const path = prefix ? `${prefix}.${key}` : key;
|
|
24
|
-
if (typeof value === 'string') {
|
|
25
|
-
flat[path] = value;
|
|
26
|
-
}
|
|
27
|
-
else if (value && typeof value === 'object' && !Array.isArray(value)) {
|
|
28
|
-
const nested = flattenMessages(value, path);
|
|
29
|
-
Object.assign(flat, nested.flat);
|
|
30
|
-
skipped.push(...nested.skipped);
|
|
31
|
-
}
|
|
32
|
-
else {
|
|
33
|
-
skipped.push(path);
|
|
34
|
-
}
|
|
35
|
-
}
|
|
36
|
-
return { flat, skipped };
|
|
37
|
-
}
|
|
38
|
-
/** Rebuild the nested message object from a flat dot-keyed map. Inverse of
|
|
39
|
-
* flattenMessages for string leaves. A key whose path collides with an
|
|
40
|
-
* already-set string (e.g. both `a` and `a.b` exist) keeps the deeper
|
|
41
|
-
* object and drops the scalar — message stores never have both. */
|
|
42
|
-
export function unflattenMessages(flat) {
|
|
43
|
-
const root = {};
|
|
44
|
-
for (const [flatKey, value] of Object.entries(flat)) {
|
|
45
|
-
const parts = flatKey.split('.');
|
|
46
|
-
let node = root;
|
|
47
|
-
for (let i = 0; i < parts.length - 1; i += 1) {
|
|
48
|
-
const part = parts[i];
|
|
49
|
-
const existing = node[part];
|
|
50
|
-
if (!existing || typeof existing !== 'object' || Array.isArray(existing)) {
|
|
51
|
-
node[part] = {};
|
|
52
|
-
}
|
|
53
|
-
node = node[part];
|
|
54
|
-
}
|
|
55
|
-
node[parts[parts.length - 1]] = value;
|
|
56
|
-
}
|
|
57
|
-
return root;
|
|
58
|
-
}
|
|
59
|
-
/** Deep-merge `override` (Fimo bundle) over `base` (static messages) so keys
|
|
60
|
-
* absent from Fimo fall back to the shipped file instead of next-intl's
|
|
61
|
-
* MISSING_MESSAGE. Objects merge recursively; leaves in `override` win. */
|
|
62
|
-
export function deepMergeMessages(base, override) {
|
|
63
|
-
const out = { ...base };
|
|
64
|
-
for (const [key, value] of Object.entries(override)) {
|
|
65
|
-
const current = out[key];
|
|
66
|
-
if (value &&
|
|
67
|
-
typeof value === 'object' &&
|
|
68
|
-
!Array.isArray(value) &&
|
|
69
|
-
current &&
|
|
70
|
-
typeof current === 'object' &&
|
|
71
|
-
!Array.isArray(current)) {
|
|
72
|
-
out[key] = deepMergeMessages(current, value);
|
|
73
|
-
}
|
|
74
|
-
else {
|
|
75
|
-
out[key] = value;
|
|
76
|
-
}
|
|
77
|
-
}
|
|
78
|
-
return out;
|
|
79
|
-
}
|
|
@@ -1,17 +0,0 @@
|
|
|
1
|
-
export interface LabelBundle {
|
|
2
|
-
dictionaries: Record<string, Record<string, string>>;
|
|
3
|
-
defaultLocale: string;
|
|
4
|
-
locales: string[];
|
|
5
|
-
updatedAt: string | null;
|
|
6
|
-
}
|
|
7
|
-
export declare const API_URL_ENV_KEYS: readonly ["VITE_API_URL", "NEXT_PUBLIC_FIMO_API_URL", "NUXT_PUBLIC_FIMO_API_URL", "PUBLIC_FIMO_API_URL", "FIMO_API_URL"];
|
|
8
|
-
export declare function resolveTenantApiUrl(env: Record<string, string>): string | undefined;
|
|
9
|
-
export declare function serializeBundle(bundle: LabelBundle): string;
|
|
10
|
-
export declare function fetchLabelDictionary(apiUrl: string, locale: string): Promise<{
|
|
11
|
-
locale: string;
|
|
12
|
-
labels: Record<string, string>;
|
|
13
|
-
updatedAt: string | null;
|
|
14
|
-
}>;
|
|
15
|
-
export declare function fetchLabelBundle(apiUrl: string, locales: string[], defaultLocale: string): Promise<LabelBundle>;
|
|
16
|
-
export declare function fetchWithTimeout(url: URL, timeoutMs: number): Promise<Response>;
|
|
17
|
-
//# sourceMappingURL=labels-fetch.pure.d.ts.map
|
|
@@ -1,71 +0,0 @@
|
|
|
1
|
-
// Label fetching, PURE core: fetch/URL/AbortController only — no node
|
|
2
|
-
// builtins, importable from client-bundled code (Nuxt composables, universal
|
|
3
|
-
// SFCs). The node-flavored wrapper (env-file reading, config, signatures)
|
|
4
|
-
// lives in labels-fetch.server.ts.
|
|
5
|
-
// The tenant API base URL env var, in framework flavor order.
|
|
6
|
-
export const API_URL_ENV_KEYS = [
|
|
7
|
-
'VITE_API_URL',
|
|
8
|
-
'NEXT_PUBLIC_FIMO_API_URL',
|
|
9
|
-
'NUXT_PUBLIC_FIMO_API_URL',
|
|
10
|
-
'PUBLIC_FIMO_API_URL',
|
|
11
|
-
'FIMO_API_URL',
|
|
12
|
-
];
|
|
13
|
-
export function resolveTenantApiUrl(env) {
|
|
14
|
-
for (const key of API_URL_ENV_KEYS) {
|
|
15
|
-
if (env[key]) {
|
|
16
|
-
return env[key];
|
|
17
|
-
}
|
|
18
|
-
}
|
|
19
|
-
return undefined;
|
|
20
|
-
}
|
|
21
|
-
export function serializeBundle(bundle) {
|
|
22
|
-
return JSON.stringify({
|
|
23
|
-
dictionaries: bundle.dictionaries,
|
|
24
|
-
defaultLocale: bundle.defaultLocale,
|
|
25
|
-
locales: bundle.locales,
|
|
26
|
-
});
|
|
27
|
-
}
|
|
28
|
-
export async function fetchLabelDictionary(apiUrl, locale) {
|
|
29
|
-
const url = new URL('/labels', apiUrl);
|
|
30
|
-
url.searchParams.set('locale', locale);
|
|
31
|
-
const response = await fetchWithTimeout(url, 4000);
|
|
32
|
-
if (!response.ok) {
|
|
33
|
-
return { locale, labels: {}, updatedAt: null };
|
|
34
|
-
}
|
|
35
|
-
const body = (await response.json());
|
|
36
|
-
if (Array.isArray(body.data)) {
|
|
37
|
-
return {
|
|
38
|
-
locale,
|
|
39
|
-
labels: Object.fromEntries(body.data.map((row) => [row.key, row.value ?? row.defaultValue ?? ''])),
|
|
40
|
-
updatedAt: null,
|
|
41
|
-
};
|
|
42
|
-
}
|
|
43
|
-
return {
|
|
44
|
-
locale,
|
|
45
|
-
labels: body.data?.labels ?? {},
|
|
46
|
-
updatedAt: body.data?.updatedAt ?? null,
|
|
47
|
-
};
|
|
48
|
-
}
|
|
49
|
-
export async function fetchLabelBundle(apiUrl, locales, defaultLocale) {
|
|
50
|
-
const loaded = await Promise.all(locales.map((locale) => fetchLabelDictionary(apiUrl, locale).catch(() => ({ locale, labels: {}, updatedAt: null }))));
|
|
51
|
-
const sortedUpdatedAt = loaded
|
|
52
|
-
.map((entry) => entry.updatedAt)
|
|
53
|
-
.filter((value) => typeof value === 'string')
|
|
54
|
-
.sort();
|
|
55
|
-
return {
|
|
56
|
-
dictionaries: Object.fromEntries(loaded.map((entry) => [entry.locale, entry.labels])),
|
|
57
|
-
defaultLocale,
|
|
58
|
-
locales,
|
|
59
|
-
updatedAt: sortedUpdatedAt[sortedUpdatedAt.length - 1] ?? null,
|
|
60
|
-
};
|
|
61
|
-
}
|
|
62
|
-
export async function fetchWithTimeout(url, timeoutMs) {
|
|
63
|
-
const controller = new AbortController();
|
|
64
|
-
const timeout = setTimeout(() => controller.abort(), timeoutMs);
|
|
65
|
-
try {
|
|
66
|
-
return await fetch(url, { signal: controller.signal });
|
|
67
|
-
}
|
|
68
|
-
finally {
|
|
69
|
-
clearTimeout(timeout);
|
|
70
|
-
}
|
|
71
|
-
}
|
|
@@ -1,7 +0,0 @@
|
|
|
1
|
-
import { type LabelBundle } from './labels-fetch.pure.js';
|
|
2
|
-
export { API_URL_ENV_KEYS, fetchLabelBundle, fetchLabelDictionary, fetchWithTimeout, resolveTenantApiUrl, serializeBundle, type LabelBundle, } from './labels-fetch.pure.js';
|
|
3
|
-
export declare function labelSignature(bundle: LabelBundle): string;
|
|
4
|
-
export declare function emptyLabelBundle(rootDir: string): LabelBundle;
|
|
5
|
-
export declare function loadLabelBundle(rootDir: string): Promise<LabelBundle>;
|
|
6
|
-
export declare function readProjectEnv(rootDir: string): Promise<Record<string, string>>;
|
|
7
|
-
//# sourceMappingURL=labels-fetch.server.d.ts.map
|
|
@@ -1,97 +0,0 @@
|
|
|
1
|
-
// Framework-neutral label fetching (server-side). Shared by the Vite
|
|
2
|
-
// translations plugin (fimo/vite) and the Next server loaders (fimo/next/server).
|
|
3
|
-
// The fetch core is in labels-fetch.pure.ts (client-safe); this module adds
|
|
4
|
-
// the node-flavored parts: env-file reading, config resolution, signatures.
|
|
5
|
-
import { Buffer } from 'node:buffer';
|
|
6
|
-
import { readFile } from 'node:fs/promises';
|
|
7
|
-
import { join } from 'node:path';
|
|
8
|
-
import { getFimoDefaultLocale, getFimoLocales } from '../i18n/locale.js';
|
|
9
|
-
import { getConfigServer } from './fimo-config.server.js';
|
|
10
|
-
import { API_URL_ENV_KEYS, fetchLabelBundle, serializeBundle, } from './labels-fetch.pure.js';
|
|
11
|
-
export { API_URL_ENV_KEYS, fetchLabelBundle, fetchLabelDictionary, fetchWithTimeout, resolveTenantApiUrl, serializeBundle, } from './labels-fetch.pure.js';
|
|
12
|
-
export function labelSignature(bundle) {
|
|
13
|
-
return Buffer.from(serializeBundle(bundle)).toString('base64');
|
|
14
|
-
}
|
|
15
|
-
export function emptyLabelBundle(rootDir) {
|
|
16
|
-
const config = getConfigServer(rootDir);
|
|
17
|
-
const defaultLocale = getFimoDefaultLocale(config);
|
|
18
|
-
const locales = getFimoLocales(config);
|
|
19
|
-
return {
|
|
20
|
-
dictionaries: Object.fromEntries(locales.map((locale) => [locale, {}])),
|
|
21
|
-
defaultLocale,
|
|
22
|
-
locales,
|
|
23
|
-
updatedAt: null,
|
|
24
|
-
};
|
|
25
|
-
}
|
|
26
|
-
export async function loadLabelBundle(rootDir) {
|
|
27
|
-
const config = getConfigServer(rootDir);
|
|
28
|
-
const defaultLocale = getFimoDefaultLocale(config);
|
|
29
|
-
const locales = getFimoLocales(config);
|
|
30
|
-
const env = await readProjectEnv(rootDir);
|
|
31
|
-
const apiUrl = resolveTenantApiUrlFromEnv(env);
|
|
32
|
-
if (!apiUrl) {
|
|
33
|
-
return {
|
|
34
|
-
dictionaries: Object.fromEntries(locales.map((locale) => [locale, {}])),
|
|
35
|
-
defaultLocale,
|
|
36
|
-
locales,
|
|
37
|
-
updatedAt: null,
|
|
38
|
-
};
|
|
39
|
-
}
|
|
40
|
-
try {
|
|
41
|
-
return await fetchLabelBundle(apiUrl, locales, defaultLocale);
|
|
42
|
-
}
|
|
43
|
-
catch {
|
|
44
|
-
return {
|
|
45
|
-
dictionaries: Object.fromEntries(locales.map((locale) => [locale, {}])),
|
|
46
|
-
defaultLocale,
|
|
47
|
-
locales,
|
|
48
|
-
updatedAt: null,
|
|
49
|
-
};
|
|
50
|
-
}
|
|
51
|
-
}
|
|
52
|
-
function resolveTenantApiUrlFromEnv(env) {
|
|
53
|
-
for (const key of API_URL_ENV_KEYS) {
|
|
54
|
-
if (env[key]) {
|
|
55
|
-
return env[key];
|
|
56
|
-
}
|
|
57
|
-
}
|
|
58
|
-
return undefined;
|
|
59
|
-
}
|
|
60
|
-
export async function readProjectEnv(rootDir) {
|
|
61
|
-
const values = {
|
|
62
|
-
...(await readEnvFile(join(rootDir, '.env'))),
|
|
63
|
-
...(await readEnvFile(join(rootDir, '.env.local'))),
|
|
64
|
-
};
|
|
65
|
-
for (const key of API_URL_ENV_KEYS) {
|
|
66
|
-
if (process.env[key]) {
|
|
67
|
-
values[key] = process.env[key];
|
|
68
|
-
}
|
|
69
|
-
}
|
|
70
|
-
return values;
|
|
71
|
-
}
|
|
72
|
-
async function readEnvFile(filePath) {
|
|
73
|
-
try {
|
|
74
|
-
const content = await readFile(filePath, 'utf8');
|
|
75
|
-
const values = {};
|
|
76
|
-
for (const line of content.split(/\r?\n/)) {
|
|
77
|
-
const trimmed = line.trim();
|
|
78
|
-
if (!trimmed || trimmed.startsWith('#')) {
|
|
79
|
-
continue;
|
|
80
|
-
}
|
|
81
|
-
const index = trimmed.indexOf('=');
|
|
82
|
-
if (index === -1) {
|
|
83
|
-
continue;
|
|
84
|
-
}
|
|
85
|
-
const key = trimmed.slice(0, index).trim();
|
|
86
|
-
const value = trimmed
|
|
87
|
-
.slice(index + 1)
|
|
88
|
-
.trim()
|
|
89
|
-
.replace(/^['"]|['"]$/g, '');
|
|
90
|
-
values[key] = value;
|
|
91
|
-
}
|
|
92
|
-
return values;
|
|
93
|
-
}
|
|
94
|
-
catch {
|
|
95
|
-
return {};
|
|
96
|
-
}
|
|
97
|
-
}
|
|
@@ -1,10 +0,0 @@
|
|
|
1
|
-
// Pure label accessor factory — no node builtins, no framework imports.
|
|
2
|
-
// Template frameworks load the dictionary server-side (+layout.server.ts
|
|
3
|
-
// load, a Vue data service, …), hand it to components through their own
|
|
4
|
-
// data flow, and build `t` locally:
|
|
5
|
-
//
|
|
6
|
-
// const t = makeFimoT(data.fimoLabels);
|
|
7
|
-
// <h1 {...fimoLabelAttrs('hero.title')}>{t('hero.title', 'Fallback')}</h1>
|
|
8
|
-
export function makeFimoT(labels) {
|
|
9
|
-
return (key, fallback = '') => labels?.[key] ?? fallback;
|
|
10
|
-
}
|
|
@@ -1,30 +0,0 @@
|
|
|
1
|
-
import { type FimoServerOptions } from '../wrapped/server.js';
|
|
2
|
-
import { type FieldShapes } from './reshape.js';
|
|
3
|
-
export interface FimoStrapiFetchOptions extends FimoServerOptions {
|
|
4
|
-
/** Strapi REST path segment (pluralName) → Fimo type name. */
|
|
5
|
-
types: Record<string, string>;
|
|
6
|
-
/** Fimo type names that are single types (served as `{data:{}}`, not a list). */
|
|
7
|
-
singletons?: string[];
|
|
8
|
-
/** Path prefix before the content segment (Strapi default `/api`). */
|
|
9
|
-
apiPrefix?: string;
|
|
10
|
-
/** Per-type structural field-shape manifest (single-component unwrap + block
|
|
11
|
-
* item reshaping). Baked by the migration; absent → pure value-inspection. */
|
|
12
|
-
fieldShapes?: Record<string, FieldShapes>;
|
|
13
|
-
}
|
|
14
|
-
export interface ParsedStrapiUrl {
|
|
15
|
-
pluralName: string;
|
|
16
|
-
documentId?: string;
|
|
17
|
-
locale?: string;
|
|
18
|
-
pageSize?: number;
|
|
19
|
-
where?: Record<string, unknown>;
|
|
20
|
-
}
|
|
21
|
-
/**
|
|
22
|
-
* Parse a Strapi REST URL into the parts the Fimo read needs. Best-effort:
|
|
23
|
-
* pagination + locale + top-level `$eq` filters (enough for list, detail-by-slug,
|
|
24
|
-
* and singleton reads); richer `qs` filters are ignored (fake content tolerates
|
|
25
|
-
* an unfiltered set).
|
|
26
|
-
*/
|
|
27
|
-
export declare function parseStrapiUrl(rawUrl: string, apiPrefix?: string): ParsedStrapiUrl | null;
|
|
28
|
-
/** Build a `fetch`-compatible function that serves scaffolded content from Fimo. */
|
|
29
|
-
export declare function createFimoStrapiFetch(options: FimoStrapiFetchOptions, realFetch?: typeof fetch): typeof fetch;
|
|
30
|
-
//# sourceMappingURL=client.d.ts.map
|
|
@@ -1,89 +0,0 @@
|
|
|
1
|
-
// Native `fimo/strapi` read adapter — the generic interception point. Every
|
|
2
|
-
// Strapi client (hand-rolled or `@strapi/client`) ultimately does a single
|
|
3
|
-
// `fetch(<strapiRestUrl>)`; this returns a `fetch`-compatible function that
|
|
4
|
-
// serves that request from Fimo (via `fetchEntries` + the reshape) when the URL
|
|
5
|
-
// maps to a scaffolded content type, and transparently falls through to the
|
|
6
|
-
// real fetch (the live CMS) otherwise. Intercepting at the fetch boundary is
|
|
7
|
-
// what makes "any Strapi project" work — the client's method surface is
|
|
8
|
-
// irrelevant.
|
|
9
|
-
import { fetchEntries } from '../wrapped/server.js';
|
|
10
|
-
import { fimoEntriesToStrapiList, fimoEntryToStrapiSingle } from './reshape.js';
|
|
11
|
-
function positiveInt(value) {
|
|
12
|
-
if (value == null) {
|
|
13
|
-
return undefined;
|
|
14
|
-
}
|
|
15
|
-
const n = Number(value);
|
|
16
|
-
return Number.isFinite(n) && n > 0 ? n : undefined;
|
|
17
|
-
}
|
|
18
|
-
/**
|
|
19
|
-
* Parse a Strapi REST URL into the parts the Fimo read needs. Best-effort:
|
|
20
|
-
* pagination + locale + top-level `$eq` filters (enough for list, detail-by-slug,
|
|
21
|
-
* and singleton reads); richer `qs` filters are ignored (fake content tolerates
|
|
22
|
-
* an unfiltered set).
|
|
23
|
-
*/
|
|
24
|
-
export function parseStrapiUrl(rawUrl, apiPrefix = '/api') {
|
|
25
|
-
let url;
|
|
26
|
-
try {
|
|
27
|
-
url = new URL(rawUrl, 'http://fimo.local');
|
|
28
|
-
}
|
|
29
|
-
catch {
|
|
30
|
-
return null;
|
|
31
|
-
}
|
|
32
|
-
const marker = `${apiPrefix}/`;
|
|
33
|
-
const at = url.pathname.indexOf(marker);
|
|
34
|
-
if (at === -1) {
|
|
35
|
-
return null;
|
|
36
|
-
}
|
|
37
|
-
const segments = url.pathname
|
|
38
|
-
.slice(at + marker.length)
|
|
39
|
-
.split('/')
|
|
40
|
-
.filter(Boolean);
|
|
41
|
-
if (segments.length === 0) {
|
|
42
|
-
return null;
|
|
43
|
-
}
|
|
44
|
-
const q = url.searchParams;
|
|
45
|
-
const where = {};
|
|
46
|
-
for (const [key, value] of q.entries()) {
|
|
47
|
-
const eq = key.match(/^filters\[([^\][]+)\]\[\$eq\]$/);
|
|
48
|
-
if (eq) {
|
|
49
|
-
where[eq[1]] = value;
|
|
50
|
-
}
|
|
51
|
-
}
|
|
52
|
-
return {
|
|
53
|
-
pluralName: segments[0],
|
|
54
|
-
documentId: segments[1],
|
|
55
|
-
locale: q.get('locale') ?? undefined,
|
|
56
|
-
pageSize: positiveInt(q.get('pagination[pageSize]') ?? q.get('pagination[limit]')),
|
|
57
|
-
where: Object.keys(where).length > 0 ? where : undefined,
|
|
58
|
-
};
|
|
59
|
-
}
|
|
60
|
-
/** Build a `fetch`-compatible function that serves scaffolded content from Fimo. */
|
|
61
|
-
export function createFimoStrapiFetch(options, realFetch = fetch) {
|
|
62
|
-
const singletons = new Set(options.singletons ?? []);
|
|
63
|
-
const read = { apiUrl: options.apiUrl, rootDir: options.rootDir };
|
|
64
|
-
return (async (input, init) => {
|
|
65
|
-
const rawUrl = typeof input === 'string' ? input : input instanceof URL ? input.href : input.url;
|
|
66
|
-
const parsed = parseStrapiUrl(rawUrl ?? '', options.apiPrefix);
|
|
67
|
-
const typeName = parsed ? options.types[parsed.pluralName] : undefined;
|
|
68
|
-
if (!parsed || !typeName) {
|
|
69
|
-
return realFetch(input, init); // not a mapped content read → real CMS
|
|
70
|
-
}
|
|
71
|
-
const entries = await fetchEntries(typeName, {
|
|
72
|
-
...read,
|
|
73
|
-
...(parsed.where ? { where: parsed.where } : {}),
|
|
74
|
-
...(parsed.pageSize ? { limit: parsed.pageSize } : {}),
|
|
75
|
-
});
|
|
76
|
-
if (entries.length === 0) {
|
|
77
|
-
return realFetch(input, init); // nothing scaffolded → fall back to the CMS
|
|
78
|
-
}
|
|
79
|
-
const shapes = options.fieldShapes?.[typeName];
|
|
80
|
-
const single = Boolean(parsed.documentId) || singletons.has(typeName);
|
|
81
|
-
const payload = single
|
|
82
|
-
? fimoEntryToStrapiSingle(parsed.documentId ? (entries.find((e) => e.id === parsed.documentId) ?? entries[0]) : entries[0], shapes)
|
|
83
|
-
: fimoEntriesToStrapiList(entries, { pageSize: parsed.pageSize, total: entries.length, shapes });
|
|
84
|
-
return new Response(JSON.stringify(payload), {
|
|
85
|
-
status: 200,
|
|
86
|
-
headers: { 'content-type': 'application/json' },
|
|
87
|
-
});
|
|
88
|
-
});
|
|
89
|
-
}
|
|
@@ -1,5 +0,0 @@
|
|
|
1
|
-
export { createFimoStrapiFetch, parseStrapiUrl } from './client.js';
|
|
2
|
-
export type { FimoStrapiFetchOptions, ParsedStrapiUrl } from './client.js';
|
|
3
|
-
export { fimoEntriesToStrapiList, fimoEntryToStrapiItem, fimoEntryToStrapiSingle } from './reshape.js';
|
|
4
|
-
export type { StrapiListEnvelope, StrapiPagination, StrapiSingleEnvelope } from './reshape.js';
|
|
5
|
-
//# sourceMappingURL=index.d.ts.map
|
|
@@ -1,6 +0,0 @@
|
|
|
1
|
-
// `fimo/strapi` — native read adapter that serves a wrapped Strapi frontend's
|
|
2
|
-
// content from Fimo. Drop `createFimoStrapiFetch(...)` in at the CMS client's
|
|
3
|
-
// single `fetch` and every read (list, detail, singleton) is served from Fimo,
|
|
4
|
-
// falling through to the live CMS for anything not scaffolded.
|
|
5
|
-
export { createFimoStrapiFetch, parseStrapiUrl } from './client.js';
|
|
6
|
-
export { fimoEntriesToStrapiList, fimoEntryToStrapiItem, fimoEntryToStrapiSingle } from './reshape.js';
|
|
@@ -1,46 +0,0 @@
|
|
|
1
|
-
import type { FimoEntry } from '../wrapped/server.js';
|
|
2
|
-
export interface StrapiPagination {
|
|
3
|
-
page: number;
|
|
4
|
-
pageSize: number;
|
|
5
|
-
pageCount: number;
|
|
6
|
-
total: number;
|
|
7
|
-
}
|
|
8
|
-
export interface StrapiListEnvelope {
|
|
9
|
-
data: Record<string, unknown>[];
|
|
10
|
-
meta: {
|
|
11
|
-
pagination: StrapiPagination;
|
|
12
|
-
};
|
|
13
|
-
}
|
|
14
|
-
export interface StrapiSingleEnvelope {
|
|
15
|
-
data: Record<string, unknown> | null;
|
|
16
|
-
meta: Record<string, unknown>;
|
|
17
|
-
}
|
|
18
|
-
/**
|
|
19
|
-
* The field-shape manifest, baked into the generated `fimo-strapi.ts` by the
|
|
20
|
-
* migration. It carries ONLY the structural (blocks) fields — a Fimo blocks
|
|
21
|
-
* value is always an array, but a Strapi single component reads as an object, so
|
|
22
|
-
* the reshape needs to know which array to unwrap. Leaf fields (scalars, media,
|
|
23
|
-
* richtext, references) never appear here; they are handled by value-inspection.
|
|
24
|
-
* Kept in sync with `computeFieldShapes` in `scripts/cms/strapi/shapes.ts`.
|
|
25
|
-
*/
|
|
26
|
-
export interface BlockFieldShape {
|
|
27
|
-
/** 'single' → unwrap the one-item array to an object; 'list' → keep the array. */
|
|
28
|
-
readonly kind: 'single' | 'list';
|
|
29
|
-
/** Nested structural shapes for a fixed-shape block (single/repeatable component). */
|
|
30
|
-
readonly fields?: FieldShapes;
|
|
31
|
-
/** Per-variant nested shapes for a dynamic zone, keyed by variant uid (`_type`). */
|
|
32
|
-
readonly variants?: Record<string, FieldShapes>;
|
|
33
|
-
}
|
|
34
|
-
export type FieldShapes = Record<string, BlockFieldShape>;
|
|
35
|
-
/** One Fimo entry → one Strapi-v5 item (`id` + `documentId` + reshaped fields). */
|
|
36
|
-
export declare function fimoEntryToStrapiItem(entry: FimoEntry, shapes?: FieldShapes): Record<string, unknown>;
|
|
37
|
-
/** Fimo entries → a Strapi-v5 collection envelope (`{ data, meta.pagination }`). */
|
|
38
|
-
export declare function fimoEntriesToStrapiList(entries: FimoEntry[], options?: {
|
|
39
|
-
page?: number;
|
|
40
|
-
pageSize?: number;
|
|
41
|
-
total?: number;
|
|
42
|
-
shapes?: FieldShapes;
|
|
43
|
-
}): StrapiListEnvelope;
|
|
44
|
-
/** Fimo entry → a Strapi-v5 single/singleton envelope (`{ data, meta }`). */
|
|
45
|
-
export declare function fimoEntryToStrapiSingle(entry: FimoEntry | undefined, shapes?: FieldShapes): StrapiSingleEnvelope;
|
|
46
|
-
//# sourceMappingURL=reshape.d.ts.map
|
|
@@ -1,114 +0,0 @@
|
|
|
1
|
-
// Native `fimo/strapi` read adapter — the generic reshape from Fimo entries
|
|
2
|
-
// (as `fetchEntries` returns them) into the Strapi-v5 response shape a wrapped
|
|
3
|
-
// Strapi frontend's components read. Value-inspection based, so it is generic:
|
|
4
|
-
// no content-type or field names are hardcoded. Relation POPULATION (turning a
|
|
5
|
-
// reference handle into a nested object) is resolved one level up in the client
|
|
6
|
-
// — this module reshapes a single entry's own field values.
|
|
7
|
-
/** ProseMirror/TipTap doc → plain text (Strapi richtext is a string; string
|
|
8
|
-
* consumers like read-time estimates must never receive an object). */
|
|
9
|
-
function docToText(node) {
|
|
10
|
-
if (!node || typeof node !== 'object') {
|
|
11
|
-
return '';
|
|
12
|
-
}
|
|
13
|
-
const record = node;
|
|
14
|
-
if (typeof record.text === 'string') {
|
|
15
|
-
return record.text;
|
|
16
|
-
}
|
|
17
|
-
if (Array.isArray(record.content)) {
|
|
18
|
-
return record.content.map(docToText).join(' ').trim();
|
|
19
|
-
}
|
|
20
|
-
return '';
|
|
21
|
-
}
|
|
22
|
-
function isRichTextDoc(value) {
|
|
23
|
-
return Boolean(value) && typeof value === 'object' && value.type === 'doc';
|
|
24
|
-
}
|
|
25
|
-
/** A Fimo media value (seeded/loaded as `{ url, alt, width, height, … }`). */
|
|
26
|
-
function isMedia(value) {
|
|
27
|
-
return (Boolean(value) &&
|
|
28
|
-
typeof value === 'object' &&
|
|
29
|
-
!Array.isArray(value) &&
|
|
30
|
-
typeof value.url === 'string');
|
|
31
|
-
}
|
|
32
|
-
function reshapeMedia(value) {
|
|
33
|
-
return {
|
|
34
|
-
url: value.url,
|
|
35
|
-
alternativeText: value.alt ?? value.alternativeText ?? null,
|
|
36
|
-
...(typeof value.width === 'number' ? { width: value.width } : {}),
|
|
37
|
-
...(typeof value.height === 'number' ? { height: value.height } : {}),
|
|
38
|
-
...(typeof value.name === 'string' ? { name: value.name } : {}),
|
|
39
|
-
...(typeof value.mime === 'string' ? { mime: value.mime } : {}),
|
|
40
|
-
};
|
|
41
|
-
}
|
|
42
|
-
function reshapeValue(value) {
|
|
43
|
-
if (isRichTextDoc(value)) {
|
|
44
|
-
return docToText(value);
|
|
45
|
-
}
|
|
46
|
-
if (isMedia(value)) {
|
|
47
|
-
return reshapeMedia(value);
|
|
48
|
-
}
|
|
49
|
-
return value; // scalars, arrays, reference handles pass through
|
|
50
|
-
}
|
|
51
|
-
/** Reshape one block item: drop Fimo-internal `_key`/`_type` (exposing `_key` as
|
|
52
|
-
* Strapi's `id`), keep everything else — `__component` passes through, nested
|
|
53
|
-
* structural fields recurse per the manifest, leaves are value-inspected. */
|
|
54
|
-
function reshapeBlockItem(item, shape) {
|
|
55
|
-
if (!item || typeof item !== 'object' || Array.isArray(item)) {
|
|
56
|
-
return {};
|
|
57
|
-
}
|
|
58
|
-
const record = item;
|
|
59
|
-
const nested = shape.variants ? (shape.variants[String(record._type)] ?? {}) : (shape.fields ?? {});
|
|
60
|
-
const out = {};
|
|
61
|
-
if (typeof record._key === 'string') {
|
|
62
|
-
out.id = record._key;
|
|
63
|
-
}
|
|
64
|
-
for (const [key, value] of Object.entries(record)) {
|
|
65
|
-
if (key === '_key' || key === '_type') {
|
|
66
|
-
continue;
|
|
67
|
-
}
|
|
68
|
-
out[key] = reshapeFieldValue(value, nested[key]);
|
|
69
|
-
}
|
|
70
|
-
return out;
|
|
71
|
-
}
|
|
72
|
-
/** Reshape a single field value against its manifest entry. No entry → a leaf. */
|
|
73
|
-
function reshapeFieldValue(value, shape) {
|
|
74
|
-
if (!shape) {
|
|
75
|
-
return reshapeValue(value);
|
|
76
|
-
}
|
|
77
|
-
const items = Array.isArray(value) ? value : [];
|
|
78
|
-
if (shape.kind === 'single') {
|
|
79
|
-
return items.length > 0 ? reshapeBlockItem(items[0], shape) : null;
|
|
80
|
-
}
|
|
81
|
-
return items.map((item) => reshapeBlockItem(item, shape));
|
|
82
|
-
}
|
|
83
|
-
function reshapeEntryData(data, shapes) {
|
|
84
|
-
const out = {};
|
|
85
|
-
for (const [field, value] of Object.entries(data)) {
|
|
86
|
-
out[field] = reshapeFieldValue(value, shapes[field]);
|
|
87
|
-
}
|
|
88
|
-
return out;
|
|
89
|
-
}
|
|
90
|
-
/** One Fimo entry → one Strapi-v5 item (`id` + `documentId` + reshaped fields). */
|
|
91
|
-
export function fimoEntryToStrapiItem(entry, shapes = {}) {
|
|
92
|
-
return { id: entry.id, documentId: entry.id, ...reshapeEntryData(entry.data, shapes) };
|
|
93
|
-
}
|
|
94
|
-
/** Fimo entries → a Strapi-v5 collection envelope (`{ data, meta.pagination }`). */
|
|
95
|
-
export function fimoEntriesToStrapiList(entries, options = {}) {
|
|
96
|
-
const data = entries.map((entry) => fimoEntryToStrapiItem(entry, options.shapes));
|
|
97
|
-
const total = options.total ?? data.length;
|
|
98
|
-
const pageSize = options.pageSize ?? (data.length || 1);
|
|
99
|
-
return {
|
|
100
|
-
data,
|
|
101
|
-
meta: {
|
|
102
|
-
pagination: {
|
|
103
|
-
page: options.page ?? 1,
|
|
104
|
-
pageSize,
|
|
105
|
-
pageCount: pageSize > 0 ? Math.max(1, Math.ceil(total / pageSize)) : 1,
|
|
106
|
-
total,
|
|
107
|
-
},
|
|
108
|
-
},
|
|
109
|
-
};
|
|
110
|
-
}
|
|
111
|
-
/** Fimo entry → a Strapi-v5 single/singleton envelope (`{ data, meta }`). */
|
|
112
|
-
export function fimoEntryToStrapiSingle(entry, shapes = {}) {
|
|
113
|
-
return { data: entry ? fimoEntryToStrapiItem(entry, shapes) : null, meta: {} };
|
|
114
|
-
}
|
|
@@ -1,7 +0,0 @@
|
|
|
1
|
-
// fimo/svelte — CLIENT-SAFE surface for .svelte components (they hydrate:
|
|
2
|
-
// importing the server barrel here pulls node builtins into the client
|
|
3
|
-
// bundle, the exact class the Nuxt dogfood broke on). Server loaders live in
|
|
4
|
-
// fimo/svelte/server, called from +layout.server.ts / +page.server.ts —
|
|
5
|
-
// SvelteKit's own server-only convention.
|
|
6
|
-
export { fimoLabelAttrs, fimoMediaAttrs, FIMO_SOURCE_ATTR } from '../wrapped/dom.js';
|
|
7
|
-
export { makeFimoT } from '../shared/labels-t.pure.js';
|