fimo 2.2.0-staging.11 → 2.2.0-staging.12

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.
@@ -103,7 +103,7 @@ Reference filters take the target `id` string (`{"author":"750e..."}`). On a to-
103
103
 
104
104
  `--populate` is a comma-separated list of reference fields to embed. Without it, reference fields return reference objects like `{ "id": "..." }`; with `--populate author,contributors`, those fields return the target entry objects for the requested locale. Only reference fields can be populated (scalar fields 400), and populate never selects which target fields come back. A populated target missing in the requested locale resolves to null unless you pass `localeFallbacks=true`, which falls back to the best available locale variant, marked by its `locale` field. Limits: 200 references per to-many write, 500 distinct populated documents per field per request (400 past either).
105
105
 
106
- Polymorphic references declare `targets: ["Post", "Product"]` instead of `target` (never both) and their values carry both parts: `{ "contentType": "Post", "id": "..." }`. Populate returns mixed full entries, and generated hooks expose `$type` on each populated value to branch on. `--where` on polymorphic fields is not supported yet and fails with a clear error.
106
+ Polymorphic references declare `targets: ["Post", "Product"]` instead of `target` (never both) and their values carry both parts: `{ "contentType": "Post", "id": "..." }`. Populate returns mixed full entries, and generated hooks expose `$type` on each populated value to branch on. `--where` on polymorphic fields is not supported yet and fails with a clear error. Fixed and polymorphic references also work inside blocks with these same write shapes (`id`, `documentId`, or `slug` selectors), but nested fields cannot be populated or filtered.
107
107
 
108
108
  In generated application code, the equivalent is `populate: ['author', 'contributors'] as const` in query params. Let the generated client infer the result when possible; if a named TypeScript shape is needed, use `BlogPostResult<TFields, TPopulate>` as documented by `fimo skills get fimo-code --ref content/clients`. Writes use `BlogPostInput`.
109
109
 
@@ -140,6 +140,7 @@ Use `schema.fields` for one repeated shape and `schema.oneOf` for selectable var
140
140
  - Fimo adds stable `_key` values; `oneOf` items also receive `_type`.
141
141
  - Nest blocks up to seven levels. One `oneOf` supports up to 40 variants and one blocks field up to 200 items.
142
142
  - Nested scalar values use the same source-tracked value type as top-level fields.
143
+ - Fixed and polymorphic references work inside blocks with the same value shapes as top-level reference fields.
143
144
  - Nested reference population and filtering are not supported.
144
145
 
145
146
  ## References
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "bundled": true,
3
3
  "bundler": "esbuild",
4
- "bundledAt": "2026-08-28T13:10:10.719Z",
4
+ "bundledAt": "2026-08-28T14:46:45.101Z",
5
5
  "cliVersion": "2.1.0",
