fimo 0.7.0-experimental.2 → 0.7.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 (135) hide show
  1. package/README.md +1 -1
  2. package/assets/skills/fimo-cli/references/domains.md +1 -1
  3. package/assets/skills/fimo-studio/references/custom-domains.md +1 -1
  4. package/dist/build/vite/index.d.ts.map +1 -0
  5. package/dist/build/vite/plugins/content-sync.d.ts.map +1 -0
  6. package/dist/build/vite/plugins/content-sync.test.d.ts +2 -0
  7. package/dist/build/vite/plugins/content-sync.test.d.ts.map +1 -0
  8. package/dist/build/vite/plugins/content-sync.test.js +64 -0
  9. package/dist/build/vite/plugins/data-id.d.ts.map +1 -0
  10. package/dist/build/vite/plugins/fimo-config.d.ts.map +1 -0
  11. package/dist/build/vite/plugins/fimo-config.test.d.ts +2 -0
  12. package/dist/build/vite/plugins/fimo-config.test.d.ts.map +1 -0
  13. package/dist/build/vite/plugins/fimo-config.test.js +46 -0
  14. package/dist/build/vite/plugins/overlay.d.ts.map +1 -0
  15. package/dist/build/vite/plugins/robots.d.ts.map +1 -0
  16. package/dist/build/vite/plugins/seo.d.ts.map +1 -0
  17. package/dist/build/vite/plugins/server-config.d.ts.map +1 -0
  18. package/dist/build/vite/plugins/sitemap.d.ts.map +1 -0
  19. package/dist/build/vite/plugins/translations.d.ts.map +1 -0
  20. package/dist/build/vite/plugins/translations.test.d.ts +2 -0
  21. package/dist/build/vite/plugins/translations.test.d.ts.map +1 -0
  22. package/dist/build/vite/plugins/translations.test.js +266 -0
  23. package/dist/cli/bundle.json +2 -3
  24. package/dist/cli/index.js +110670 -1196
  25. package/dist/runtime/app/FimoProviders.d.ts.map +1 -0
  26. package/dist/runtime/app/FimoScripts.d.ts.map +1 -0
  27. package/dist/runtime/app/index.d.ts.map +1 -0
  28. package/dist/runtime/app/prefetch.d.ts.map +1 -0
  29. package/dist/runtime/content/DataSourcesProvider.d.ts.map +1 -0
  30. package/dist/runtime/content/RefreshContentProvider.d.ts.map +1 -0
  31. package/dist/runtime/content/RefreshContentProvider.test.d.ts +2 -0
  32. package/dist/runtime/content/RefreshContentProvider.test.d.ts.map +1 -0
  33. package/dist/runtime/content/RefreshContentProvider.test.js +33 -0
  34. package/dist/runtime/content/parseCollectionResponse.d.ts.map +1 -0
  35. package/dist/runtime/content/parseCollectionResponse.test.d.ts +2 -0
  36. package/dist/runtime/content/parseCollectionResponse.test.d.ts.map +1 -0
  37. package/dist/runtime/content/parseCollectionResponse.test.js +20 -0
  38. package/dist/runtime/content/utils/retrieve-data-sources.d.ts.map +1 -0
  39. package/dist/runtime/core/AppErrorBoundary.d.ts.map +1 -0
  40. package/dist/runtime/core/ViteLifecycles.d.ts.map +1 -0
  41. package/dist/runtime/i18n/FimoLocaleContext.d.ts.map +1 -0
  42. package/dist/runtime/i18n/locale.d.ts.map +1 -0
  43. package/dist/runtime/i18n/locale.test.d.ts +2 -0
  44. package/dist/runtime/i18n/locale.test.d.ts.map +1 -0
  45. package/dist/runtime/i18n/locale.test.js +58 -0
  46. package/dist/runtime/iframe/ConsoleErrorProvider.d.ts.map +1 -0
  47. package/dist/runtime/iframe/ScreenshotProvider.d.ts.map +1 -0
  48. package/dist/runtime/iframe/runtime/overlay-hmr.d.ts.map +1 -0
  49. package/dist/runtime/iframe/runtime/overlay-runtime.d.ts.map +1 -0
  50. package/dist/runtime/index.d.ts +0 -3
  51. package/dist/runtime/index.d.ts.map +1 -0
  52. package/dist/runtime/index.js +0 -2
  53. package/dist/runtime/paths/get-fimo-paths.d.ts.map +1 -0
  54. package/dist/runtime/paths/get-fimo-paths.test.d.ts +2 -0
  55. package/dist/runtime/paths/get-fimo-paths.test.d.ts.map +1 -0
  56. package/dist/runtime/paths/get-fimo-paths.test.js +69 -0
  57. package/dist/runtime/paths/index.d.ts.map +1 -0
  58. package/dist/runtime/paths/types.d.ts.map +1 -0
  59. package/dist/runtime/primitives/components/Boolean.d.ts.map +1 -0
  60. package/dist/runtime/primitives/components/Date.d.ts.map +1 -0
  61. package/dist/runtime/primitives/components/Image.d.ts.map +1 -0
  62. package/dist/runtime/primitives/components/RichText.d.ts.map +1 -0
  63. package/dist/runtime/primitives/components/StaticImage.d.ts.map +1 -0
  64. package/dist/runtime/primitives/components/Text.d.ts.map +1 -0
  65. package/dist/runtime/primitives/components/Video.d.ts.map +1 -0
  66. package/dist/runtime/primitives/components/index.d.ts.map +1 -0
  67. package/dist/runtime/primitives/index.d.ts.map +1 -0
  68. package/dist/runtime/primitives/lib/image-optimization.d.ts.map +1 -0
  69. package/dist/runtime/primitives/lib/index.d.ts.map +1 -0
  70. package/dist/runtime/primitives/lib/primitives.d.ts.map +1 -0
  71. package/dist/runtime/primitives/lib/query.d.ts.map +1 -0
  72. package/dist/runtime/primitives/lib/template.d.ts.map +1 -0
  73. package/dist/runtime/primitives/lib/useFimoLiveValue.d.ts.map +1 -0
  74. package/dist/runtime/primitives/public-surface.test.d.ts +2 -0
  75. package/dist/runtime/primitives/public-surface.test.d.ts.map +1 -0
  76. package/dist/runtime/primitives/public-surface.test.js +49 -0
  77. package/dist/runtime/primitives/translations.d.ts.map +1 -0
  78. package/dist/runtime/react-router/index.d.ts.map +1 -0
  79. package/dist/runtime/react-router/navigation-utils.d.ts.map +1 -0
  80. package/dist/runtime/react-router/navigation.d.ts.map +1 -0
  81. package/dist/runtime/react-router/navigation.test.d.ts +2 -0
  82. package/dist/runtime/react-router/navigation.test.d.ts.map +1 -0
  83. package/dist/runtime/react-router/navigation.test.js +35 -0
  84. package/dist/runtime/react-router/routes.d.ts.map +1 -0
  85. package/dist/runtime/react-router/routes.test.d.ts +2 -0
  86. package/dist/runtime/react-router/routes.test.d.ts.map +1 -0
  87. package/dist/runtime/react-router/routes.test.js +67 -0
  88. package/dist/runtime/routing/AppResetScrollOnNavigate.d.ts.map +1 -0
  89. package/dist/runtime/routing/AppRoutesProvider.d.ts.map +1 -0
  90. package/dist/runtime/seo/buildSeoForPath.d.ts.map +1 -0
  91. package/dist/runtime/seo/htmlProps.d.ts.map +1 -0
  92. package/dist/runtime/seo/index.d.ts.map +1 -0
  93. package/dist/runtime/seo/resolver.d.ts.map +1 -0
  94. package/dist/runtime/shared/fimo-config.server.d.ts.map +1 -0
  95. package/dist/runtime/shared/fimo-config.types.d.ts.map +1 -0
  96. package/dist/runtime/t/OptionalTracking.d.ts.map +1 -0
  97. package/dist/runtime/t/OptionalTracking.test.d.ts +2 -0
  98. package/dist/runtime/t/OptionalTracking.test.d.ts.map +1 -0
  99. package/dist/runtime/t/OptionalTracking.test.js +11 -0
  100. package/dist/runtime/t/Pageview.d.ts.map +1 -0
  101. package/dist/runtime/templates.d.ts.map +1 -0
  102. package/dist/scripts/generated-types.test.js +212 -0
  103. package/dist/scripts/lint-translation-keys.test.d.ts +1 -0
  104. package/dist/scripts/lint-translation-keys.test.js +16 -0
  105. package/dist/scripts/sync-schemas.test.d.ts +1 -0
  106. package/dist/scripts/sync-schemas.test.js +144 -0
  107. package/dist/scripts/validate-route-metadata.test.d.ts +1 -0
  108. package/dist/scripts/validate-route-metadata.test.js +116 -0
  109. package/dist/scripts/validate-schemas.test.d.ts +1 -0
  110. package/dist/scripts/validate-schemas.test.js +263 -0
  111. package/package.json +13 -31
  112. package/scripts/add-ext.mjs +47 -0
  113. package/scripts/bump-version.mjs +58 -0
  114. package/scripts/bundle-cli.mjs +123 -0
  115. package/scripts/install-cli.mjs +36 -0
  116. package/scripts/lib/channels.mjs +70 -0
  117. package/scripts/lib/dev-release-state.mjs +44 -0
  118. package/scripts/lib/dev-release-state.test.ts +36 -0
  119. package/scripts/lib/postinstall-onboarding.test.ts +82 -0
  120. package/scripts/lib/release-core.mjs +225 -0
  121. package/scripts/lib/release-core.test.ts +147 -0
  122. package/scripts/migration-qa-gate.test.ts +155 -0
  123. package/scripts/publish-npm.mjs +180 -0
  124. package/templates/react-router/package.json +1 -1
  125. package/templates/react-router/src/pages/Index.tsx +1 -1
  126. package/dist/runtime/content/client/createContentClient.d.ts +0 -5
  127. package/dist/runtime/content/client/createContentClient.js +0 -300
  128. package/dist/runtime/content/client/createSingletonClient.d.ts +0 -5
  129. package/dist/runtime/content/client/createSingletonClient.js +0 -108
  130. package/dist/runtime/content/client/internal.d.ts +0 -44
  131. package/dist/runtime/content/client/internal.js +0 -236
  132. package/dist/runtime/content/client/types.d.ts +0 -136
  133. package/dist/runtime/content/client/useQueryLocale.d.ts +0 -2
  134. package/dist/runtime/content/client/useQueryLocale.js +0 -6
  135. /package/dist/{runtime/content/client/types.js → scripts/generated-types.test.d.ts} +0 -0
