fimo 0.14.0 → 0.15.0-staging.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (168) hide show
  1. package/README.md +1 -0
  2. package/assets/astro/components/Boolean.astro +14 -0
  3. package/assets/astro/components/Date.astro +23 -0
  4. package/assets/astro/components/Image.astro +79 -0
  5. package/assets/astro/components/Json.astro +14 -0
  6. package/assets/astro/components/RichText.astro +17 -0
  7. package/assets/astro/components/StaticImage.astro +66 -0
  8. package/assets/astro/components/Text.astro +23 -0
  9. package/assets/astro/components/Video.astro +47 -0
  10. package/assets/astro/components/index.d.ts +8 -0
  11. package/assets/astro/components/index.js +8 -0
  12. package/assets/content-templates/shared/server.eta +56 -0
  13. package/assets/content-templates/sveltekit/content.eta +86 -0
  14. package/assets/skills/fimo/SKILL.md +2 -0
  15. package/assets/skills/fimo-cli/SKILL.md +0 -1
  16. package/assets/skills/fimo-cli/references/branches.md +14 -13
  17. package/assets/skills/fimo-cli/references/deploy.md +4 -3
  18. package/assets/skills/fimo-cli/references/describe.md +1 -1
  19. package/assets/skills/fimo-cli/references/init.md +11 -11
  20. package/assets/skills/fimo-cli/references/labels.md +3 -3
  21. package/assets/skills/fimo-cli/references/locales.md +1 -1
  22. package/assets/skills/fimo-cli/references/project-vars-and-secrets.md +1 -1
  23. package/assets/skills/fimo-code/SKILL.md +11 -3
  24. package/assets/skills/fimo-code/references/assets.md +2 -2
  25. package/assets/skills/fimo-code/references/content/clients.md +5 -1
  26. package/assets/skills/fimo-code/references/content.md +3 -1
  27. package/assets/skills/fimo-code/references/frameworks/astro.md +127 -0
  28. package/assets/skills/fimo-code/references/frameworks/compatibility.md +19 -0
  29. package/assets/skills/fimo-code/references/frameworks/hosting.md +15 -3
  30. package/assets/skills/fimo-code/references/frameworks/react-router.md +37 -209
  31. package/assets/skills/fimo-code/references/frameworks/sveltekit.md +110 -0
  32. package/assets/skills/fimo-code/references/frameworks/vite-react.md +35 -166
  33. package/assets/skills/fimo-code/references/labels.md +16 -6
  34. package/assets/skills/fimo-code/references/locales.md +3 -1
  35. package/assets/skills/fimo-code/references/ui.md +19 -15
  36. package/assets/starters/fimo-site/template/.fimo/agents/content-guide/capabilities.yaml +0 -1
  37. package/assets/starters/fimo-site/template/.fimo/agents/site-review/capabilities.yaml +0 -1
  38. package/assets/sveltekit/components/Boolean.svelte +19 -0
  39. package/assets/sveltekit/components/Date.svelte +33 -0
  40. package/assets/sveltekit/components/FimoProvider.svelte +35 -0
  41. package/assets/sveltekit/components/Image.svelte +80 -0
  42. package/assets/sveltekit/components/Json.svelte +18 -0
  43. package/assets/sveltekit/components/Label.svelte +16 -0
  44. package/assets/sveltekit/components/Number.svelte +21 -0
  45. package/assets/sveltekit/components/RichText.svelte +19 -0
  46. package/assets/sveltekit/components/Text.svelte +30 -0
  47. package/assets/sveltekit/components/Video.svelte +37 -0
  48. package/assets/sveltekit/components/index.d.ts +10 -0
  49. package/assets/sveltekit/components/index.js +10 -0
  50. package/dist/build/astro/config.d.ts +4 -0
  51. package/dist/build/astro/config.js +45 -0
  52. package/dist/build/next/config.js +16 -0
  53. package/dist/build/sveltekit/config.d.ts +6 -0
  54. package/dist/build/sveltekit/config.js +8 -0
  55. package/dist/build/sveltekit/data-id.d.ts +3 -0
  56. package/dist/build/sveltekit/data-id.js +171 -0
  57. package/dist/build/sveltekit/vite.d.ts +13 -0
  58. package/dist/build/sveltekit/vite.js +15 -0
  59. package/dist/build/vite/index.js +2 -0
  60. package/dist/build/vite/plugins/data-id.d.ts +9 -0
  61. package/dist/build/vite/plugins/data-id.js +6 -1
  62. package/dist/build/vite/plugins/runtime-env.d.ts +21 -0
  63. package/dist/build/vite/plugins/runtime-env.js +68 -0
  64. package/dist/cli/bundle.json +1 -1
  65. package/dist/cli/index.js +100 -99
  66. package/dist/runtime/app/FimoProviders.d.ts +1 -1
  67. package/dist/runtime/app/FimoProviders.js +11 -5
  68. package/dist/runtime/astro/internal.d.ts +6 -0
  69. package/dist/runtime/astro/internal.js +16 -0
  70. package/dist/runtime/astro/preview.d.ts +14 -0
  71. package/dist/runtime/astro/preview.js +103 -0
  72. package/dist/runtime/astro/render-rich-text.d.ts +2 -0
  73. package/dist/runtime/astro/render-rich-text.js +1 -0
  74. package/dist/runtime/astro/server-runtime.d.ts +3 -0
  75. package/dist/runtime/astro/server-runtime.js +20 -0
  76. package/dist/runtime/astro/server.d.ts +15 -0
  77. package/dist/runtime/astro/server.js +14 -0
  78. package/dist/runtime/content/client/collection-core.d.ts +2 -17
  79. package/dist/runtime/content/client/collection-core.js +15 -8
  80. package/dist/runtime/content/client/core-types.d.ts +96 -0
  81. package/dist/runtime/content/client/core-types.js +1 -0
  82. package/dist/runtime/content/client/internal.d.ts +4 -4
  83. package/dist/runtime/content/client/internal.js +4 -3
  84. package/dist/runtime/content/client/runtime.d.ts +1 -1
  85. package/dist/runtime/content/client/server-content-client.d.ts +3 -3
  86. package/dist/runtime/content/client/server-content-client.js +2 -29
  87. package/dist/runtime/content/client/singleton-core.d.ts +2 -9
  88. package/dist/runtime/content/client/singleton-core.js +9 -4
  89. package/dist/runtime/content/client/types.d.ts +5 -115
  90. package/dist/runtime/content/index.d.ts +3 -3
  91. package/dist/runtime/labels/fetch.js +5 -3
  92. package/dist/runtime/next/client.d.ts +2 -1
  93. package/dist/runtime/next/facade.d.ts +1 -1
  94. package/dist/runtime/next/provider.js +1 -1
  95. package/dist/runtime/next/server-runtime.js +1 -1
  96. package/dist/runtime/next/server.d.ts +5 -4
  97. package/dist/runtime/paths/get-fimo-paths.js +8 -3
  98. package/dist/runtime/preview/bridge.d.ts +8 -0
  99. package/dist/runtime/preview/bridge.js +20 -0
  100. package/dist/runtime/preview/location.d.ts +23 -0
  101. package/dist/runtime/preview/location.js +93 -0
  102. package/dist/runtime/preview/routes.d.ts +10 -0
  103. package/dist/runtime/preview/routes.js +29 -0
  104. package/dist/runtime/primitives/index.d.ts +4 -1
  105. package/dist/runtime/primitives/index.js +1 -0
  106. package/dist/runtime/primitives/lib/image-optimization.d.ts +27 -2
  107. package/dist/runtime/primitives/lib/image-optimization.js +40 -0
  108. package/dist/runtime/primitives/lib/index.d.ts +1 -1
  109. package/dist/runtime/primitives/lib/primitives.d.ts +0 -54
  110. package/dist/runtime/primitives/lib/primitives.js +6 -0
  111. package/dist/runtime/primitives/lib/render-rich-text.d.ts +3 -0
  112. package/dist/runtime/primitives/lib/render-rich-text.js +112 -0
  113. package/dist/runtime/react/primitives/contracts.d.ts +40 -2
  114. package/dist/runtime/react/primitives/renderers/ImageRenderer.js +15 -34
  115. package/dist/runtime/react/primitives/renderers/RichTextRenderer.d.ts +2 -1
  116. package/dist/runtime/react/primitives/renderers/StaticImage.js +13 -30
  117. package/dist/runtime/routing/AppRoutesProvider.js +20 -8
  118. package/dist/runtime/shared/env-identity.d.ts +43 -0
  119. package/dist/runtime/shared/env-identity.js +88 -0
  120. package/dist/runtime/shared/local-runtime-env.d.ts +65 -0
  121. package/dist/runtime/shared/local-runtime-env.js +169 -0
  122. package/dist/runtime/{react-router → shared}/preview-location.d.ts +7 -13
  123. package/dist/runtime/{react-router → shared}/preview-location.js +26 -27
  124. package/dist/runtime/sveltekit/content.d.ts +25 -0
  125. package/dist/runtime/sveltekit/content.js +33 -0
  126. package/dist/runtime/sveltekit/context-contract.d.ts +5 -0
  127. package/dist/runtime/sveltekit/context-contract.js +2 -0
  128. package/dist/runtime/sveltekit/context.d.ts +8 -0
  129. package/dist/runtime/sveltekit/context.js +25 -0
  130. package/dist/runtime/sveltekit/index.d.ts +7 -0
  131. package/dist/runtime/sveltekit/index.js +4 -0
  132. package/dist/runtime/sveltekit/internal.d.ts +6 -0
  133. package/dist/runtime/sveltekit/internal.js +12 -0
  134. package/dist/runtime/sveltekit/labels.d.ts +16 -0
  135. package/dist/runtime/sveltekit/labels.js +13 -0
  136. package/dist/runtime/sveltekit/preview.d.ts +19 -0
  137. package/dist/runtime/sveltekit/preview.js +77 -0
  138. package/dist/runtime/sveltekit/runtime.d.ts +8 -0
  139. package/dist/runtime/sveltekit/runtime.js +17 -0
  140. package/dist/runtime/templates.d.ts +1 -1
  141. package/dist/runtime/templates.js +1 -1
  142. package/dist/runtime/vite/Label.d.ts +13 -0
  143. package/dist/runtime/vite/Label.js +16 -0
  144. package/dist/scripts/extract-translations.js +103 -43
  145. package/dist/scripts/sync-forms.js +1 -1
  146. package/dist/scripts/sync-schemas.js +83 -16
  147. package/package.json +56 -2
  148. package/release.json +2 -2
  149. package/templates/nextjs/_gitignore +1 -0
  150. package/templates/react-router/package.json +1 -1
  151. package/templates/sveltekit/.fimo/config.json +15 -0
  152. package/templates/sveltekit/AGENTS.md +16 -0
  153. package/templates/sveltekit/CLAUDE.md +5 -0
  154. package/templates/sveltekit/_gitignore +16 -0
  155. package/templates/sveltekit/package.json +28 -0
  156. package/templates/sveltekit/pnpm-workspace.yaml +4 -0
  157. package/templates/sveltekit/src/app.html +11 -0
  158. package/templates/sveltekit/src/routes/+layout.server.ts +8 -0
  159. package/templates/sveltekit/src/routes/+layout.svelte +12 -0
  160. package/templates/sveltekit/src/routes/+page.svelte +48 -0
  161. package/templates/sveltekit/svelte.config.js +5 -0
  162. package/templates/sveltekit/tsconfig.json +14 -0
  163. package/templates/sveltekit/vite.config.ts +5 -0
  164. /package/assets/content-templates/{form-template.eta → forms/react.eta} +0 -0
  165. /package/assets/content-templates/{next-client-template.eta → next/client.eta} +0 -0
  166. /package/assets/content-templates/{next-server-template.eta → next/server.eta} +0 -0
  167. /package/assets/content-templates/{hooks-template.eta → react/client.eta} +0 -0
  168. /package/assets/content-templates/{types-template.eta → shared/types.eta} +0 -0
