fimo 0.2.5-experimental.1782387685584 → 0.2.5-staging.14
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/assets/skills/fimo-cli/SKILL.md +1 -0
- package/assets/skills/fimo-cli/references/schedules.md +46 -0
- package/dist/build/vite/index.d.ts.map +1 -1
- package/dist/build/vite/index.js +1 -0
- package/dist/build/vite/plugins/translations.d.ts.map +1 -1
- package/dist/build/vite/plugins/translations.js +36 -1
- package/dist/build/vite/plugins/translations.test.js +61 -3
- package/dist/cli/bundle.json +2 -2
- package/dist/cli/index.js +319 -122
- package/dist/runtime/app/FimoScripts.d.ts.map +1 -1
- package/dist/runtime/content/RefreshContentProvider.d.ts +16 -0
- package/dist/runtime/content/RefreshContentProvider.d.ts.map +1 -1
- package/dist/runtime/content/RefreshContentProvider.js +55 -6
- package/dist/runtime/content/RefreshContentProvider.test.d.ts +2 -0
- package/dist/runtime/content/RefreshContentProvider.test.d.ts.map +1 -0
- package/dist/runtime/content/RefreshContentProvider.test.js +33 -0
- package/dist/runtime/primitives/translations.d.ts.map +1 -1
- package/dist/runtime/templates.d.ts +13 -0
- package/dist/runtime/templates.d.ts.map +1 -1
- package/dist/runtime/templates.js +51 -0
- package/package.json +1 -1
- package/templates/react-router/package.json +1 -1
|
@@ -69,6 +69,7 @@ Load the matching reference when the user's request fires its trigger. Recipes t
|
|
|
69
69
|
- "What's in this project / show the project structure / describe the project / what schemas does it have / how many routes / project tree / show me what's here" → `references/describe.md`
|
|
70
70
|
- "Deploy / publish / go live / push my changes" → `references/deploy.md`
|
|
71
71
|
- "Get the preview URL / open the preview / what's the URL for this env / refresh the preview link / I just want to see the site / the preview URL stopped working" → `references/preview.md`
|
|
72
|
+
- "Schedule a command / cron / nightly publish / recurring checks / `fimo schedules`" → `references/schedules.md`
|
|
72
73
|
- "Set env vars / configure API keys / add project secrets / local `.env.local` / `fimo vars` / `fimo secrets`" → `references/project-vars-and-secrets.md`
|
|
73
74
|
- "Enable provider tools / integrations / API key connection for a provider / agent integration access" → `references/integrations.md`
|
|
74
75
|
- "Firecrawl / scrape a public website / competitor research / agent web research" → `references/integrations.md` **+ load `references/integrations/firecrawl.md`**
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
# Scheduled Commands
|
|
2
|
+
|
|
3
|
+
`fimo schedules` manages recurring commands for a project env. A schedule is scoped to the env of the current checkout by default, or to `--env <name>` when passed.
|
|
4
|
+
|
|
5
|
+
## Commands
|
|
6
|
+
|
|
7
|
+
```bash
|
|
8
|
+
fimo schedules create <name> --cron "0 2 * * *" --do "fimo publish"
|
|
9
|
+
fimo schedules create smoke --cron "*/15 * * * *" --do "pnpm test"
|
|
10
|
+
fimo schedules list
|
|
11
|
+
fimo schedules run <name>
|
|
12
|
+
fimo schedules delete <name>
|
|
13
|
+
```
|
|
14
|
+
|
|
15
|
+
## Runtime Model
|
|
16
|
+
|
|
17
|
+
Scheduled commands run in the project's env runtime, not on the user's machine. On each run, Fimo prepares the env, refreshes the project files and dependencies, authenticates as the schedule owner, then executes the stored command from the project root.
|
|
18
|
+
|
|
19
|
+
`--do` can be any CLI command that makes sense for the project, such as `fimo publish`, `fimo validate`, `pnpm test`, or a project script.
|
|
20
|
+
|
|
21
|
+
Cron expressions use standard five-field syntax. Schedules must not run more often than every five minutes.
|
|
22
|
+
|
|
23
|
+
## Useful Patterns
|
|
24
|
+
|
|
25
|
+
Nightly publish:
|
|
26
|
+
|
|
27
|
+
```bash
|
|
28
|
+
fimo schedules create nightly --cron "0 2 * * *" --do "fimo publish"
|
|
29
|
+
```
|
|
30
|
+
|
|
31
|
+
Recurring smoke test:
|
|
32
|
+
|
|
33
|
+
```bash
|
|
34
|
+
fimo schedules create smoke --cron "*/15 * * * *" --do "pnpm test"
|
|
35
|
+
```
|
|
36
|
+
|
|
37
|
+
Manual run without waiting for cron:
|
|
38
|
+
|
|
39
|
+
```bash
|
|
40
|
+
fimo schedules run smoke
|
|
41
|
+
```
|
|
42
|
+
|
|
43
|
+
## Notes
|
|
44
|
+
|
|
45
|
+
- `list`, `run`, and `delete` operate on the current checkout env unless `--env` is passed.
|
|
46
|
+
- A failed command marks the schedule `failed`, stores the error/log excerpt, and `fimo schedules run` exits non-zero.
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/build/vite/index.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAU,YAAY,EAAE,MAAM,MAAM,CAAC;
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/build/vite/index.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAU,YAAY,EAAE,MAAM,MAAM,CAAC;AAyEjD,MAAM,WAAW,eAAe;IAC9B;;;;;OAKG;IACH,OAAO,CAAC,EAAE,OAAO,CAAC;IAElB;;;;;;OAMG;IACH,MAAM,CAAC,EAAE,OAAO,CAAC;IAEjB;;;;;;;;;;OAUG;IACH,GAAG,CAAC,EAAE,OAAO,CAAC;CACf;AAGD,wBAAgB,IAAI,CAAC,IAAI,GAAE,eAAoB,GAAG,YAAY,EAAE,CAsB/D"}
|
package/dist/build/vite/index.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"translations.d.ts","sourceRoot":"","sources":["../../../../src/build/vite/plugins/translations.ts"],"names":[],"mappings":"AAKA,OAAO,KAAK,EAAE,MAAM,EAAiB,MAAM,MAAM,CAAC;AAQlD,UAAU,WAAW;IACnB,YAAY,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC,CAAC;IACrD,aAAa,EAAE,MAAM,CAAC;IACtB,OAAO,EAAE,MAAM,EAAE,CAAC;IAClB,SAAS,EAAE,MAAM,GAAG,IAAI,CAAC;CAC1B;
|
|
1
|
+
{"version":3,"file":"translations.d.ts","sourceRoot":"","sources":["../../../../src/build/vite/plugins/translations.ts"],"names":[],"mappings":"AAKA,OAAO,KAAK,EAAE,MAAM,EAAiB,MAAM,MAAM,CAAC;AAQlD,UAAU,WAAW;IACnB,YAAY,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC,CAAC;IACrD,aAAa,EAAE,MAAM,CAAC;IACtB,OAAO,EAAE,MAAM,EAAE,CAAC;IAClB,SAAS,EAAE,MAAM,GAAG,IAAI,CAAC;CAC1B;AAwCD,MAAM,CAAC,OAAO,UAAU,kBAAkB,IAAI,MAAM,CAmOnD;AAoED,wBAAsB,eAAe,CAAC,OAAO,EAAE,MAAM,GAAG,OAAO,CAAC,WAAW,CAAC,CAwC3E"}
|
|
@@ -40,6 +40,16 @@ export default function translationsPlugin() {
|
|
|
40
40
|
data: labelUpdatePayload(bundle),
|
|
41
41
|
});
|
|
42
42
|
};
|
|
43
|
+
const emitContentUpdate = (payload) => {
|
|
44
|
+
if (!devServer) {
|
|
45
|
+
return;
|
|
46
|
+
}
|
|
47
|
+
devServer.ws.send({
|
|
48
|
+
type: 'custom',
|
|
49
|
+
event: 'fimo:content-updated',
|
|
50
|
+
data: payload,
|
|
51
|
+
});
|
|
52
|
+
};
|
|
43
53
|
const scheduleRefresh = () => {
|
|
44
54
|
if (refreshTimer) {
|
|
45
55
|
clearTimeout(refreshTimer);
|
|
@@ -165,6 +175,10 @@ export default function translationsPlugin() {
|
|
|
165
175
|
}
|
|
166
176
|
try {
|
|
167
177
|
const message = JSON.parse(raw);
|
|
178
|
+
if (message.type === 'content:changed') {
|
|
179
|
+
emitContentUpdate(parseContentChangePayload(message.payload));
|
|
180
|
+
return;
|
|
181
|
+
}
|
|
168
182
|
if (message.type !== 'labels:changed') {
|
|
169
183
|
return;
|
|
170
184
|
}
|
|
@@ -194,6 +208,27 @@ export default function translationsPlugin() {
|
|
|
194
208
|
return sockets;
|
|
195
209
|
}
|
|
196
210
|
}
|
|
211
|
+
function parseContentChangePayload(payload) {
|
|
212
|
+
if (!payload || typeof payload !== 'object') {
|
|
213
|
+
return { changes: [] };
|
|
214
|
+
}
|
|
215
|
+
const source = payload;
|
|
216
|
+
const changes = Array.isArray(source.changes)
|
|
217
|
+
? source.changes
|
|
218
|
+
.filter((change) => Boolean(change) && typeof change === 'object')
|
|
219
|
+
.map((change) => ({
|
|
220
|
+
action: typeof change.action === 'string' ? change.action : undefined,
|
|
221
|
+
contentType: typeof change.contentType === 'string' ? change.contentType : undefined,
|
|
222
|
+
documentId: typeof change.documentId === 'string' ? change.documentId : undefined,
|
|
223
|
+
id: typeof change.id === 'string' ? change.id : undefined,
|
|
224
|
+
locale: typeof change.locale === 'string' ? change.locale : undefined,
|
|
225
|
+
}))
|
|
226
|
+
: [];
|
|
227
|
+
return {
|
|
228
|
+
changes,
|
|
229
|
+
updatedAt: typeof source.updatedAt === 'string' ? source.updatedAt : undefined,
|
|
230
|
+
};
|
|
231
|
+
}
|
|
197
232
|
function serializeBundle(bundle) {
|
|
198
233
|
return JSON.stringify({
|
|
199
234
|
dictionaries: bundle.dictionaries,
|
|
@@ -389,7 +424,7 @@ async function generateOneTimeToken(apiUrl) {
|
|
|
389
424
|
throw new Error('Not signed in to Fimo.');
|
|
390
425
|
}
|
|
391
426
|
const response = await fetch(new URL('/api/auth/one-time-token/generate', apiUrl), {
|
|
392
|
-
method: '
|
|
427
|
+
method: 'GET',
|
|
393
428
|
headers: {
|
|
394
429
|
Authorization: `Bearer ${bearer}`,
|
|
395
430
|
},
|
|
@@ -152,7 +152,7 @@ describe('translationsPlugin', () => {
|
|
|
152
152
|
}
|
|
153
153
|
close() { }
|
|
154
154
|
});
|
|
155
|
-
|
|
155
|
+
const fetchMock = vi.fn(async (input, init) => {
|
|
156
156
|
const url = new URL(input.toString());
|
|
157
157
|
if (url.pathname === '/labels') {
|
|
158
158
|
return jsonResponse({ data: { labels: {}, updatedAt: null } });
|
|
@@ -161,10 +161,12 @@ describe('translationsPlugin', () => {
|
|
|
161
161
|
return jsonResponse({ data: { eventsServerUrl: 'https://events.example.test' } });
|
|
162
162
|
}
|
|
163
163
|
if (url.pathname === '/api/auth/one-time-token/generate') {
|
|
164
|
-
|
|
164
|
+
expect(init?.method).toBe('GET');
|
|
165
|
+
return jsonResponse({ token: 'event-token' });
|
|
165
166
|
}
|
|
166
167
|
return { ok: false, json: async () => ({}) };
|
|
167
|
-
})
|
|
168
|
+
});
|
|
169
|
+
vi.stubGlobal('fetch', fetchMock);
|
|
168
170
|
const plugin = createPlugin(root, 'serve');
|
|
169
171
|
await buildStart(plugin);
|
|
170
172
|
const server = createDevServer();
|
|
@@ -176,6 +178,62 @@ describe('translationsPlugin', () => {
|
|
|
176
178
|
expect(socketUrls[0]).toContain(`wss://events.example.test/parties/events/${projectId}`);
|
|
177
179
|
expect(socketUrls[0]).toContain('authToken=event-token');
|
|
178
180
|
});
|
|
181
|
+
it('forwards content changes over custom HMR without a full page reload', async () => {
|
|
182
|
+
const root = createProjectRoot();
|
|
183
|
+
const projectId = '11111111-1111-4111-8111-111111111111';
|
|
184
|
+
writeFileSync(join(root, '.fimo.settings.json'), JSON.stringify({ projectId, apiUrl: 'https://api.example.test' }));
|
|
185
|
+
process.env.FIMO_API_TOKEN = 'test-api-token';
|
|
186
|
+
const sockets = [];
|
|
187
|
+
vi.stubGlobal('WebSocket', class {
|
|
188
|
+
onopen = null;
|
|
189
|
+
onmessage = null;
|
|
190
|
+
onclose = null;
|
|
191
|
+
onerror = null;
|
|
192
|
+
constructor(_url) {
|
|
193
|
+
sockets.push(this);
|
|
194
|
+
}
|
|
195
|
+
close() { }
|
|
196
|
+
});
|
|
197
|
+
vi.stubGlobal('fetch', vi.fn(async (input) => {
|
|
198
|
+
const url = new URL(input.toString());
|
|
199
|
+
if (url.pathname === '/labels') {
|
|
200
|
+
return jsonResponse({ data: { labels: {}, updatedAt: null } });
|
|
201
|
+
}
|
|
202
|
+
if (url.pathname === '/runtime-config') {
|
|
203
|
+
return jsonResponse({ data: { eventsServerUrl: 'https://events.example.test' } });
|
|
204
|
+
}
|
|
205
|
+
if (url.pathname === '/api/auth/one-time-token/generate') {
|
|
206
|
+
return jsonResponse({ data: { token: 'event-token' } });
|
|
207
|
+
}
|
|
208
|
+
return { ok: false, json: async () => ({}) };
|
|
209
|
+
}));
|
|
210
|
+
const plugin = createPlugin(root, 'serve');
|
|
211
|
+
await buildStart(plugin);
|
|
212
|
+
const server = createDevServer();
|
|
213
|
+
if (typeof plugin.configureServer !== 'function') {
|
|
214
|
+
throw new Error('Expected configureServer hook');
|
|
215
|
+
}
|
|
216
|
+
plugin.configureServer.call(pluginContext, server);
|
|
217
|
+
await waitFor(() => sockets.length > 0);
|
|
218
|
+
sockets[0].onmessage?.({
|
|
219
|
+
data: JSON.stringify({
|
|
220
|
+
type: 'content:changed',
|
|
221
|
+
payload: {
|
|
222
|
+
changes: [{ action: 'update', contentType: 'LiveNote', id: 'entry-1', locale: 'en' }],
|
|
223
|
+
updatedAt: '2026-06-24T00:00:00.000Z',
|
|
224
|
+
},
|
|
225
|
+
}),
|
|
226
|
+
});
|
|
227
|
+
expect(server.ws.send).toHaveBeenCalledWith({
|
|
228
|
+
type: 'custom',
|
|
229
|
+
event: 'fimo:content-updated',
|
|
230
|
+
data: {
|
|
231
|
+
changes: [{ action: 'update', contentType: 'LiveNote', id: 'entry-1', locale: 'en' }],
|
|
232
|
+
updatedAt: '2026-06-24T00:00:00.000Z',
|
|
233
|
+
},
|
|
234
|
+
});
|
|
235
|
+
expect(server.ws.send).not.toHaveBeenCalledWith({ type: 'full-reload', path: '*' });
|
|
236
|
+
});
|
|
179
237
|
it('still waits for labels during production builds', async () => {
|
|
180
238
|
const root = createProjectRoot();
|
|
181
239
|
const fetchMock = mockLabels({ headline: 'Hola' });
|
package/dist/cli/bundle.json
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
{
|
|
2
2
|
"bundled": true,
|
|
3
3
|
"bundler": "esbuild",
|
|
4
|
-
"bundledAt": "2026-06-
|
|
5
|
-
"cliVersion": "0.2.5-
|
|
4
|
+
"bundledAt": "2026-06-26T17:21:55.057Z",
|
|
5
|
+
"cliVersion": "0.2.5-staging.14",
|
|
6
6
|
"external": [
|
|
7
7
|
"oxc-parser",
|
|
8
8
|
"fsevents"
|