@@ -0,0 +1,155 @@
1
+ import { spawnSync } from 'node:child_process';
2
+ import { mkdirSync, mkdtempSync, readFileSync, rmSync, writeFileSync } from 'node:fs';
3
+ import { tmpdir } from 'node:os';
4
+ import path from 'node:path';
5
+ import { fileURLToPath } from 'node:url';
6
+
7
+ import { afterEach, beforeEach, describe, expect, it } from 'vitest';
8
+
9
+ const __dirname = path.dirname(fileURLToPath(import.meta.url));
10
+ const SCRIPT = path.resolve(__dirname, '../assets/skills/fimo-migration/scripts/write-migration-report.mjs');
11
+
12
+ interface GateResult {
13
+ status: number | null;
14
+ report: string;
15
+ }
16
+
17
+ let dir: string;
18
+
19
+ beforeEach(() => {
20
+ dir = mkdtempSync(path.join(tmpdir(), 'fimo-qa-gate-'));
21
+ });
22
+
23
+ afterEach(() => {
24
+ rmSync(dir, { recursive: true, force: true });
25
+ });
26
+
27
+ function runGate(manifest: unknown, opts: { withBuildArtifact?: boolean } = {}): GateResult {
28
+ mkdirSync(path.join(dir, '.fimo', 'migration'), { recursive: true });
29
+ writeFileSync(path.join(dir, '.fimo', 'migration', 'manifest.json'), JSON.stringify(manifest, null, 2));
30
+ if (opts.withBuildArtifact) {
31
+ mkdirSync(path.join(dir, 'dist', 'client'), { recursive: true });
32
+ writeFileSync(path.join(dir, 'dist', 'client', 'index.html'), '<html></html>');
33
+ }
34
+ const reportPath = path.join(dir, 'report.md');
35
+ const child = spawnSync(process.execPath, [SCRIPT, dir, '--no-update-manifest', '--output', reportPath], {
36
+ encoding: 'utf8',
37
+ });
38
+ return { status: child.status, report: readFileSync(reportPath, 'utf8') };
39
+ }
40
+
41
+ function statusLine(report: string): string {
42
+ const lines = report.split('\n');
43
+ const idx = lines.findIndex((line) => line.trim() === '## Status');
44
+ return lines[idx + 2]?.trim() ?? '';
45
+ }
46
+
47
+ function checkFailed(report: string, id: string): boolean {
48
+ return report.includes(`- [ ] `) && new RegExp(`- \\[ \\] .*\\(${id}\\)`).test(report);
49
+ }
50
+
51
+ describe('migration QA gate (write-migration-report.mjs)', () => {
52
+ it('fails generated-only entries even when seed JSON exists (no silent pass)', () => {
53
+ const { status, report } = runGate({
54
+ source: { mode: 'url-only', platform: 'wix' },
55
+ routes: [{ route: '/', status: 'verified' }],
56
+ contentTypes: [{ name: 'game', status: 'generated' }],
57
+ entries: [{ contentType: 'game', sourceCount: 11, generatedCount: 11, importedCount: 0 }],
58
+ media: [],
59
+ forms: [],
60
+ unresolved: [],
61
+ });
62
+ expect(status).toBe(1);
63
+ expect(statusLine(report)).toBe('draft-only');
64
+ expect(checkFailed(report, 'cms-counts')).toBe(true);
65
+ expect(checkFailed(report, 'schemas-pushed')).toBe(true);
66
+ });
67
+
68
+ it('fails referenced media that is only bundled locally under public/', () => {
69
+ const { status, report } = runGate({
70
+ source: { mode: 'url-only', platform: 'wix' },
71
+ routes: [{ route: '/', status: 'verified' }],
72
+ contentTypes: [],
73
+ entries: [],
74
+ media: [{ id: 'm1', referencedBy: ['/'], status: 'imported', targetUrl: '/assets/thepark/hero.webp' }],
75
+ forms: [],
76
+ unresolved: [],
77
+ });
78
+ expect(status).toBe(1);
79
+ expect(statusLine(report)).toBe('draft-only');
80
+ expect(checkFailed(report, 'media-covered')).toBe(true);
81
+ expect(report).toContain('Uploaded to Fimo: 0');
82
+ expect(report).toContain('Bundled locally (not uploaded): 1');
83
+ });
84
+
85
+ it('passes when entries are imported, schemas pushed, and media uploaded to Fimo', () => {
86
+ const { status, report } = runGate(
87
+ {
88
+ source: { mode: 'url-only', platform: 'wix' },
89
+ routes: [{ route: '/', status: 'verified' }],
90
+ contentTypes: [{ name: 'game', status: 'imported' }],
91
+ entries: [{ contentType: 'game', sourceCount: 3, generatedCount: 3, importedCount: 3, status: 'imported' }],
92
+ media: [
93
+ { id: 'm1', referencedBy: ['/'], targetAssetId: '11111111-1111-1111-1111-111111111111' },
94
+ {
95
+ id: 'm2',
96
+ referencedBy: ['/'],
97
+ targetUrl: 'https://assets.fimo.team/abc/hero.webp',
98
+ sourceUrl: 'https://static.wixstatic.com/hero.webp',
99
+ },
100
+ ],
101
+ forms: [],
102
+ unresolved: [],
103
+ },
104
+ { withBuildArtifact: true }
105
+ );
106
+ expect(status).toBe(0);
107
+ expect(statusLine(report)).toBe('complete');
108
+ expect(report).toContain('Uploaded to Fimo: 2');
109
+ });
110
+
111
+ it('rejects a target URL that merely echoes the source URL (nothing uploaded)', () => {
112
+ const { status, report } = runGate({
113
+ source: { mode: 'url-only', platform: 'wix' },
114
+ routes: [{ route: '/', status: 'verified' }],
115
+ contentTypes: [],
116
+ entries: [],
117
+ media: [
118
+ {
119
+ id: 'm1',
120
+ referencedBy: ['/'],
121
+ sourceUrl: 'https://static.wixstatic.com/hero.webp',
122
+ targetUrl: 'https://static.wixstatic.com/hero.webp',
123
+ },
124
+ ],
125
+ forms: [],
126
+ unresolved: [],
127
+ });
128
+ expect(status).toBe(1);
129
+ expect(checkFailed(report, 'media-covered')).toBe(true);
130
+ });
131
+
132
+ it('allows complete-with-unresolved when gaps are explicit, owner-visible unresolved items', () => {
133
+ const { status, report } = runGate(
134
+ {
135
+ source: { mode: 'url-only', platform: 'wix' },
136
+ routes: [{ route: '/', status: 'verified' }],
137
+ contentTypes: [{ name: 'game', status: 'unresolved' }],
138
+ entries: [{ contentType: 'game', sourceCount: 5, generatedCount: 5, importedCount: 0, status: 'unresolved' }],
139
+ media: [{ id: 'm1', referencedBy: ['/'], status: 'unresolved' }],
140
+ forms: [],
141
+ unresolved: [
142
+ {
143
+ severity: 'high',
144
+ area: 'cms',
145
+ message: 'Game entries need a source export before import.',
146
+ suggestedAction: 'Provide the Wix CMS export and re-run import.',
147
+ },
148
+ ],
149
+ },
150
+ { withBuildArtifact: true }
151
+ );
152
+ expect(status).toBe(0);
153
+ expect(statusLine(report)).toBe('complete-with-unresolved');
154
+ });
155
+ });
@@ -0,0 +1,180 @@
1
+ #!/usr/bin/env node
2
+ // Publish the fimo CLI to npm on a channel.
3
+ //
4
+ // Usage:
5
+ // node scripts/publish-npm.mjs --channel experimental [--dry-run]
6
+ // node scripts/publish-npm.mjs --channel staging [--dry-run]
7
+ // node scripts/publish-npm.mjs --channel latest (CI only; --bootstrap for the first publish)
8
+ // node scripts/publish-npm.mjs --channel experimental --plan (print the plan, touch nothing)
9
+ //
10
+ // What it does:
11
+ // 1. Resolves the channel from the shared table (lib/channels.mjs).
12
+ // 2. Guards: @latest refuses to publish outside CI unless --bootstrap.
13
+ // 3. Builds in place (clean → build → build:bundle) with the workspace
14
+ // manifest UNTOUCHED, then `npm pack`s straight into a temp stage dir.
15
+ // 4. Mutates ONLY the extracted stage copy: synthesized X.Y.Z-<tag>.<n>
16
+ // version, template fimo pin, channel release.json (allowlist-enforced —
17
+ // staging URLs for experimental/staging, prod for latest, never a tunnel).
18
+ // 5. Runs `npm publish --tag <distTag>` from the stage (no --provenance —
19
+ // strapi/fimo is private).
20
+ // 6. On real experimental publishes, records the exact version in repo-root
21
+ // .fimo/dev.json for local source-checkout deploys.
22
+ //
23
+ // The workspace is never mutated: no transient package.json version, no
24
+ // release.json, no template rewrites — a crash or a long OTP prompt can't
25
+ // leave the repo dirty (it used to sit mutated for the whole multi-minute
26
+ // build + 2FA window).
27
+
28
+ import { spawnSync } from 'node:child_process';
29
+ import { mkdtempSync, readFileSync, readdirSync, rmSync, writeFileSync } from 'node:fs';
30
+ import { tmpdir } from 'node:os';
31
+ import { dirname, join, resolve } from 'node:path';
32
+ import { fileURLToPath } from 'node:url';
33
+
34
+ import { resolveChannel } from './lib/channels.mjs';
35
+ import { writeDevReleaseState } from './lib/dev-release-state.mjs';
36
+ import {
37
+ computePublishVersion,
38
+ nextVersionFromChangesets,
39
+ parsePublishedVersions,
40
+ pinTemplateFimo,
41
+ stripTemplateLockfile,
42
+ writeReleaseJson,
43
+ } from './lib/release-core.mjs';
44
+
45
+ const HERE = dirname(fileURLToPath(import.meta.url));
46
+ const CLI_ROOT = resolve(HERE, '..');
47
+ const PKG_PATH = resolve(CLI_ROOT, 'package.json');
48
+
49
+ function parseArgs(argv) {
50
+ const args = { channel: null, dryRun: false, bootstrap: false, plan: false };
51
+ for (let i = 0; i < argv.length; i++) {
52
+ const arg = argv[i];
53
+ if (arg === '--channel') {
54
+ args.channel = argv[++i];
55
+ } else if (arg === '--dry-run') {
56
+ args.dryRun = true;
57
+ } else if (arg === '--bootstrap') {
58
+ args.bootstrap = true;
59
+ } else if (arg === '--plan') {
60
+ args.plan = true;
61
+ }
62
+ }
63
+ return args;
64
+ }
65
+
66
+ function run(cmd, args, cwd) {
67
+ console.log(`[publish-npm] $ ${cmd} ${args.join(' ')}`);
68
+ const result = spawnSync(cmd, args, { cwd, stdio: 'inherit' });
69
+ if (result.status !== 0) {
70
+ console.error(`[publish-npm] ${cmd} ${args[0]} failed (exit ${result.status}).`);
71
+ process.exit(result.status ?? 1);
72
+ }
73
+ }
74
+
75
+ // All published versions of the package, or null when the registry can't be
76
+ // reached (offline, first-ever publish) or the output shape is unrecognized —
77
+ // callers fall back to the run-number/timestamp counter.
78
+ function fetchPublishedVersions(name) {
79
+ const result = spawnSync('npm', ['view', name, 'versions', '--json'], { encoding: 'utf8' });
80
+ if (result.status !== 0) {
81
+ return null;
82
+ }
83
+ return parsePublishedVersions(result.stdout);
84
+ }
85
+
86
+ const { channel: channelName, dryRun, bootstrap, plan } = parseArgs(process.argv.slice(2));
87
+
88
+ if (!channelName) {
89
+ console.error('[publish-npm] missing --channel <experimental|staging|latest>');
90
+ process.exit(1);
91
+ }
92
+
93
+ const channel = resolveChannel(channelName);
94
+ const isCI = !!process.env.CI;
95
+
96
+ // Prod guard (D11): @latest never originates on a laptop.
97
+ if (channel.distTag === 'latest' && !isCI && !bootstrap) {
98
+ console.error(
99
+ '[publish-npm] refusing to publish @latest outside CI.\n' +
100
+ ' Latest ships from CI on the production branch. For the first-ever publish, pass --bootstrap.'
101
+ );
102
+ process.exit(1);
103
+ }
104
+
105
+ const pkg = JSON.parse(readFileSync(PKG_PATH, 'utf8'));
106
+ // Prerelease channels base off the version the pending changesets will
107
+ // produce at the next production release, so a staging build advertises the
108
+ // version prod will actually ship.
109
+ const baseVersion = channel.prerelease
110
+ ? nextVersionFromChangesets(pkg.version, resolve(CLI_ROOT, '../../.changeset'))
111
+ : pkg.version;
112
+ const runId = process.env.GITHUB_RUN_NUMBER;
113
+ let existingVersions;
114
+ if (channel.prerelease) {
115
+ existingVersions = fetchPublishedVersions(pkg.name) ?? undefined;
116
+ if (!existingVersions) {
117
+ console.warn('[publish-npm] could not fetch published versions from npm — using run/timestamp counter.');
118
+ }
119
+ }
120
+ const publishVersion = computePublishVersion(baseVersion, channel, { runId, existingVersions });
121
+
122
+ console.log(`[publish-npm] channel: ${channelName} (dist-tag: ${channel.distTag})`);
123
+ console.log(
124
+ `[publish-npm] version: ${publishVersion}${publishVersion !== baseVersion ? ` (from ${baseVersion})` : ''}`
125
+ );
126
+ console.log(`[publish-npm] apiUrl: ${channel.apiUrl}`);
127
+ console.log(`[publish-npm] webUrl: ${channel.webUrl}`);
128
+
129
+ if (plan) {
130
+ console.log('[publish-npm] --plan: nothing written, nothing published.');
131
+ process.exit(0);
132
+ }
133
+
134
+ const stageRoot = mkdtempSync(join(tmpdir(), 'fimo-publish-'));
135
+ try {
136
+ // 1. Build in place — the build output doesn't depend on the version, so
137
+ // the workspace manifest stays untouched.
138
+ run('pnpm', ['run', 'prepublishOnly'], CLI_ROOT);
139
+
140
+ // 2. Pack the workspace tarball (honors the `files` whitelist) directly
141
+ // into the stage dir; --ignore-scripts because we just built.
142
+ run('npm', ['pack', '--ignore-scripts', '--pack-destination', stageRoot], CLI_ROOT);
143
+ const tarball = readdirSync(stageRoot).find((name) => name.endsWith('.tgz'));
144
+ if (!tarball) {
145
+ console.error(`[publish-npm] npm pack produced no tarball in ${stageRoot}.`);
146
+ process.exit(1);
147
+ }
148
+ run('tar', ['-xzf', join(stageRoot, tarball), '-C', stageRoot], stageRoot);
149
+ const stage = join(stageRoot, 'package');
150
+
151
+ // 3. Mutate ONLY the stage copy: synthesized version, template pin,
152
+ // channel release.json.
153
+ const stagePkg = JSON.parse(readFileSync(join(stage, 'package.json'), 'utf8'));
154
+ writeFileSync(join(stage, 'package.json'), JSON.stringify({ ...stagePkg, version: publishVersion }, null, 2) + '\n');
155
+ pinTemplateFimo(stage, publishVersion);
156
+ stripTemplateLockfile(stage);
157
+ writeReleaseJson(stage, channel);
158
+
159
+ // 4. Publish from the stage. No --provenance: sigstore provenance requires
160
+ // a PUBLIC source repo, and strapi/fimo is private (npm returns 422
161
+ // otherwise). Trusted Publishing (OIDC auth) works regardless of repo
162
+ // visibility — it's only the attestation that's public-only.
163
+ const publishArgs = ['publish', '--access', 'public', '--tag', channel.distTag, '--ignore-scripts'];
164
+ if (dryRun) {
165
+ publishArgs.push('--dry-run');
166
+ }
167
+ run('npm', publishArgs, stage);
168
+
169
+ if (!dryRun && channel.distTag === 'experimental') {
170
+ const devStatePath = writeDevReleaseState(CLI_ROOT, {
171
+ version: publishVersion,
172
+ channel: channel.distTag,
173
+ });
174
+ console.log(`[publish-npm] wrote ${devStatePath}`);
175
+ }
176
+ } finally {
177
+ rmSync(stageRoot, { recursive: true, force: true });
178
+ }
179
+
180
+ console.log(`[publish-npm] done${dryRun ? ' (dry-run — nothing published)' : ''}.`);
@@ -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.7.0-experimental.2",
24
+ "fimo": "0.7.0-staging.1",
25
25
  "input-otp": "^1.4.2",