@@ -1,14 +1,14 @@
1
1
  /**
2
- * Extract label keys from React components using oxc-parser.
2
+ * Extract label keys from application source.
3
3
  *
4
- * Labels are DB-owned. This scanner reports literal `t("key")` call sites so
5
- * `fimo validate` can verify the tenant DB contains visible values. It never
6
- * writes label values to local files.
4
+ * Labels are DB-owned. This scanner reports literal `t("key")` calls and
5
+ * literal `<Label value="key">` elements so `fimo validate` can verify that
6
+ * the tenant DB contains visible values. It never writes label values.
7
7
  */
8
8
  import { existsSync, readFileSync, readdirSync, statSync } from 'node:fs';
9
9
  import { join } from 'node:path';
10
10
  import { parseSync } from 'oxc-parser';
11
- function findReactFiles(dir, files = []) {
11
+ function findSourceFiles(dir, files = []) {
12
12
  if (!existsSync(dir)) {
13
13
  return files;
14
14
  }
@@ -20,67 +20,127 @@ function findReactFiles(dir, files = []) {
20
20
  if (entry === 'ui' || entry === 'node_modules') {
21
21
  continue;
22
22
  }
23
- findReactFiles(fullPath, files);
23
+ findSourceFiles(fullPath, files);
24
24
  }
25
- else if (/\.(ts|tsx|js|jsx)$/.test(entry)) {
25
+ else if (/\.(ts|tsx|js|jsx|svelte)$/.test(entry)) {
26
26
  files.push(fullPath);
27
27
  }
28
28
  }
29
29
  return files;
30
30
  }
31
- function extractFromFile(filePath) {
32
- const declarations = [];
33
- try {
34
- const code = readFileSync(filePath, 'utf-8');
35
- const result = parseSync(filePath, code);
36
- const ast = result.program;
37
- function visit(node) {
38
- if (!node || typeof node !== 'object') {
39
- return;
31
+ function importedNames(body, source, importedName) {
32
+ const names = new Set();
33
+ for (const statement of body) {
34
+ if (statement.type !== 'ImportDeclaration' || statement.source.value !== source) {
35
+ continue;
36
+ }
37
+ for (const specifier of statement.specifiers) {
38
+ if (specifier.type === 'ImportSpecifier' &&
39
+ specifier.imported.type === 'Identifier' &&
40
+ specifier.imported.name === importedName) {
41
+ names.add(specifier.local.name);
40
42
  }
41
- if (node.type === 'CallExpression') {
42
- const callee = node.callee;
43
- const isTranslationCall = (callee?.type === 'Identifier' && callee.name === 't') ||
44
- (callee?.type === 'MemberExpression' &&
45
- callee.computed === false &&
46
- callee.object?.type === 'Identifier' &&
47
- callee.object.name === 'labels' &&
48
- callee.property?.type === 'Identifier' &&
49
- callee.property.name === 't');
50
- if (isTranslationCall) {
51
- const args = node.arguments || [];
52
- const firstArg = args[0];
53
- if (firstArg?.type === 'Literal' && typeof firstArg.value === 'string') {
54
- const key = firstArg.value;
55
- declarations.push({ key, file: filePath });
56
- }
43
+ }
44
+ }
45
+ return names;
46
+ }
47
+ function extractFromModule(filePath, code, parsePath = filePath) {
48
+ const declarations = [];
49
+ const result = parseSync(parsePath, code);
50
+ const ast = result.program;
51
+ const fimoLabelNames = importedNames(ast.body, 'fimo/ui', 'Label');
52
+ function visit(node) {
53
+ if (!node || typeof node !== 'object') {
54
+ return;
55
+ }
56
+ if (node.type === 'CallExpression') {
57
+ const callee = node.callee;
58
+ const isTranslationCall = (callee?.type === 'Identifier' && callee.name === 't') ||
59
+ (callee?.type === 'MemberExpression' &&
60
+ callee.computed === false &&
61
+ callee.object?.type === 'Identifier' &&
62
+ callee.object.name === 'labels' &&
63
+ callee.property?.type === 'Identifier' &&
64
+ callee.property.name === 't');
65
+ if (isTranslationCall) {
66
+ const args = node.arguments || [];
67
+ const firstArg = args[0];
68
+ if (firstArg?.type === 'Literal' && typeof firstArg.value === 'string') {
69
+ declarations.push({ key: firstArg.value, file: filePath });
57
70
  }
58
71
  }
59
- for (const key of Object.keys(node)) {
60
- const child = node[key];
61
- if (Array.isArray(child)) {
62
- for (const item of child) {
63
- visit(item);
64
- }
72
+ }
73
+ if (node.type === 'JSXOpeningElement' &&
74
+ node.name?.type === 'JSXIdentifier' &&
75
+ fimoLabelNames.has(node.name.name)) {
76
+ for (const attribute of node.attributes ?? []) {
77
+ if (attribute?.type === 'JSXAttribute' &&
78
+ attribute.name?.type === 'JSXIdentifier' &&
79
+ attribute.name.name === 'value' &&
80
+ attribute.value?.type === 'Literal' &&
81
+ typeof attribute.value.value === 'string') {
82
+ declarations.push({ key: attribute.value.value, file: filePath });
65
83
  }
66
- else if (typeof child === 'object' && child !== null) {
67
- visit(child);
84
+ }
85
+ }
86
+ for (const key of Object.keys(node)) {
87
+ const child = node[key];
88
+ if (Array.isArray(child)) {
89
+ for (const item of child) {
90
+ visit(item);
68
91
  }
69
92
  }
93
+ else if (typeof child === 'object' && child !== null) {
94
+ visit(child);
95
+ }
70
96
  }
71
- visit(ast);
97
+ }
98
+ visit(ast);
99
+ return declarations;
100
+ }
101
+ const SVELTE_SCRIPT = /<script\b[^>]*>([\s\S]*?)<\/script>/gi;
102
+ const SVELTE_COMMENT = /<!--[\s\S]*?-->/g;
103
+ const SVELTE_T_CALL_KEY = /(?:\blabels\.t|(?<![\w$.])t)\(\s*(['"])([^'"]*)\1/g;
104
+ function extractFromSvelte(filePath, code) {
105
+ const declarations = [];
106
+ const labelNames = new Set();
107
+ const markup = code
108
+ .replace(SVELTE_SCRIPT, (_script, contents) => {
109
+ const ast = parseSync(`${filePath}.ts`, contents).program;
110
+ for (const name of importedNames(ast.body, 'fimo/sveltekit/components', 'Label')) {
111
+ labelNames.add(name);
112
+ }
113
+ declarations.push(...extractFromModule(filePath, contents, `${filePath}.ts`));
114
+ return '';
115
+ })
116
+ .replace(SVELTE_COMMENT, '');
117
+ for (const name of labelNames) {
118
+ const labelKey = new RegExp(`<${name}\\b[^>]*?\\svalue=(?:"([^"]*)"|'([^']*)')`, 'g');
119
+ for (const match of markup.matchAll(labelKey)) {
120
+ declarations.push({ key: (match[1] ?? match[2]), file: filePath });
121
+ }
122
+ }
123
+ for (const match of markup.matchAll(SVELTE_T_CALL_KEY)) {
124
+ declarations.push({ key: match[2], file: filePath });
125
+ }
126
+ return declarations;
127
+ }
128
+ function extractFromFile(filePath) {
129
+ try {
130
+ const code = readFileSync(filePath, 'utf-8');
131
+ return filePath.endsWith('.svelte') ? extractFromSvelte(filePath, code) : extractFromModule(filePath, code);
72
132
  }
73
133
  catch (error) {
74
134
  console.error(`Failed to parse ${filePath}:`, error);
135
+ return [];
75
136
  }
76
- return declarations;
77
137
  }
78
138
  export function extractLabelKeys(options = {}) {
79
139
  const cwd = options.cwd ?? process.cwd();
80
140
  const srcDir = join(cwd, 'src');
81
141
  const declarations = [];
82
142
  const seen = new Set();
83
- for (const file of findReactFiles(srcDir)) {
143
+ for (const file of findSourceFiles(srcDir)) {
84
144
  for (const declaration of extractFromFile(file)) {
85
145
  if (seen.has(declaration.key)) {
86
146
  continue;
@@ -44,7 +44,7 @@ export function syncForms(options = {}) {
44
44
  }
45
45
  };
46
46
  const formsDir = resolve(cwd, FORMS_REL);
47
- const templatePath = assetPath('content-templates', 'form-template.eta');
47
+ const templatePath = assetPath('content-templates', 'forms', 'react.eta');
48
48
  const templateSource = readFileSync(templatePath, 'utf-8');
49
49
  const eta = new Eta({ views: undefined, useWith: true, varName: 'it', autoEscape: false });
50
50
  const formFiles = getFormFiles(formsDir);
@@ -5,7 +5,7 @@ import { basename, dirname, join, resolve } from 'node:path';
5
5
  * hook files generated from the hooks template.
6
6
  *
7
7
  * - Scans `<cwd>/src/schemas/*.json`.
8
- * - Renders shared types plus either the React facade or split Next server/client facades.
8
+ * - Renders shared types plus the framework's React, Next, Astro, or SvelteKit facade.
9
9
  * - Creates/updates `<Pascal>.types.ts` and `<Pascal>.ts`, plus `<Pascal>.client.ts` for Next; deletes orphans.
10
10
  */
11
11
  import { camelCase, pascalCase } from 'change-case';
@@ -25,6 +25,14 @@ const SCHEMA_DEFINITION_SCHEMA = z.object({
25
25
  i18n: z.object({ enabled: z.boolean().optional() }).optional(),
26
26
  fields: z.record(z.string(), z.unknown()).optional(),
27
27
  });
28
+ const CONTENT_TEMPLATE_PATHS = {
29
+ types: { current: 'shared/types.eta', legacy: 'types-template.eta' },
30
+ sharedServer: { current: 'shared/server.eta' },
31
+ reactClient: { current: 'react/client.eta', legacy: 'hooks-template.eta' },
32
+ nextServer: { current: 'next/server.eta' },
33
+ nextClient: { current: 'next/client.eta' },
34
+ svelteKitContent: { current: 'sveltekit/content.eta' },
35
+ };
28
36
  function getSchemaFiles(schemasDir) {
29
37
  if (!existsSync(schemasDir)) {
30
38
  return [];
@@ -101,58 +109,117 @@ function resolveProjectRuntime(cwd) {
101
109
  packageRoot: dirname(packagePath),
102
110
  version,
103
111
  providesNextRuntime: packageExportsSubpath(packageJson, './next') && packageExportsSubpath(packageJson, './next/client'),
112
+ providesAstroRuntime: packageExportsSubpath(packageJson, './astro'),
113
+ providesSvelteKitRuntime: packageExportsSubpath(packageJson, './sveltekit'),
104
114
  };
105
115
  }
106
116
  function contentRuntimeImportFor(framework) {
107
- return framework === 'nextjs' ? 'fimo/next' : 'fimo/ui';
117
+ if (framework === 'nextjs') {
118
+ return 'fimo/next';
119
+ }
120
+ if (framework === 'astro') {
121
+ return 'fimo/astro';
122
+ }
123
+ // @vercel/frameworks reports SvelteKit as `sveltekit-1`, while Fimo's
124
+ // public runtime profile is `sveltekit`. Both describe the same codegen
125
+ // target; callers may supply either the detector evidence or the profile.
126
+ if (framework === 'sveltekit' || framework === 'sveltekit-1') {
127
+ return 'fimo/sveltekit';
128
+ }
129
+ return 'fimo/ui';
108
130
  }
109
- function templatePath(runtime, fileName) {
131
+ function templatePath(runtime, templateName) {
132
+ const template = CONTENT_TEMPLATE_PATHS[templateName];
110
133
  if (runtime && !isVersionAtLeast(runtime.version, MINIMUM_THIN_CONTENT_CLIENT_RUNTIME_VERSION)) {
111
- return join(runtime.packageRoot, 'assets', 'content-templates', fileName);
134
+ const legacyPath = 'legacy' in template ? template.legacy : template.current;
135
+ return join(runtime.packageRoot, 'assets', 'content-templates', legacyPath);
112
136
  }
113
- return assetPath('content-templates', fileName);
137
+ return assetPath('content-templates', template.current);
114
138
  }
115
139
  function resolveSchemaGenerationTarget(runtimeImport, runtime) {
116
140
  if (runtimeImport === 'fimo/ui') {
117
141
  return 'react';
118
142
  }
119
- if (!runtime.providesNextRuntime) {
143
+ if (runtimeImport === 'fimo/next' && !runtime.providesNextRuntime) {
120
144
  throw new Error(`Cannot generate schema clients because project-local fimo@${runtime.version} does not provide the ` +
121
145
  'fimo/next and fimo/next/client exports required by this Next.js project. ' +
122
146
  `Run \`${CONTENT_CLIENT_RUNTIME_UPGRADE_COMMAND}\` in the project, then run \`npx fimo@latest validate\` again. ` +
123
147
  'No generated files were changed.');
124
148
  }
125
- return 'next';
149
+ if (runtimeImport === 'fimo/astro' && !runtime.providesAstroRuntime) {
150
+ throw new Error(`Cannot generate schema clients because project-local fimo@${runtime.version} does not provide the ` +
151
+ 'fimo/astro export required by this Astro project. ' +
152
+ `Run \`${CONTENT_CLIENT_RUNTIME_UPGRADE_COMMAND}\` in the project, then run \`npx fimo@latest validate\` again. ` +
153
+ 'No generated files were changed.');
154
+ }
155
+ if (runtimeImport === 'fimo/sveltekit' && !runtime.providesSvelteKitRuntime) {
156
+ throw new Error(`Cannot generate schema clients because project-local fimo@${runtime.version} does not provide the ` +
157
+ 'fimo/sveltekit export required by this SvelteKit project. ' +
158
+ `Run \`${CONTENT_CLIENT_RUNTIME_UPGRADE_COMMAND}\` in the project, then run \`npx fimo@latest validate\` again. ` +
159
+ 'No generated files were changed.');
160
+ }
161
+ if (runtimeImport === 'fimo/next') {
162
+ return 'next';
163
+ }
164
+ return runtimeImport === 'fimo/astro' ? 'astro' : 'sveltekit';
126
165
  }
127
166
  function templatesForTarget(target, runtime, runtimeImport) {
128
167
  if (target === 'next') {
129
168
  return [
130
169
  {
131
170
  suffix: '.types.ts',
132
- path: templatePath(runtime, 'types-template.eta'),
171
+ path: templatePath(runtime, 'types'),
133
172
  runtimeImport: 'fimo/next',
134
173
  },
135
174
  {
136
175
  suffix: '.ts',
137
- path: templatePath(runtime, 'next-server-template.eta'),
176
+ path: templatePath(runtime, 'nextServer'),
138
177
  runtimeImport: 'fimo/next',
139
178
  },
140
179
  {
141
180
  suffix: '.client.ts',
142
- path: templatePath(runtime, 'next-client-template.eta'),
181
+ path: templatePath(runtime, 'nextClient'),
143
182
  runtimeImport: 'fimo/next/client',
144
183
  },
145
184
  ];
146
185
  }
186
+ if (target === 'astro') {
187
+ return [
188
+ {
189
+ suffix: '.types.ts',
190
+ path: templatePath(runtime, 'types'),
191
+ runtimeImport: 'fimo/astro',
192
+ },
193
+ {
194
+ suffix: '.ts',
195
+ path: templatePath(runtime, 'sharedServer'),
196
+ runtimeImport: 'fimo/astro',
197
+ },
198
+ ];
199
+ }
200
+ if (target === 'sveltekit') {
201
+ return [
202
+ {
203
+ suffix: '.types.ts',
204
+ path: templatePath(runtime, 'types'),
205
+ runtimeImport: 'fimo/sveltekit',
206
+ },
207
+ {
208
+ suffix: '.ts',
209
+ path: templatePath(runtime, 'svelteKitContent'),
210
+ runtimeImport: 'fimo/sveltekit',
211
+ },
212
+ ];
213
+ }
147
214
  return [
148
215
  {
149
216
  suffix: '.types.ts',
150
- path: templatePath(runtime, 'types-template.eta'),
217
+ path: templatePath(runtime, 'types'),
151
218
  runtimeImport,
152
219
  },
153
220
  {
154
221
  suffix: '.ts',
155
- path: templatePath(runtime, 'hooks-template.eta'),
222
+ path: templatePath(runtime, 'reactClient'),
156
223
  runtimeImport,
157
224
  },
158
225
  ];
@@ -219,7 +286,7 @@ function renderContentRegistry(schemas) {
219
286
  const imports = schemas.map(({ pascal }) => `import * as ${pascal} from "./${pascal}";`).join('\n');
220
287
  const entries = schemas.map(({ pascal, registryKey }) => ` ${registryKey}: ${pascal},`).join('\n');
221
288
  return `${GENERATED_REGISTRY_MARKER}
222
- // Server code imports this registry and passes it to createFimo({ content, locale }).
289
+ // Generated content registry for framework facades and project integrations.
223
290
 
224
291
  ${imports}
225
292
 
@@ -306,10 +373,10 @@ export function syncSchemas(options) {
306
373
  const runtime = schemas.length > 0 || generatedFilesOnDisk.length > 0 ? resolveProjectRuntime(cwd) : null;
307
374
  const runtimeImport = contentRuntimeImportFor(options.framework);
308
375
  const generationTarget = runtime ? resolveSchemaGenerationTarget(runtimeImport, runtime) : null;
309
- const needsNextRegistry = generationTarget === 'next' && schemas.length > 0;
376
+ const needsContentRegistry = (generationTarget === 'next' || generationTarget === 'sveltekit') && schemas.length > 0;
310
377
  const ownsExistingRegistry = existsSync(registryPath) && readFileSync(registryPath, 'utf-8').startsWith(GENERATED_REGISTRY_MARKER);
311
- const shouldGenerateRegistry = needsNextRegistry && (!existsSync(registryPath) || ownsExistingRegistry);
312
- if (needsNextRegistry && !shouldGenerateRegistry) {
378
+ const shouldGenerateRegistry = needsContentRegistry && (!existsSync(registryPath) || ownsExistingRegistry);
379
+ if (needsContentRegistry && !shouldGenerateRegistry) {
313
380
  log(`Kept project-owned schema barrel: ${registryPath}`);
314
381
  }
315
382
  const templates = generationTarget && runtime ? templatesForTarget(generationTarget, runtime, runtimeImport) : [];
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "fimo",
3
- "version": "0.14.0",
3
+ "version": "0.15.0-staging.1",
4
4
  "description": "Fimo CLI - create, deploy, and manage Fimo projects",
5
5
  "bin": {
6
6
  "fimo": "dist/cli/index.js"
@@ -26,6 +26,22 @@
26
26
  "types": "./dist/runtime/content/index.d.ts",
27
27
  "import": "./dist/runtime/content/index.js"
28
28
  },
29
+ "./astro": {
30
+ "types": "./dist/runtime/astro/server.d.ts",
31
+ "import": "./dist/runtime/astro/server.js"
32
+ },
33
+ "./astro/components": {
34
+ "types": "./assets/astro/components/index.d.ts",
35
+ "import": "./assets/astro/components/index.js"
36
+ },
37
+ "./astro/config": {
38
+ "types": "./dist/build/astro/config.d.ts",
39
+ "import": "./dist/build/astro/config.js"
40
+ },
41
+ "./astro/internal": {
42
+ "types": "./dist/runtime/astro/internal.d.ts",
43
+ "import": "./dist/runtime/astro/internal.js"
44
+ },
29
45
  "./next": {
30
46
  "types": "./dist/runtime/next/server.d.ts",
31
47
  "import": "./dist/runtime/next/server.js"
@@ -51,6 +67,27 @@
51
67
  "types": "./dist/runtime/react-router/preset.d.ts",
52
68
  "import": "./dist/runtime/react-router/preset.js"
53
69
  },
70
+ "./sveltekit": {
71
+ "types": "./dist/runtime/sveltekit/index.d.ts",
72
+ "import": "./dist/runtime/sveltekit/index.js"
73
+ },
74
+ "./sveltekit/components": {
75
+ "types": "./assets/sveltekit/components/index.d.ts",
76
+ "svelte": "./assets/sveltekit/components/index.js",
77
+ "import": "./assets/sveltekit/components/index.js"
78
+ },
79
+ "./sveltekit/config": {
80
+ "types": "./dist/build/sveltekit/config.d.ts",
81
+ "import": "./dist/build/sveltekit/config.js"
82
+ },
83
+ "./sveltekit/internal": {
84
+ "types": "./dist/runtime/sveltekit/internal.d.ts",
85
+ "import": "./dist/runtime/sveltekit/internal.js"
86
+ },
87
+ "./sveltekit/vite": {
88
+ "types": "./dist/build/sveltekit/vite.d.ts",
89
+ "import": "./dist/build/sveltekit/vite.js"
90
+ },
54
91
  "./paths": {
55
92
  "types": "./dist/runtime/paths/index.d.ts",
56
93
  "import": "./dist/runtime/paths/index.js"
@@ -84,12 +121,17 @@
84
121
  "publish:dry-run": "npm publish --dry-run",
85
122
  "postinstall": "node scripts/postinstall.mjs",
86
123
  "test": "vitest run",
87
- "test:integration:next": "vitest run --config vitest.integration.config.ts",
124
+ "test:integration:astro": "vitest run --config vitest.integration.config.ts test/integration/astro-runtime.test.ts",
125
+ "test:integration:frameworks": "vitest run --config vitest.integration.config.ts",
126
+ "test:integration:next": "vitest run --config vitest.integration.config.ts test/integration/next-runtime.test.ts",
127
+ "test:integration:sveltekit": "vitest run --config vitest.integration.config.ts test/integration/sveltekit-runtime.test.ts",
88
128
  "test:watch": "vitest",
89
129
  "clean": "rm -rf dist *.tsbuildinfo"
90
130
  },
91
131
  "dependencies": {
132
+ "@astrojs/vercel": "11.0.4",
92
133
  "@rollup/pluginutils": "^5.1.0",
134
+ "@sveltejs/adapter-vercel": "6.3.4",
93
135
  "change-case": "^5.4.4",
94
136
  "eta": "^3.5.0",
95
137
  "oxc-parser": "^0.118.0",
@@ -127,16 +169,25 @@
127
169
  "yaml": "2.8.2"
128
170
  },
129
171
  "peerDependencies": {
172
+ "@sveltejs/kit": "^2.0.0",
130
173
  "@tanstack/react-query": "^5.80.6",
174
+ "astro": "^5.0.0 || ^6.0.0 || ^7.0.0",
131
175
  "react": "^18.2.0 || ^19.0.0",
132
176
  "react-dom": "^18.2.0 || ^19.0.0",
133
177
  "react-router": "^7.0.0 || ^8.0.0",
178
+ "svelte": "^5.0.0",
134
179
  "vite": "^7.0.0 || ^8.0.0"
135
180
  },
136
181
  "peerDependenciesMeta": {
137
182
  "@tanstack/react-query": {
138
183
  "optional": true
139
184
  },
185
+ "@sveltejs/kit": {
186
+ "optional": true
187
+ },
188
+ "astro": {
189
+ "optional": true
190
+ },
140
191
  "react": {
141
192
  "optional": true
142
193
  },
@@ -146,6 +197,9 @@
146
197
  "react-router": {
147
198
  "optional": true
148
199
  },
200
+ "svelte": {
201
+ "optional": true
202
+ },
149
203
  "vite": {
150
204
  "optional": true
151
205
  }
package/release.json CHANGED
@@ -1,5 +1,5 @@
1
1
  {
2
2
  "source": "npm",
3
- "apiUrl": "https://api.fimo.ai",
4
- "webUrl": "https://fimo.ai"
3
+ "apiUrl": "https://api.staging.fimo.team",
4
+ "webUrl": "https://staging.fimo.team"
5
5
  }
@@ -7,6 +7,7 @@ node_modules/
7
7
  # Build outputs
8
8
  .next/
9
9
  out/
10
+ .vercel/
10
11
  next-env.d.ts
11
12
  *.tsbuildinfo
12
13
 
@@ -21,7 +21,7 @@
21
21
  "cmdk": "^1.1.1",
22
22
  "date-fns": "^4.1.0",
23
23
  "embla-carousel-react": "^8.6.0",
24
- "fimo": "0.14.0",
24
+ "fimo": "0.15.0-staging.1",
25
25
  "input-otp": "^1.4.2",
26
26
  "isbot": "^5",
27
27
  "lucide-react": "^0.577.0",
@@ -0,0 +1,15 @@
1
+ {
2
+ "runtime": { "framework": "sveltekit" },
3
+ "i18n": {
4
+ "defaultLocale": "en",
5
+ "locales": ["en"]
6
+ },
7
+ "routes": {
8
+ "home": {
9
+ "label": "Home",
10
+ "path": "/"
11
+ }
12
+ },
13
+ "version": "4",
14
+ "source": "cli"
15
+ }
@@ -0,0 +1,16 @@
1
+ # Fimo project
2
+
3
+ This SvelteKit application is connected to Fimo for content, editing, environments, and hosting.
4
+
5
+ ## Load Fimo guidance before changing Fimo code
6
+
7
+ ```bash
8
+ pnpm exec fimo skills get fimo-code
9
+ pnpm exec fimo skills get fimo-code --ref frameworks/sveltekit
10
+ ```
11
+
12
+ Load only the additional task reference you need. The SvelteKit reference owns the framework API and setup rules.
13
+
14
+ The scaffolded homepage is disposable. Replace it when building the requested site. The application owns routes, locale resolution, links, HTML language and direction, and SEO.
15
+
16
+ Never edit `.fimo/project.json`; it links this directory to its Fimo project. Keep secrets in Fimo, not committed environment files. Offer `fimo deploy` only when the developer wants a hosted preview or publication.
@@ -0,0 +1,5 @@
1
+ # Fimo project
2
+
3
+ Instructions for this project live in `AGENTS.md`. Read it:
4
+
5
+ @AGENTS.md
@@ -0,0 +1,16 @@
1
+ # System files
2
+ .DS_Store
3
+
4
+ # Dependencies
5
+ node_modules/
6
+
7
+ # Build outputs
8
+ .svelte-kit/
9
+ .vercel/
10
+ build/
11
+ *.tsbuildinfo
12
+
13
+ # Env
14
+ .env
15
+ .env.*
16
+ !.env.example
@@ -0,0 +1,28 @@
1
+ {
2
+ "name": "fimo-template",
3
+ "version": "0.0.0",
4
+ "private": true,
5
+ "type": "module",
6
+ "scripts": {
7
+ "dev": "vite dev",
8
+ "build": "vite build",
9
+ "preview": "vite preview",
10
+ "check": "svelte-kit sync && svelte-check --tsconfig ./tsconfig.json",
11
+ "prepare": "svelte-kit sync"
12
+ },
13
+ "dependencies": {
14
+ "fimo": "latest"
15
+ },
16
+ "devDependencies": {
17
+ "@sveltejs/kit": "^2.70.2",
18
+ "@sveltejs/vite-plugin-svelte": "^7.2.0",
19
+ "@types/node": "^24.12.0",
20
+ "svelte": "^5.56.8",
21
+ "svelte-check": "^4.7.6",
22
+ "typescript": "^5.9.3",
23
+ "vite": "^8.2.0"
24
+ },
25
+ "engines": {
26
+ "node": ">=22.22.0"
27
+ }
28
+ }
@@ -0,0 +1,4 @@
1
+ # Build approval is handled by Fimo-controlled install commands. Do not add a
2
+ # package-level allowlist because Fimo installs with dangerouslyAllowAllBuilds.
3
+
4
+ packages: []
@@ -0,0 +1,11 @@
1
+ <!doctype html>
2
+ <html lang="en">
3
+ <head>
4
+ <meta charset="utf-8" />
5
+ <meta name="viewport" content="width=device-width" />
6
+ %sveltekit.head%
7
+ </head>
8
+ <body data-sveltekit-preload-data="hover">
9
+ <div style="display: contents">%sveltekit.body%</div>
10
+ </body>
11
+ </html>
@@ -0,0 +1,8 @@
1
+ import { env } from '$env/dynamic/public';
2
+ import { getLabels } from 'fimo/sveltekit';
3
+
4
+ export async function load(event) {
5
+ return {
6
+ labels: await getLabels({ apiUrl: env.PUBLIC_FIMO_API_URL, event, locale: 'en' }),
7
+ };
8
+ }
@@ -0,0 +1,12 @@
1
+ <script lang="ts">
2
+ import { FimoProvider } from 'fimo/sveltekit/components';
3
+ import type { Snippet } from 'svelte';
4
+
5
+ import type { LayoutData } from './$types';
6
+
7
+ let { data, children }: { data: LayoutData; children: Snippet } = $props();
8
+ </script>
9
+
10
+ <FimoProvider labels={data.labels}>
11
+ {@render children()}
12
+ </FimoProvider>