6
6
  "external": [
7
7
  "oxc-parser",
package/dist/cli/index.js CHANGED
@@ -1223,7 +1223,7 @@ Fix: add a route entry to ${configDisplayPath} "routes" with a matching "path".`
1223
1223
  Fix: add the route to src/routes.ts. Example: route('${route.path.replace(/^\//,"")}', './pages/YourPage.tsx')`);i18n.routingStrategy==="path-prefix"&&i18n.locales.length>1&&errors.push(...validateLocaleRouteCollisions({configRoutes,fileRoutes,i18n,configDisplayPath,routesSourceUsesLegacyHelper:sourceUsesLegacyFimoRoutes(routesFilePath)}));let routeById=new Map;for(let route of configRoutes){if(routeById.has(route.id)){let previousPath=routeById.get(route.id).path;errors.push(`Duplicate route id "${route.id}" found for paths "${previousPath}" and "${route.path}".`);continue}routeById.set(route.id,route)}for(let route of configRoutes){for(let issue of route.contract.issues)errors.push(`Route "${route.id}" (${route.path}): ${issue.message}
1224
1224
  Fix: ${issue.fix}`);errors.push(...validatePageSources(route))}errors.push(...validateRouteShapeCollisions(configRoutes));for(let route of configRoutes){let file=registrablePaths(route).map(candidate=>pathToFile.get(candidate)).find(Boolean);if(!file)continue;let routeModulePath=path24.resolve(srcDir,file);if(!fs21.existsSync(routeModulePath)){errors.push(`Route "${route.id}" (${route.path}) references missing module "${file}".
1225
1225
  Fix: create the file at src/${file}.`);continue}if(route.seo){let GLOBAL_VARIABLES=new Set(["siteName","label"]),seoFields=[{name:"title",value:route.seo.title},{name:"description",value:route.seo.description}];for(let field of seoFields){if(typeof field.value!="string")continue;let templatePattern=/\{\{([^}]+)\}\}/g,match;for(;(match=templatePattern.exec(field.value))!==null;){let variable=match[1].trim();if(GLOBAL_VARIABLES.has(variable))continue;let entryParamNames=route.contract.entryParams.map(param=>param.name),availableParams=entryParamNames.length>0?entryParamNames.join(", "):"none";if(variable.includes(".")){let paramKey=variable.split(".")[0];entryParamNames.includes(paramKey)||errors.push(`Route "${route.id}" (${route.path}): SEO ${field.name} references "{{${variable}}}" but "${paramKey}" is not a declared entry param. Available params: ${availableParams}.`)}else errors.push(`Route "${route.id}" (${route.path}): SEO ${field.name} references "{{${variable}}}" which is not a global variable (siteName, label) and not a dotted param reference (e.g., "{{param.field}}"). Available params: ${availableParams}.`)}}}}return{errors,routesChecked:configRoutes.length}}__name(validateRouteMetadata,"validateRouteMetadata");function validatePageSources(route){if(!route.sources)return[];let errors=[],entryParamNames=new Set(route.contract.entryParams.map(param=>param.name));for(let[sourceName,rawSource]of Object.entries(route.sources)){if(!/^[A-Za-z][\w-]*$/.test(sourceName)||!isRecord2(rawSource)){errors.push(`Route "${route.id}" (${route.path}): source "${sourceName}" is not a valid named source.`);continue}if(rawSource.type==="labels"){let match=rawSource.match;(!Array.isArray(match)||match.length===0||match.some(pattern=>{if(typeof pattern!="string")return!0;let wildcard=pattern.indexOf("*");return wildcard!==-1&&!(pattern.length>2&&pattern.endsWith(".*")&&wildcard===pattern.length-1)}))&&errors.push(`Route "${route.id}" (${route.path}): Labels source "${sourceName}" needs exact keys or prefixes ending in .*.`);continue}if(rawSource.type!=="single"&&rawSource.type!=="collection"){errors.push(`Route "${route.id}" (${route.path}): source "${sourceName}" type must be labels, single, or collection.`);continue}if((typeof rawSource.schema!="string"||!rawSource.schema)&&errors.push(`Route "${route.id}" (${route.path}): source "${sourceName}" needs a schema.`),rawSource.type==="collection"&&rawSource.query!==void 0&&!isRecord2(rawSource.query)){errors.push(`Route "${route.id}" (${route.path}): Collection source "${sourceName}" query must be an object.`);continue}for(let expression of pageSourceParamExpressions(rawSource.query)){let param=/^\$params\.([A-Za-z_][\w-]*)\.([A-Za-z_][\w.-]*)$/.exec(expression)?.[1];(!param||!entryParamNames.has(param))&&errors.push(`Route "${route.id}" (${route.path}): source "${sourceName}" references undeclared param "${param??expression}". Fix: use $params.<declared-entry-param>.<field>.`)}}return errors}__name(validatePageSources,"validatePageSources");function validateI18nConfig(config){let rawI18n=isRecord2(config.i18n)?config.i18n:{},errors=[],rawDefaultLocale=typeof rawI18n.defaultLocale=="string"?rawI18n.defaultLocale:"en",defaultLocale=normalizeFimoLocale(rawDefaultLocale);defaultLocale||errors.push(`Invalid i18n.defaultLocale "${rawDefaultLocale}". Use a structurally valid locale tag, for example "en", "es", or "pt-BR".`);let rawLocales=Array.isArray(rawI18n.locales)?rawI18n.locales:defaultLocale?[defaultLocale]:[];rawI18n.locales!==void 0&&!Array.isArray(rawI18n.locales)&&errors.push("i18n.locales must be an array of locale strings.");let locales=[],seenLocales=new Set;for(let rawLocale of rawLocales){if(typeof rawLocale!="string"){errors.push(`Invalid i18n.locales entry ${JSON.stringify(rawLocale)}. Locale entries must be strings.`);continue}let locale=normalizeFimoLocale(rawLocale);if(!locale){errors.push(`Invalid i18n.locales entry "${rawLocale}". Use a structurally valid locale tag, for example "en", "es", or "pt-BR".`);continue}seenLocales.has(locale)||(seenLocales.add(locale),locales.push(locale))}let effectiveDefaultLocale=defaultLocale??"en";seenLocales.has(effectiveDefaultLocale)||(errors.push(`i18n.locales must include i18n.defaultLocale "${effectiveDefaultLocale}".`),locales.unshift(effectiveDefaultLocale));let rawRouting=isRecord2(rawI18n.routing)?rawI18n.routing:{},routingStrategy=typeof rawRouting.strategy=="string"?rawRouting.strategy:"path-prefix";return routingStrategy!=="path-prefix"&&errors.push(`Unsupported i18n.routing.strategy "${routingStrategy}". Supported now: "path-prefix".`),{defaultLocale:effectiveDefaultLocale,locales,routingStrategy,errors}}__name(validateI18nConfig,"validateI18nConfig");function validateRouteShapeCollisions(configRoutes){let errors=[],byShape=new Map;for(let route of configRoutes){let shape=route.contract.canonicalPath.split("/").map(segment=>segment.startsWith(":")?":param":segment).join("/"),previous=byShape.get(shape);if(previous&&(previous.contract.localeParam||route.contract.localeParam)){errors.push(`Routes "${previous.id}" (${previous.path}) and "${route.id}" (${route.path}) both answer "${route.contract.canonicalPath}".
1226
- Fix: keep one of them, or give them distinct paths \u2014 a locale segment does not make two routes distinct.`);continue}previous||byShape.set(shape,route)}return errors}__name(validateRouteShapeCollisions,"validateRouteShapeCollisions");function validateLocaleRouteCollisions(args){let errors=[],reservedPrefixes=new Set(args.i18n.locales.map(locale=>locale.toLowerCase()));for(let route of args.configRoutes){let first2=firstPathSegment(route.path);first2&&reservedPrefixes.has(first2.toLowerCase())&&errors.push(`Route "${route.id}" (${route.path}) uses reserved locale prefix "${first2}". Declare the locale as "/:locale/..." in ${args.configDisplayPath} and as ":locale?/..." in React Router so the default locale remains unprefixed.`)}if(!args.routesSourceUsesLegacyHelper)for(let route of args.fileRoutes){if(!route.path||route.isLayout)continue;let first2=firstPathSegment(route.path);first2&&normalizeLocaleSpelling(first2)===`:${FIMO_LOCALE_PARAM}`||(first2?.startsWith(":")||first2==="*")&&errors.push(`Route "${route.path}" is a top-level dynamic route in a multi-locale project. It can capture locale URLs like "/${args.i18n.locales[0]}". Put an explicit optional ":locale?" segment first in the React Router path, or keep this route outside the localized route boundary.`)}return errors}__name(validateLocaleRouteCollisions,"validateLocaleRouteCollisions");function sourceUsesLegacyFimoRoutes(routesFilePath){if(!fs21.existsSync(routesFilePath))return!1;let source=fs21.readFileSync(routesFilePath,"utf-8"),importedName=source.match(/import\s*\{(?<imports>[^}]*)\}\s*from\s*['"]fimo\/react-router\/routes['"]/s)?.groups?.imports?.split(",").map(specifier=>specifier.trim().match(/^fimoRoutes(?:\s+as\s+(?<alias>[A-Za-z_$][\w$]*))?$/)?.groups?.alias??(specifier.trim()==="fimoRoutes"?"fimoRoutes":null)).find(name=>!!name);return importedName?new RegExp(`\\b${importedName}\\s*\\(`).test(source):!1}__name(sourceUsesLegacyFimoRoutes,"sourceUsesLegacyFimoRoutes");function normalizeLocaleSpelling(routePath){return routePath.replace(`:${FIMO_LOCALE_PARAM}?`,`:${FIMO_LOCALE_PARAM}`)}__name(normalizeLocaleSpelling,"normalizeLocaleSpelling");function registrablePaths(route){let declared=normalizeLocaleSpelling(route.contract.path);return route.contract.localeParam?.optional?[declared,route.contract.canonicalPath]:[declared]}__name(registrablePaths,"registrablePaths");function firstPathSegment(routePath){return routePath.split("/").filter(Boolean)[0]??null}__name(firstPathSegment,"firstPathSegment");function isRecord2(value){return typeof value=="object"&&value!==null&&!Array.isArray(value)}__name(isRecord2,"isRecord");function normalizeFimoLocale(locale){let trimmed=locale?.trim();if(!trimmed)return null;let candidate=trimmed.replace(/_/g,"-");try{return Intl.getCanonicalLocales(candidate)[0]??null}catch{return null}}__name(normalizeFimoLocale,"normalizeFimoLocale");init_esm();import{existsSync as existsSync6,readFileSync as readFileSync6,readdirSync as readdirSync4}from"node:fs";import{basename as basename2,join as join7,resolve as resolve6}from"node:path";var SCHEMAS_REL2="src/schemas",FIELD_TYPES=new Set(FieldTypeSchema.options),RESERVED_FIELD_NAMES=new Set(RESERVED_CONTENT_FIELD_NAMES);function validateSchemas(options={}){let cwd=resolve6(options.cwd??process.cwd()),schemasDir=join7(cwd,SCHEMAS_REL2);if(!existsSync6(schemasDir))return{errors:[],schemasChecked:0};let files=readdirSync4(schemasDir).filter(f=>f.endsWith(".json")).sort(),errors=[],schemaUids=new Set;for(let fileName of files){let schema=readSchemaObject(join7(schemasDir,fileName));schema&&typeof schema.uid=="string"&&schemaUids.add(schema.uid)}for(let fileName of files){let file=join7(SCHEMAS_REL2,fileName);errors.push(...validateSchemaFile(join7(schemasDir,fileName),file,schemaUids))}return{errors,schemasChecked:files.length}}__name(validateSchemas,"validateSchemas");function readSchemaObject(absolutePath){let schema;try{schema=JSON.parse(readFileSync6(absolutePath,"utf-8"))}catch{return null}return typeof schema=="object"&&schema!==null&&!Array.isArray(schema)?schema:null}__name(readSchemaObject,"readSchemaObject");function validateSchemaFile(absolutePath,file,schemaUids=new Set){let schema;try{schema=JSON.parse(readFileSync6(absolutePath,"utf-8"))}catch(error){return[{rule:"schemas/parse",message:`Invalid JSON: ${error instanceof Error?error.message:String(error)}`,file}]}if(typeof schema!="object"||schema===null||Array.isArray(schema))return[{rule:"schemas/shape",message:"Schema must be a JSON object.",file}];let errors=[],{uid,name,fields,i18n,isSingleton,groups}=schema,expectedUid=basename2(file,".json");if(typeof uid!="string"||uid.length===0?errors.push({rule:"schemas/uid",message:`Missing "uid". Set it to the file name: "${expectedUid}".`,file}):uid!==expectedUid?errors.push({rule:"schemas/uid",message:`"uid" is "${uid}" but the file is ${expectedUid}.json \u2014 they must match.`,file}):CONTENT_TYPE_NAME_PATTERN.test(uid)||errors.push({rule:"schemas/type-name",message:`"uid" must use ${CONTENT_TYPE_NAME_PATTERN_DESCRIPTION}.`,file}),(typeof name!="string"||name.length===0)&&errors.push({rule:"schemas/name",message:'Missing "name" (the human-readable label).',file}),isSingleton!==void 0&&typeof isSingleton!="boolean"&&errors.push({rule:"schemas/singleton",message:'"isSingleton" must be a boolean when provided.',file}),i18n!==void 0)if(typeof i18n!="object"||i18n===null||Array.isArray(i18n))errors.push({rule:"schemas/i18n",message:'"i18n" must be an object like { "enabled": false }.',file});else{let keys=Object.keys(i18n);(keys.length!==1||keys[0]!=="enabled"||typeof i18n.enabled!="boolean")&&errors.push({rule:"schemas/i18n",message:'"i18n" only supports one boolean option: { "enabled": true | false }.',file})}let groupIds=new Set;if(groups!==void 0)if(!Array.isArray(groups))errors.push({rule:"schemas/groups",message:'"groups" must be an array.',file});else{let defaults=0;for(let[index2,group]of groups.entries()){let parsed=ContentFieldGroupSchema.safeParse(group);if(!parsed.success){errors.push({rule:"schemas/groups",message:`Group at index ${index2} is invalid: ${parsed.error.issues[0]?.message??"invalid group"}.`,file});continue}groupIds.has(parsed.data.id)&&errors.push({rule:"schemas/groups",message:`Group ID "${parsed.data.id}" is declared more than once.`,file}),groupIds.add(parsed.data.id),parsed.data.default&&(defaults+=1)}defaults>1&&errors.push({rule:"schemas/groups",message:"At most one group may be the default.",file})}if(typeof fields!="object"||fields===null||Array.isArray(fields))return errors.push({rule:"schemas/fields",message:'Missing "fields" object.',file}),errors;let parsedFields={};for(let[fieldName,definition]of Object.entries(fields)){if(!CONTENT_FIELD_NAME_PATTERN.test(fieldName)){errors.push({rule:"schemas/field-name",message:`Field "${fieldName}" must use ${CONTENT_FIELD_NAME_PATTERN_DESCRIPTION}.`,file});continue}if(RESERVED_FIELD_NAMES.has(fieldName)){errors.push({rule:"schemas/reserved-field",message:`Field "${fieldName}" is reserved entry metadata \u2014 the platform manages it automatically. Remove it from the schema${fieldName==="slug"?" (set slug inside entry data on create instead)":""}.`,file});continue}if(typeof definition!="object"||definition===null||Array.isArray(definition)){errors.push({rule:"schemas/field-shape",message:`Field "${fieldName}" must be an object like { "type": "string" }.`,file});continue}let{type}=definition,memberships=Array.isArray(definition.group)?definition.group:definition.group===void 0?[]:[definition.group];new Set(memberships).size!==memberships.length&&errors.push({rule:"schemas/field-group",message:`Field "${fieldName}" lists the same group more than once.`,file});for(let membership of memberships)(typeof membership!="string"||!groupIds.has(membership))&&errors.push({rule:"schemas/field-group",message:`Field "${fieldName}" references unknown group "${String(membership)}".`,file});if((typeof type!="string"||!FIELD_TYPES.has(type))&&errors.push({rule:"schemas/field-type",message:`Field "${fieldName}" has unsupported type "${String(type)}". Allowed: ${[...FIELD_TYPES].join(", ")}.`,file}),"localized"in definition&&errors.push({rule:"schemas/field-i18n",message:`Field "${fieldName}": "localized" is not supported. Use "i18n": false to share one value across locales.`,file}),"i18n"in definition&&(typeof definition.i18n!="boolean"?errors.push({rule:"schemas/field-i18n",message:`Field "${fieldName}": "i18n" must be true or false.`,file}):type==="blocks"&&errors.push({rule:"schemas/field-i18n",message:`Field "${fieldName}": "i18n" is not supported on blocks fields.`,file})),type==="blocks"){let parsed2=ContentFieldDefinitionSchema.safeParse(definition);if(!parsed2.success){for(let issue of parsed2.error.issues)errors.push({rule:"schemas/structured-content-shape",message:`Field "${fieldName}"${issue.path.length>0?` at ${issue.path.join(".")}`:""}: ${issue.message}`,file});continue}parsedFields[fieldName]=parsed2.data,walkContentFields({[fieldName]:parsed2.data},({field,path:path53,structuredDepth,insideStructuredContent})=>{if(insideStructuredContent&&field.group!==void 0&&errors.push({rule:"schemas/field-group",message:`Field "${path53.join(".")}" cannot use "group" inside blocks.`,file}),field.type==="blocks"&&structuredDepth>=MAX_STRUCTURED_CONTENT_DEPTH&&errors.push({rule:"schemas/structured-content-depth",message:`Field "${path53.join(".")}" exceeds the ${MAX_STRUCTURED_CONTENT_DEPTH}-level blocks limit.`,file}),!(!insideStructuredContent||field.type!=="reference")){if(field.targets){errors.push({rule:"schemas/structured-content-reference",message:`Reference field "${path53.join(".")}" must use one fixed "target" inside blocks.`,file});return}field.target&&schemaUids.size>0&&!schemaUids.has(field.target)&&errors.push({rule:"schemas/reference-target",message:`Reference field "${path53.join(".")}" targets unknown schema "${field.target}".`,file})}});continue}let parsed=ContentFieldDefinitionSchema.safeParse(definition);if(parsed.success&&(parsedFields[fieldName]=parsed.data),type==="reference"){let{target,targets,many}=definition;if(targets!==void 0&&target!==void 0)errors.push({rule:"schemas/reference-target",message:`Reference field "${fieldName}" declares both "target" and "targets" \u2014 a field is fixed or polymorphic, never both.`,file});else if(targets!==void 0){let targetList=Array.isArray(targets)?targets:null;if(!targetList||targetList.length===0||new Set(targetList).size!==targetList.length)errors.push({rule:"schemas/reference-target",message:`Reference field "${fieldName}" must declare "targets" as a non-empty array of unique content types.`,file});else for(let candidate of targetList)typeof candidate!="string"||!CONTENT_TYPE_NAME_PATTERN.test(candidate)?errors.push({rule:"schemas/reference-target",message:`Reference field "${fieldName}" has an invalid entry in "targets".`,file}):schemaUids.size>0&&!schemaUids.has(candidate)&&errors.push({rule:"schemas/reference-target",message:`Reference field "${fieldName}" targets unknown schema "${candidate}".`,file})}else typeof target!="string"||!CONTENT_TYPE_NAME_PATTERN.test(target)?errors.push({rule:"schemas/reference-target",message:`Reference field "${fieldName}" must declare a valid "target" content type (or "targets" for a polymorphic field).`,file}):schemaUids.size>0&&!schemaUids.has(target)&&errors.push({rule:"schemas/reference-target",message:`Reference field "${fieldName}" targets unknown schema "${target}".`,file});many!==void 0&&typeof many!="boolean"&&errors.push({rule:"schemas/reference-many",message:`Reference field "${fieldName}" must use a boolean "many" value.`,file})}else for(let referenceOnlyKey of["target","targets","many"])referenceOnlyKey in definition&&errors.push({rule:"schemas/reference-shape",message:`Field "${fieldName}": "${referenceOnlyKey}" is only supported on reference fields.`,file})}for(let problem of collectConditionProblems(parsedFields))errors.push({rule:"schemas/conditions",message:problem.message,file});return errors}__name(validateSchemaFile,"validateSchemaFile");init_api();init_config();init_auth_errors();init_project();import fs22 from"node:fs";import path25 from"node:path";var MIN_DB_LABELS_FIMO_VERSION="0.2.5",MIN_DB_LABELS_TEMPLATE_VERSION=4;function getDbLabelsRuntimeWarning(cwd){if(readExplicitConfigVersion(cwd)<MIN_DB_LABELS_TEMPLATE_VERSION)return null;let projectPackage=readPackageJson2(path25.join(cwd,"package.json")),fimoSpec=findFimoSpec(projectPackage);if(!fimoSpec)return buildWarning("package.json has no fimo dependency");let source=findRuntimeVersion(cwd,fimoSpec);return!source||!isVersionOlder(source.version,MIN_DB_LABELS_FIMO_VERSION)?null:buildWarning(`${source.label} is fimo@${source.version}`)}__name(getDbLabelsRuntimeWarning,"getDbLabelsRuntimeWarning");function readExplicitConfigVersion(cwd){try{let raw=JSON.parse(fs22.readFileSync(resolveProjectConfigPath(cwd),"utf8")),parsed=Number.parseInt(String(raw.version),10);return Number.isFinite(parsed)&&parsed>0?parsed:0}catch{return 0}}__name(readExplicitConfigVersion,"readExplicitConfigVersion");function buildWarning(subject){return[`CRITICAL: ${subject}, but DB-owned labels require fimo >= ${MIN_DB_LABELS_FIMO_VERSION}.`,"Labels changed with `fimo labels` or Fimo Studio may not render in this project until the app runtime is upgraded.","Run `npm install fimo@latest` (or your package manager equivalent), then run `fimo validate` again."].join(" ")}__name(buildWarning,"buildWarning");function findRuntimeVersion(cwd,fimoSpec){let installed=readPackageJson2(path25.join(cwd,"node_modules","fimo","package.json"));if(typeof installed?.version=="string")return{label:"installed node_modules/fimo",version:installed.version};let packageLockVersion=readPackageLockVersion(cwd);if(packageLockVersion)return{label:"package-lock.json",version:packageLockVersion};let exactSpecVersion=parseVersion2(fimoSpec);if(exactSpecVersion)return{label:"package.json dependency",version:exactSpecVersion.normalized};if(fimoSpec.startsWith("file:")){let fileVersion=readFileDependencyVersion(cwd,fimoSpec);if(fileVersion)return{label:"package.json file dependency target",version:fileVersion}}return null}__name(findRuntimeVersion,"findRuntimeVersion");function findFimoSpec(pkg){let dependenciesSpec=pkg?.dependencies?.fimo;if(typeof dependenciesSpec=="string"&&dependenciesSpec.trim())return dependenciesSpec.trim();let devDependenciesSpec=pkg?.devDependencies?.fimo;return typeof devDependenciesSpec=="string"&&devDependenciesSpec.trim()?devDependenciesSpec.trim():null}__name(findFimoSpec,"findFimoSpec");function readFileDependencyVersion(cwd,spec){let rawPath=spec.slice(5),packageRoot=path25.isAbsolute(rawPath)?rawPath:path25.resolve(cwd,rawPath),pkg=readPackageJson2(path25.join(packageRoot,"package.json"));return typeof pkg?.version=="string"?pkg.version:null}__name(readFileDependencyVersion,"readFileDependencyVersion");function readPackageLockVersion(cwd){try{let lock=JSON.parse(fs22.readFileSync(path25.join(cwd,"package-lock.json"),"utf8")),packagesVersion=lock.packages?.["node_modules/fimo"]?.version;if(typeof packagesVersion=="string")return packagesVersion;let dependencyVersion=lock.dependencies?.fimo?.version;return typeof dependencyVersion=="string"?dependencyVersion:null}catch{return null}}__name(readPackageLockVersion,"readPackageLockVersion");function readPackageJson2(filePath){try{return JSON.parse(fs22.readFileSync(filePath,"utf8"))}catch{return null}}__name(readPackageJson2,"readPackageJson");function isVersionOlder(version,minimum){let parsed=parseVersion2(version),parsedMinimum=parseVersion2(minimum);if(!parsed||!parsedMinimum)return!1;for(let key of["major","minor","patch"]){if(parsed[key]<parsedMinimum[key])return!0;if(parsed[key]>parsedMinimum[key])return!1}return!1}__name(isVersionOlder,"isVersionOlder");function parseVersion2(version){let match=version.trim().match(/^v?(\d+)\.(\d+)\.(\d+)(?:[-+].*)?$/);return match?{major:Number(match[1]),minor:Number(match[2]),patch:Number(match[3]),normalized:`${Number(match[1])}.${Number(match[2])}.${Number(match[3])}`}:null}__name(parseVersion2,"parseVersion");import{execFile as execFile3}from"node:child_process";import fs23 from"node:fs";import path26 from"node:path";import{promisify as promisify3}from"node:util";var execFileAsync3=promisify3(execFile3),LOCKFILE_MANAGERS2={"pnpm-lock.yaml":"pnpm","bun.lock":"bun","bun.lockb":"bun","yarn.lock":"yarn","package-lock.json":"npm"};async function inspectDependencyLifecycleScripts(cwd,execute=executeCommand){let selection=selectPackageManager(cwd);if(!selection)return{status:"warning",message:"Dependency lifecycle-script approval check skipped: no package manager could be selected from package.json or a lockfile."};if(!fs23.existsSync(path26.join(cwd,"node_modules")))return{status:"warning",message:`Dependency lifecycle-script approval check skipped: ${selection.name} has no installed node_modules state to inspect.`};if(selection.name==="yarn")return{status:"warning",message:"Dependency lifecycle-script approval check skipped: Yarn has no stable standalone blocked-script listing command."};if(selection.name==="npm")return{status:"warning",message:"Dependency lifecycle-script approval check skipped: this npm version has no supported read-only blocked-script listing command."};try{let versionResult=await execute(selection.name,["--version"],cwd);if(selection.version&&!matchesRequestedVersion(versionResult.stdout,selection.version))return{status:"warning",message:`Dependency lifecycle-script approval check skipped: the installed ${selection.name} version ${versionResult.stdout.trim()||"unknown"} does not match the exact packageManager pin ${selection.name}@${selection.version}.`};if(selection.name==="pnpm"&&!supportsPnpmIgnoredBuilds(versionResult.stdout))return{status:"warning",message:`Dependency lifecycle-script approval check skipped: pnpm ${versionResult.stdout.trim()||"unknown"} does not support pnpm ignored-builds inspection (requires pnpm 10.1+).`};let args=selection.name==="pnpm"?["ignored-builds"]:["pm","untrusted"],result=await execute(selection.name,args,cwd);return selection.name==="pnpm"?parsePnpmIgnoredBuilds(result.stdout,fs23.existsSync(path26.join(cwd,"node_modules",".modules.yaml"))):parseBunUntrusted(result.stdout)}catch(error){return{status:"warning",message:`Dependency lifecycle-script approval check skipped: could not inspect ${selection.name} install state (${error instanceof Error?error.message:String(error)}).`}}}__name(inspectDependencyLifecycleScripts,"inspectDependencyLifecycleScripts");function parsePnpmIgnoredBuilds(output,hasModulesManifest){let lines=output.split(/\r?\n/),section2=null,sawAutomaticSection=!1,sawExplicitSection=!1,sawNone=!1,packages=[];for(let line of lines){let trimmed=line.trim();if(/^Automatically ignored builds during installation:/i.test(trimmed)){section2="automatic",sawAutomaticSection=!0;continue}if(/^Explicitly ignored package builds/i.test(trimmed)){section2="explicit",sawExplicitSection=!0;continue}if(section2!=="automatic"||trimmed.length===0)continue;let entry=/^\s{2,}(\S.*)$/.exec(line)?.[1]?.trim();if(!entry){section2=null;continue}if(/^Cannot identify as no node_modules found$/i.test(entry))return{status:"warning",message:hasModulesManifest?"Dependency lifecycle-script approval check skipped: pnpm could not confirm whether dependency install scripts were blocked.":"Dependency lifecycle-script approval check skipped: pnpm recorded no install state under node_modules to inspect."};if(/^None$/i.test(entry))sawNone=!0;else{let packageEntry=entry.replace(/^[-*]\s+/,"");if(/\s/.test(packageEntry))return unrecognizedOutput("pnpm");packages.push(pnpmPackageName(packageEntry))}}return packages.length>0?{status:"blocked",packageManager:"pnpm",packages:[...new Set(packages)]}:sawAutomaticSection&&(sawExplicitSection||sawNone)?{status:"clean",packageManager:"pnpm"}:unrecognizedOutput("pnpm")}__name(parsePnpmIgnoredBuilds,"parsePnpmIgnoredBuilds");function parseBunUntrusted(output){let packages=output.split(/\r?\n/).map(line=>line.trim().replace(/^[-*]\s+/,"")).map(line=>/^\.\/node_modules\/(.+?)\s+@[^\s]+$/.exec(line)?.[1]??null).filter(packageName=>packageName!==null);return packages.length>0&&/These dependencies had their lifecycle scripts blocked during install\./i.test(output)?{status:"blocked",packageManager:"bun",packages:[...new Set(packages)]}:/Found 0 untrusted dependencies with scripts\./i.test(output)?{status:"clean",packageManager:"bun"}:unrecognizedOutput("bun")}__name(parseBunUntrusted,"parseBunUntrusted");function unrecognizedOutput(packageManager){return{status:"warning",message:`Dependency lifecycle-script approval check skipped: ${packageManager} returned unrecognized successful output.`}}__name(unrecognizedOutput,"unrecognizedOutput");function pnpmPackageName(entry){return(entry.startsWith("@")?/^(@[^/]+\/[^@]+)(?:@.+)?$/.exec(entry):/^([^@]+)(?:@.+)?$/.exec(entry))?.[1]??entry}__name(pnpmPackageName,"pnpmPackageName");function selectPackageManager(cwd){let packageJson=readPackageJson3(path26.join(cwd,"package.json")),exact=typeof packageJson?.packageManager=="string"?parseExactPackageManager(packageJson.packageManager):null;if(exact)return exact;let lockfiles=Object.entries(LOCKFILE_MANAGERS2).filter(([lockfile])=>fs23.existsSync(path26.join(cwd,lockfile)));return lockfiles.length!==1?null:{name:lockfiles[0][1]}}__name(selectPackageManager,"selectPackageManager");function parseExactPackageManager(value){let match=/^(pnpm|bun|npm|yarn)@(\d+\.\d+\.\d+(?:[-+][0-9A-Za-z.-]+)?)$/.exec(value.trim());return match?{name:match[1],version:match[2]}:null}__name(parseExactPackageManager,"parseExactPackageManager");function supportsPnpmIgnoredBuilds(version){let match=/^(\d+)\.(\d+)\.(\d+)/.exec(version.trim());if(!match)return!1;let major=Number(match[1]),minor=Number(match[2]);return major>10||major===10&&minor>=1}__name(supportsPnpmIgnoredBuilds,"supportsPnpmIgnoredBuilds");function matchesRequestedVersion(actual,requested){return actual.trim()===requested.split("+",1)[0]}__name(matchesRequestedVersion,"matchesRequestedVersion");async function executeCommand(command,args,cwd){let result=await execFileAsync3(command,args,{cwd,encoding:"utf8"});return{stdout:String(result.stdout),stderr:String(result.stderr)}}__name(executeCommand,"executeCommand");function readPackageJson3(filePath){try{return JSON.parse(fs23.readFileSync(filePath,"utf8"))}catch{return null}}__name(readPackageJson3,"readPackageJson");init_project_context();init_project();init_dist();import{existsSync as existsSync8,readFileSync as readFileSync8,readdirSync as readdirSync6,statSync as statSync3}from"node:fs";import{join as join9,resolve as resolve7}from"node:path";import fs24 from"node:fs/promises";import path27 from"node:path";async function pathExists(targetPath){try{return await fs24.access(targetPath),!0}catch{return!1}}__name(pathExists,"pathExists");async function removeDataFiles(dir){let entries2=await fs24.readdir(dir,{withFileTypes:!0});for(let entry of entries2){let fullPath=path27.join(dir,entry.name);entry.isDirectory()?await removeDataFiles(fullPath):entry.name.endsWith(".data")&&await fs24.rm(fullPath)}}__name(removeDataFiles,"removeDataFiles");async function exportStatic(options={}){let cwd=options.cwd??process.cwd(),clientDir=path27.resolve(cwd,"dist/client"),serverDir=path27.resolve(cwd,"dist/server");if(!await pathExists(clientDir))throw new Error("Missing dist/client output. Run `react-router build` first.");await pathExists(serverDir)&&await fs24.rm(serverDir,{recursive:!0,force:!0}),await removeDataFiles(clientDir),console.log("Static export complete.")}__name(exportStatic,"exportStatic");import{existsSync as existsSync7,readFileSync as readFileSync7,readdirSync as readdirSync5,statSync as statSync2}from"fs";import{createRequire as createRequire2}from"node:module";import{join as join8}from"path";var require3=createRequire2(import.meta.url),parseSync2;function parse7(filePath,code2){return parseSync2??=require3("oxc-parser").parseSync,parseSync2(filePath,code2)}__name(parse7,"parse");var SRC_DIR="src";function getLineAndColumn(code2,offset){let lines=code2.slice(0,offset).split(`
1226
+ Fix: keep one of them, or give them distinct paths \u2014 a locale segment does not make two routes distinct.`);continue}previous||byShape.set(shape,route)}return errors}__name(validateRouteShapeCollisions,"validateRouteShapeCollisions");function validateLocaleRouteCollisions(args){let errors=[],reservedPrefixes=new Set(args.i18n.locales.map(locale=>locale.toLowerCase()));for(let route of args.configRoutes){let first2=firstPathSegment(route.path);first2&&reservedPrefixes.has(first2.toLowerCase())&&errors.push(`Route "${route.id}" (${route.path}) uses reserved locale prefix "${first2}". Declare the locale as "/:locale/..." in ${args.configDisplayPath} and as ":locale?/..." in React Router so the default locale remains unprefixed.`)}if(!args.routesSourceUsesLegacyHelper)for(let route of args.fileRoutes){if(!route.path||route.isLayout)continue;let first2=firstPathSegment(route.path);first2&&normalizeLocaleSpelling(first2)===`:${FIMO_LOCALE_PARAM}`||(first2?.startsWith(":")||first2==="*")&&errors.push(`Route "${route.path}" is a top-level dynamic route in a multi-locale project. It can capture locale URLs like "/${args.i18n.locales[0]}". Put an explicit optional ":locale?" segment first in the React Router path, or keep this route outside the localized route boundary.`)}return errors}__name(validateLocaleRouteCollisions,"validateLocaleRouteCollisions");function sourceUsesLegacyFimoRoutes(routesFilePath){if(!fs21.existsSync(routesFilePath))return!1;let source=fs21.readFileSync(routesFilePath,"utf-8"),importedName=source.match(/import\s*\{(?<imports>[^}]*)\}\s*from\s*['"]fimo\/react-router\/routes['"]/s)?.groups?.imports?.split(",").map(specifier=>specifier.trim().match(/^fimoRoutes(?:\s+as\s+(?<alias>[A-Za-z_$][\w$]*))?$/)?.groups?.alias??(specifier.trim()==="fimoRoutes"?"fimoRoutes":null)).find(name=>!!name);return importedName?new RegExp(`\\b${importedName}\\s*\\(`).test(source):!1}__name(sourceUsesLegacyFimoRoutes,"sourceUsesLegacyFimoRoutes");function normalizeLocaleSpelling(routePath){return routePath.replace(`:${FIMO_LOCALE_PARAM}?`,`:${FIMO_LOCALE_PARAM}`)}__name(normalizeLocaleSpelling,"normalizeLocaleSpelling");function registrablePaths(route){let declared=normalizeLocaleSpelling(route.contract.path);return route.contract.localeParam?.optional?[declared,route.contract.canonicalPath]:[declared]}__name(registrablePaths,"registrablePaths");function firstPathSegment(routePath){return routePath.split("/").filter(Boolean)[0]??null}__name(firstPathSegment,"firstPathSegment");function isRecord2(value){return typeof value=="object"&&value!==null&&!Array.isArray(value)}__name(isRecord2,"isRecord");function normalizeFimoLocale(locale){let trimmed=locale?.trim();if(!trimmed)return null;let candidate=trimmed.replace(/_/g,"-");try{return Intl.getCanonicalLocales(candidate)[0]??null}catch{return null}}__name(normalizeFimoLocale,"normalizeFimoLocale");init_esm();import{existsSync as existsSync6,readFileSync as readFileSync6,readdirSync as readdirSync4}from"node:fs";import{basename as basename2,join as join7,resolve as resolve6}from"node:path";var SCHEMAS_REL2="src/schemas",FIELD_TYPES=new Set(FieldTypeSchema.options),RESERVED_FIELD_NAMES=new Set(RESERVED_CONTENT_FIELD_NAMES);function validateSchemas(options={}){let cwd=resolve6(options.cwd??process.cwd()),schemasDir=join7(cwd,SCHEMAS_REL2);if(!existsSync6(schemasDir))return{errors:[],schemasChecked:0};let files=readdirSync4(schemasDir).filter(f=>f.endsWith(".json")).sort(),errors=[],schemaUids=new Set;for(let fileName of files){let schema=readSchemaObject(join7(schemasDir,fileName));schema&&typeof schema.uid=="string"&&schemaUids.add(schema.uid)}for(let fileName of files){let file=join7(SCHEMAS_REL2,fileName);errors.push(...validateSchemaFile(join7(schemasDir,fileName),file,schemaUids))}return{errors,schemasChecked:files.length}}__name(validateSchemas,"validateSchemas");function readSchemaObject(absolutePath){let schema;try{schema=JSON.parse(readFileSync6(absolutePath,"utf-8"))}catch{return null}return typeof schema=="object"&&schema!==null&&!Array.isArray(schema)?schema:null}__name(readSchemaObject,"readSchemaObject");function validateSchemaFile(absolutePath,file,schemaUids=new Set){let schema;try{schema=JSON.parse(readFileSync6(absolutePath,"utf-8"))}catch(error){return[{rule:"schemas/parse",message:`Invalid JSON: ${error instanceof Error?error.message:String(error)}`,file}]}if(typeof schema!="object"||schema===null||Array.isArray(schema))return[{rule:"schemas/shape",message:"Schema must be a JSON object.",file}];let errors=[],{uid,name,fields,i18n,isSingleton,groups}=schema,expectedUid=basename2(file,".json");if(typeof uid!="string"||uid.length===0?errors.push({rule:"schemas/uid",message:`Missing "uid". Set it to the file name: "${expectedUid}".`,file}):uid!==expectedUid?errors.push({rule:"schemas/uid",message:`"uid" is "${uid}" but the file is ${expectedUid}.json \u2014 they must match.`,file}):CONTENT_TYPE_NAME_PATTERN.test(uid)||errors.push({rule:"schemas/type-name",message:`"uid" must use ${CONTENT_TYPE_NAME_PATTERN_DESCRIPTION}.`,file}),(typeof name!="string"||name.length===0)&&errors.push({rule:"schemas/name",message:'Missing "name" (the human-readable label).',file}),isSingleton!==void 0&&typeof isSingleton!="boolean"&&errors.push({rule:"schemas/singleton",message:'"isSingleton" must be a boolean when provided.',file}),i18n!==void 0)if(typeof i18n!="object"||i18n===null||Array.isArray(i18n))errors.push({rule:"schemas/i18n",message:'"i18n" must be an object like { "enabled": false }.',file});else{let keys=Object.keys(i18n);(keys.length!==1||keys[0]!=="enabled"||typeof i18n.enabled!="boolean")&&errors.push({rule:"schemas/i18n",message:'"i18n" only supports one boolean option: { "enabled": true | false }.',file})}let groupIds=new Set;if(groups!==void 0)if(!Array.isArray(groups))errors.push({rule:"schemas/groups",message:'"groups" must be an array.',file});else{let defaults=0;for(let[index2,group]of groups.entries()){let parsed=ContentFieldGroupSchema.safeParse(group);if(!parsed.success){errors.push({rule:"schemas/groups",message:`Group at index ${index2} is invalid: ${parsed.error.issues[0]?.message??"invalid group"}.`,file});continue}groupIds.has(parsed.data.id)&&errors.push({rule:"schemas/groups",message:`Group ID "${parsed.data.id}" is declared more than once.`,file}),groupIds.add(parsed.data.id),parsed.data.default&&(defaults+=1)}defaults>1&&errors.push({rule:"schemas/groups",message:"At most one group may be the default.",file})}if(typeof fields!="object"||fields===null||Array.isArray(fields))return errors.push({rule:"schemas/fields",message:'Missing "fields" object.',file}),errors;let parsedFields={};for(let[fieldName,definition]of Object.entries(fields)){if(!CONTENT_FIELD_NAME_PATTERN.test(fieldName)){errors.push({rule:"schemas/field-name",message:`Field "${fieldName}" must use ${CONTENT_FIELD_NAME_PATTERN_DESCRIPTION}.`,file});continue}if(RESERVED_FIELD_NAMES.has(fieldName)){errors.push({rule:"schemas/reserved-field",message:`Field "${fieldName}" is reserved entry metadata \u2014 the platform manages it automatically. Remove it from the schema${fieldName==="slug"?" (set slug inside entry data on create instead)":""}.`,file});continue}if(typeof definition!="object"||definition===null||Array.isArray(definition)){errors.push({rule:"schemas/field-shape",message:`Field "${fieldName}" must be an object like { "type": "string" }.`,file});continue}let{type}=definition,memberships=Array.isArray(definition.group)?definition.group:definition.group===void 0?[]:[definition.group];new Set(memberships).size!==memberships.length&&errors.push({rule:"schemas/field-group",message:`Field "${fieldName}" lists the same group more than once.`,file});for(let membership of memberships)(typeof membership!="string"||!groupIds.has(membership))&&errors.push({rule:"schemas/field-group",message:`Field "${fieldName}" references unknown group "${String(membership)}".`,file});if((typeof type!="string"||!FIELD_TYPES.has(type))&&errors.push({rule:"schemas/field-type",message:`Field "${fieldName}" has unsupported type "${String(type)}". Allowed: ${[...FIELD_TYPES].join(", ")}.`,file}),"localized"in definition&&errors.push({rule:"schemas/field-i18n",message:`Field "${fieldName}": "localized" is not supported. Use "i18n": false to share one value across locales.`,file}),"i18n"in definition&&(typeof definition.i18n!="boolean"?errors.push({rule:"schemas/field-i18n",message:`Field "${fieldName}": "i18n" must be true or false.`,file}):type==="blocks"&&errors.push({rule:"schemas/field-i18n",message:`Field "${fieldName}": "i18n" is not supported on blocks fields.`,file})),type==="blocks"){let parsed2=ContentFieldDefinitionSchema.safeParse(definition);if(!parsed2.success){for(let issue of parsed2.error.issues)errors.push({rule:"schemas/structured-content-shape",message:`Field "${fieldName}"${issue.path.length>0?` at ${issue.path.join(".")}`:""}: ${issue.message}`,file});continue}parsedFields[fieldName]=parsed2.data,walkContentFields({[fieldName]:parsed2.data},({field,path:path53,structuredDepth,insideStructuredContent})=>{if(insideStructuredContent&&field.group!==void 0&&errors.push({rule:"schemas/field-group",message:`Field "${path53.join(".")}" cannot use "group" inside blocks.`,file}),field.type==="blocks"&&structuredDepth>=MAX_STRUCTURED_CONTENT_DEPTH&&errors.push({rule:"schemas/structured-content-depth",message:`Field "${path53.join(".")}" exceeds the ${MAX_STRUCTURED_CONTENT_DEPTH}-level blocks limit.`,file}),!insideStructuredContent||field.type!=="reference"||schemaUids.size===0)return;let referenceTargets=Array.isArray(field.targets)?field.targets:field.target?[field.target]:[];for(let target of referenceTargets)schemaUids.has(target)||errors.push({rule:"schemas/reference-target",message:`Reference field "${path53.join(".")}" targets unknown schema "${target}".`,file})});continue}let parsed=ContentFieldDefinitionSchema.safeParse(definition);if(parsed.success&&(parsedFields[fieldName]=parsed.data),type==="reference"){let{target,targets,many}=definition;if(targets!==void 0&&target!==void 0)errors.push({rule:"schemas/reference-target",message:`Reference field "${fieldName}" declares both "target" and "targets" \u2014 a field is fixed or polymorphic, never both.`,file});else if(targets!==void 0){let targetList=Array.isArray(targets)?targets:null;if(!targetList||targetList.length===0||new Set(targetList).size!==targetList.length)errors.push({rule:"schemas/reference-target",message:`Reference field "${fieldName}" must declare "targets" as a non-empty array of unique content types.`,file});else for(let candidate of targetList)typeof candidate!="string"||!CONTENT_TYPE_NAME_PATTERN.test(candidate)?errors.push({rule:"schemas/reference-target",message:`Reference field "${fieldName}" has an invalid entry in "targets".`,file}):schemaUids.size>0&&!schemaUids.has(candidate)&&errors.push({rule:"schemas/reference-target",message:`Reference field "${fieldName}" targets unknown schema "${candidate}".`,file})}else typeof target!="string"||!CONTENT_TYPE_NAME_PATTERN.test(target)?errors.push({rule:"schemas/reference-target",message:`Reference field "${fieldName}" must declare a valid "target" content type (or "targets" for a polymorphic field).`,file}):schemaUids.size>0&&!schemaUids.has(target)&&errors.push({rule:"schemas/reference-target",message:`Reference field "${fieldName}" targets unknown schema "${target}".`,file});many!==void 0&&typeof many!="boolean"&&errors.push({rule:"schemas/reference-many",message:`Reference field "${fieldName}" must use a boolean "many" value.`,file})}else for(let referenceOnlyKey of["target","targets","many"])referenceOnlyKey in definition&&errors.push({rule:"schemas/reference-shape",message:`Field "${fieldName}": "${referenceOnlyKey}" is only supported on reference fields.`,file})}for(let problem of collectConditionProblems(parsedFields))errors.push({rule:"schemas/conditions",message:problem.message,file});return errors}__name(validateSchemaFile,"validateSchemaFile");init_api();init_config();init_auth_errors();init_project();import fs22 from"node:fs";import path25 from"node:path";var MIN_DB_LABELS_FIMO_VERSION="0.2.5",MIN_DB_LABELS_TEMPLATE_VERSION=4;function getDbLabelsRuntimeWarning(cwd){if(readExplicitConfigVersion(cwd)<MIN_DB_LABELS_TEMPLATE_VERSION)return null;let projectPackage=readPackageJson2(path25.join(cwd,"package.json")),fimoSpec=findFimoSpec(projectPackage);if(!fimoSpec)return buildWarning("package.json has no fimo dependency");let source=findRuntimeVersion(cwd,fimoSpec);return!source||!isVersionOlder(source.version,MIN_DB_LABELS_FIMO_VERSION)?null:buildWarning(`${source.label} is fimo@${source.version}`)}__name(getDbLabelsRuntimeWarning,"getDbLabelsRuntimeWarning");function readExplicitConfigVersion(cwd){try{let raw=JSON.parse(fs22.readFileSync(resolveProjectConfigPath(cwd),"utf8")),parsed=Number.parseInt(String(raw.version),10);return Number.isFinite(parsed)&&parsed>0?parsed:0}catch{return 0}}__name(readExplicitConfigVersion,"readExplicitConfigVersion");function buildWarning(subject){return[`CRITICAL: ${subject}, but DB-owned labels require fimo >= ${MIN_DB_LABELS_FIMO_VERSION}.`,"Labels changed with `fimo labels` or Fimo Studio may not render in this project until the app runtime is upgraded.","Run `npm install fimo@latest` (or your package manager equivalent), then run `fimo validate` again."].join(" ")}__name(buildWarning,"buildWarning");function findRuntimeVersion(cwd,fimoSpec){let installed=readPackageJson2(path25.join(cwd,"node_modules","fimo","package.json"));if(typeof installed?.version=="string")return{label:"installed node_modules/fimo",version:installed.version};let packageLockVersion=readPackageLockVersion(cwd);if(packageLockVersion)return{label:"package-lock.json",version:packageLockVersion};let exactSpecVersion=parseVersion2(fimoSpec);if(exactSpecVersion)return{label:"package.json dependency",version:exactSpecVersion.normalized};if(fimoSpec.startsWith("file:")){let fileVersion=readFileDependencyVersion(cwd,fimoSpec);if(fileVersion)return{label:"package.json file dependency target",version:fileVersion}}return null}__name(findRuntimeVersion,"findRuntimeVersion");function findFimoSpec(pkg){let dependenciesSpec=pkg?.dependencies?.fimo;if(typeof dependenciesSpec=="string"&&dependenciesSpec.trim())return dependenciesSpec.trim();let devDependenciesSpec=pkg?.devDependencies?.fimo;return typeof devDependenciesSpec=="string"&&devDependenciesSpec.trim()?devDependenciesSpec.trim():null}__name(findFimoSpec,"findFimoSpec");function readFileDependencyVersion(cwd,spec){let rawPath=spec.slice(5),packageRoot=path25.isAbsolute(rawPath)?rawPath:path25.resolve(cwd,rawPath),pkg=readPackageJson2(path25.join(packageRoot,"package.json"));return typeof pkg?.version=="string"?pkg.version:null}__name(readFileDependencyVersion,"readFileDependencyVersion");function readPackageLockVersion(cwd){try{let lock=JSON.parse(fs22.readFileSync(path25.join(cwd,"package-lock.json"),"utf8")),packagesVersion=lock.packages?.["node_modules/fimo"]?.version;if(typeof packagesVersion=="string")return packagesVersion;let dependencyVersion=lock.dependencies?.fimo?.version;return typeof dependencyVersion=="string"?dependencyVersion:null}catch{return null}}__name(readPackageLockVersion,"readPackageLockVersion");function readPackageJson2(filePath){try{return JSON.parse(fs22.readFileSync(filePath,"utf8"))}catch{return null}}__name(readPackageJson2,"readPackageJson");function isVersionOlder(version,minimum){let parsed=parseVersion2(version),parsedMinimum=parseVersion2(minimum);if(!parsed||!parsedMinimum)return!1;for(let key of["major","minor","patch"]){if(parsed[key]<parsedMinimum[key])return!0;if(parsed[key]>parsedMinimum[key])return!1}return!1}__name(isVersionOlder,"isVersionOlder");function parseVersion2(version){let match=version.trim().match(/^v?(\d+)\.(\d+)\.(\d+)(?:[-+].*)?$/);return match?{major:Number(match[1]),minor:Number(match[2]),patch:Number(match[3]),normalized:`${Number(match[1])}.${Number(match[2])}.${Number(match[3])}`}:null}__name(parseVersion2,"parseVersion");import{execFile as execFile3}from"node:child_process";import fs23 from"node:fs";import path26 from"node:path";import{promisify as promisify3}from"node:util";var execFileAsync3=promisify3(execFile3),LOCKFILE_MANAGERS2={"pnpm-lock.yaml":"pnpm","bun.lock":"bun","bun.lockb":"bun","yarn.lock":"yarn","package-lock.json":"npm"};async function inspectDependencyLifecycleScripts(cwd,execute=executeCommand){let selection=selectPackageManager(cwd);if(!selection)return{status:"warning",message:"Dependency lifecycle-script approval check skipped: no package manager could be selected from package.json or a lockfile."};if(!fs23.existsSync(path26.join(cwd,"node_modules")))return{status:"warning",message:`Dependency lifecycle-script approval check skipped: ${selection.name} has no installed node_modules state to inspect.`};if(selection.name==="yarn")return{status:"warning",message:"Dependency lifecycle-script approval check skipped: Yarn has no stable standalone blocked-script listing command."};if(selection.name==="npm")return{status:"warning",message:"Dependency lifecycle-script approval check skipped: this npm version has no supported read-only blocked-script listing command."};try{let versionResult=await execute(selection.name,["--version"],cwd);if(selection.version&&!matchesRequestedVersion(versionResult.stdout,selection.version))return{status:"warning",message:`Dependency lifecycle-script approval check skipped: the installed ${selection.name} version ${versionResult.stdout.trim()||"unknown"} does not match the exact packageManager pin ${selection.name}@${selection.version}.`};if(selection.name==="pnpm"&&!supportsPnpmIgnoredBuilds(versionResult.stdout))return{status:"warning",message:`Dependency lifecycle-script approval check skipped: pnpm ${versionResult.stdout.trim()||"unknown"} does not support pnpm ignored-builds inspection (requires pnpm 10.1+).`};let args=selection.name==="pnpm"?["ignored-builds"]:["pm","untrusted"],result=await execute(selection.name,args,cwd);return selection.name==="pnpm"?parsePnpmIgnoredBuilds(result.stdout,fs23.existsSync(path26.join(cwd,"node_modules",".modules.yaml"))):parseBunUntrusted(result.stdout)}catch(error){return{status:"warning",message:`Dependency lifecycle-script approval check skipped: could not inspect ${selection.name} install state (${error instanceof Error?error.message:String(error)}).`}}}__name(inspectDependencyLifecycleScripts,"inspectDependencyLifecycleScripts");function parsePnpmIgnoredBuilds(output,hasModulesManifest){let lines=output.split(/\r?\n/),section2=null,sawAutomaticSection=!1,sawExplicitSection=!1,sawNone=!1,packages=[];for(let line of lines){let trimmed=line.trim();if(/^Automatically ignored builds during installation:/i.test(trimmed)){section2="automatic",sawAutomaticSection=!0;continue}if(/^Explicitly ignored package builds/i.test(trimmed)){section2="explicit",sawExplicitSection=!0;continue}if(section2!=="automatic"||trimmed.length===0)continue;let entry=/^\s{2,}(\S.*)$/.exec(line)?.[1]?.trim();if(!entry){section2=null;continue}if(/^Cannot identify as no node_modules found$/i.test(entry))return{status:"warning",message:hasModulesManifest?"Dependency lifecycle-script approval check skipped: pnpm could not confirm whether dependency install scripts were blocked.":"Dependency lifecycle-script approval check skipped: pnpm recorded no install state under node_modules to inspect."};if(/^None$/i.test(entry))sawNone=!0;else{let packageEntry=entry.replace(/^[-*]\s+/,"");if(/\s/.test(packageEntry))return unrecognizedOutput("pnpm");packages.push(pnpmPackageName(packageEntry))}}return packages.length>0?{status:"blocked",packageManager:"pnpm",packages:[...new Set(packages)]}:sawAutomaticSection&&(sawExplicitSection||sawNone)?{status:"clean",packageManager:"pnpm"}:unrecognizedOutput("pnpm")}__name(parsePnpmIgnoredBuilds,"parsePnpmIgnoredBuilds");function parseBunUntrusted(output){let packages=output.split(/\r?\n/).map(line=>line.trim().replace(/^[-*]\s+/,"")).map(line=>/^\.\/node_modules\/(.+?)\s+@[^\s]+$/.exec(line)?.[1]??null).filter(packageName=>packageName!==null);return packages.length>0&&/These dependencies had their lifecycle scripts blocked during install\./i.test(output)?{status:"blocked",packageManager:"bun",packages:[...new Set(packages)]}:/Found 0 untrusted dependencies with scripts\./i.test(output)?{status:"clean",packageManager:"bun"}:unrecognizedOutput("bun")}__name(parseBunUntrusted,"parseBunUntrusted");function unrecognizedOutput(packageManager){return{status:"warning",message:`Dependency lifecycle-script approval check skipped: ${packageManager} returned unrecognized successful output.`}}__name(unrecognizedOutput,"unrecognizedOutput");function pnpmPackageName(entry){return(entry.startsWith("@")?/^(@[^/]+\/[^@]+)(?:@.+)?$/.exec(entry):/^([^@]+)(?:@.+)?$/.exec(entry))?.[1]??entry}__name(pnpmPackageName,"pnpmPackageName");function selectPackageManager(cwd){let packageJson=readPackageJson3(path26.join(cwd,"package.json")),exact=typeof packageJson?.packageManager=="string"?parseExactPackageManager(packageJson.packageManager):null;if(exact)return exact;let lockfiles=Object.entries(LOCKFILE_MANAGERS2).filter(([lockfile])=>fs23.existsSync(path26.join(cwd,lockfile)));return lockfiles.length!==1?null:{name:lockfiles[0][1]}}__name(selectPackageManager,"selectPackageManager");function parseExactPackageManager(value){let match=/^(pnpm|bun|npm|yarn)@(\d+\.\d+\.\d+(?:[-+][0-9A-Za-z.-]+)?)$/.exec(value.trim());return match?{name:match[1],version:match[2]}:null}__name(parseExactPackageManager,"parseExactPackageManager");function supportsPnpmIgnoredBuilds(version){let match=/^(\d+)\.(\d+)\.(\d+)/.exec(version.trim());if(!match)return!1;let major=Number(match[1]),minor=Number(match[2]);return major>10||major===10&&minor>=1}__name(supportsPnpmIgnoredBuilds,"supportsPnpmIgnoredBuilds");function matchesRequestedVersion(actual,requested){return actual.trim()===requested.split("+",1)[0]}__name(matchesRequestedVersion,"matchesRequestedVersion");async function executeCommand(command,args,cwd){let result=await execFileAsync3(command,args,{cwd,encoding:"utf8"});return{stdout:String(result.stdout),stderr:String(result.stderr)}}__name(executeCommand,"executeCommand");function readPackageJson3(filePath){try{return JSON.parse(fs23.readFileSync(filePath,"utf8"))}catch{return null}}__name(readPackageJson3,"readPackageJson");init_project_context();init_project();init_dist();import{existsSync as existsSync8,readFileSync as readFileSync8,readdirSync as readdirSync6,statSync as statSync3}from"node:fs";import{join as join9,resolve as resolve7}from"node:path";import fs24 from"node:fs/promises";import path27 from"node:path";async function pathExists(targetPath){try{return await fs24.access(targetPath),!0}catch{return!1}}__name(pathExists,"pathExists");async function removeDataFiles(dir){let entries2=await fs24.readdir(dir,{withFileTypes:!0});for(let entry of entries2){let fullPath=path27.join(dir,entry.name);entry.isDirectory()?await removeDataFiles(fullPath):entry.name.endsWith(".data")&&await fs24.rm(fullPath)}}__name(removeDataFiles,"removeDataFiles");async function exportStatic(options={}){let cwd=options.cwd??process.cwd(),clientDir=path27.resolve(cwd,"dist/client"),serverDir=path27.resolve(cwd,"dist/server");if(!await pathExists(clientDir))throw new Error("Missing dist/client output. Run `react-router build` first.");await pathExists(serverDir)&&await fs24.rm(serverDir,{recursive:!0,force:!0}),await removeDataFiles(clientDir),console.log("Static export complete.")}__name(exportStatic,"exportStatic");import{existsSync as existsSync7,readFileSync as readFileSync7,readdirSync as readdirSync5,statSync as statSync2}from"fs";import{createRequire as createRequire2}from"node:module";import{join as join8}from"path";var require3=createRequire2(import.meta.url),parseSync2;function parse7(filePath,code2){return parseSync2??=require3("oxc-parser").parseSync,parseSync2(filePath,code2)}__name(parse7,"parse");var SRC_DIR="src";function getLineAndColumn(code2,offset){let lines=code2.slice(0,offset).split(`
1227
1227
  `);return{line:lines.length,column:lines[lines.length-1].length+1}}__name(getLineAndColumn,"getLineAndColumn");function isStringLiteral(node){return node?node.type==="Literal"&&typeof node.value=="string"||node.type==="TemplateLiteral"&&(!node.expressions||node.expressions.length===0):!1}__name(isStringLiteral,"isStringLiteral");var DYNAMIC_KEY_MESSAGE=`Dynamic translation key detected. The first argument to t() must be a string literal, not a variable or expression.
1228
1228
 
1229
1229
  This breaks source tracking for inline editing. Translation keys must be statically analyzable.
@@ -271,23 +271,18 @@ export function validateSchemaFile(absolutePath, file, schemaUids = new Set()) {
271
271
  file,
272
272
  });
273
273
  }
274
- if (!insideStructuredContent || field.type !== 'reference') {
274
+ if (!insideStructuredContent || field.type !== 'reference' || schemaUids.size === 0) {
275
275
  return;
276
276
  }
277
- if (field.targets) {
278
- errors.push({
279
- rule: 'schemas/structured-content-reference',
280
- message: `Reference field "${path.join('.')}" must use one fixed "target" inside blocks.`,
281
- file,
282
- });
283
- return;
284
- }
285
- if (field.target && schemaUids.size > 0 && !schemaUids.has(field.target)) {
286
- errors.push({
287
- rule: 'schemas/reference-target',
288
- message: `Reference field "${path.join('.')}" targets unknown schema "${field.target}".`,
289
- file,
290
- });
277
+ const referenceTargets = Array.isArray(field.targets) ? field.targets : field.target ? [field.target] : [];
278
+ for (const target of referenceTargets) {
279
+ if (!schemaUids.has(target)) {
280
+ errors.push({
281
+ rule: 'schemas/reference-target',
282
+ message: `Reference field "${path.join('.')}" targets unknown schema "${target}".`,
283
+ file,
284
+ });
285
+ }
291
286
  }
292
287
  });
293
288
  continue;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "fimo",
3
- "version": "2.2.0-staging.11",
3
+ "version": "2.2.0-staging.12",
4
4
  "description": "Fimo CLI - create, deploy, and manage Fimo projects",
5
5
  "keywords": [
6
6
  "astro-component"
@@ -189,12 +189,12 @@
189
189
  "devDependencies": {
190
190
  "@clack/core": "^1.2.0",
191
191
  "@clack/prompts": "^1.2.0",
192
- "@fimo/agents-bundle": "2.2.0-staging.11",
193
- "@fimo/api-contracts": "2.2.0-staging.11",
194
- "@fimo/cli-kit": "2.2.0-staging.11",
195
- "@fimo/framework-detect": "2.2.0-staging.11",
196
- "@fimo/routes": "2.2.0-staging.11",
197
- "@fimo/tsconfig": "2.2.0-staging.11",
192
+ "@fimo/agents-bundle": "2.2.0-staging.12",
193
+ "@fimo/api-contracts": "2.2.0-staging.12",
194
+ "@fimo/cli-kit": "2.2.0-staging.12",
195
+ "@fimo/framework-detect": "2.2.0-staging.12",
196
+ "@fimo/routes": "2.2.0-staging.12",
197
+ "@fimo/tsconfig": "2.2.0-staging.12",
198
198
  "@types/node": "22.18.6",
199
199
  "agent-install": "^0.0.5",
200
200
  "astro": "^7.1.6",
@@ -9,8 +9,8 @@
9
9
  "check": "astro sync && astro check"
10
10
  },
11
11
  "dependencies": {
12
- "fimo": "2.2.0-staging.11",
13
- "fimo-astro": "2.2.0-staging.11"
12
+ "fimo": "2.2.0-staging.12",
13
+ "fimo-astro": "2.2.0-staging.12"
14
14
  },
15
15
  "devDependencies": {
16
16
  "@astrojs/check": "^0.9.10",
@@ -12,9 +12,9 @@
12
12
  },
13
13
  "dependencies": {
14
14
  "@tanstack/react-query": "^5.80.6",
15
- "fimo": "2.2.0-staging.11",
16
- "fimo-next": "2.2.0-staging.11",
17
- "fimo-react": "2.2.0-staging.11",
15
+ "fimo": "2.2.0-staging.12",
16
+ "fimo-next": "2.2.0-staging.12",
17
+ "fimo-react": "2.2.0-staging.12",
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
  "cmdk": "^1.1.1",
22
22
  "date-fns": "^4.1.0",
23
23
  "embla-carousel-react": "^8.6.0",
24
- "fimo": "2.2.0-staging.11",
25
- "fimo-react": "2.2.0-staging.11",
26
- "fimo-react-router": "2.2.0-staging.11",
27
- "fimo-vite": "2.2.0-staging.11",
24
+ "fimo": "2.2.0-staging.12",
25
+ "fimo-react": "2.2.0-staging.12",
26
+ "fimo-react-router": "2.2.0-staging.12",
27
+ "fimo-vite": "2.2.0-staging.12",
28
28
  "input-otp": "^1.4.2",
29
29
  "isbot": "^5",
30
30
  "lucide-react": "^0.577.0",
@@ -11,8 +11,8 @@
11
11
  "prepare": "svelte-kit sync"
12
12
  },
13
13
  "dependencies": {
14
- "fimo": "2.2.0-staging.11",
15
- "fimo-sveltekit": "2.2.0-staging.11"
14
+ "fimo": "2.2.0-staging.12",
15
+ "fimo-sveltekit": "2.2.0-staging.12"
16
16
  },
17
17
  "devDependencies": {
18
18
  "@sveltejs/kit": "^2.70.2",
@@ -11,10 +11,10 @@
11
11
  },
12
12
  "dependencies": {
13
13
  "@tanstack/react-query": "^5.80.6",
14
- "fimo": "2.2.0-staging.11",
15
- "fimo-react": "2.2.0-staging.11",
16
- "fimo-react-router": "2.2.0-staging.11",
17
- "fimo-vite": "2.2.0-staging.11",
14
+ "fimo": "2.2.0-staging.12",
15
+ "fimo-react": "2.2.0-staging.12",
16
+ "fimo-react-router": "2.2.0-staging.12",
17
+ "fimo-vite": "2.2.0-staging.12",
18
18
  "react": "^19.2.7",
19
19
  "react-dom": "^19.2.7",
20
20
  "react-router": "^8.3.0"