26
26
  "isbot": "^5",
27
27
  "lucide-react": "^0.577.0",
@@ -29,7 +29,7 @@ const Index = () => {
29
29
  <a href="https://fimo.ai" target="_blank" rel="noreferrer">
30
30
  Fimo.ai
31
31
  </a>
32
- <a href="https://fimo.ai/docs" target="_blank" rel="noreferrer">
32
+ <a href="https://docs.fimo.ai" target="_blank" rel="noreferrer">
33
33
  Documentation
34
34
  </a>
35
35
  <a href="https://blog.fimo.ai" target="_blank" rel="noreferrer">
@@ -1,5 +0,0 @@
1
- import type { CollectionContentClient, ContentClientConfig } from './types.js';
2
- export declare function createContentClient<T extends {
3
- id: string;
4
- }, TInput extends object>(config: ContentClientConfig): CollectionContentClient<T, TInput>;
5
- //# sourceMappingURL=createContentClient.d.ts.map
@@ -1,300 +0,0 @@
1
- import { useInfiniteQuery, useMutation, useQuery, useQueryClient, useSuspenseQuery } from '@tanstack/react-query';
2
- import { parseCollectionResponse } from '../parseCollectionResponse.js';
3
- import { apiBase, buildReadPath, fimoHeaders, interpretSchema, readCollectionLocale, resolveClientHandle, splitWritePayload, validateReferences, withMutationLocale, wrapEntry, } from './internal.js';
4
- import { useQueryLocale } from './useQueryLocale.js';
5
- export function createContentClient(config) {
6
- const references = config.references ?? {};
7
- const info = interpretSchema(config.schema);
8
- if (info.isSingleton) {
9
- throw new Error(`Schema "${info.uid}" is a singleton — use createSingletonClient instead.`);
10
- }
11
- validateReferences(info, references);
12
- const meta = { uid: info.uid, pascal: info.pascal, localized: info.localized };
13
- const self = { wrapWithSource, meta };
14
- const resolve = (uid) => uid === info.uid ? self : resolveClientHandle(references[uid]);
15
- function wrapWithSource(entry) {
16
- return wrapEntry(info, entry, { alwaysIncludeLocale: true, resolve });
17
- }
18
- function readPath(url, options) {
19
- return buildReadPath(url, readCollectionLocale(options), options.populate, options.localeFallbacks);
20
- }
21
- async function getById(id, options = {}) {
22
- const res = await fetch(`${apiBase()}${readPath(`/entries/${info.uid}/${id}`, options)}`, {
23
- headers: fimoHeaders(),
24
- });
25
- if (!res.ok) {
26
- throw new Error(await res.text());
27
- }
28
- const data = await res.json();
29
- return wrapWithSource(data.data);
30
- }
31
- async function getBySlug(slug, options = {}) {
32
- const res = await fetch(`${apiBase()}${readPath(`/entries/${info.uid}/slug/${encodeURIComponent(slug)}`, options)}`, { headers: fimoHeaders() });
33
- if (res.status === 404) {
34
- return null;
35
- }
36
- if (!res.ok) {
37
- throw new Error(await res.text());
38
- }
39
- const data = await res.json();
40
- return wrapWithSource(data.data);
41
- }
42
- async function getByField(fieldName, value, options = {}) {
43
- const res = await fetch(`${apiBase()}${readPath(`/entries/${info.uid}/field/${encodeURIComponent(fieldName)}/${encodeURIComponent(value)}`, options)}`, { headers: fimoHeaders() });
44
- if (res.status === 404) {
45
- return null;
46
- }
47
- if (!res.ok) {
48
- throw new Error(await res.text());
49
- }
50
- const data = await res.json();
51
- return wrapWithSource(data.data);
52
- }
53
- function toQueryString(params) {
54
- const { where, sort, fields, populate, locale: requestedLocale, ...flat } = params;
55
- const query = new URLSearchParams();
56
- for (const [key, value] of Object.entries(flat)) {
57
- if (value == null) {
58
- continue;
59
- }
60
- query.set(key, String(value));
61
- }
62
- if (sort != null) {
63
- query.set('sort', Array.isArray(sort) ? sort.join(',') : String(sort));
64
- }
65
- if (fields != null) {
66
- query.set('fields', typeof fields === 'string' ? fields : fields.join(','));
67
- }
68
- if (populate != null) {
69
- query.set('populate', typeof populate === 'string' ? populate : populate.join(','));
70
- }
71
- if (where && Object.keys(where).length > 0) {
72
- query.set('where', JSON.stringify(where));
73
- }
74
- const locale = readCollectionLocale({ locale: requestedLocale });
75
- if (locale) {
76
- query.set('locale', locale);
77
- }
78
- return query.toString();
79
- }
80
- async function list(params = {}) {
81
- const qs = toQueryString(params);
82
- const res = await fetch(`${apiBase()}/entries/${info.uid}${qs ? '?' + qs : ''}`, {
83
- headers: fimoHeaders(),
84
- });
85
- if (!res.ok) {
86
- throw new Error(await res.text());
87
- }
88
- const data = parseCollectionResponse(await res.json());
89
- const items = data.data.map(wrapWithSource);
90
- const pageInfo = {
91
- limit: data.meta.limit,
92
- offset: data.meta.offset,
93
- total: data.meta.total,
94
- hasMore: data.meta.hasMore,
95
- nextOffset: data.meta.hasMore ? data.meta.offset + data.meta.limit : null,
96
- };
97
- return { items, pageInfo };
98
- }
99
- async function get(params = {}) {
100
- return (await list(params)).items;
101
- }
102
- async function create(payload) {
103
- const res = await fetch(`${apiBase()}/entries/${info.uid}`, {
104
- method: 'POST',
105
- headers: fimoHeaders({ 'Content-Type': 'application/json' }),
106
- body: JSON.stringify(splitWritePayload(payload)),
107
- });
108
- if (!res.ok) {
109
- throw new Error(await res.text());
110
- }
111
- const data = await res.json();
112
- return wrapWithSource(data.data);
113
- }
114
- async function update(id, payload) {
115
- const write = splitWritePayload(payload);
116
- const url = buildReadPath(`/entries/${info.uid}/${id}`, readCollectionLocale({ locale: write.locale }), undefined);
117
- const res = await fetch(`${apiBase()}${url}`, {
118
- method: 'PUT',
119
- headers: fimoHeaders({ 'Content-Type': 'application/json' }),
120
- body: JSON.stringify({ data: write.data }),
121
- });
122
- if (!res.ok) {
123
- throw new Error(await res.text());
124
- }
125
- const data = await res.json();
126
- return wrapWithSource(data.data);
127
- }
128
- async function remove(id, options = {}) {
129
- const url = buildReadPath(`/entries/${info.uid}/${id}`, readCollectionLocale(options), undefined);
130
- const res = await fetch(`${apiBase()}${url}`, { method: 'DELETE', headers: fimoHeaders() });
131
- if (!res.ok) {
132
- throw new Error(await res.text());
133
- }
134
- }
135
- const qk = {
136
- root: [info.pascal],
137
- byId: (id, options = {}) => [info.pascal, 'id', id, options.locale, options.populate],
138
- bySlug: (slug, options = {}) => [info.pascal, 'slug', slug, options.locale, options.populate],
139
- byField: (field, value, options = {}) => [info.pascal, field, value, options.locale, options.populate],
140
- list: (params) => [info.pascal, params],
141
- infiniteList: (params) => [info.pascal, 'infinite', params],
142
- };
143
- function useGetById(id, options = {}) {
144
- const locale = useQueryLocale(options.locale);
145
- const localizedOptions = { ...options, locale };
146
- return useQuery({
147
- queryKey: qk.byId(id, localizedOptions),
148
- queryFn: () => getById(id, localizedOptions),
149
- });
150
- }
151
- function useGetBySlug(slug, options = {}) {
152
- const locale = readCollectionLocale(options, useQueryLocale(options.locale));
153
- const localizedOptions = { ...options, locale };
154
- return useQuery({
155
- queryKey: qk.bySlug(slug, localizedOptions),
156
- queryFn: () => getBySlug(slug, localizedOptions),
157
- enabled: !!slug,
158
- });
159
- }
160
- function useGetByField(fieldName, value, options = {}) {
161
- const locale = readCollectionLocale(options, useQueryLocale(options.locale));
162
- const localizedOptions = { ...options, locale };
163
- return useQuery({
164
- queryKey: qk.byField(fieldName, value, localizedOptions),
165
- queryFn: () => getByField(fieldName, value, localizedOptions),
166
- enabled: !!value,
167
- });
168
- }
169
- function useGet(params = {}) {
170
- const locale = readCollectionLocale(params, useQueryLocale(params.locale));
171
- const localizedParams = { ...params, locale };
172
- return useQuery({
173
- queryKey: qk.list(localizedParams),
174
- queryFn: () => list(localizedParams),
175
- select: (page) => page.items,
176
- });
177
- }
178
- function useList(params = {}) {
179
- const locale = readCollectionLocale(params, useQueryLocale(params.locale));
180
- const localizedParams = { ...params, locale };
181
- return useQuery({
182
- queryKey: qk.list(localizedParams),
183
- queryFn: () => list(localizedParams),
184
- });
185
- }
186
- function useInfiniteList(params = {}) {
187
- const locale = readCollectionLocale(params, useQueryLocale(params.locale));
188
- const localizedParams = { ...params, locale };
189
- const query = useInfiniteQuery({
190
- queryKey: qk.infiniteList(localizedParams),
191
- queryFn: ({ pageParam }) => list({ ...localizedParams, offset: pageParam }),
192
- initialPageParam: 0,
193
- getNextPageParam: (lastPage) => lastPage.pageInfo.nextOffset ?? undefined,
194
- });
195
- return {
196
- ...query,
197
- items: query.data?.pages.flatMap((page) => page.items) ?? [],
198
- total: query.data?.pages[0]?.pageInfo.total ?? 0,
199
- hasMore: query.hasNextPage,
200
- loadMore: () => query.fetchNextPage(),
201
- isLoadingMore: query.isFetchingNextPage,
202
- };
203
- }
204
- function useSuspenseGetById(id, options = {}) {
205
- const locale = useQueryLocale(options.locale);
206
- const localizedOptions = { ...options, locale };
207
- return useSuspenseQuery({
208
- queryKey: qk.byId(id, localizedOptions),
209
- queryFn: () => getById(id, localizedOptions),
210
- });
211
- }
212
- function useSuspenseGetBySlug(slug, options = {}) {
213
- const locale = readCollectionLocale(options, useQueryLocale(options.locale));
214
- const localizedOptions = { ...options, locale };
215
- return useSuspenseQuery({
216
- queryKey: qk.bySlug(slug, localizedOptions),
217
- queryFn: () => getBySlug(slug, localizedOptions),
218
- });
219
- }
220
- function useSuspenseGetByField(fieldName, value, options = {}) {
221
- const locale = readCollectionLocale(options, useQueryLocale(options.locale));
222
- const localizedOptions = { ...options, locale };
223
- return useSuspenseQuery({
224
- queryKey: qk.byField(fieldName, value, localizedOptions),
225
- queryFn: () => getByField(fieldName, value, localizedOptions),
226
- });
227
- }
228
- function useSuspenseGet(params = {}) {
229
- const locale = readCollectionLocale(params, useQueryLocale(params.locale));
230
- const localizedParams = { ...params, locale };
231
- return useSuspenseQuery({
232
- queryKey: qk.list(localizedParams),
233
- queryFn: () => list(localizedParams),
234
- select: (page) => page.items,
235
- });
236
- }
237
- function useCreate() {
238
- const qc = useQueryClient();
239
- const activeLocale = useQueryLocale();
240
- return useMutation({
241
- mutationFn: (payload) => create(withMutationLocale(payload, activeLocale)),
242
- onSuccess: () => qc.invalidateQueries({ queryKey: qk.root }),
243
- });
244
- }
245
- function useUpdate() {
246
- const qc = useQueryClient();
247
- const activeLocale = useQueryLocale();
248
- return useMutation({
249
- mutationFn: ({ id, ...patch }) => update(id, withMutationLocale(patch, activeLocale)),
250
- onSuccess: (_data, vars) => {
251
- qc.invalidateQueries({ queryKey: qk.root });
252
- qc.invalidateQueries({ queryKey: qk.byId(vars.id, { locale: vars.locale ?? activeLocale }) });
253
- },
254
- });
255
- }
256
- function useRemove() {
257
- const qc = useQueryClient();
258
- const activeLocale = useQueryLocale();
259
- return useMutation({
260
- mutationFn: (value) => {
261
- const { id, locale } = typeof value === 'string'
262
- ? { id: value, locale: activeLocale }
263
- : { ...value, locale: value.locale ?? activeLocale };
264
- return remove(id, { locale });
265
- },
266
- onSuccess: (_data, value) => {
267
- const { id, locale } = typeof value === 'string'
268
- ? { id: value, locale: activeLocale }
269
- : { ...value, locale: value.locale ?? activeLocale };
270
- qc.invalidateQueries({ queryKey: qk.root });
271
- qc.removeQueries({ queryKey: qk.byId(id, { locale }), exact: false });
272
- },
273
- });
274
- }
275
- return {
276
- meta,
277
- wrapWithSource,
278
- getById,
279
- getBySlug,
280
- getByField,
281
- list,
282
- get,
283
- create,
284
- update,
285
- remove,
286
- useGetById,
287
- useGetBySlug,
288
- useGetByField,
289
- useGet,
290
- useList,
291
- useInfiniteList,
292
- useSuspenseGetById,
293
- useSuspenseGetBySlug,
294
- useSuspenseGetByField,
295
- useSuspenseGet,
296
- useCreate,
297
- useUpdate,
298
- useRemove,
299
- };
300
- }
@@ -1,5 +0,0 @@
1
- import type { ContentClientConfig, SingletonContentClient } from './types.js';
2
- export declare function createSingletonClient<T extends {
3
- id: string;
4
- }, TInput extends object>(config: ContentClientConfig): SingletonContentClient<T, TInput>;
5
- //# sourceMappingURL=createSingletonClient.d.ts.map