fimo 0.7.0-staging.1 → 0.7.0-staging.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +3 -2
- package/assets/skills/fimo/SKILL.md +1 -1
- package/assets/skills/fimo/references/assets.md +2 -2
- package/assets/skills/fimo/references/content.md +1 -1
- package/assets/skills/fimo/references/design.md +1 -1
- package/assets/skills/fimo-cli/SKILL.md +1 -1
- package/assets/skills/fimo-cli/references/assets.md +7 -24
- package/assets/skills/fimo-cli/references/content.md +2 -2
- package/assets/skills/fimo-cli/references/domains.md +1 -1
- package/assets/skills/fimo-cli/references/media.md +132 -142
- package/assets/skills/fimo-studio/references/custom-domains.md +1 -1
- package/dist/cli/bundle.json +3 -2
- package/dist/cli/index.js +1203 -110677
- package/dist/runtime/content/client/createContentClient.d.ts +5 -0
- package/dist/runtime/content/client/createContentClient.js +300 -0
- package/dist/runtime/content/client/createSingletonClient.d.ts +5 -0
- package/dist/runtime/content/client/createSingletonClient.js +108 -0
- package/dist/runtime/content/client/internal.d.ts +44 -0
- package/dist/runtime/content/client/internal.js +236 -0
- package/dist/runtime/content/client/types.d.ts +136 -0
- package/dist/runtime/content/client/useQueryLocale.d.ts +2 -0
- package/dist/runtime/content/client/useQueryLocale.js +6 -0
- package/dist/runtime/index.d.ts +3 -0
- package/dist/runtime/index.js +2 -0
- package/package.json +31 -13
- package/templates/react-router/package.json +1 -1
- package/templates/react-router/src/pages/Index.tsx +1 -1
- package/dist/build/vite/index.d.ts.map +0 -1
- package/dist/build/vite/plugins/content-sync.d.ts.map +0 -1
- package/dist/build/vite/plugins/content-sync.test.d.ts +0 -2
- package/dist/build/vite/plugins/content-sync.test.d.ts.map +0 -1
- package/dist/build/vite/plugins/content-sync.test.js +0 -64
- package/dist/build/vite/plugins/data-id.d.ts.map +0 -1
- package/dist/build/vite/plugins/fimo-config.d.ts.map +0 -1
- package/dist/build/vite/plugins/fimo-config.test.d.ts +0 -2
- package/dist/build/vite/plugins/fimo-config.test.d.ts.map +0 -1
- package/dist/build/vite/plugins/fimo-config.test.js +0 -46
- package/dist/build/vite/plugins/overlay.d.ts.map +0 -1
- package/dist/build/vite/plugins/robots.d.ts.map +0 -1
- package/dist/build/vite/plugins/seo.d.ts.map +0 -1
- package/dist/build/vite/plugins/server-config.d.ts.map +0 -1
- package/dist/build/vite/plugins/sitemap.d.ts.map +0 -1
- package/dist/build/vite/plugins/translations.d.ts.map +0 -1
- package/dist/build/vite/plugins/translations.test.d.ts +0 -2
- package/dist/build/vite/plugins/translations.test.d.ts.map +0 -1
- package/dist/build/vite/plugins/translations.test.js +0 -266
- package/dist/runtime/app/FimoProviders.d.ts.map +0 -1
- package/dist/runtime/app/FimoScripts.d.ts.map +0 -1
- package/dist/runtime/app/index.d.ts.map +0 -1
- package/dist/runtime/app/prefetch.d.ts.map +0 -1
- package/dist/runtime/content/DataSourcesProvider.d.ts.map +0 -1
- package/dist/runtime/content/RefreshContentProvider.d.ts.map +0 -1
- package/dist/runtime/content/RefreshContentProvider.test.d.ts +0 -2
- package/dist/runtime/content/RefreshContentProvider.test.d.ts.map +0 -1
- package/dist/runtime/content/RefreshContentProvider.test.js +0 -33
- package/dist/runtime/content/parseCollectionResponse.d.ts.map +0 -1
- package/dist/runtime/content/parseCollectionResponse.test.d.ts +0 -2
- package/dist/runtime/content/parseCollectionResponse.test.d.ts.map +0 -1
- package/dist/runtime/content/parseCollectionResponse.test.js +0 -20
- package/dist/runtime/content/utils/retrieve-data-sources.d.ts.map +0 -1
- package/dist/runtime/core/AppErrorBoundary.d.ts.map +0 -1
- package/dist/runtime/core/ViteLifecycles.d.ts.map +0 -1
- package/dist/runtime/i18n/FimoLocaleContext.d.ts.map +0 -1
- package/dist/runtime/i18n/locale.d.ts.map +0 -1
- package/dist/runtime/i18n/locale.test.d.ts +0 -2
- package/dist/runtime/i18n/locale.test.d.ts.map +0 -1
- package/dist/runtime/i18n/locale.test.js +0 -58
- package/dist/runtime/iframe/ConsoleErrorProvider.d.ts.map +0 -1
- package/dist/runtime/iframe/ScreenshotProvider.d.ts.map +0 -1
- package/dist/runtime/iframe/runtime/overlay-hmr.d.ts.map +0 -1
- package/dist/runtime/iframe/runtime/overlay-runtime.d.ts.map +0 -1
- package/dist/runtime/index.d.ts.map +0 -1
- package/dist/runtime/paths/get-fimo-paths.d.ts.map +0 -1
- package/dist/runtime/paths/get-fimo-paths.test.d.ts +0 -2
- package/dist/runtime/paths/get-fimo-paths.test.d.ts.map +0 -1
- package/dist/runtime/paths/get-fimo-paths.test.js +0 -69
- package/dist/runtime/paths/index.d.ts.map +0 -1
- package/dist/runtime/paths/types.d.ts.map +0 -1
- package/dist/runtime/primitives/components/Boolean.d.ts.map +0 -1
- package/dist/runtime/primitives/components/Date.d.ts.map +0 -1
- package/dist/runtime/primitives/components/Image.d.ts.map +0 -1
- package/dist/runtime/primitives/components/RichText.d.ts.map +0 -1
- package/dist/runtime/primitives/components/StaticImage.d.ts.map +0 -1
- package/dist/runtime/primitives/components/Text.d.ts.map +0 -1
- package/dist/runtime/primitives/components/Video.d.ts.map +0 -1
- package/dist/runtime/primitives/components/index.d.ts.map +0 -1
- package/dist/runtime/primitives/index.d.ts.map +0 -1
- package/dist/runtime/primitives/lib/image-optimization.d.ts.map +0 -1
- package/dist/runtime/primitives/lib/index.d.ts.map +0 -1
- package/dist/runtime/primitives/lib/primitives.d.ts.map +0 -1
- package/dist/runtime/primitives/lib/query.d.ts.map +0 -1
- package/dist/runtime/primitives/lib/template.d.ts.map +0 -1
- package/dist/runtime/primitives/lib/useFimoLiveValue.d.ts.map +0 -1
- package/dist/runtime/primitives/public-surface.test.d.ts +0 -2
- package/dist/runtime/primitives/public-surface.test.d.ts.map +0 -1
- package/dist/runtime/primitives/public-surface.test.js +0 -49
- package/dist/runtime/primitives/translations.d.ts.map +0 -1
- package/dist/runtime/react-router/index.d.ts.map +0 -1
- package/dist/runtime/react-router/navigation-utils.d.ts.map +0 -1
- package/dist/runtime/react-router/navigation.d.ts.map +0 -1
- package/dist/runtime/react-router/navigation.test.d.ts +0 -2
- package/dist/runtime/react-router/navigation.test.d.ts.map +0 -1
- package/dist/runtime/react-router/navigation.test.js +0 -35
- package/dist/runtime/react-router/routes.d.ts.map +0 -1
- package/dist/runtime/react-router/routes.test.d.ts +0 -2
- package/dist/runtime/react-router/routes.test.d.ts.map +0 -1
- package/dist/runtime/react-router/routes.test.js +0 -67
- package/dist/runtime/routing/AppResetScrollOnNavigate.d.ts.map +0 -1
- package/dist/runtime/routing/AppRoutesProvider.d.ts.map +0 -1
- package/dist/runtime/seo/buildSeoForPath.d.ts.map +0 -1
- package/dist/runtime/seo/htmlProps.d.ts.map +0 -1
- package/dist/runtime/seo/index.d.ts.map +0 -1
- package/dist/runtime/seo/resolver.d.ts.map +0 -1
- package/dist/runtime/shared/fimo-config.server.d.ts.map +0 -1
- package/dist/runtime/shared/fimo-config.types.d.ts.map +0 -1
- package/dist/runtime/t/OptionalTracking.d.ts.map +0 -1
- package/dist/runtime/t/OptionalTracking.test.d.ts +0 -2
- package/dist/runtime/t/OptionalTracking.test.d.ts.map +0 -1
- package/dist/runtime/t/OptionalTracking.test.js +0 -11
- package/dist/runtime/t/Pageview.d.ts.map +0 -1
- package/dist/runtime/templates.d.ts.map +0 -1
- package/dist/scripts/generated-types.test.js +0 -212
- package/dist/scripts/lint-translation-keys.test.d.ts +0 -1
- package/dist/scripts/lint-translation-keys.test.js +0 -16
- package/dist/scripts/sync-schemas.test.d.ts +0 -1
- package/dist/scripts/sync-schemas.test.js +0 -144
- package/dist/scripts/validate-route-metadata.test.d.ts +0 -1
- package/dist/scripts/validate-route-metadata.test.js +0 -116
- package/dist/scripts/validate-schemas.test.d.ts +0 -1
- package/dist/scripts/validate-schemas.test.js +0 -263
- package/scripts/add-ext.mjs +0 -47
- package/scripts/bump-version.mjs +0 -58
- package/scripts/bundle-cli.mjs +0 -123
- package/scripts/install-cli.mjs +0 -36
- package/scripts/lib/channels.mjs +0 -70
- package/scripts/lib/dev-release-state.mjs +0 -44
- package/scripts/lib/dev-release-state.test.ts +0 -36
- package/scripts/lib/postinstall-onboarding.test.ts +0 -82
- package/scripts/lib/release-core.mjs +0 -225
- package/scripts/lib/release-core.test.ts +0 -147
- package/scripts/migration-qa-gate.test.ts +0 -155
- package/scripts/publish-npm.mjs +0 -180
- /package/dist/{scripts/generated-types.test.d.ts → runtime/content/client/types.js} +0 -0
|
@@ -1,147 +0,0 @@
|
|
|
1
|
-
import { mkdtempSync, rmSync, writeFileSync } from 'node:fs';
|
|
2
|
-
import { tmpdir } from 'node:os';
|
|
3
|
-
import { join } from 'node:path';
|
|
4
|
-
|
|
5
|
-
import { afterEach, describe, expect, it } from 'vitest';
|
|
6
|
-
|
|
7
|
-
import {
|
|
8
|
-
computePublishVersion,
|
|
9
|
-
nextPrereleaseCounter,
|
|
10
|
-
nextVersionFromChangesets,
|
|
11
|
-
parsePublishedVersions,
|
|
12
|
-
// @ts-expect-error plain mjs module without types
|
|
13
|
-
} from './release-core.mjs';
|
|
14
|
-
|
|
15
|
-
const stagingChannel = { prerelease: true, distTag: 'staging' };
|
|
16
|
-
const latestChannel = { prerelease: false, distTag: 'latest' };
|
|
17
|
-
|
|
18
|
-
describe('computePublishVersion', () => {
|
|
19
|
-
it('returns the base version verbatim for stable channels', () => {
|
|
20
|
-
expect(computePublishVersion('0.3.3', latestChannel, { runId: '7' })).toBe('0.3.3');
|
|
21
|
-
});
|
|
22
|
-
|
|
23
|
-
it('synthesizes a prerelease version from the run id', () => {
|
|
24
|
-
expect(computePublishVersion('0.3.3', stagingChannel, { runId: '26' })).toBe('0.3.3-staging.26');
|
|
25
|
-
});
|
|
26
|
-
|
|
27
|
-
it('strips an existing prerelease suffix before synthesizing', () => {
|
|
28
|
-
expect(computePublishVersion('0.3.3-staging.9', stagingChannel, { runId: '26' })).toBe('0.3.3-staging.26');
|
|
29
|
-
});
|
|
30
|
-
|
|
31
|
-
it('counts per base version when published versions are provided', () => {
|
|
32
|
-
const existingVersions = ['0.3.4', '0.3.5-staging.1', '0.3.5-staging.2'];
|
|
33
|
-
expect(computePublishVersion('0.3.5', stagingChannel, { runId: '30', existingVersions })).toBe('0.3.5-staging.3');
|
|
34
|
-
});
|
|
35
|
-
|
|
36
|
-
it('starts at 1 for a base with no published prereleases', () => {
|
|
37
|
-
const existingVersions = ['0.3.4', '0.3.4-staging.26', '0.3.4-staging.27'];
|
|
38
|
-
expect(computePublishVersion('0.3.5', stagingChannel, { runId: '30', existingVersions })).toBe('0.3.5-staging.1');
|
|
39
|
-
});
|
|
40
|
-
});
|
|
41
|
-
|
|
42
|
-
describe('parsePublishedVersions', () => {
|
|
43
|
-
it('passes through the npm <= 11 array-of-strings shape', () => {
|
|
44
|
-
expect(parsePublishedVersions('["0.3.4", "0.3.5-staging.1"]')).toEqual(['0.3.4', '0.3.5-staging.1']);
|
|
45
|
-
});
|
|
46
|
-
|
|
47
|
-
it('wraps the npm <= 11 bare string returned for a single published version', () => {
|
|
48
|
-
expect(parsePublishedVersions('"0.0.0"')).toEqual(['0.0.0']);
|
|
49
|
-
});
|
|
50
|
-
|
|
51
|
-
it('unwraps the npm 12 shape (--json always returns an array)', () => {
|
|
52
|
-
expect(parsePublishedVersions('[["0.3.4", "0.3.5-staging.1", "0.3.5"]]')).toEqual([
|
|
53
|
-
'0.3.4',
|
|
54
|
-
'0.3.5-staging.1',
|
|
55
|
-
'0.3.5',
|
|
56
|
-
]);
|
|
57
|
-
});
|
|
58
|
-
|
|
59
|
-
it('returns null for unparseable output', () => {
|
|
60
|
-
expect(parsePublishedVersions('')).toBeNull();
|
|
61
|
-
expect(parsePublishedVersions('npm ERR! something')).toBeNull();
|
|
62
|
-
});
|
|
63
|
-
|
|
64
|
-
it('returns null when nothing version-shaped survives (error envelopes, empty lists)', () => {
|
|
65
|
-
expect(parsePublishedVersions('{"error":{"code":"E401"}}')).toBeNull();
|
|
66
|
-
expect(parsePublishedVersions('[]')).toBeNull();
|
|
67
|
-
expect(parsePublishedVersions('[{"error":{"code":"E404"}}]')).toBeNull();
|
|
68
|
-
expect(parsePublishedVersions('null')).toBeNull();
|
|
69
|
-
});
|
|
70
|
-
});
|
|
71
|
-
|
|
72
|
-
describe('nextPrereleaseCounter', () => {
|
|
73
|
-
it('returns 1 when nothing matches the base + tag', () => {
|
|
74
|
-
expect(nextPrereleaseCounter([], '0.3.5', 'staging')).toBe(1);
|
|
75
|
-
expect(nextPrereleaseCounter(['0.3.5', '0.3.5-experimental.4'], '0.3.5', 'staging')).toBe(1);
|
|
76
|
-
});
|
|
77
|
-
|
|
78
|
-
it('continues past the highest published counter for the base', () => {
|
|
79
|
-
const versions = ['0.3.4-staging.26', '0.3.4-staging.27', '0.3.3-staging.25'];
|
|
80
|
-
expect(nextPrereleaseCounter(versions, '0.3.4', 'staging')).toBe(28);
|
|
81
|
-
});
|
|
82
|
-
|
|
83
|
-
it('does not confuse prefix-sharing bases (0.3.4 vs 0.3.40)', () => {
|
|
84
|
-
expect(nextPrereleaseCounter(['0.3.40-staging.9'], '0.3.4', 'staging')).toBe(1);
|
|
85
|
-
});
|
|
86
|
-
|
|
87
|
-
it('ignores non-integer counters (timestamps still count, garbage does not)', () => {
|
|
88
|
-
expect(nextPrereleaseCounter(['0.3.4-staging.abc', '0.3.4-staging.2.5'], '0.3.4', 'staging')).toBe(1);
|
|
89
|
-
expect(nextPrereleaseCounter(['0.3.3-experimental.1783410168478'], '0.3.3', 'experimental')).toBe(1783410168479);
|
|
90
|
-
});
|
|
91
|
-
});
|
|
92
|
-
|
|
93
|
-
describe('nextVersionFromChangesets', () => {
|
|
94
|
-
let dir: string;
|
|
95
|
-
|
|
96
|
-
afterEach(() => {
|
|
97
|
-
if (dir) {
|
|
98
|
-
rmSync(dir, { recursive: true, force: true });
|
|
99
|
-
}
|
|
100
|
-
});
|
|
101
|
-
|
|
102
|
-
function changesetDir(files: Record<string, string>): string {
|
|
103
|
-
dir = mkdtempSync(join(tmpdir(), 'changesets-'));
|
|
104
|
-
writeFileSync(join(dir, 'README.md'), '# Changesets\n');
|
|
105
|
-
for (const [name, frontmatter] of Object.entries(files)) {
|
|
106
|
-
writeFileSync(join(dir, name), `---\n${frontmatter}\n---\n\nsummary\n`);
|
|
107
|
-
}
|
|
108
|
-
return dir;
|
|
109
|
-
}
|
|
110
|
-
|
|
111
|
-
it('returns the base when the directory does not exist', () => {
|
|
112
|
-
expect(nextVersionFromChangesets('0.3.3', '/nonexistent/.changeset')).toBe('0.3.3');
|
|
113
|
-
});
|
|
114
|
-
|
|
115
|
-
it('returns the base when no changesets are pending', () => {
|
|
116
|
-
expect(nextVersionFromChangesets('0.3.3', changesetDir({}))).toBe('0.3.3');
|
|
117
|
-
});
|
|
118
|
-
|
|
119
|
-
it('applies a patch bump for pending patch changesets', () => {
|
|
120
|
-
const d = changesetDir({ 'a.md': "'fimo': patch" });
|
|
121
|
-
expect(nextVersionFromChangesets('0.3.3', d)).toBe('0.3.4');
|
|
122
|
-
});
|
|
123
|
-
|
|
124
|
-
it('takes the max bump across pending changesets', () => {
|
|
125
|
-
const d = changesetDir({
|
|
126
|
-
'a.md': "'fimo': patch",
|
|
127
|
-
'b.md': "'fimo': minor",
|
|
128
|
-
'c.md': "'fimo': patch",
|
|
129
|
-
});
|
|
130
|
-
expect(nextVersionFromChangesets('0.3.3', d)).toBe('0.4.0');
|
|
131
|
-
});
|
|
132
|
-
|
|
133
|
-
it('handles major bumps and double-quoted package names', () => {
|
|
134
|
-
const d = changesetDir({ 'a.md': '"fimo": major' });
|
|
135
|
-
expect(nextVersionFromChangesets('0.3.3', d)).toBe('1.0.0');
|
|
136
|
-
});
|
|
137
|
-
|
|
138
|
-
it('ignores changesets for other packages', () => {
|
|
139
|
-
const d = changesetDir({ 'a.md': "'@fimo/fimovm': minor" });
|
|
140
|
-
expect(nextVersionFromChangesets('0.3.3', d)).toBe('0.3.3');
|
|
141
|
-
});
|
|
142
|
-
|
|
143
|
-
it('ignores an existing prerelease suffix on the base', () => {
|
|
144
|
-
const d = changesetDir({ 'a.md': "'fimo': patch" });
|
|
145
|
-
expect(nextVersionFromChangesets('0.3.3-staging.9', d)).toBe('0.3.4');
|
|
146
|
-
});
|
|
147
|
-
});
|
|
@@ -1,155 +0,0 @@
|
|
|
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
|
-
});
|
package/scripts/publish-npm.mjs
DELETED
|
@@ -1,180 +0,0 @@
|
|
|
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)' : ''}.`);
|
|
File without changes
|