fimo 0.8.0 → 0.9.0-experimental.10
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/content-translator/scripts/write-locale-files.ts +66 -0
- package/assets/agents-starter/claude/settings.json +19 -0
- package/assets/content-templates/hooks-template.eta +4 -2
- package/assets/content-templates/types-template.eta +67 -1
- package/assets/skills/fimo/SKILL.md +1 -1
- package/assets/skills/fimo/references/content.md +86 -15
- package/assets/skills/fimo/references/translations.md +75 -0
- package/assets/skills/fimo-cli/SKILL.md +6 -2
- package/assets/skills/fimo-cli/references/content.md +61 -2
- package/assets/skills/fimo-cli/references/integrations/firecrawl.md +20 -4
- package/assets/skills/fimo-cli/references/integrations.md +5 -1
- package/assets/skills/fimo-cli/references/webhooks.md +162 -0
- package/assets/skills/fimo-migration/SKILL.md +24 -2
- package/assets/skills/fimo-migration/references/content.md +24 -0
- package/assets/skills/fimo-migration/references/manifest.md +46 -0
- package/assets/skills/fimo-migration/references/pages.md +234 -0
- package/assets/skills/fimo-migration/references/qa.md +24 -2
- package/assets/skills/fimo-migration/references/workflow.md +12 -1
- package/assets/skills/fimo-migration/references/wrap.md +662 -0
- package/assets/skills/fimo-studio/SKILL.md +5 -3
- package/assets/skills/fimo-studio/references/cms.md +9 -1
- package/assets/skills/fimo-studio/references/visual-editing.md +5 -3
- package/assets/skills/fimo-studio/references/webhooks.md +42 -0
- package/dist/build/vite/plugins/translations.d.ts +2 -8
- package/dist/build/vite/plugins/translations.js +31 -131
- package/dist/cli/bundle.json +1 -1
- package/dist/cli/index.js +1337 -189
- package/dist/runtime/app/FimoProviders.d.ts +1 -11
- package/dist/runtime/app/FimoProviders.js +31 -30
- package/dist/runtime/astro/index.d.ts +2 -0
- package/dist/runtime/astro/index.js +3 -0
- package/dist/runtime/astro/server.d.ts +3 -0
- package/dist/runtime/astro/server.js +6 -0
- package/dist/runtime/content/client/internal.js +1 -0
- package/dist/runtime/content/client/types.d.ts +16 -0
- package/dist/runtime/core/BuiltWithBadge.d.ts +2 -0
- package/dist/runtime/core/BuiltWithBadge.js +68 -0
- package/dist/runtime/i18n/FimoLocaleContext.js +1 -0
- package/dist/runtime/next/FimoNextProviders.d.ts +24 -0
- package/dist/runtime/next/FimoNextProviders.js +20 -0
- package/dist/runtime/next/FimoNextScripts.d.ts +10 -0
- package/dist/runtime/next/FimoNextScripts.js +19 -0
- package/dist/runtime/next/index.d.ts +14 -0
- package/dist/runtime/next/index.js +14 -0
- package/dist/runtime/next/server.d.ts +2 -0
- package/dist/runtime/next/server.js +4 -0
- package/dist/runtime/nuxt/composables.d.ts +9 -0
- package/dist/runtime/nuxt/composables.js +57 -0
- package/dist/runtime/nuxt/index.d.ts +3 -0
- package/dist/runtime/nuxt/index.js +6 -0
- package/dist/runtime/nuxt/server.d.ts +3 -0
- package/dist/runtime/nuxt/server.js +6 -0
- package/dist/runtime/primitives/components/RichText.d.ts +5 -1
- package/dist/runtime/primitives/components/RichText.js +2 -2
- package/dist/runtime/primitives/labels-hook.d.ts +33 -0
- package/dist/runtime/primitives/labels-hook.js +41 -0
- package/dist/runtime/primitives/labels-store.d.ts +12 -0
- package/dist/runtime/primitives/labels-store.js +60 -0
- package/dist/runtime/primitives/lib/useFimoLiveValue.d.ts +5 -3
- package/dist/runtime/primitives/lib/useFimoLiveValue.js +11 -4
- package/dist/runtime/primitives/translations.d.ts +1 -27
- package/dist/runtime/primitives/translations.js +7 -85
- package/dist/runtime/react/FimoSpaProviders.d.ts +36 -0
- package/dist/runtime/react/FimoSpaProviders.js +122 -0
- package/dist/runtime/react/index.d.ts +35 -0
- package/dist/runtime/react/index.js +40 -0
- package/dist/runtime/react/spa-content-cache.d.ts +38 -0
- package/dist/runtime/react/spa-content-cache.js +64 -0
- package/dist/runtime/remix/FimoRemixProviders.d.ts +16 -0
- package/dist/runtime/remix/FimoRemixProviders.js +12 -0
- package/dist/runtime/remix/FimoRemixScripts.d.ts +10 -0
- package/dist/runtime/remix/FimoRemixScripts.js +19 -0
- package/dist/runtime/remix/index.d.ts +12 -0
- package/dist/runtime/remix/index.js +11 -0
- package/dist/runtime/remix/server.d.ts +2 -0
- package/dist/runtime/remix/server.js +2 -0
- package/dist/runtime/seo/neutral.d.ts +22 -0
- package/dist/runtime/seo/neutral.js +17 -0
- package/dist/runtime/shared/display-routes.d.ts +14 -0
- package/dist/runtime/shared/display-routes.js +25 -0
- package/dist/runtime/shared/fimo-config.server.d.ts +0 -1
- package/dist/runtime/shared/fimo-config.server.js +1 -13
- package/dist/runtime/shared/fimo-config.types.d.ts +2 -0
- package/dist/runtime/shared/i18n-messages.d.ts +29 -0
- package/dist/runtime/shared/i18n-messages.js +79 -0
- package/dist/runtime/shared/labels-fetch.pure.d.ts +17 -0
- package/dist/runtime/shared/labels-fetch.pure.js +71 -0
- package/dist/runtime/shared/labels-fetch.server.d.ts +7 -0
- package/dist/runtime/shared/labels-fetch.server.js +97 -0
- package/dist/runtime/shared/labels-t.pure.d.ts +2 -0
- package/dist/runtime/shared/labels-t.pure.js +10 -0
- package/dist/runtime/strapi/client.d.ts +30 -0
- package/dist/runtime/strapi/client.js +132 -0
- package/dist/runtime/strapi/index.d.ts +5 -0
- package/dist/runtime/strapi/index.js +6 -0
- package/dist/runtime/strapi/reshape.d.ts +50 -0
- package/dist/runtime/strapi/reshape.js +121 -0
- package/dist/runtime/svelte/index.d.ts +3 -0
- package/dist/runtime/svelte/index.js +7 -0
- package/dist/runtime/svelte/server.d.ts +3 -0
- package/dist/runtime/svelte/server.js +6 -0
- package/dist/runtime/vue/index.d.ts +3 -0
- package/dist/runtime/vue/index.js +5 -0
- package/dist/runtime/vue/server.d.ts +3 -0
- package/dist/runtime/vue/server.js +6 -0
- package/dist/runtime/wrapped/FimoFileLink.d.ts +17 -0
- package/dist/runtime/wrapped/FimoFileLink.js +19 -0
- package/dist/runtime/wrapped/FimoMediaImage.d.ts +14 -0
- package/dist/runtime/wrapped/FimoMediaImage.js +20 -0
- package/dist/runtime/wrapped/FimoMediaVideo.d.ts +14 -0
- package/dist/runtime/wrapped/FimoMediaVideo.js +20 -0
- package/dist/runtime/wrapped/FimoRichTextBlock.d.ts +26 -0
- package/dist/runtime/wrapped/FimoRichTextBlock.js +22 -0
- package/dist/runtime/wrapped/FimoText.d.ts +22 -0
- package/dist/runtime/wrapped/FimoText.js +20 -0
- package/dist/runtime/wrapped/WrappedContentContext.d.ts +28 -0
- package/dist/runtime/wrapped/WrappedContentContext.js +30 -0
- package/dist/runtime/wrapped/WrappedProviders.d.ts +33 -0
- package/dist/runtime/wrapped/WrappedProviders.js +101 -0
- package/dist/runtime/wrapped/content-fetch.pure.d.ts +20 -0
- package/dist/runtime/wrapped/content-fetch.pure.js +106 -0
- package/dist/runtime/wrapped/dom.d.ts +4 -0
- package/dist/runtime/wrapped/dom.js +23 -0
- package/dist/runtime/wrapped/server.d.ts +80 -0
- package/dist/runtime/wrapped/server.js +207 -0
- package/dist/scripts/apply-wrap-templates.d.ts +61 -0
- package/dist/scripts/apply-wrap-templates.js +137 -0
- package/dist/scripts/apply-wrap.d.ts +73 -0
- package/dist/scripts/apply-wrap.js +340 -0
- package/dist/scripts/cms/discover.d.ts +2 -0
- package/dist/scripts/cms/discover.js +17 -0
- package/dist/scripts/cms/example-content.d.ts +33 -0
- package/dist/scripts/cms/example-content.js +165 -0
- package/dist/scripts/cms/strapi/detect.d.ts +2 -0
- package/dist/scripts/cms/strapi/detect.js +186 -0
- package/dist/scripts/cms/strapi/index.d.ts +3 -0
- package/dist/scripts/cms/strapi/index.js +25 -0
- package/dist/scripts/cms/strapi/list-source.d.ts +17 -0
- package/dist/scripts/cms/strapi/list-source.js +90 -0
- package/dist/scripts/cms/strapi/schema-map.d.ts +9 -0
- package/dist/scripts/cms/strapi/schema-map.js +289 -0
- package/dist/scripts/cms/strapi/shapes.d.ts +25 -0
- package/dist/scripts/cms/strapi/shapes.js +48 -0
- package/dist/scripts/cms/strapi/touchpoints.d.ts +31 -0
- package/dist/scripts/cms/strapi/touchpoints.js +145 -0
- package/dist/scripts/cms/strapi/wire-client.d.ts +15 -0
- package/dist/scripts/cms/strapi/wire-client.js +107 -0
- package/dist/scripts/cms/strapi/wiring.d.ts +17 -0
- package/dist/scripts/cms/strapi/wiring.js +46 -0
- package/dist/scripts/cms/types.d.ts +145 -0
- package/dist/scripts/cms/types.js +11 -0
- package/dist/scripts/detect-framework.d.ts +39 -0
- package/dist/scripts/detect-framework.js +139 -0
- package/dist/scripts/draft-rows.d.ts +19 -0
- package/dist/scripts/draft-rows.js +178 -0
- package/dist/scripts/draft-schemas.d.ts +27 -0
- package/dist/scripts/draft-schemas.js +247 -0
- package/dist/scripts/extract-translations.js +16 -11
- package/dist/scripts/frameworks/astro/routes.d.ts +3 -0
- package/dist/scripts/frameworks/astro/routes.js +44 -0
- package/dist/scripts/frameworks/astro/template-style.d.ts +7 -0
- package/dist/scripts/frameworks/astro/template-style.js +19 -0
- package/dist/scripts/frameworks/discover.d.ts +83 -0
- package/dist/scripts/frameworks/discover.js +288 -0
- package/dist/scripts/frameworks/discovered-route.d.ts +9 -0
- package/dist/scripts/frameworks/discovered-route.js +17 -0
- package/dist/scripts/frameworks/next/rewire.d.ts +43 -0
- package/dist/scripts/frameworks/next/rewire.js +282 -0
- package/dist/scripts/frameworks/next/routes.d.ts +30 -0
- package/dist/scripts/frameworks/next/routes.js +176 -0
- package/dist/scripts/frameworks/next/wiring.d.ts +33 -0
- package/dist/scripts/frameworks/next/wiring.js +186 -0
- package/dist/scripts/frameworks/nuxt/routes.d.ts +3 -0
- package/dist/scripts/frameworks/nuxt/routes.js +38 -0
- package/dist/scripts/frameworks/nuxt/template-style.d.ts +9 -0
- package/dist/scripts/frameworks/nuxt/template-style.js +27 -0
- package/dist/scripts/frameworks/remix/routes.d.ts +3 -0
- package/dist/scripts/frameworks/remix/routes.js +36 -0
- package/dist/scripts/frameworks/support.d.ts +75 -0
- package/dist/scripts/frameworks/support.js +165 -0
- package/dist/scripts/frameworks/sveltekit/routes.d.ts +3 -0
- package/dist/scripts/frameworks/sveltekit/routes.js +35 -0
- package/dist/scripts/frameworks/sveltekit/template-style.d.ts +9 -0
- package/dist/scripts/frameworks/sveltekit/template-style.js +54 -0
- package/dist/scripts/frameworks/vite/routes.d.ts +20 -0
- package/dist/scripts/frameworks/vite/routes.js +143 -0
- package/dist/scripts/frameworks/vite/wiring.d.ts +22 -0
- package/dist/scripts/frameworks/vite/wiring.js +144 -0
- package/dist/scripts/i18n-namespace.d.ts +27 -0
- package/dist/scripts/i18n-namespace.js +155 -0
- package/dist/scripts/lib/humanize.d.ts +14 -0
- package/dist/scripts/lib/humanize.js +54 -0
- package/dist/scripts/lib/validation-report.d.ts +1 -3
- package/dist/scripts/lib/validation-report.js +3 -9
- package/dist/scripts/render-parity/capture.d.ts +64 -0
- package/dist/scripts/render-parity/capture.js +436 -0
- package/dist/scripts/render-parity/compare.d.ts +59 -0
- package/dist/scripts/render-parity/compare.js +0 -0
- package/dist/scripts/render-parity/normalize-html.d.ts +13 -0
- package/dist/scripts/render-parity/normalize-html.js +223 -0
- package/dist/scripts/render-parity/types.d.ts +41 -0
- package/dist/scripts/render-parity/types.js +1 -0
- package/dist/scripts/routes/bindings.d.ts +93 -0
- package/dist/scripts/routes/bindings.js +117 -0
- package/dist/scripts/routes/label-prefixes.d.ts +49 -0
- package/dist/scripts/routes/label-prefixes.js +136 -0
- package/dist/scripts/routes/module-resolve.d.ts +20 -0
- package/dist/scripts/routes/module-resolve.js +78 -0
- package/dist/scripts/routes/page-structure.d.ts +72 -0
- package/dist/scripts/routes/page-structure.js +234 -0
- package/dist/scripts/scan-wrappable.d.ts +200 -0
- package/dist/scripts/scan-wrappable.js +1355 -0
- package/dist/scripts/validate-route-metadata.js +11 -0
- package/dist/scripts/validate-schemas.js +42 -1
- package/package.json +88 -4
- package/release.json +2 -2
- package/templates/react-router/package.json +1 -1
- package/templates/react-router/translations/en.json +1 -0
|
@@ -6,9 +6,9 @@ description: >-
|
|
|
6
6
|
Triggers: "in Fimo Studio", "in the dashboard", "in the web app",
|
|
7
7
|
"how do I X in the UI", or screenshots of the dashboard. Topics
|
|
8
8
|
covered: dashboard content and CMS, publishing from the dashboard,
|
|
9
|
-
dashboard SEO/domain settings, visual editing, version
|
|
10
|
-
dashboard sharing/billing, and dashboard chat. Never describe
|
|
11
|
-
locations, buttons, or panels from memory — always read the relevant
|
|
9
|
+
dashboard SEO/domain settings, outbound webhooks, visual editing, version
|
|
10
|
+
history UI, dashboard sharing/billing, and dashboard chat. Never describe
|
|
11
|
+
UI locations, buttons, or panels from memory — always read the relevant
|
|
12
12
|
reference file first.
|
|
13
13
|
---
|
|
14
14
|
|
|
@@ -37,6 +37,7 @@ Read the relevant reference file when the user's question matches a topic.
|
|
|
37
37
|
| AI Chat | references/ai-chat.md | "What can the AI do?", "How do I give context?" |
|
|
38
38
|
| Code Editor | references/code-editor.md | "How do I edit code?", "Where are my files?" |
|
|
39
39
|
| SEO Settings | references/seo-settings.md | "How do I set up SEO?", "Favicon, title, share image" |
|
|
40
|
+
| Webhooks | references/webhooks.md | "React to forms", "Send events to another service" |
|
|
40
41
|
| Preview & Responsive | references/preview-and-responsive.md | "How does it look on mobile?", "How do I test sizes?" |
|
|
41
42
|
| Visual Editing | references/visual-editing.md | "Can I edit visually?", "How do I change colors?" |
|
|
42
43
|
| Version History | references/version-history.md | "How do I undo?", "How do I go back?" |
|
|
@@ -53,6 +54,7 @@ Read the relevant reference file when the user's question matches a topic.
|
|
|
53
54
|
| Custom domains | Publishing (must publish first), Billing (premium required) |
|
|
54
55
|
| Adding images | Media Library, AI Chat, Edit Mode |
|
|
55
56
|
| Content/CMS | AI Chat, Media Library |
|
|
57
|
+
| Webhooks | Project secrets, Forms, Publishing, Agents |
|
|
56
58
|
| Visual changes | Edit Mode (quick tweaks), AI Chat (bigger changes), Code Editor (full control) |
|
|
57
59
|
| Something went wrong | Version History, AI Chat |
|
|
58
60
|
| Working with others | Sharing, Collaboration |
|
|
@@ -42,7 +42,7 @@ When you open a Collection, the entry list provides several tools:
|
|
|
42
42
|
2. Click the **+** (plus) icon button at the top of the Collection (labeled **New entry** on hover). If the Collection is empty, use the **Create entry** button in the center of the empty state instead.
|
|
43
43
|
3. Fill in the form fields (text, images, dates, whatever the collection defines).
|
|
44
44
|
|
|
45
|
-
Changes save automatically as you fill in each field (the save is debounced), so there is no Save button. The save status appears in the top bar (Saving / Saved).
|
|
45
|
+
Changes save automatically as you fill in each field (the save is debounced), so there is no Save button. The save status appears in the top bar (Saving / Saved). An incomplete new entry waits until its required fields are valid before it becomes part of the Collection. If the Collection is wired to a page on your site, new content shows up automatically.
|
|
46
46
|
|
|
47
47
|
## Editing an Entry
|
|
48
48
|
|
|
@@ -51,6 +51,14 @@ Changes save automatically as you fill in each field (the save is debounced), so
|
|
|
51
51
|
|
|
52
52
|
Your edits save automatically as you change each field (the save is debounced), so there is no Save button. Watch the top bar for the save status (Saving / Saved). The entry editor's only header actions are delete (trash icon) and copy link.
|
|
53
53
|
|
|
54
|
+
If a field is invalid, the editor keeps the change unsaved and pins a status bar below the header. Choose **Review issues** to jump to the first invalid field, then **Next issue** to move through the remaining fields. Collapsed blocks open automatically. Once every issue is fixed, the editor confirms that all fields are valid and resumes saving.
|
|
55
|
+
|
|
56
|
+
## Editing Text
|
|
57
|
+
|
|
58
|
+
- Short text such as titles, names, and slugs uses one compact line.
|
|
59
|
+
- Plain multiline text such as summaries and descriptions starts taller and grows as you write.
|
|
60
|
+
- Formatted content uses the rich-text editor for headings, links, lists, and media.
|
|
61
|
+
|
|
54
62
|
## Bulk Operations
|
|
55
63
|
|
|
56
64
|
Need to update or remove several entries at once?
|
|
@@ -12,7 +12,9 @@ For text elements, the side panel shows editable fields where you can change the
|
|
|
12
12
|
|
|
13
13
|
### Editing Styles
|
|
14
14
|
|
|
15
|
-
|
|
15
|
+
Style controls are only available on projects created before July 2026. Newer projects edit styles through the AI chat instead (for example: "make the hero title larger and dark blue"). If the user does not see the style controls described below, their project is a newer one -- point them to the chat.
|
|
16
|
+
|
|
17
|
+
On projects that have them, the side panel organizes style controls into these categories:
|
|
16
18
|
|
|
17
19
|
- **Typography** -- Font family, font size, font weight, line height, letter spacing, text alignment, and bold/italic/underline/strikethrough toggles.
|
|
18
20
|
- **Color** -- Text and foreground colors.
|
|
@@ -31,5 +33,5 @@ Click the **Edit mode** toggle again or close the side panel. The side editor cl
|
|
|
31
33
|
|
|
32
34
|
## When to Use Edit Mode vs. Chat
|
|
33
35
|
|
|
34
|
-
- **Edit mode** is best for
|
|
35
|
-
- **Chat** is better for
|
|
36
|
+
- **Edit mode** is best for content changes -- updating text (including bold, italic, underline, and links), and replacing images. On older projects it also covers quick style tweaks like colors and spacing.
|
|
37
|
+
- **Chat** is better for style and structural changes -- changing colors or spacing (on newer projects), adding new sections, restructuring a page layout, or building new functionality.
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
# Webhooks
|
|
2
|
+
|
|
3
|
+
> **Beta:** Test each destination and review its delivery history before depending on it in production.
|
|
4
|
+
|
|
5
|
+
Webhooks send project activity to an external HTTPS endpoint. Use them for focused reactions such as adding a waitlist submission to an email tool, notifying Slack after a publish, or starting a GitHub workflow after an agent review.
|
|
6
|
+
|
|
7
|
+
Only project members with Full Access can see Webhooks in Settings or manage its destinations and delivery history.
|
|
8
|
+
|
|
9
|
+
## Create one
|
|
10
|
+
|
|
11
|
+
1. Open the project and choose **Settings**.
|
|
12
|
+
2. Select **Webhooks**.
|
|
13
|
+
3. Choose **New webhook**.
|
|
14
|
+
4. Name it, select one event, and enter the destination URL.
|
|
15
|
+
5. Optionally narrow form, agent, media, or entry events with the filter shown for that event.
|
|
16
|
+
6. Review the request and select **Save**.
|
|
17
|
+
|
|
18
|
+
The event groups are Entries, Forms, Publish, Agents, Analytics, and Media. Webhooks belong to the project, with up to 20 configurations. They listen to main by default or can include every environment.
|
|
19
|
+
|
|
20
|
+
Entry webhooks show **Combine rapid changes**. The default waits 5 seconds after direct editing stops and sends one delivery with the combined changes. Immediate, 30-second, and 1-minute windows are also available. Bulk edits and environment merges already produce batches and send immediately.
|
|
21
|
+
|
|
22
|
+
## Configure the request
|
|
23
|
+
|
|
24
|
+
The default payload is the complete Fimo event, including its type, project, environment, timestamp, and event-specific data. Keep this when the destination can accept it.
|
|
25
|
+
|
|
26
|
+
Choose **Custom template** when a service expects a specific JSON shape. Use **Insert variable** to pick fields for the selected event. Variables use paths such as `{{ event.data.submission.data.email }}`. The preview catches invalid JSON and missing variables before save.
|
|
27
|
+
|
|
28
|
+
When Studio recognizes a Resend, Slack, or GitHub URL, it offers a request setup for that service. The suggestion never changes the request automatically. Accept it to prefill relevant headers and JSON, then review every field.
|
|
29
|
+
|
|
30
|
+
## Add authentication
|
|
31
|
+
|
|
32
|
+
Under **Headers**, add a header name and choose its value type. Prefer a project secret for API keys and tokens; choose whether the destination needs the raw secret, a `Bearer ` prefix, or a `token ` prefix. Fixed values are suitable only for non-sensitive headers.
|
|
33
|
+
|
|
34
|
+
Fimo signs requests by default. Save the signing secret shown after creation so a custom receiver can verify `X-Fimo-Signature`. Rotate it from the webhook configuration if it is exposed.
|
|
35
|
+
|
|
36
|
+
## Test and monitor
|
|
37
|
+
|
|
38
|
+
Use **Send test** before depending on a workflow. Tests use documented sample data and travel through the same delivery path as real events.
|
|
39
|
+
|
|
40
|
+
Open **Deliveries** to see the event ID, masked destination, request metadata and payload excerpt, queue message ID, status, HTTP response, duration, and individual attempts. Fimo automatically retries temporary failures. After all automatic retries are exhausted, open the failed delivery and choose **Retry now**. A delivery accepts up to five manual retries.
|
|
41
|
+
|
|
42
|
+
Destination credentials are masked in configuration and delivery details. A successful test means the destination returned a successful HTTP response; it does not necessarily mean the provider completed its downstream action, so check the provider when that distinction matters.
|
|
@@ -1,11 +1,5 @@
|
|
|
1
1
|
import type { Plugin } from 'vite';
|
|
2
|
-
|
|
3
|
-
dictionaries: Record<string, Record<string, string>>;
|
|
4
|
-
defaultLocale: string;
|
|
5
|
-
locales: string[];
|
|
6
|
-
updatedAt: string | null;
|
|
7
|
-
}
|
|
2
|
+
import { loadLabelBundle } from '../../../runtime/shared/labels-fetch.server.js';
|
|
8
3
|
export default function translationsPlugin(): Plugin;
|
|
9
|
-
export
|
|
10
|
-
export {};
|
|
4
|
+
export { loadLabelBundle };
|
|
11
5
|
//# sourceMappingURL=translations.d.ts.map
|
|
@@ -1,10 +1,8 @@
|
|
|
1
1
|
import { createHash } from 'node:crypto';
|
|
2
|
-
import { Buffer } from 'node:buffer';
|
|
3
2
|
import { readFile } from 'node:fs/promises';
|
|
4
3
|
import { homedir } from 'node:os';
|
|
5
4
|
import { join } from 'node:path';
|
|
6
|
-
import {
|
|
7
|
-
import { getConfigServer } from '../../../runtime/shared/fimo-config.server.js';
|
|
5
|
+
import { emptyLabelBundle, labelSignature, loadLabelBundle, readProjectEnv, resolveTenantApiUrl, serializeBundle, } from '../../../runtime/shared/labels-fetch.server.js';
|
|
8
6
|
const VIRTUAL_ID = 'virtual:translations';
|
|
9
7
|
const RESOLVED_ID = '\0' + VIRTUAL_ID;
|
|
10
8
|
export default function translationsPlugin() {
|
|
@@ -16,6 +14,12 @@ export default function translationsPlugin() {
|
|
|
16
14
|
let reconnectTimer;
|
|
17
15
|
let refreshTimer;
|
|
18
16
|
let pollTimer;
|
|
17
|
+
// Set on closeBundle. Without it, closing a socket fires its own onclose,
|
|
18
|
+
// which schedules a reconnect AFTER the cleanup ran — resurrecting the
|
|
19
|
+
// event sockets forever and pinning the event loop. That is exactly how a
|
|
20
|
+
// prerender script using `createServer({ middlewareMode: true })` finished
|
|
21
|
+
// its work and then never exited.
|
|
22
|
+
let disposed = false;
|
|
19
23
|
const refreshLabels = async () => {
|
|
20
24
|
bundle = await loadLabelBundle(rootDir);
|
|
21
25
|
return bundle;
|
|
@@ -51,6 +55,9 @@ export default function translationsPlugin() {
|
|
|
51
55
|
});
|
|
52
56
|
};
|
|
53
57
|
const scheduleRefresh = () => {
|
|
58
|
+
if (disposed) {
|
|
59
|
+
return;
|
|
60
|
+
}
|
|
54
61
|
if (refreshTimer) {
|
|
55
62
|
clearTimeout(refreshTimer);
|
|
56
63
|
}
|
|
@@ -58,6 +65,17 @@ export default function translationsPlugin() {
|
|
|
58
65
|
void refreshLabelsAndInvalidateIfChanged().catch(() => undefined);
|
|
59
66
|
}, 150);
|
|
60
67
|
};
|
|
68
|
+
const adoptEventSockets = (sockets) => {
|
|
69
|
+
if (disposed) {
|
|
70
|
+
// The server closed while the async connect was in flight — sockets
|
|
71
|
+
// landing now must close immediately or they outlive the cleanup.
|
|
72
|
+
for (const socket of sockets) {
|
|
73
|
+
socket.close();
|
|
74
|
+
}
|
|
75
|
+
return;
|
|
76
|
+
}
|
|
77
|
+
eventSockets = sockets;
|
|
78
|
+
};
|
|
61
79
|
return {
|
|
62
80
|
name: 'fimo-virtual-translations',
|
|
63
81
|
enforce: 'pre',
|
|
@@ -78,11 +96,7 @@ export default function translationsPlugin() {
|
|
|
78
96
|
devServer = server;
|
|
79
97
|
server.watcher.add([join(rootDir, 'fimo-config.json'), join(rootDir, '.env'), join(rootDir, '.env.local')]);
|
|
80
98
|
void refreshLabelsAndInvalidateIfChanged().catch(() => undefined);
|
|
81
|
-
connectLabelEvents(rootDir, scheduleRefresh)
|
|
82
|
-
.then((sockets) => {
|
|
83
|
-
eventSockets = sockets;
|
|
84
|
-
})
|
|
85
|
-
.catch(() => undefined);
|
|
99
|
+
connectLabelEvents(rootDir, scheduleRefresh).then(adoptEventSockets).catch(() => undefined);
|
|
86
100
|
pollTimer = setInterval(() => {
|
|
87
101
|
void refreshLabelsAndInvalidateIfChanged().catch(() => undefined);
|
|
88
102
|
}, 2000);
|
|
@@ -128,6 +142,9 @@ export default function translationsPlugin() {
|
|
|
128
142
|
return [];
|
|
129
143
|
},
|
|
130
144
|
closeBundle() {
|
|
145
|
+
// Order matters: dispose FIRST so the onclose handlers fired by the
|
|
146
|
+
// socket closes below (and any in-flight connect) cannot reschedule.
|
|
147
|
+
disposed = true;
|
|
131
148
|
for (const socket of eventSockets) {
|
|
132
149
|
socket.close();
|
|
133
150
|
}
|
|
@@ -145,7 +162,7 @@ export default function translationsPlugin() {
|
|
|
145
162
|
};
|
|
146
163
|
async function connectLabelEvents(root, onLabelsChanged) {
|
|
147
164
|
const env = await readProjectEnv(root);
|
|
148
|
-
const apiUrl = env
|
|
165
|
+
const apiUrl = resolveTenantApiUrl(env);
|
|
149
166
|
if (!apiUrl) {
|
|
150
167
|
return [];
|
|
151
168
|
}
|
|
@@ -194,12 +211,11 @@ export default function translationsPlugin() {
|
|
|
194
211
|
}
|
|
195
212
|
};
|
|
196
213
|
socket.onclose = () => {
|
|
214
|
+
if (disposed) {
|
|
215
|
+
return;
|
|
216
|
+
}
|
|
197
217
|
reconnectTimer = setTimeout(() => {
|
|
198
|
-
void connectLabelEvents(root, onLabelsChanged)
|
|
199
|
-
.then((nextSockets) => {
|
|
200
|
-
eventSockets = nextSockets;
|
|
201
|
-
})
|
|
202
|
-
.catch(() => undefined);
|
|
218
|
+
void connectLabelEvents(root, onLabelsChanged).then(adoptEventSockets).catch(() => undefined);
|
|
203
219
|
}, 2000);
|
|
204
220
|
};
|
|
205
221
|
socket.onerror = () => undefined;
|
|
@@ -229,24 +245,6 @@ function parseContentChangePayload(payload) {
|
|
|
229
245
|
updatedAt: typeof source.updatedAt === 'string' ? source.updatedAt : undefined,
|
|
230
246
|
};
|
|
231
247
|
}
|
|
232
|
-
function serializeBundle(bundle) {
|
|
233
|
-
return JSON.stringify({
|
|
234
|
-
dictionaries: bundle.dictionaries,
|
|
235
|
-
defaultLocale: bundle.defaultLocale,
|
|
236
|
-
locales: bundle.locales,
|
|
237
|
-
});
|
|
238
|
-
}
|
|
239
|
-
function emptyLabelBundle(rootDir) {
|
|
240
|
-
const config = getConfigServer(rootDir);
|
|
241
|
-
const defaultLocale = getFimoDefaultLocale(config);
|
|
242
|
-
const locales = getFimoLocales(config);
|
|
243
|
-
return {
|
|
244
|
-
dictionaries: Object.fromEntries(locales.map((locale) => [locale, {}])),
|
|
245
|
-
defaultLocale,
|
|
246
|
-
locales,
|
|
247
|
-
updatedAt: null,
|
|
248
|
-
};
|
|
249
|
-
}
|
|
250
248
|
function findVirtualTranslationModules(server) {
|
|
251
249
|
const direct = server.moduleGraph.getModuleById(RESOLVED_ID);
|
|
252
250
|
const matches = new Set(direct ? [direct] : []);
|
|
@@ -257,73 +255,13 @@ function findVirtualTranslationModules(server) {
|
|
|
257
255
|
}
|
|
258
256
|
return [...matches];
|
|
259
257
|
}
|
|
260
|
-
function labelSignature(bundle) {
|
|
261
|
-
return Buffer.from(serializeBundle(bundle)).toString('base64');
|
|
262
|
-
}
|
|
263
258
|
function labelUpdatePayload(bundle) {
|
|
264
259
|
return {
|
|
265
260
|
...bundle,
|
|
266
261
|
signature: labelSignature(bundle),
|
|
267
262
|
};
|
|
268
263
|
}
|
|
269
|
-
export
|
|
270
|
-
const config = getConfigServer(rootDir);
|
|
271
|
-
const defaultLocale = getFimoDefaultLocale(config);
|
|
272
|
-
const locales = getFimoLocales(config);
|
|
273
|
-
const env = await readProjectEnv(rootDir);
|
|
274
|
-
const apiUrl = env.VITE_API_URL;
|
|
275
|
-
if (!apiUrl) {
|
|
276
|
-
return {
|
|
277
|
-
dictionaries: Object.fromEntries(locales.map((locale) => [locale, {}])),
|
|
278
|
-
defaultLocale,
|
|
279
|
-
locales,
|
|
280
|
-
updatedAt: null,
|
|
281
|
-
};
|
|
282
|
-
}
|
|
283
|
-
try {
|
|
284
|
-
const loaded = await Promise.all(locales.map((locale) => fetchLabelDictionary(apiUrl, locale).catch(() => ({ locale, labels: {}, updatedAt: null }))));
|
|
285
|
-
const updatedAt = loaded
|
|
286
|
-
.map((entry) => entry.updatedAt)
|
|
287
|
-
.filter((value) => typeof value === 'string')
|
|
288
|
-
.sort()
|
|
289
|
-
.at(-1);
|
|
290
|
-
return {
|
|
291
|
-
dictionaries: Object.fromEntries(loaded.map((entry) => [entry.locale, entry.labels])),
|
|
292
|
-
defaultLocale,
|
|
293
|
-
locales,
|
|
294
|
-
updatedAt: updatedAt ?? null,
|
|
295
|
-
};
|
|
296
|
-
}
|
|
297
|
-
catch {
|
|
298
|
-
return {
|
|
299
|
-
dictionaries: Object.fromEntries(locales.map((locale) => [locale, {}])),
|
|
300
|
-
defaultLocale,
|
|
301
|
-
locales,
|
|
302
|
-
updatedAt: null,
|
|
303
|
-
};
|
|
304
|
-
}
|
|
305
|
-
}
|
|
306
|
-
async function fetchLabelDictionary(apiUrl, locale) {
|
|
307
|
-
const url = new URL('/labels', apiUrl);
|
|
308
|
-
url.searchParams.set('locale', locale);
|
|
309
|
-
const response = await fetchWithTimeout(url, 4000);
|
|
310
|
-
if (!response.ok) {
|
|
311
|
-
return { locale, labels: {}, updatedAt: null };
|
|
312
|
-
}
|
|
313
|
-
const body = (await response.json());
|
|
314
|
-
if (Array.isArray(body.data)) {
|
|
315
|
-
return {
|
|
316
|
-
locale,
|
|
317
|
-
labels: Object.fromEntries(body.data.map((row) => [row.key, row.value ?? row.defaultValue ?? ''])),
|
|
318
|
-
updatedAt: null,
|
|
319
|
-
};
|
|
320
|
-
}
|
|
321
|
-
return {
|
|
322
|
-
locale,
|
|
323
|
-
labels: body.data?.labels ?? {},
|
|
324
|
-
updatedAt: body.data?.updatedAt ?? null,
|
|
325
|
-
};
|
|
326
|
-
}
|
|
264
|
+
export { loadLabelBundle };
|
|
327
265
|
async function loadRuntimeConfig(apiUrl) {
|
|
328
266
|
try {
|
|
329
267
|
const response = await fetchWithTimeout(new URL('/runtime-config', apiUrl), 2000);
|
|
@@ -358,44 +296,6 @@ async function fetchWithTimeout(url, timeoutMs) {
|
|
|
358
296
|
clearTimeout(timeout);
|
|
359
297
|
}
|
|
360
298
|
}
|
|
361
|
-
async function readProjectEnv(rootDir) {
|
|
362
|
-
const values = {
|
|
363
|
-
...(await readEnvFile(join(rootDir, '.env'))),
|
|
364
|
-
...(await readEnvFile(join(rootDir, '.env.local'))),
|
|
365
|
-
};
|
|
366
|
-
for (const key of ['VITE_API_URL']) {
|
|
367
|
-
if (process.env[key]) {
|
|
368
|
-
values[key] = process.env[key];
|
|
369
|
-
}
|
|
370
|
-
}
|
|
371
|
-
return values;
|
|
372
|
-
}
|
|
373
|
-
async function readEnvFile(filePath) {
|
|
374
|
-
try {
|
|
375
|
-
const content = await readFile(filePath, 'utf8');
|
|
376
|
-
const values = {};
|
|
377
|
-
for (const line of content.split(/\r?\n/)) {
|
|
378
|
-
const trimmed = line.trim();
|
|
379
|
-
if (!trimmed || trimmed.startsWith('#')) {
|
|
380
|
-
continue;
|
|
381
|
-
}
|
|
382
|
-
const index = trimmed.indexOf('=');
|
|
383
|
-
if (index === -1) {
|
|
384
|
-
continue;
|
|
385
|
-
}
|
|
386
|
-
const key = trimmed.slice(0, index).trim();
|
|
387
|
-
const value = trimmed
|
|
388
|
-
.slice(index + 1)
|
|
389
|
-
.trim()
|
|
390
|
-
.replace(/^['"]|['"]$/g, '');
|
|
391
|
-
values[key] = value;
|
|
392
|
-
}
|
|
393
|
-
return values;
|
|
394
|
-
}
|
|
395
|
-
catch {
|
|
396
|
-
return {};
|
|
397
|
-
}
|
|
398
|
-
}
|
|
399
299
|
async function readProjectSettings(rootDir) {
|
|
400
300
|
try {
|
|
401
301
|
const raw = await readFile(join(rootDir, '.fimo.settings.json'), 'utf8');
|