fimo 2.6.0 → 2.7.0-experimental.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.
- package/assets/skills/fimo-cli/SKILL.md +1 -1
- package/assets/skills/fimo-cli/references/analytics.md +29 -1
- package/assets/skills/fimo-cli/references/content.md +2 -2
- package/assets/skills/fimo-cli/references/hosting.md +11 -0
- package/assets/skills/fimo-cli/references/locales.md +2 -2
- package/assets/skills/fimo-cli/references/pages.md +14 -2
- package/assets/skills/fimo-code/SKILL.md +8 -0
- package/assets/skills/fimo-code/references/analytics.md +104 -0
- package/assets/skills/fimo-code/references/frameworks/astro.md +2 -0
- package/assets/skills/fimo-code/references/frameworks/next.md +12 -9
- package/assets/skills/fimo-code/references/ui.md +6 -1
- package/assets/skills/fimo-migration/SKILL.md +9 -6
- package/assets/skills/fimo-migration/references/frameworks/nextjs.md +24 -27
- package/assets/skills/fimo-migration/references/media.md +98 -62
- package/assets/skills/fimo-migration/references/qa.md +111 -11
- package/assets/skills/fimo-migration/references/workers/content-execution.md +2 -1
- package/assets/skills/fimo-migration/references/workers/validator.md +22 -6
- package/assets/skills/fimo-migration/references/workers.md +5 -3
- package/assets/skills/fimo-migration/scripts/check-rendered-guardrails.mjs +500 -0
- package/assets/skills/fimo-studio/SKILL.md +1 -0
- package/assets/skills/fimo-studio/references/analytics.md +28 -0
- package/assets/skills/fimo-studio/references/publish-and-deploy.md +1 -0
- package/dist/cli/bundle.json +1 -1
- package/dist/cli/index.js +27 -27
- package/dist/runtime/paths/get-fimo-paths.d.ts +3 -1
- package/dist/runtime/paths/get-fimo-paths.js +59 -5
- package/dist/runtime/primitives/lib/primitives.d.ts +20 -1
- package/dist/runtime/primitives/lib/primitives.js +24 -1
- package/dist/runtime/routes/index.d.ts +19 -3
- package/dist/runtime/routes/index.js +27 -2
- package/dist/runtime/templates.d.ts +1 -1
- package/dist/runtime/templates.js +1 -0
- package/dist/scripts/validate-page-registry.js +14 -3
- package/package.json +10 -9
- package/release.json +2 -2
- package/templates/astro/package.json +2 -2
- package/templates/nextjs/package.json +3 -3
- package/templates/react-router/package.json +4 -4
- package/templates/sveltekit/package.json +2 -2
- package/templates/vite/package.json +4 -4
|
@@ -6,7 +6,9 @@ import type { FimoPath, GetFimoPathsOptions } from './types.js';
|
|
|
6
6
|
*
|
|
7
7
|
* - Static paths in config: emitted as-is.
|
|
8
8
|
* - Dynamic paths (with `:slug`-style entry params): expanded by fetching
|
|
9
|
-
* schema
|
|
9
|
+
* every schema entry from `VITE_API_URL`, page by page. One path per entry.
|
|
10
|
+
* - A route with an external param is never emitted: the application owns
|
|
11
|
+
* those values and lists those URLs itself.
|
|
10
12
|
* - Locales come from the route contract, not from the project locale list: a
|
|
11
13
|
* route is emitted once per locale only when its path declares a `:locale`
|
|
12
14
|
* segment. The default locale is always unprefixed. A route without a
|
|
@@ -11,6 +11,9 @@ const isValidationBuild = !!process.env.FIMO_VALIDATION_BUILD;
|
|
|
11
11
|
const MAX_FETCH_ATTEMPTS = 3;
|
|
12
12
|
const RETRYABLE_FETCH_STATUSES = new Set([502, 503, 504]);
|
|
13
13
|
const RETRY_BASE_DELAY_MS = 250;
|
|
14
|
+
// The content API pages with `limit` (capped at 100) and `offset`; its
|
|
15
|
+
// `meta.hasMore` says whether another page follows.
|
|
16
|
+
const ENTRIES_PAGE_SIZE = 100;
|
|
14
17
|
function readEnvValue(rootDir, key) {
|
|
15
18
|
if (process.env[key]) {
|
|
16
19
|
return process.env[key];
|
|
@@ -42,9 +45,6 @@ function resolveApiBaseUrl(rootDir) {
|
|
|
42
45
|
async function fetchEntries(baseUrl, schema, rootDir, locale) {
|
|
43
46
|
const url = new URL(`${baseUrl.replace(/\/$/, '')}/entries/${schema}`);
|
|
44
47
|
url.searchParams.set('locale', locale);
|
|
45
|
-
if (isValidationBuild) {
|
|
46
|
-
url.searchParams.set('limit', '1');
|
|
47
|
-
}
|
|
48
48
|
const headers = {};
|
|
49
49
|
// `FIMO_ENV` is a customer-facing variable name and keeps its spelling; the
|
|
50
50
|
// header this build emits is canonical. A
|
|
@@ -54,11 +54,47 @@ async function fetchEntries(baseUrl, schema, rootDir, locale) {
|
|
|
54
54
|
if (branch) {
|
|
55
55
|
headers['X-Fimo-Branch'] = branch;
|
|
56
56
|
}
|
|
57
|
+
// A validation build only proves the route expands, so one entry is enough.
|
|
58
|
+
if (isValidationBuild) {
|
|
59
|
+
url.searchParams.set('limit', '1');
|
|
60
|
+
return (await fetchEntriesPage(url, headers, schema)).data;
|
|
61
|
+
}
|
|
62
|
+
// Every entry becomes a URL, so walk the whole collection page by page.
|
|
63
|
+
const entries = [];
|
|
64
|
+
let offset = 0;
|
|
65
|
+
for (;;) {
|
|
66
|
+
const pageUrl = new URL(url);
|
|
67
|
+
pageUrl.searchParams.set('limit', String(ENTRIES_PAGE_SIZE));
|
|
68
|
+
pageUrl.searchParams.set('offset', String(offset));
|
|
69
|
+
const page = await fetchEntriesPage(pageUrl, headers, schema);
|
|
70
|
+
entries.push(...page.data);
|
|
71
|
+
if (!page.meta) {
|
|
72
|
+
// Without pagination metadata a short page is the whole collection; a
|
|
73
|
+
// full one may hide more entries, and a truncated list is a silent bug.
|
|
74
|
+
if (page.data.length >= ENTRIES_PAGE_SIZE) {
|
|
75
|
+
throw new Error(`Entries response for ${schema} returned ${page.data.length} rows without pagination metadata, so every path cannot be enumerated.`);
|
|
76
|
+
}
|
|
77
|
+
return entries;
|
|
78
|
+
}
|
|
79
|
+
if (!page.meta.hasMore) {
|
|
80
|
+
return entries;
|
|
81
|
+
}
|
|
82
|
+
const nextOffset = page.meta.offset + page.meta.limit;
|
|
83
|
+
if (nextOffset <= offset) {
|
|
84
|
+
throw new Error(`Entries pagination for ${schema} did not advance past offset ${offset}.`);
|
|
85
|
+
}
|
|
86
|
+
offset = nextOffset;
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
async function fetchEntriesPage(url, headers, schema) {
|
|
57
90
|
for (let attempt = 1; attempt <= MAX_FETCH_ATTEMPTS; attempt += 1) {
|
|
58
91
|
const res = await fetch(url, { headers });
|
|
59
92
|
if (res.ok) {
|
|
60
93
|
const payload = (await res.json());
|
|
61
|
-
return
|
|
94
|
+
return {
|
|
95
|
+
data: Array.isArray(payload?.data) ? payload.data : [],
|
|
96
|
+
meta: readPageMeta(payload?.meta),
|
|
97
|
+
};
|
|
62
98
|
}
|
|
63
99
|
if (!RETRYABLE_FETCH_STATUSES.has(res.status) || attempt === MAX_FETCH_ATTEMPTS) {
|
|
64
100
|
const attemptDetail = attempt > 1 ? ` after ${attempt} attempts` : '';
|
|
@@ -68,6 +104,16 @@ async function fetchEntries(baseUrl, schema, rootDir, locale) {
|
|
|
68
104
|
}
|
|
69
105
|
throw new Error(`Failed to fetch ${schema}`);
|
|
70
106
|
}
|
|
107
|
+
function readPageMeta(meta) {
|
|
108
|
+
if (!meta || typeof meta !== 'object') {
|
|
109
|
+
return undefined;
|
|
110
|
+
}
|
|
111
|
+
const { limit, offset, hasMore } = meta;
|
|
112
|
+
if (typeof limit !== 'number' || typeof offset !== 'number' || typeof hasMore !== 'boolean') {
|
|
113
|
+
return undefined;
|
|
114
|
+
}
|
|
115
|
+
return { limit, offset, hasMore };
|
|
116
|
+
}
|
|
71
117
|
// Glob-ish matcher for route keys: '*' matches one segment, '**' matches any.
|
|
72
118
|
function compilePattern(pattern) {
|
|
73
119
|
const escaped = pattern
|
|
@@ -87,7 +133,9 @@ function matchesAny(routeKey, patterns) {
|
|
|
87
133
|
*
|
|
88
134
|
* - Static paths in config: emitted as-is.
|
|
89
135
|
* - Dynamic paths (with `:slug`-style entry params): expanded by fetching
|
|
90
|
-
* schema
|
|
136
|
+
* every schema entry from `VITE_API_URL`, page by page. One path per entry.
|
|
137
|
+
* - A route with an external param is never emitted: the application owns
|
|
138
|
+
* those values and lists those URLs itself.
|
|
91
139
|
* - Locales come from the route contract, not from the project locale list: a
|
|
92
140
|
* route is emitted once per locale only when its path declares a `:locale`
|
|
93
141
|
* segment. The default locale is always unprefixed. A route without a
|
|
@@ -125,6 +173,12 @@ export async function getFimoPaths(opts = {}) {
|
|
|
125
173
|
if (contract.unresolvedParams.length > 0) {
|
|
126
174
|
return false;
|
|
127
175
|
}
|
|
176
|
+
// Values the application owns cannot be enumerated here. The app lists
|
|
177
|
+
// those URLs itself — React Router `prerender`, Next `generateStaticParams`
|
|
178
|
+
// — and concatenates them with this result.
|
|
179
|
+
if (contract.externalParams.length > 0) {
|
|
180
|
+
return false;
|
|
181
|
+
}
|
|
128
182
|
if (opts.include && opts.include.length > 0 && !matchesAny(contract.id, opts.include)) {
|
|
129
183
|
return false;
|
|
130
184
|
}
|
|
@@ -98,7 +98,24 @@ export declare class FimoMedia {
|
|
|
98
98
|
get __fimo(): FimoSource;
|
|
99
99
|
}
|
|
100
100
|
/**
|
|
101
|
-
* FimoBoolean -
|
|
101
|
+
* FimoBoolean - a boolean with source tracking.
|
|
102
|
+
*
|
|
103
|
+
* Read `.value` before any condition, comparison, attribute, or third-party
|
|
104
|
+
* call, and pass the wrapper itself to `<Boolean>` so Studio keeps the edit
|
|
105
|
+
* target.
|
|
106
|
+
*
|
|
107
|
+
* `if (entry.featured)` is ALWAYS true, including for `false`, because the
|
|
108
|
+
* wrapper is an object. That is a language rule, not a gap to close here:
|
|
109
|
+
* `ToBoolean` on an object never consults `valueOf`, `Symbol.toPrimitive`, a
|
|
110
|
+
* `Boolean` subclass, or a `Proxy`, and TypeScript reports nothing (it
|
|
111
|
+
* special-cases only Promises and functions). Removing the wrapper is the only
|
|
112
|
+
* real fix; do not add a coercion hook that appears to solve it.
|
|
113
|
+
*
|
|
114
|
+
* `toString` and `toJSON` cover the two boundaries that DO give us a hook, so
|
|
115
|
+
* the wrapper stops leaking there: `${flag}` reads `"true"`/`"false"` instead
|
|
116
|
+
* of `"[object Object]"`, and `JSON.stringify` emits `false` instead of
|
|
117
|
+
* `{"value":false}` — a payload that rehydrates as a permanently truthy object.
|
|
118
|
+
* Framework boundaries still use `serializeContent`, which also drops metadata.
|
|
102
119
|
*/
|
|
103
120
|
export declare class FimoBoolean {
|
|
104
121
|
readonly value: boolean;
|
|
@@ -107,6 +124,8 @@ export declare class FimoBoolean {
|
|
|
107
124
|
constructor(value: boolean, source: string, context?: FimoSourceContext);
|
|
108
125
|
get __fimo(): FimoSource;
|
|
109
126
|
valueOf(): boolean;
|
|
127
|
+
toString(): string;
|
|
128
|
+
toJSON(): boolean;
|
|
110
129
|
}
|
|
111
130
|
/**
|
|
112
131
|
* Tiptap JSONContent structure for rich text
|
|
@@ -180,7 +180,24 @@ export class FimoMedia {
|
|
|
180
180
|
}
|
|
181
181
|
_f = FIMO_SOURCE, _g = FIMO_SOURCE_CONTEXT;
|
|
182
182
|
/**
|
|
183
|
-
* FimoBoolean -
|
|
183
|
+
* FimoBoolean - a boolean with source tracking.
|
|
184
|
+
*
|
|
185
|
+
* Read `.value` before any condition, comparison, attribute, or third-party
|
|
186
|
+
* call, and pass the wrapper itself to `<Boolean>` so Studio keeps the edit
|
|
187
|
+
* target.
|
|
188
|
+
*
|
|
189
|
+
* `if (entry.featured)` is ALWAYS true, including for `false`, because the
|
|
190
|
+
* wrapper is an object. That is a language rule, not a gap to close here:
|
|
191
|
+
* `ToBoolean` on an object never consults `valueOf`, `Symbol.toPrimitive`, a
|
|
192
|
+
* `Boolean` subclass, or a `Proxy`, and TypeScript reports nothing (it
|
|
193
|
+
* special-cases only Promises and functions). Removing the wrapper is the only
|
|
194
|
+
* real fix; do not add a coercion hook that appears to solve it.
|
|
195
|
+
*
|
|
196
|
+
* `toString` and `toJSON` cover the two boundaries that DO give us a hook, so
|
|
197
|
+
* the wrapper stops leaking there: `${flag}` reads `"true"`/`"false"` instead
|
|
198
|
+
* of `"[object Object]"`, and `JSON.stringify` emits `false` instead of
|
|
199
|
+
* `{"value":false}` — a payload that rehydrates as a permanently truthy object.
|
|
200
|
+
* Framework boundaries still use `serializeContent`, which also drops metadata.
|
|
184
201
|
*/
|
|
185
202
|
export class FimoBoolean {
|
|
186
203
|
constructor(value, source, context) {
|
|
@@ -212,6 +229,12 @@ export class FimoBoolean {
|
|
|
212
229
|
valueOf() {
|
|
213
230
|
return this.value;
|
|
214
231
|
}
|
|
232
|
+
toString() {
|
|
233
|
+
return String(this.value);
|
|
234
|
+
}
|
|
235
|
+
toJSON() {
|
|
236
|
+
return this.value;
|
|
237
|
+
}
|
|
215
238
|
}
|
|
216
239
|
_h = FIMO_SOURCE, _j = FIMO_SOURCE_CONTEXT;
|
|
217
240
|
/**
|
|
@@ -11,7 +11,12 @@ export interface FimoRouteSeoConfig {
|
|
|
11
11
|
siteName?: string;
|
|
12
12
|
}
|
|
13
13
|
export interface FimoRouteParamConfig<Query = unknown> {
|
|
14
|
-
|
|
14
|
+
/**
|
|
15
|
+
* Omitted means `'entry'`; the reserved name `locale` infers `'locale'`.
|
|
16
|
+
* `'external'` marks a placeholder whose values the application owns: it is
|
|
17
|
+
* matched from URLs but never looked up in the CMS and never enumerated.
|
|
18
|
+
*/
|
|
19
|
+
type?: 'entry' | 'locale' | 'external';
|
|
15
20
|
schema?: string;
|
|
16
21
|
queryField?: string;
|
|
17
22
|
displayField?: string;
|
|
@@ -67,7 +72,16 @@ export interface FimoLocaleParam {
|
|
|
67
72
|
*/
|
|
68
73
|
optional: boolean;
|
|
69
74
|
}
|
|
70
|
-
|
|
75
|
+
/**
|
|
76
|
+
* A placeholder whose values the application owns: a synced catalogue, a
|
|
77
|
+
* browser-only id, a computed segment. Never read from the CMS, never
|
|
78
|
+
* enumerated by Fimo.
|
|
79
|
+
*/
|
|
80
|
+
export interface FimoExternalParam {
|
|
81
|
+
type: 'external';
|
|
82
|
+
name: string;
|
|
83
|
+
}
|
|
84
|
+
export type FimoRouteParam = FimoEntryParam | FimoLocaleParam | FimoExternalParam;
|
|
71
85
|
export interface FimoRouteIssue {
|
|
72
86
|
/** The parameter the issue is about, when it is about one. */
|
|
73
87
|
param?: string;
|
|
@@ -85,6 +99,8 @@ export interface FimoRouteContract<RouteConfig extends FimoRouteConfig = FimoRou
|
|
|
85
99
|
canonicalPath: string;
|
|
86
100
|
params: FimoRouteParam[];
|
|
87
101
|
entryParams: FimoEntryParam[];
|
|
102
|
+
/** Params the application owns. Non-empty means Fimo cannot enumerate this route's URLs. */
|
|
103
|
+
externalParams: FimoExternalParam[];
|
|
88
104
|
localeParam: FimoLocaleParam | null;
|
|
89
105
|
/** Placeholders in the path that no usable descriptor resolves. */
|
|
90
106
|
unresolvedParams: string[];
|
|
@@ -120,7 +136,7 @@ export declare function hasLocaleSegment(path: string): boolean;
|
|
|
120
136
|
* Normalize one declared route into the contract every consumer reads.
|
|
121
137
|
*
|
|
122
138
|
* Parameter types resolve in this order:
|
|
123
|
-
* 1. an explicit `type
|
|
139
|
+
* 1. an explicit `type` (`entry`, `locale`, or `external`);
|
|
124
140
|
* 2. the reserved name `locale`, which infers the locale type;
|
|
125
141
|
* 3. otherwise the entry type, which is what every route written before
|
|
126
142
|
* locale-aware routing existed relies on.
|
|
@@ -85,6 +85,26 @@ function buildLocaleParam(name, descriptor, index, issues) {
|
|
|
85
85
|
}
|
|
86
86
|
return { type: "locale", name, optional: descriptor?.optional !== false };
|
|
87
87
|
}
|
|
88
|
+
function buildExternalParam(name, descriptor, issues) {
|
|
89
|
+
if (name === FIMO_LOCALE_PARAM) {
|
|
90
|
+
issues.push({
|
|
91
|
+
param: name,
|
|
92
|
+
message: `Param "${name}" is reserved for the locale segment and cannot use \`"type": "external"\`.`,
|
|
93
|
+
fix: `Drop \`"type": "external"\` from \`params.${name}\`, or rename the segment.`
|
|
94
|
+
});
|
|
95
|
+
return null;
|
|
96
|
+
}
|
|
97
|
+
for (const field of ["schema", "queryField", "displayField", "query", "optional"]) {
|
|
98
|
+
if (descriptor[field] !== void 0) {
|
|
99
|
+
issues.push({
|
|
100
|
+
param: name,
|
|
101
|
+
message: `External param "${name}" must not declare "${field}" \u2014 its values are owned by the application, never read from the CMS.`,
|
|
102
|
+
fix: `Remove "${field}" from \`params.${name}\`.`
|
|
103
|
+
});
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
return { type: "external", name };
|
|
107
|
+
}
|
|
88
108
|
function buildEntryParam(name, descriptor, issues) {
|
|
89
109
|
if (name === FIMO_LOCALE_PARAM) {
|
|
90
110
|
issues.push({
|
|
@@ -153,7 +173,8 @@ function resolveRouteContract(id, route) {
|
|
|
153
173
|
}
|
|
154
174
|
seen.add(name);
|
|
155
175
|
const descriptor = declared[name];
|
|
156
|
-
const
|
|
176
|
+
const type = resolveParamType(name, descriptor);
|
|
177
|
+
const resolved = type === "locale" ? buildLocaleParam(name, descriptor, index, issues) : type === "external" && descriptor ? buildExternalParam(name, descriptor, issues) : buildEntryParam(name, descriptor, issues);
|
|
157
178
|
if (resolved) {
|
|
158
179
|
params.push(resolved);
|
|
159
180
|
} else {
|
|
@@ -178,6 +199,7 @@ function resolveRouteContract(id, route) {
|
|
|
178
199
|
canonicalPath,
|
|
179
200
|
params,
|
|
180
201
|
entryParams: params.filter((param) => param.type === "entry"),
|
|
202
|
+
externalParams: params.filter((param) => param.type === "external"),
|
|
181
203
|
localeParam,
|
|
182
204
|
unresolvedParams,
|
|
183
205
|
issues
|
|
@@ -217,6 +239,7 @@ function matchContract(urlSegments, contract, vocabulary) {
|
|
|
217
239
|
}
|
|
218
240
|
const params = {};
|
|
219
241
|
const entryParams = {};
|
|
242
|
+
const entryNames = new Set(contract.entryParams.map((param) => param.name));
|
|
220
243
|
let localeFromPath = null;
|
|
221
244
|
let score = 0;
|
|
222
245
|
let offset = 0;
|
|
@@ -244,7 +267,9 @@ function matchContract(urlSegments, contract, vocabulary) {
|
|
|
244
267
|
const rawValue = catchAll ? urlSegments.slice(index + offset).join("/") : value;
|
|
245
268
|
const decoded = safeDecode(rawValue);
|
|
246
269
|
params[name] = decoded;
|
|
247
|
-
|
|
270
|
+
if (entryNames.has(name)) {
|
|
271
|
+
entryParams[name] = decoded;
|
|
272
|
+
}
|
|
248
273
|
if (catchAll) {
|
|
249
274
|
break;
|
|
250
275
|
}
|
|
@@ -76,7 +76,7 @@ export declare function rewriteFimoValidateCommand(pkg: PkgWithScripts, pin?: st
|
|
|
76
76
|
* only the ones it uses; every pin is derived from the `fimo` pin so the
|
|
77
77
|
* platform keeps a single version selector (FIMO-1635).
|
|
78
78
|
*/
|
|
79
|
-
export declare const FIMO_PACKAGES: readonly ['fimo', 'fimo-sveltekit', 'fimo-astro', 'fimo-vite', 'fimo-react', 'fimo-react-router', 'fimo-next'];
|
|
79
|
+
export declare const FIMO_PACKAGES: readonly ['fimo-analytics', 'fimo', 'fimo-sveltekit', 'fimo-astro', 'fimo-vite', 'fimo-react', 'fimo-react-router', 'fimo-next'];
|
|
80
80
|
export type FimoPackage = (typeof FIMO_PACKAGES)[number];
|
|
81
81
|
/**
|
|
82
82
|
* Pin for a companion package given the `fimo` pin. Semver and dist-tags carry
|
|
@@ -1,6 +1,10 @@
|
|
|
1
1
|
import fs from 'node:fs';
|
|
2
2
|
import path from 'node:path';
|
|
3
|
-
import { pageSourceParamExpressions, parsePageParamExpression, } from '@fimo/api-contracts';
|
|
3
|
+
import { isEntryParamBinding, pageSourceParamExpressions, parsePageParamExpression, } from '@fimo/api-contracts';
|
|
4
|
+
/** Only an entry binding names content; the rest declare where values come from. */
|
|
5
|
+
function isEntryBinding(binding) {
|
|
6
|
+
return isEntryParamBinding(binding);
|
|
7
|
+
}
|
|
4
8
|
function isRecord(value) {
|
|
5
9
|
return typeof value === 'object' && value !== null && !Array.isArray(value);
|
|
6
10
|
}
|
|
@@ -74,7 +78,14 @@ function validateQuery(query, pointer, schema, params, schemas) {
|
|
|
74
78
|
for (const expression of pageSourceParamExpressions(query)) {
|
|
75
79
|
const parsed = parsePageParamExpression(expression);
|
|
76
80
|
const binding = parsed ? params[parsed.param] : undefined;
|
|
77
|
-
if (!parsed || !binding
|
|
81
|
+
if (!parsed || !binding) {
|
|
82
|
+
continue;
|
|
83
|
+
}
|
|
84
|
+
if (!isEntryBinding(binding)) {
|
|
85
|
+
errors.push(`${pointer}: ${expression} reads param "${parsed.param}", which is not a Collection entry param.`);
|
|
86
|
+
continue;
|
|
87
|
+
}
|
|
88
|
+
if (typeof binding.schema !== 'string') {
|
|
78
89
|
continue;
|
|
79
90
|
}
|
|
80
91
|
const boundSchema = schemas.get(binding.schema);
|
|
@@ -99,7 +110,7 @@ export function validatePageRegistrySchemaReferences(cwd, config, displayPath) {
|
|
|
99
110
|
const params = isRecord(rawRoute.params) ? rawRoute.params : {};
|
|
100
111
|
for (const [param, binding] of Object.entries(params)) {
|
|
101
112
|
const pointer = `${displayPath}#routes.${routeId}.params.${param}`;
|
|
102
|
-
if (typeof binding.schema !== 'string') {
|
|
113
|
+
if (!isEntryBinding(binding) || typeof binding.schema !== 'string') {
|
|
103
114
|
continue;
|
|
104
115
|
}
|
|
105
116
|
const schema = schemas.get(binding.schema);
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "fimo",
|
|
3
|
-
"version": "2.
|
|
3
|
+
"version": "2.7.0-experimental.1",
|
|
4
4
|
"description": "Fimo CLI - create, deploy, and manage Fimo projects",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"astro-component"
|
|
@@ -165,7 +165,8 @@
|
|
|
165
165
|
"scripts": {
|
|
166
166
|
"build": "pnpm run clean && tsc -b tsconfig.json && chmod +x dist/cli/index.js",
|
|
167
167
|
"build:bundle": "node scripts/bundle-cli.mjs && node scripts/bundle-routes.mjs",
|
|
168
|
-
"check:types": "tsc -b tsconfig.json && tsc -p tsconfig.test.json",
|
|
168
|
+
"check:types": "tsc -b tsconfig.json && tsc -p tsconfig.test.json && pnpm check:types:guardrails",
|
|
169
|
+
"check:types:guardrails": "tsc -p tsconfig.guardrails.json",
|
|
169
170
|
"start": "tsx src/cli/index.ts",
|
|
170
171
|
"install-cli": "pnpm build && node scripts/install-cli.mjs",
|
|
171
172
|
"publish-cli": "node scripts/publish-tarball.mjs",
|
|
@@ -193,13 +194,13 @@
|
|
|
193
194
|
"devDependencies": {
|
|
194
195
|
"@clack/core": "^1.2.0",
|
|
195
196
|
"@clack/prompts": "^1.2.0",
|
|
196
|
-
"@fimo/agents-bundle": "2.
|
|
197
|
-
"@fimo/api-contracts": "2.
|
|
198
|
-
"@fimo/cli-kit": "2.
|
|
199
|
-
"@fimo/framework-detect": "2.
|
|
200
|
-
"@fimo/routes": "2.
|
|
201
|
-
"@fimo/sdk": "2.
|
|
202
|
-
"@fimo/tsconfig": "2.
|
|
197
|
+
"@fimo/agents-bundle": "2.7.0-experimental.1",
|
|
198
|
+
"@fimo/api-contracts": "2.7.0-experimental.1",
|
|
199
|
+
"@fimo/cli-kit": "2.7.0-experimental.1",
|
|
200
|
+
"@fimo/framework-detect": "2.7.0-experimental.1",
|
|
201
|
+
"@fimo/routes": "2.7.0-experimental.1",
|
|
202
|
+
"@fimo/sdk": "2.7.0-experimental.1",
|
|
203
|
+
"@fimo/tsconfig": "2.7.0-experimental.1",
|
|
203
204
|
"@types/node": "22.18.6",
|
|
204
205
|
"agent-install": "^0.0.5",
|
|
205
206
|
"astro": "^7.1.6",
|
package/release.json
CHANGED
|
@@ -12,9 +12,9 @@
|
|
|
12
12
|
},
|
|
13
13
|
"dependencies": {
|
|
14
14
|
"@tanstack/react-query": "^5.80.6",
|
|
15
|
-
"fimo": "2.
|
|
16
|
-
"fimo-next": "2.
|
|
17
|
-
"fimo-react": "2.
|
|
15
|
+
"fimo": "2.7.0-experimental.1",
|
|
16
|
+
"fimo-next": "2.7.0-experimental.1",
|
|
17
|
+
"fimo-react": "2.7.0-experimental.1",
|
|
18
18
|
"next": "^16.3.3",
|
|
19
19
|
"react": "^19.2.7",
|
|
20
20
|
"react-dom": "^19.2.7"
|
|
@@ -21,10 +21,10 @@
|
|
|
21
21
|
"cn": "0.2.4",
|
|
22
22
|
"date-fns": "^4.1.0",
|
|
23
23
|
"embla-carousel-react": "^8.6.0",
|
|
24
|
-
"fimo": "2.
|
|
25
|
-
"fimo-react": "2.
|
|
26
|
-
"fimo-react-router": "2.
|
|
27
|
-
"fimo-vite": "2.
|
|
24
|
+
"fimo": "2.7.0-experimental.1",
|
|
25
|
+
"fimo-react": "2.7.0-experimental.1",
|
|
26
|
+
"fimo-react-router": "2.7.0-experimental.1",
|
|
27
|
+
"fimo-vite": "2.7.0-experimental.1",
|
|
28
28
|
"input-otp": "^1.4.2",
|
|
29
29
|
"isbot": "^5",
|
|
30
30
|
"lucide-react": "^0.577.0",
|
|
@@ -11,10 +11,10 @@
|
|
|
11
11
|
},
|
|
12
12
|
"dependencies": {
|
|
13
13
|
"@tanstack/react-query": "^5.80.6",
|
|
14
|
-
"fimo": "2.
|
|
15
|
-
"fimo-react": "2.
|
|
16
|
-
"fimo-react-router": "2.
|
|
17
|
-
"fimo-vite": "2.
|
|
14
|
+
"fimo": "2.7.0-experimental.1",
|
|
15
|
+
"fimo-react": "2.7.0-experimental.1",
|
|
16
|
+
"fimo-react-router": "2.7.0-experimental.1",
|
|
17
|
+
"fimo-vite": "2.7.0-experimental.1",
|
|
18
18
|
"react": "^19.2.7",
|
|
19
19
|
"react-dom": "^19.2.7",
|
|
20
20
|
"react-router": "^8.3.0"
|