fimo 0.9.0-experimental.4 → 0.9.0-experimental.5

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.
@@ -318,6 +318,35 @@ four gates pass:
318
318
  SPA); the gate enforces the rest. Enforced only once items are wrapped;
319
319
  Next pages-router-only projects get a warning, not a failure.
320
320
 
321
+ **When a CMS was scaffolded, the four gates do NOT prove the reads resolve from
322
+ Fimo.** Parity drift from real→example content is expected and gets ignored, so
323
+ a wiring that silently falls through to the live CMS passes every gate while the
324
+ page renders the CMS's own content (or 404s when the CMS is down). This is the
325
+ CMS analog of the mandatory browser check — prove Fimo _ownership_ by hand:
326
+ load the homepage plus one page per scaffolded type and confirm the rendered
327
+ value is a scaffolded EXAMPLE marker (`Title example 1`, `Meta Title example 1`,
328
+ a `<type>-example-1` slug), NOT real CMS content and NOT a 404. Then confirm in
329
+ Studio Edit mode that a scaffolded block is selectable, not just rendered (a
330
+ block item with no stable id renders but cannot be edited — editability is the
331
+ whole point). **A single type (`Global`, `Header`, `Footer`) that reads empty
332
+ while collections render is the singular-vs-plural keying bug**: the generated
333
+ `fimo-strapi.ts` must key single types by their singular path (`'global'`, not
334
+ `'globals'`), and in dev the adapter logs `/api/global looks like a scaffolded
335
+ type but is not in the type map` on that fallthrough. Fix it in the CLI
336
+ (`scripts/cms/strapi/schema-map.ts`) and regenerate with
337
+ `fimo migrate --scaffold-cms` — never hand-edit the generated module.
338
+
339
+ **Populate real routes** (scaffolded CMS only): example entries carry generated
340
+ slugs, so only unfiltered reads (the homepage) resolve — every real route the
341
+ site links to misses its slug filter and 404s (the adapter answers a filtered
342
+ miss on a Fimo-owned type with an empty envelope; it never leaks the read to
343
+ the old CMS). Harvest the site's real slugs (live production `sitemap.xml`,
344
+ else nav/footer links in the code, else ask the user) and create one entry of
345
+ the routed type per linked route — matching slug + path field, distinct title,
346
+ varied block subset. Then click through the nav: every linked page renders,
347
+ none 404. This is navigability, not the content migration (the real pull is a
348
+ later, separate step).
349
+
321
350
  **Config-driven sites** (all copy in a data file, templates render only
322
351
  expressions): the scanner lists copy-bearing `.json`/`.yml` files and
323
352
  markdown groups in the plan (`dataFiles`) and the coverage gate fails until
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "bundled": true,
3
3
  "bundler": "esbuild",
4
- "bundledAt": "2026-07-24T15:20:57.674Z",
4
+ "bundledAt": "2026-07-24T17:59:36.047Z",
5
5
  "cliVersion": "0.8.0",
6
6
  "external": [
7
7
  "oxc-parser",
package/dist/cli/index.js CHANGED
@@ -644,7 +644,7 @@ ${code2}`}function bindsForeignT(code2){return/\buseI18n\s*\(/.test(code2)||/(?:
644
644
  </FimoSpaProviders>`+code2.slice(renderArg.end),importBlock=`import { FimoSpaProviders } from 'fimo/react';
645
645
  import fimoConfig from '${configImportPath.startsWith(".")?configImportPath:`./${configImportPath}`}';`,lastImportEnd=-1;for(let node of program2.body??[])node.type==="ImportDeclaration"&&(lastImportEnd=node.end);return next2=lastImportEnd>=0?`${next2.slice(0,lastImportEnd)}
646
646
  ${importBlock}${next2.slice(lastImportEnd)}`:`${importBlock}
647
- ${next2}`,fs38.writeFileSync(entryFile,next2),{changes:[`wired FimoSpaProviders into ${rel}`],warnings:[],wired:!0}}var init_wiring2=__esm({"src/scripts/frameworks/vite/wiring.ts"(){__name(ensureSpaEntryWiring,"ensureSpaEntryWiring")}});import{join as join22,relative as relative5}from"node:path";function discoverFrameworkRoutes(cwd,detection){if(detection.framework==="vite"&&detection.react)return discoverReactRouterDomRoutes(cwd);switch(detection.framework){case"next":return[...detection.appDir?discoverNextRoutes(join22(cwd,detection.appDir)):[],...detection.pagesDir?discoverNextPagesRoutes(join22(cwd,detection.pagesDir)):[]];case"remix":return detection.pagesDir?discoverRemixRoutes(join22(cwd,detection.pagesDir)):[];case"astro":return detection.pagesDir?discoverAstroRoutes(join22(cwd,detection.pagesDir)):[];case"nuxt":return detection.pagesDir?discoverNuxtRoutes(join22(cwd,detection.pagesDir)):[];case"sveltekit":return detection.pagesDir?discoverSvelteKitRoutes(join22(cwd,detection.pagesDir)):[];default:return[]}}function frameworkDataDirs(framework){switch(framework){case"nuxt":return["content"];default:return[]}}function frameworkJsxWrapperModule(detection){switch(detection.framework){case"next":return"fimo/next";case"remix":return"fimo/remix";case"vite":return detection.react?"fimo/react":null;default:return null}}function frameworkTemplateWrapStyle(framework){switch(framework){case"astro":return astroTemplateWrapStyle;case"nuxt":return nuxtTemplateWrapStyle;case"sveltekit":return svelteTemplateWrapStyle;default:return null}}function frameworkRootWiring(detection){switch(detection.framework){case"next":return cwd=>ensureNextLayoutWiring(cwd,detection);case"vite":return detection.react?ensureSpaEntryWiring:null;default:return null}}function frameworkCollectionRewire(detection){switch(detection.framework){case"next":return(cwd,scan,drafts)=>rewireNextCollectionPages(cwd,detection,scan,drafts);default:return null}}function frameworkWiringMarker(detection,cwd){switch(detection.framework){case"next":{if(!detection.appDir)return{files:["pages/_app.tsx","pages/_app.jsx","src/pages/_app.tsx","src/pages/_app.jsx"],marker:/FimoNextProviders/,hint:"mount FimoNextProviders in pages/_app (pages router has no automated recipe yet)",warnOnly:!0};let resolved=cwd?nextRootLayoutFiles(cwd,detection.appDir).map(file=>relative5(cwd,file)):[];return{files:resolved.length>0?resolved:[`${detection.appDir}/layout.tsx`,`${detection.appDir}/layout.jsx`,`${detection.appDir}/layout.js`],marker:/FimoNextProviders/,hint:`mount FimoNextProviders + FimoNextScripts in ${detection.appDir}/layout (agent prompt section 0)`}}case"vite":return detection.react?{files:["src/main.tsx","src/main.jsx","src/index.tsx","src/index.jsx"],marker:/FimoSpaProviders/,hint:"wrap the root render with FimoSpaProviders from fimo/react (agent prompt section 0)"}:null;case"remix":return{files:["app/root.tsx","app/root.jsx"],marker:/FimoRemixProviders/,hint:"mount FimoRemixProviders in app/root (agent prompt section 0)"};case"sveltekit":return{files:["src/routes/+layout.server.js","src/routes/+layout.server.ts"],marker:/fimoLabels/,hint:"return fimoLabels from the root +layout.server load (wrap.md SvelteKit notes)"};default:return null}}var init_discover=__esm({"src/scripts/frameworks/discover.ts"(){init_routes();init_template_style();init_rewire();init_routes2();init_wiring();init_routes3();init_template_style2();init_routes4();init_routes5();init_template_style3();init_routes6();init_wiring2();__name(discoverFrameworkRoutes,"discoverFrameworkRoutes");__name(frameworkDataDirs,"frameworkDataDirs");__name(frameworkJsxWrapperModule,"frameworkJsxWrapperModule");__name(frameworkTemplateWrapStyle,"frameworkTemplateWrapStyle");__name(frameworkRootWiring,"frameworkRootWiring");__name(frameworkCollectionRewire,"frameworkCollectionRewire");__name(frameworkWiringMarker,"frameworkWiringMarker")}});import fs39 from"node:fs";import path41 from"node:path";function ensureFimoDependencyPin(cwd){let changes=[],warnings=[],pkgPath=path41.join(cwd,"package.json"),pkg=null;try{pkg=JSON.parse(fs39.readFileSync(pkgPath,"utf8"))}catch{pkg=null}if(!pkg)return warnings.push("No package.json found \u2014 add the fimo dependency manually."),{changes,warnings};let deps=pkg.dependencies??{},touched=!1;return deps.fimo||(deps.fimo=resolveRemoteFimoPin()??"latest",changes.push(`added fimo@${deps.fimo} to dependencies`),touched=!0),deps["@tanstack/react-query"]||(deps["@tanstack/react-query"]="^5.90.0",changes.push("added @tanstack/react-query (fimo peer)"),touched=!0),touched&&(pkg.dependencies=deps,fs39.writeFileSync(pkgPath,`${JSON.stringify(pkg,null,2)}
647
+ ${next2}`,fs38.writeFileSync(entryFile,next2),{changes:[`wired FimoSpaProviders into ${rel}`],warnings:[],wired:!0}}var init_wiring2=__esm({"src/scripts/frameworks/vite/wiring.ts"(){__name(ensureSpaEntryWiring,"ensureSpaEntryWiring")}});import{join as join22,relative as relative5}from"node:path";function discoverFrameworkRoutes(cwd,detection){if(detection.framework==="vite"&&detection.react)return discoverReactRouterDomRoutes(cwd);switch(detection.framework){case"next":return[...detection.appDir?discoverNextRoutes(join22(cwd,detection.appDir)):[],...detection.pagesDir?discoverNextPagesRoutes(join22(cwd,detection.pagesDir)):[]];case"remix":return detection.pagesDir?discoverRemixRoutes(join22(cwd,detection.pagesDir)):[];case"astro":return detection.pagesDir?discoverAstroRoutes(join22(cwd,detection.pagesDir)):[];case"nuxt":return detection.pagesDir?discoverNuxtRoutes(join22(cwd,detection.pagesDir)):[];case"sveltekit":return detection.pagesDir?discoverSvelteKitRoutes(join22(cwd,detection.pagesDir)):[];default:return[]}}function frameworkDataDirs(framework){switch(framework){case"nuxt":return["content"];default:return[]}}function frameworkJsxWrapperModule(detection){switch(detection.framework){case"next":return"fimo/next";case"remix":return"fimo/remix";case"vite":return detection.react?"fimo/react":null;default:return null}}function frameworkTemplateWrapStyle(framework){switch(framework){case"astro":return astroTemplateWrapStyle;case"nuxt":return nuxtTemplateWrapStyle;case"sveltekit":return svelteTemplateWrapStyle;default:return null}}function frameworkRootWiring(detection){switch(detection.framework){case"next":return cwd=>ensureNextLayoutWiring(cwd,detection);case"vite":return detection.react?ensureSpaEntryWiring:null;default:return null}}function frameworkCollectionRewire(detection){switch(detection.framework){case"next":return(cwd,scan,drafts)=>rewireNextCollectionPages(cwd,detection,scan,drafts);default:return null}}function frameworkWiringMarker(detection,cwd){switch(detection.framework){case"next":{if(!detection.appDir)return{files:["pages/_app.tsx","pages/_app.jsx","src/pages/_app.tsx","src/pages/_app.jsx"],marker:/FimoNextProviders/,hint:"mount FimoNextProviders in pages/_app (pages router has no automated recipe yet)",warnOnly:!0};let resolved=cwd?nextRootLayoutFiles(cwd,detection.appDir).map(file=>relative5(cwd,file)):[];return{files:resolved.length>0?resolved:[`${detection.appDir}/layout.tsx`,`${detection.appDir}/layout.jsx`,`${detection.appDir}/layout.js`],marker:/FimoNextProviders/,hint:`mount FimoNextProviders + FimoNextScripts in ${detection.appDir}/layout (agent prompt section 0)`}}case"vite":return detection.react?{files:["src/main.tsx","src/main.jsx","src/index.tsx","src/index.jsx"],marker:/FimoSpaProviders/,hint:"wrap the root render with FimoSpaProviders from fimo/react (agent prompt section 0)"}:null;case"remix":return{files:["app/root.tsx","app/root.jsx"],marker:/FimoRemixProviders/,hint:"mount FimoRemixProviders in app/root (agent prompt section 0)"};case"sveltekit":return{files:["src/routes/+layout.server.js","src/routes/+layout.server.ts"],marker:/fimoLabels/,hint:"return fimoLabels from the root +layout.server load (wrap.md SvelteKit notes)"};default:return null}}var init_discover=__esm({"src/scripts/frameworks/discover.ts"(){init_routes();init_template_style();init_rewire();init_routes2();init_wiring();init_routes3();init_template_style2();init_routes4();init_routes5();init_template_style3();init_routes6();init_wiring2();__name(discoverFrameworkRoutes,"discoverFrameworkRoutes");__name(frameworkDataDirs,"frameworkDataDirs");__name(frameworkJsxWrapperModule,"frameworkJsxWrapperModule");__name(frameworkTemplateWrapStyle,"frameworkTemplateWrapStyle");__name(frameworkRootWiring,"frameworkRootWiring");__name(frameworkCollectionRewire,"frameworkCollectionRewire");__name(frameworkWiringMarker,"frameworkWiringMarker")}});import fs39 from"node:fs";import path41 from"node:path";function ensureFimoDependencyPin(cwd){let changes=[],warnings=[],pkgPath=path41.join(cwd,"package.json"),pkg=null;try{pkg=JSON.parse(fs39.readFileSync(pkgPath,"utf8"))}catch{pkg=null}if(!pkg)return warnings.push("No package.json found \u2014 add the fimo dependency manually."),{changes,warnings};let deps=pkg.dependencies??{},touched=!1,resolved=resolveRemoteFimoPin();if(!deps.fimo)deps.fimo=resolved??"latest",changes.push(`added fimo@${deps.fimo} to dependencies`),touched=!0;else if(resolved&&deps.fimo!==resolved&&!/^(file|link|workspace):/.test(deps.fimo)){let previous=deps.fimo;deps.fimo=resolved,changes.push(`updated fimo ${previous} \u2192 ${resolved} (runtime must match the CLI that generated the wiring)`),touched=!0}return deps["@tanstack/react-query"]||(deps["@tanstack/react-query"]="^5.90.0",changes.push("added @tanstack/react-query (fimo peer)"),touched=!0),touched&&(pkg.dependencies=deps,fs39.writeFileSync(pkgPath,`${JSON.stringify(pkg,null,2)}
648
648
  `),changes.push("run your package manager install to apply")),{changes,warnings}}var init_fimo_dep=__esm({"src/cli/utils/fimo-dep.ts"(){init_templates();__name(ensureFimoDependencyPin,"ensureFimoDependencyPin")}});function renderStarterAgentPrompt(){return`# Migrate this site to Fimo
649
649
 
650
650
  This is an adopted site connected to Fimo. You (the coding agent) own the
@@ -783,6 +783,20 @@ blank or throws, fix it at that component (a defensive default, or the field it
783
783
  truly needs) and re-check. Record anything you could not resolve in \xA78 caveats. Do
784
784
  NOT re-scaffold, re-wire, or hand-wrap content \u2014 the machine owns those; you own
785
785
  per-page render fidelity.
786
+
787
+ POPULATE REAL ROUTES \u2014 the example entries carry GENERATED slugs
788
+ (\`<type>-example-N\`), so only the homepage resolves; every real route the site
789
+ links to (its nav, \`/pricing\`-style pages) misses and 404s, which reads as "the
790
+ migration lost my pages". Fix it with judgment the machine does not have: find
791
+ the site's REAL route slugs (the live production site's \`sitemap.xml\` if it is
792
+ reachable, else the nav/footer links in the code, else ask the user for the top
793
+ ~10 routes), and for each one create an entry of the routed type with that slug
794
+ (and its path field \u2014 e.g. \`fullPath\` \u2014 when the type has one) via
795
+ \`npx fimo entries create <Type> --body '<json>'\`, reusing an example entry's
796
+ data shape with a distinct title and a varied block subset. Cap it at the
797
+ routes the site actually links to \u2014 this is navigability, not a content
798
+ migration (the real content pull is the later step). Then click through the
799
+ nav in \`pnpm dev\`: every linked page must render, none may 404.
786
800
  `:`
787
801
  The machine could NOT auto-wire the ${machine.cmsScaffold.cms} client. Wire the generated
788
802
  Fimo adapter (the module the machine wrote alongside your ${machine.cmsScaffold.cms} client) into
@@ -1853,7 +1867,7 @@ Next step: open ${path55.join(projectRoot,result.promptPath)} and paste it to yo
1853
1867
  `));return JSON.parse(fs50.readFileSync(filePath,"utf-8"))});syncSchemas({cwd,verbose:!1});let results=[];for(let schema of sortSchemasByReferenceDependencies(schemas3)){let contentType=toApiShape(schema);try{let result=await api.createSchema(ctx.projectId,contentType);results.push(result)}catch(error){if(isAlreadyExistsError(error)){let result=await api.updateSchema(ctx.projectId,contentType.name,contentType);results.push(result)}else{if(opts.tolerateUnknownTargets&&isUnknownTargetError(error))continue;throw error}}}return results}function applyDraftAdoption(drafts,renames,drops){let finalName=__name(uid=>renames[uid]??uid,"finalName"),kept=[],dropped=[],renamed=[];for(let draft of drafts){if(drops.has(draft.uid)){dropped.push(draft.uid);continue}let uid=finalName(draft.uid);uid!==draft.uid&&renamed.push({from:draft.uid,to:uid});let fields={};for(let[name,def]of Object.entries(draft.fields)){if(def&&typeof def=="object"&&def.type==="reference"){let ref={...def};if(typeof ref.target=="string"){if(drops.has(ref.target)){fields[name]={type:"string"};continue}ref.target=finalName(ref.target)}if(Array.isArray(ref.targets)){let targets=ref.targets.filter(target=>!drops.has(target)).map(finalName);if(targets.length===0){fields[name]={type:"string"};continue}ref.targets=targets}fields[name]=ref;continue}fields[name]=def}kept.push({uid,from:draft.uid,fields})}return{kept,dropped,renamed}}async function schemaAdoptDraftsCommand(options,cmd){let{ctx,api}=await bootstrapProjectCommand(options,cmd,{refuseReadOnly:!0});await requireOperation("cms.schema.push",{projectId:ctx.projectId,env:ctx.env});let cwd=resolveProjectCwd(options.cwd),draftsDir=path56.join(cwd,".fimo","migration","schema-drafts");if(!fs50.existsSync(draftsDir)){ui.error({command:"schemas adopt-drafts",code:"NO_SCHEMA_DRAFTS",message:"No .fimo/migration/schema-drafts directory \u2014 run `npx fimo migrate` first."}),process.exitCode=1;return}let drafts=fs50.readdirSync(draftsDir).filter(file=>file.endsWith(".json")).map(file=>JSON.parse(fs50.readFileSync(path56.join(draftsDir,file),"utf-8"))),renames={};for(let pair of(options.rename??"").split(",").map(entry=>entry.trim()).filter(Boolean)){let[from,to]=pair.split("=").map(part=>part.trim());from&&to&&(renames[from]=to)}let drops=new Set((options.drop??"").split(",").map(entry=>entry.trim()).filter(Boolean)),adoption=applyDraftAdoption(drafts.map(draft=>({uid:draft.uid,fields:draft.fields??{}})),renames,drops),schemasDir=path56.join(cwd,"src","schemas");fs50.mkdirSync(schemasDir,{recursive:!0});for(let schema of adoption.kept){let target=path56.join(schemasDir,`${schema.uid}.json`);if(fs50.existsSync(target)&&!options.force){ui.error({command:"schemas adopt-drafts",code:"SCHEMA_FILE_EXISTS",message:`src/schemas/${schema.uid}.json already exists \u2014 rerun with --force to overwrite.`}),process.exitCode=1;return}}for(let schema of adoption.kept)fs50.writeFileSync(path56.join(schemasDir,`${schema.uid}.json`),`${JSON.stringify({uid:schema.uid,name:schema.uid,fields:schema.fields},null,2)}
1854
1868
  `);let rowsDir=path56.join(cwd,".fimo","migration","rows");if(fs50.existsSync(rowsDir)){let finalName=__name(uid=>renames[uid]??uid,"finalName");for(let file of fs50.readdirSync(rowsDir).filter(entry=>entry.endsWith(".rows.json"))){let filePath=path56.join(rowsDir,file),rowsFile=JSON.parse(fs50.readFileSync(filePath,"utf-8"));if(drops.has(rowsFile.draftUid)){fs50.rmSync(filePath);continue}let newUid=finalName(rowsFile.draftUid);rowsFile.draftUid=newUid;for(let[field,reference]of Object.entries(rowsFile.references??{})){if(typeof reference.target=="string"){if(drops.has(reference.target)){delete rowsFile.references?.[field];continue}reference.target=finalName(reference.target)}if(Array.isArray(reference.targets)){let targets=reference.targets.filter(target=>!drops.has(target)).map(finalName);if(targets.length===0){delete rowsFile.references?.[field];continue}reference.targets=targets}}fs50.rmSync(filePath),fs50.writeFileSync(path56.join(rowsDir,`${newUid}.rows.json`),`${JSON.stringify(rowsFile,null,1)}
1855
1869
  `)}}if(Object.keys(renames).length>0){let sourceDirs=["app","src","pages"].map(dir=>path56.join(cwd,dir)).filter(dir=>fs50.existsSync(dir)),renameCallSites=__name(dir=>{for(let entry of fs50.readdirSync(dir,{withFileTypes:!0})){if(entry.name==="node_modules"||entry.name.startsWith("."))continue;let full=path56.join(dir,entry.name);if(entry.isDirectory()){renameCallSites(full);continue}if(!/\.(tsx|jsx|ts|js|mjs)$/.test(entry.name))continue;let source=fs50.readFileSync(full,"utf-8");if(!source.includes("wrapCollection("))continue;let next2=source;for(let[from,to]of Object.entries(renames))next2=next2.split(`wrapCollection('${from}'`).join(`wrapCollection('${to}'`),next2=next2.split(`wrapCollection("${from}"`).join(`wrapCollection("${to}"`);next2!==source&&fs50.writeFileSync(full,next2)}},"renameCallSites");for(let dir of sourceDirs)renameCallSites(dir)}let hasReferences2=adoption.kept.some(schema=>Object.values(schema.fields).some(def=>def&&typeof def=="object"&&def.type==="reference"));await pushAllSchemas(ctx,api,cwd,void 0,{tolerateUnknownTargets:hasReferences2}),hasReferences2&&await pushAllSchemas(ctx,api,cwd);let summary2={adopted:adoption.kept.map(schema=>schema.uid),renamed:adoption.renamed,dropped:adoption.dropped,pushed:adoption.kept.length};ui.result({command:"schemas adopt-drafts",data:summary2,render:__name(()=>[`Adopted ${adoption.kept.length} schema(s) into src/schemas and pushed.`,...adoption.renamed.length>0?[`Renamed: ${adoption.renamed.map(entry=>`${entry.from}\u2192${entry.to}`).join(", ")}`]:[],...adoption.dropped.length>0?[`Dropped: ${adoption.dropped.join(", ")}`]:[]].join(`
1856
- `),"render")})}async function schemaDeleteCommand(typeName,options,cmd){let{ctx,api}=await bootstrapProjectCommand(options,cmd,{refuseReadOnly:!0});await requireOperation("cms.schema.delete",{projectId:ctx.projectId,env:ctx.env});let result=await api.deleteSchema(ctx.projectId,typeName);ui.result({command:"schemas delete",data:result,render:__name(()=>renderPrettyJson8(result),"render")})}var SchemaCommandError,init_schema=__esm({"src/cli/commands/schema.ts"(){init_dist();init_sync_schemas();init_validate_schemas();init_api();init_capability_guard();init_project_context();init_project();SchemaCommandError=class extends Error{static{__name(this,"SchemaCommandError")}code;constructor(code2,message){super(message),this.name="SchemaCommandError",this.code=code2}};__name(renameOperation,"renameOperation");__name(toApiShape,"toApiShape");__name(renderPrettyJson8,"renderPrettyJson");__name(schemaListCommand,"schemaListCommand");__name(schemaGetCommand,"schemaGetCommand");__name(schemaPushCommand,"schemaPushCommand");__name(isAlreadyExistsError,"isAlreadyExistsError");__name(sortSchemasByReferenceDependencies,"sortSchemasByReferenceDependencies");__name(isUnknownTargetError,"isUnknownTargetError");__name(pushAllSchemas,"pushAllSchemas");__name(applyDraftAdoption,"applyDraftAdoption");__name(schemaAdoptDraftsCommand,"schemaAdoptDraftsCommand");__name(schemaDeleteCommand,"schemaDeleteCommand")}});function kebabCase(name){return name.replace(/([a-z0-9])([A-Z])/g,"$1-$2").replace(/[^a-zA-Z0-9]+/g,"-").toLowerCase().replace(/^-+|-+$/g,"")}function humanize(field){return field.replace(/([a-z0-9])([A-Z])/g,"$1 $2").replace(/[_-]+/g," ").replace(/^\w/,c2=>c2.toUpperCase())}function richTextDoc(text3){return{type:"doc",content:[{type:"paragraph",content:[{type:"text",text:text3}]}]}}function placeholderMedia(label){let svg=`<svg xmlns="http://www.w3.org/2000/svg" width="1200" height="630"><rect width="100%" height="100%" fill="#e5e7eb"/><text x="50%" y="50%" font-family="sans-serif" font-size="44" fill="#9ca3af" text-anchor="middle" dominant-baseline="middle">${label}</text></svg>`;return{source:"local",url:`data:image/svg+xml;utf8,${encodeURIComponent(svg)}`,alt:`${label} placeholder`,width:1200,height:630,mime:"image/svg+xml",name:"placeholder.svg"}}function exampleDate(n){return new Date(Date.UTC(2024,0,n)).toISOString().slice(0,10)}function exampleReferenceSlug(field){let targetName=field.target??field.targets?.[0];if(!targetName)return;let slug=`${kebabCase(targetName)}-example-1`;return field.many?[slug]:slug}function fillFieldValue(field,def,ctx){let baked=def.default;if(baked!==void 0)return baked;switch(def.type){case"string":return`${humanize(field)} example ${ctx.index}${ctx.localeSuffix}`;case"richtext":return richTextDoc(`Example ${humanize(ctx.schemaName).toLowerCase()} content ${ctx.index}${ctx.localeSuffix}.`);case"number":return ctx.index;case"boolean":return ctx.index%2===1;case"date":return exampleDate(ctx.index);case"json":return{};case"media":return placeholderMedia(humanize(field));case"blocks":return fillBlocks(def,ctx);default:return}}function fillBlocks(def,ctx){if(ctx.depth>=6)return[];let childCtx={...ctx,depth:ctx.depth+1},schema=def.schema;if("oneOf"in schema)return schema.oneOf.slice(0,5).map(variant=>({_type:variant.uid,...fillItem(variant.fields,childCtx)}));let itemCount=def.max===1?1:2;return Array.from({length:itemCount},()=>fillItem(schema.fields,childCtx))}function fillItem(fields,ctx){let item={};for(let[name,def]of Object.entries(fields)){let value=fillFieldValue(name,def,ctx);value!==void 0&&(item[name]=value)}return item}function generateExampleSeed(schema,options={}){let locale=options.locale,localeSuffix=locale?` (${locale})`:"",count=schema.isSingleton?1:Math.max(1,options.count??3),references={},rows=[];for(let i=1;i<=count;i+=1){let row2={};schema.isSingleton||(row2.slug=`${kebabCase(schema.name)}-example-${i}`);for(let[field,def]of Object.entries(schema.fields)){if(schema.examples&&field in schema.examples){row2[field]=schema.examples[field];continue}if(def.type==="reference"){let value2=exampleReferenceSlug(def);value2!==void 0&&(row2[field]=value2,references[field]={...def.target?{target:def.target}:{},...def.targets?{targets:def.targets}:{},by:"slug",...def.many?{many:!0}:{}});continue}let value=fillFieldValue(field,def,{localeSuffix,index:i,schemaName:schema.name,depth:0});value!==void 0&&(row2[field]=value)}rows.push(row2)}return{name:schema.name,isSingleton:schema.isSingleton,localized:schema.localized,rows,references}}var init_example_content=__esm({"src/scripts/cms/example-content.ts"(){__name(kebabCase,"kebabCase");__name(humanize,"humanize");__name(richTextDoc,"richTextDoc");__name(placeholderMedia,"placeholderMedia");__name(exampleDate,"exampleDate");__name(exampleReferenceSlug,"exampleReferenceSlug");__name(fillFieldValue,"fillFieldValue");__name(fillBlocks,"fillBlocks");__name(fillItem,"fillItem");__name(generateExampleSeed,"generateExampleSeed")}});var cms_scaffold_exports={};__export(cms_scaffold_exports,{extractSchemaTypeNames:()=>extractSchemaTypeNames,scaffoldCms:()=>scaffoldCms});function extractSchemaTypeNames(raw){let names=new Set,list=Array.isArray(raw)?raw:raw&&typeof raw=="object"?raw.data??raw.types:null;if(Array.isArray(list))for(let item of list){let name=item&&typeof item=="object"?item.name:void 0;typeof name=="string"&&names.add(name)}return names}function withoutReferences(fields){let out={};for(let[name,def]of Object.entries(fields))def.type!=="reference"&&(out[name]=def.type==="blocks"?blocksWithoutReferences(def):def);return out}function blocksWithoutReferences(def){return"oneOf"in def.schema?{...def,schema:{oneOf:def.schema.oneOf.map(variant=>({...variant,fields:withoutReferences(variant.fields)}))}}:{...def,schema:{fields:withoutReferences(def.schema.fields)}}}function hasReferences(fields){return Object.values(fields).some(def=>def.type==="reference"?!0:def.type==="blocks"?"oneOf"in def.schema?def.schema.oneOf.some(variant=>hasReferences(variant.fields)):hasReferences(def.schema.fields):!1)}function orderByDependency(schemas3){let byName=new Map(schemas3.map(schema=>[schema.name,schema])),visited=new Set,ordered=[],visit2=__name((schema,stack)=>{if(!(visited.has(schema.name)||stack.has(schema.name))){stack.add(schema.name);for(let field of Object.values(schema.fields))if(field.type==="reference"){let targets=field.targets??(field.target?[field.target]:[]);for(let target of targets){let dep=byName.get(target);dep&&dep.name!==schema.name&&visit2(dep,stack)}}stack.delete(schema.name),visited.add(schema.name),ordered.push(schema)}},"visit");for(let schema of schemas3)visit2(schema,new Set);return ordered}async function scaffoldCms(opts){let{schemas:schemas3,cms,ctx,api,locales,count=3,force=!1}=opts,existingTypes=opts.existingTypes??new Set,ordered=orderByDependency(schemas3),sp=spinner(`Scaffolding ${cms} content model in Fimo`,{rail:opts.rail??!1}),errorMessage3=__name(error=>error instanceof Error?error.message:String(error),"errorMessage"),schemasCreated=0,entriesCreated=0,skipped=0,failures=[],usedLocales=new Set,created=new Set;try{for(let schema of ordered){let exists=existingTypes.has(schema.name);if(exists&&!force){skipped+=1,created.add(schema.name);continue}let bare={name:schema.name,label:schema.label,isSingleton:schema.isSingleton,fields:withoutReferences(schema.fields)};try{exists?await api.updateSchema(ctx.projectId,schema.name,bare):await api.createSchema(ctx.projectId,bare),schemasCreated+=1,created.add(schema.name)}catch(error){if(isAlreadyExistsError(error))try{await api.updateSchema(ctx.projectId,schema.name,bare),schemasCreated+=1,created.add(schema.name);continue}catch(updateError){failures.push({type:schema.name,stage:"schema",message:errorMessage3(updateError)});continue}failures.push({type:schema.name,stage:"schema",message:errorMessage3(error)})}}for(let schema of ordered)if(!(!created.has(schema.name)||existingTypes.has(schema.name)&&!force||!hasReferences(schema.fields)))try{await api.updateSchema(ctx.projectId,schema.name,{name:schema.name,label:schema.label,isSingleton:schema.isSingleton,fields:schema.fields})}catch(error){failures.push({type:schema.name,stage:"schema",message:`references: ${errorMessage3(error)}`})}for(let schema of ordered){if(!created.has(schema.name))continue;let seedLocales=schema.localized&&locales.length>0?locales:[void 0];for(let locale of seedLocales){let seed=generateExampleSeed(schema,{count,locale});try{if(schema.isSingleton)await api.putDocument(ctx.projectId,schema.name,seed.rows[0]??{},locale?{locale}:{}),entriesCreated+=1;else{let result=await api.importEntries(ctx.projectId,schema.name,{entries:seed.rows.map(data2=>({data:data2})),...locale?{locale}:{},...Object.keys(seed.references).length>0?{references:seed.references}:{},allowDangling:!0});entriesCreated+=result.created?.length??0,result.failed&&result.failed.length>0&&failures.push({type:schema.name,stage:"entries",message:`${result.failed.length} entry(ies) rejected${locale?` (${locale})`:""}: ${result.failed[0]?.message??result.failed[0]?.code??"unknown"}`})}locale&&usedLocales.add(locale)}catch(error){failures.push({type:schema.name,stage:"entries",message:`${errorMessage3(error)}${locale?` (${locale})`:""}`})}}}let skips=skipped>0?`, ${skipped} already present`:"",issues=failures.length>0?`, ${failures.length} issue(s)`:"";return sp.succeed(`Scaffolded ${schemasCreated} ${cms} type(s) + ${entriesCreated} example entr${entriesCreated===1?"y":"ies"} in Fimo${skips}${issues}`),{cms,schemas:schemasCreated,entries:entriesCreated,locales:[...usedLocales],skipped,failures}}catch(error){throw sp.stop(),error}}var init_cms_scaffold=__esm({"src/cli/utils/cms-scaffold.ts"(){init_dist();init_example_content();init_schema();__name(extractSchemaTypeNames,"extractSchemaTypeNames");__name(withoutReferences,"withoutReferences");__name(blocksWithoutReferences,"blocksWithoutReferences");__name(hasReferences,"hasReferences");__name(orderByDependency,"orderByDependency");__name(scaffoldCms,"scaffoldCms")}});function syncStateColor(state,p2){switch(state){case"in-sync":return p2.green("in sync");case"ahead":return p2.blue("ahead");case"behind":return p2.yellow("behind");case"diverged":return p2.red("diverged");default:return p2.dim("unknown")}}function renderStatus2(report,p2,opts={}){let lines=[],sep2=opts.ascii?"|":"\xB7",header=`${p2.bold(`env: ${report.env}`)} ${p2.dim(`vs ${report.vs} ${sep2}`)} ${syncStateColor(report.syncState,p2)}`;if(lines.push(header),lines.push(""),opts.short){for(let name of SURFACE_NAMES){if(opts.surface&&opts.surface!==name)continue;let delta=report.surfaces.find(s=>s.surface===name);if(!delta)continue;let innerSep=opts.ascii?"|":"\xB7",counts=`${delta.changedCount} changed${delta.conflictCount>0?` ${innerSep} ${delta.conflictCount} conflicts`:""}`,truncatedHint=delta.truncated&&delta.items&&delta.items.length<delta.changedCount?p2.dim(` ${innerSep} ${delta.items.length} of ${delta.changedCount} shown`):"";lines.push(` ${p2.bold(name.padEnd(14))} ${p2.dim(counts)}${truncatedHint}`)}return lines.join(`
1870
+ `),"render")})}async function schemaDeleteCommand(typeName,options,cmd){let{ctx,api}=await bootstrapProjectCommand(options,cmd,{refuseReadOnly:!0});await requireOperation("cms.schema.delete",{projectId:ctx.projectId,env:ctx.env});let result=await api.deleteSchema(ctx.projectId,typeName);ui.result({command:"schemas delete",data:result,render:__name(()=>renderPrettyJson8(result),"render")})}var SchemaCommandError,init_schema=__esm({"src/cli/commands/schema.ts"(){init_dist();init_sync_schemas();init_validate_schemas();init_api();init_capability_guard();init_project_context();init_project();SchemaCommandError=class extends Error{static{__name(this,"SchemaCommandError")}code;constructor(code2,message){super(message),this.name="SchemaCommandError",this.code=code2}};__name(renameOperation,"renameOperation");__name(toApiShape,"toApiShape");__name(renderPrettyJson8,"renderPrettyJson");__name(schemaListCommand,"schemaListCommand");__name(schemaGetCommand,"schemaGetCommand");__name(schemaPushCommand,"schemaPushCommand");__name(isAlreadyExistsError,"isAlreadyExistsError");__name(sortSchemasByReferenceDependencies,"sortSchemasByReferenceDependencies");__name(isUnknownTargetError,"isUnknownTargetError");__name(pushAllSchemas,"pushAllSchemas");__name(applyDraftAdoption,"applyDraftAdoption");__name(schemaAdoptDraftsCommand,"schemaAdoptDraftsCommand");__name(schemaDeleteCommand,"schemaDeleteCommand")}});function kebabCase(name){return name.replace(/([a-z0-9])([A-Z])/g,"$1-$2").replace(/[^a-zA-Z0-9]+/g,"-").toLowerCase().replace(/^-+|-+$/g,"")}function humanize(field){return field.replace(/([a-z0-9])([A-Z])/g,"$1 $2").replace(/[_-]+/g," ").replace(/^\w/,c2=>c2.toUpperCase())}function richTextDoc(text3){return{type:"doc",content:[{type:"paragraph",content:[{type:"text",text:text3}]}]}}function placeholderMedia(label){let svg=`<svg xmlns="http://www.w3.org/2000/svg" width="1200" height="630"><rect width="100%" height="100%" fill="#e5e7eb"/><text x="50%" y="50%" font-family="sans-serif" font-size="44" fill="#9ca3af" text-anchor="middle" dominant-baseline="middle">${label}</text></svg>`;return{source:"local",url:`data:image/svg+xml;utf8,${encodeURIComponent(svg)}`,alt:`${label} placeholder`,width:1200,height:630,mime:"image/svg+xml",name:"placeholder.svg"}}function exampleDate(n){return new Date(Date.UTC(2024,0,n)).toISOString().slice(0,10)}function exampleReferenceSlug(field){let targetName=field.target??field.targets?.[0];if(!targetName)return;let slug=`${kebabCase(targetName)}-example-1`;return field.many?[slug]:slug}function fillFieldValue(field,def,ctx){let baked=def.default;if(baked!==void 0)return baked;switch(def.type){case"string":return`${humanize(field)} example ${ctx.index}${ctx.localeSuffix}`;case"richtext":return richTextDoc(`Example ${humanize(ctx.schemaName).toLowerCase()} content ${ctx.index}${ctx.localeSuffix}.`);case"number":return ctx.index;case"boolean":return ctx.index%2===1;case"date":return exampleDate(ctx.index);case"json":return{};case"media":return placeholderMedia(humanize(field));case"blocks":return fillBlocks(def,ctx);default:return}}function fillBlocks(def,ctx){if(ctx.depth>=6)return[];let childCtx={...ctx,depth:ctx.depth+1},schema=def.schema;if("oneOf"in schema){let total=schema.oneOf.length,take=Math.min(5,total),offset=total>0?(ctx.index-1)*take%total:0;return Array.from({length:take},(_2,k)=>schema.oneOf[(offset+k)%total]).map(variant=>({_type:variant.uid,...fillItem(variant.fields,childCtx)}))}let itemCount=def.max===1?1:2;return Array.from({length:itemCount},()=>fillItem(schema.fields,childCtx))}function fillItem(fields,ctx){let item={};for(let[name,def]of Object.entries(fields)){let value=fillFieldValue(name,def,ctx);value!==void 0&&(item[name]=value)}return item}function generateExampleSeed(schema,options={}){let locale=options.locale,localeSuffix=locale?` (${locale})`:"",count=schema.isSingleton?1:Math.max(1,options.count??3),references={},rows=[];for(let i=1;i<=count;i+=1){let row2={};schema.isSingleton||(row2.slug=`${kebabCase(schema.name)}-example-${i}`);for(let[field,def]of Object.entries(schema.fields)){if(schema.examples&&field in schema.examples){row2[field]=schema.examples[field];continue}if(def.type==="reference"){let value2=exampleReferenceSlug(def);value2!==void 0&&(row2[field]=value2,references[field]={...def.target?{target:def.target}:{},...def.targets?{targets:def.targets}:{},by:"slug",...def.many?{many:!0}:{}});continue}let value=fillFieldValue(field,def,{localeSuffix,index:i,schemaName:schema.name,depth:0});value!==void 0&&(row2[field]=value)}rows.push(row2)}return{name:schema.name,isSingleton:schema.isSingleton,localized:schema.localized,rows,references}}var init_example_content=__esm({"src/scripts/cms/example-content.ts"(){__name(kebabCase,"kebabCase");__name(humanize,"humanize");__name(richTextDoc,"richTextDoc");__name(placeholderMedia,"placeholderMedia");__name(exampleDate,"exampleDate");__name(exampleReferenceSlug,"exampleReferenceSlug");__name(fillFieldValue,"fillFieldValue");__name(fillBlocks,"fillBlocks");__name(fillItem,"fillItem");__name(generateExampleSeed,"generateExampleSeed")}});var cms_scaffold_exports={};__export(cms_scaffold_exports,{extractSchemaTypeNames:()=>extractSchemaTypeNames,scaffoldCms:()=>scaffoldCms});function extractSchemaTypeNames(raw){let names=new Set,list=Array.isArray(raw)?raw:raw&&typeof raw=="object"?raw.data??raw.types:null;if(Array.isArray(list))for(let item of list){let name=item&&typeof item=="object"?item.name:void 0;typeof name=="string"&&names.add(name)}return names}function withoutReferences(fields){let out={};for(let[name,def]of Object.entries(fields))def.type!=="reference"&&(out[name]=def.type==="blocks"?blocksWithoutReferences(def):def);return out}function blocksWithoutReferences(def){return"oneOf"in def.schema?{...def,schema:{oneOf:def.schema.oneOf.map(variant=>({...variant,fields:withoutReferences(variant.fields)}))}}:{...def,schema:{fields:withoutReferences(def.schema.fields)}}}function hasReferences(fields){return Object.values(fields).some(def=>def.type==="reference"?!0:def.type==="blocks"?"oneOf"in def.schema?def.schema.oneOf.some(variant=>hasReferences(variant.fields)):hasReferences(def.schema.fields):!1)}function orderByDependency(schemas3){let byName=new Map(schemas3.map(schema=>[schema.name,schema])),visited=new Set,ordered=[],visit2=__name((schema,stack)=>{if(!(visited.has(schema.name)||stack.has(schema.name))){stack.add(schema.name);for(let field of Object.values(schema.fields))if(field.type==="reference"){let targets=field.targets??(field.target?[field.target]:[]);for(let target of targets){let dep=byName.get(target);dep&&dep.name!==schema.name&&visit2(dep,stack)}}stack.delete(schema.name),visited.add(schema.name),ordered.push(schema)}},"visit");for(let schema of schemas3)visit2(schema,new Set);return ordered}async function scaffoldCms(opts){let{schemas:schemas3,cms,ctx,api,locales,count=3,force=!1}=opts,existingTypes=opts.existingTypes??new Set,ordered=orderByDependency(schemas3),sp=spinner(`Scaffolding ${cms} content model in Fimo`,{rail:opts.rail??!1}),errorMessage3=__name(error=>error instanceof Error?error.message:String(error),"errorMessage"),schemasCreated=0,entriesCreated=0,skipped=0,failures=[],usedLocales=new Set,created=new Set;try{for(let schema of ordered){let exists=existingTypes.has(schema.name);if(exists&&!force){skipped+=1,created.add(schema.name);continue}let bare={name:schema.name,label:schema.label,isSingleton:schema.isSingleton,fields:withoutReferences(schema.fields)};try{exists?await api.updateSchema(ctx.projectId,schema.name,bare):await api.createSchema(ctx.projectId,bare),schemasCreated+=1,created.add(schema.name)}catch(error){if(isAlreadyExistsError(error))try{await api.updateSchema(ctx.projectId,schema.name,bare),schemasCreated+=1,created.add(schema.name);continue}catch(updateError){failures.push({type:schema.name,stage:"schema",message:errorMessage3(updateError)});continue}failures.push({type:schema.name,stage:"schema",message:errorMessage3(error)})}}for(let schema of ordered)if(!(!created.has(schema.name)||existingTypes.has(schema.name)&&!force||!hasReferences(schema.fields)))try{await api.updateSchema(ctx.projectId,schema.name,{name:schema.name,label:schema.label,isSingleton:schema.isSingleton,fields:schema.fields})}catch(error){failures.push({type:schema.name,stage:"schema",message:`references: ${errorMessage3(error)}`})}for(let schema of ordered){if(!created.has(schema.name))continue;force&&!schema.isSingleton&&await api.bulkDeleteEntries(ctx.projectId,schema.name,{where:{}}).catch(()=>{});let seedLocales=schema.localized&&locales.length>0?locales:[void 0];for(let locale of seedLocales){let seed=generateExampleSeed(schema,{count,locale});try{if(schema.isSingleton)await api.putDocument(ctx.projectId,schema.name,seed.rows[0]??{},locale?{locale}:{}),entriesCreated+=1;else{let result=await api.importEntries(ctx.projectId,schema.name,{entries:seed.rows.map(data2=>({data:data2})),...locale?{locale}:{},...Object.keys(seed.references).length>0?{references:seed.references}:{},allowDangling:!0});entriesCreated+=result.created?.length??0,result.failed&&result.failed.length>0&&failures.push({type:schema.name,stage:"entries",message:`${result.failed.length} entry(ies) rejected${locale?` (${locale})`:""}: ${result.failed[0]?.message??result.failed[0]?.code??"unknown"}`})}locale&&usedLocales.add(locale)}catch(error){failures.push({type:schema.name,stage:"entries",message:`${errorMessage3(error)}${locale?` (${locale})`:""}`})}}}let skips=skipped>0?`, ${skipped} already present`:"",issues=failures.length>0?`, ${failures.length} issue(s)`:"";return sp.succeed(`Scaffolded ${schemasCreated} ${cms} type(s) + ${entriesCreated} example entr${entriesCreated===1?"y":"ies"} in Fimo${skips}${issues}`),{cms,schemas:schemasCreated,entries:entriesCreated,locales:[...usedLocales],skipped,failures}}catch(error){throw sp.stop(),error}}var init_cms_scaffold=__esm({"src/cli/utils/cms-scaffold.ts"(){init_dist();init_example_content();init_schema();__name(extractSchemaTypeNames,"extractSchemaTypeNames");__name(withoutReferences,"withoutReferences");__name(blocksWithoutReferences,"blocksWithoutReferences");__name(hasReferences,"hasReferences");__name(orderByDependency,"orderByDependency");__name(scaffoldCms,"scaffoldCms")}});function syncStateColor(state,p2){switch(state){case"in-sync":return p2.green("in sync");case"ahead":return p2.blue("ahead");case"behind":return p2.yellow("behind");case"diverged":return p2.red("diverged");default:return p2.dim("unknown")}}function renderStatus2(report,p2,opts={}){let lines=[],sep2=opts.ascii?"|":"\xB7",header=`${p2.bold(`env: ${report.env}`)} ${p2.dim(`vs ${report.vs} ${sep2}`)} ${syncStateColor(report.syncState,p2)}`;if(lines.push(header),lines.push(""),opts.short){for(let name of SURFACE_NAMES){if(opts.surface&&opts.surface!==name)continue;let delta=report.surfaces.find(s=>s.surface===name);if(!delta)continue;let innerSep=opts.ascii?"|":"\xB7",counts=`${delta.changedCount} changed${delta.conflictCount>0?` ${innerSep} ${delta.conflictCount} conflicts`:""}`,truncatedHint=delta.truncated&&delta.items&&delta.items.length<delta.changedCount?p2.dim(` ${innerSep} ${delta.items.length} of ${delta.changedCount} shown`):"";lines.push(` ${p2.bold(name.padEnd(14))} ${p2.dim(counts)}${truncatedHint}`)}return lines.join(`
1857
1871
  `)}let surfaceOpts={ascii:opts.ascii,...opts.all?{maxItems:Number.MAX_SAFE_INTEGER}:{}};for(let name of SURFACE_NAMES){if(opts.surface&&opts.surface!==name)continue;let delta=report.surfaces.find(s=>s.surface===name);delta&&(lines.push(renderSurface(delta,p2,surfaceOpts)),lines.push(""))}if(!opts.surface&&report.crossSurface&&report.crossSurface.length>0&&(lines.push(renderCrossSurface(report.crossSurface,p2,{ascii:opts.ascii})),lines.push("")),report.agents&&report.agents.length>0){lines.push(p2.bold("AGENTS")),lines.push(p2.dim(" declared \xB7 active \xB7 last run"));for(let agent of report.agents){let status=agent.status==="active"?p2.green("active"):agent.status==="paused"?p2.yellow("paused"):p2.red("failed"),lastRun=agent.lastRunAt?p2.dim(new Date(agent.lastRunAt).toISOString().slice(0,19)+"Z"):p2.dim("never");lines.push(` ${agent.name.padEnd(18)} ${status.padEnd(7)} ${lastRun}`)}lines.push("")}return lines.join(`
1858
1872
  `).replace(/\n+$/,"")}var init_render_status=__esm({"src/cli/utils/format/render-status.ts"(){init_esm2();init_cross_surface();init_surface();__name(syncStateColor,"syncStateColor");__name(renderStatus2,"renderStatus")}});function pickSurfaces(opts){let selected=[["code","code"],["content","content"],["i18n","translations"],["agents","activations"],["assets","assets"],["forms","forms"]].filter(([flag2])=>opts[flag2]===!0).map(([,surface])=>surface);return selected.length>0?selected:void 0}function withSoftTimeout(p2,ms){return new Promise(resolve9=>{let t2=setTimeout(()=>resolve9({kind:"timeout"}),ms);p2.then(value=>{clearTimeout(t2),resolve9({kind:"ok",value})},error=>{clearTimeout(t2),resolve9({kind:"error",error})})})}async function localCodeSurface(cwd){if(!await isGitRepo(cwd))return{surface:"code",status:"unchanged",changedCount:0,conflictCount:0,items:[]};try{let items=(await runGit2(["status","--porcelain"],cwd)).split(`
1859
1873
  `).filter(Boolean).map(line=>{let code2=line.slice(0,2),path64=line.slice(3).trim();return{verb:code2.startsWith("A")||code2.startsWith("?")?"added":code2.startsWith("D")?"removed":"changed",key:path64,origin:"local-uncommitted"}});return{surface:"code",status:items.length===0?"unchanged":"ok",changedCount:items.length,conflictCount:0,items}}catch{return{surface:"code",status:"unchanged",changedCount:0,conflictCount:0,items:[]}}}function mergeCodeSurfaces(serverCode,local){if(!serverCode)return local;if(serverCode.status==="failed"||serverCode.status==="timed_out")return{...serverCode,items:[...serverCode.items??[],...local.items??[]],changedCount:serverCode.changedCount+local.changedCount};let items=[...serverCode.items??[],...local.items??[]],changedCount=serverCode.changedCount+local.changedCount,conflictCount=serverCode.conflictCount+local.conflictCount;return{surface:"code",status:changedCount>0||conflictCount>0?"ok":"unchanged",durationMs:serverCode.durationMs,changedCount,conflictCount,items}}async function statusCommand2(arg1,maybeCmd){let cmd=maybeCmd??arg1,options=maybeCmd?arg1:cmd.opts(),program2=cmd.parent??cmd,envOverride2=envOverrideFromCommand(program2),ctx=await getProjectContext({cwd:options.cwd,envOverride:envOverride2}),api=new FimoApiClient().withEnv(ctx.env),cwd=resolveProjectCwd(options.cwd),surfaces=pickSurfaces(options),codePromise=localCodeSurface(cwd),serverPromise=withSoftTimeout(api.getEnvStatus(ctx.projectId,ctx.env,{against:options.vs,surfaces}),SOFT_TIMEOUT_MS*15),[local,serverResult]=await Promise.all([codePromise,serverPromise]);if(serverResult.kind==="timeout"){ui.error({command:"status",code:"STATUS_TIMEOUT",message:`Server did not respond within ${SOFT_TIMEOUT_MS*15/1e3}s. Try again, or run \`fimo diff\` to drill in.`});return}if(serverResult.kind==="error"){let err=serverResult.error,message=err instanceof Error?err.message:String(err);ui.error({command:"status",code:"STATUS_REQUEST_FAILED",message:`Failed to fetch status: ${message}`});return}let server=serverResult.value,serverCode=server.surfaces.find(s=>s.surface==="code"),code2=mergeCodeSurfaces(serverCode,local),merged={...server,surfaces:[code2,...server.surfaces.filter(s=>s.surface!=="code")]};ui.result({command:"status",data:merged,render:__name(rctx=>{let p2=rctx.color?palette:makeColorPalette({forceDisable:!0});return renderStatus2(merged,p2,{short:options.short,surface:surfaces?.length===1?surfaces[0]:void 0,all:options.all,ascii:rctx.ascii})},"render")})}var SOFT_TIMEOUT_MS,init_status=__esm({"src/cli/commands/status.ts"(){init_dist();init_api();init_color();init_render_status();init_git();init_project_context();init_project();SOFT_TIMEOUT_MS=2e3;__name(pickSurfaces,"pickSurfaces");__name(withSoftTimeout,"withSoftTimeout");__name(localCodeSurface,"localCodeSurface");__name(mergeCodeSurfaces,"mergeCodeSurfaces");__name(statusCommand2,"statusCommand")}});init_dist();import fs56 from"node:fs";import path63 from"node:path";var import_index=__toESM(require_commander(),1),{program,createCommand,createArgument,createOption,CommanderError,InvalidArgumentError,InvalidOptionArgumentError,Command,Argument,Option,Help}=import_index.default;init_detect_framework();init_api();init_esm();init_dist();init_api();init_config();init_project();import fs12 from"node:fs";import path13 from"node:path";init_esm();var MAX_TYPE_TO_CONFIRM_ATTEMPTS=3;async function runConsent(input){let{caps,allowShell=!1,quiet=!1,io}=input,{caps:known}=partitionCapabilities(caps);if(renderCapabilityList(known,io),quiet)return known.some(isCritical)?(io.print("! critical-tier capabilities require interactive consent (use --allow-critical only when you understand the risk)"),{decision:"denied",approved:[]}):{decision:"allowed",approved:[...known]};let highRisk=known.filter(isHighRisk);if(highRisk.length===0){let ok=await io.promptYesNo(`? Proceed with install of ${input.agentName}? [y/N]`);return{decision:ok?"allowed":"denied",approved:ok?[...known]:[]}}let approved=known.filter(cap=>!isHighRisk(cap)),denied=[];for(let cap of highRisk.filter(c2=>!isCritical(c2))){if(cap==="shell:exec"&&allowShell){approved.push(cap),io.print(` \u2713 ${cap} (--allow-shell)`);continue}await io.promptYesNo(`? Allow ${cap} for ${input.agentName}? [y/n]`)?approved.push(cap):denied.push(cap)}if(denied.length>0)return io.print(`! aborting install \u2014 denied: ${denied.join(", ")}`),{decision:"denied",approved:[]};for(let cap of highRisk.filter(isCritical)){if(!await typeToConfirm(cap,input.agentName,io))return io.print(`! aborting install \u2014 failed to confirm ${cap}`),{decision:"denied",approved:[]};approved.push(cap)}return{decision:"allowed",approved}}__name(runConsent,"runConsent");async function typeToConfirm(cap,agentName,io){io.print(""),io.print(`! ${agentName} declares the CRITICAL capability '${cap}'`),io.print(" Type the literal token to confirm; anything else aborts.");for(let attempt=1;attempt<=MAX_TYPE_TO_CONFIRM_ATTEMPTS;attempt++){if(await io.promptText(` type '${cap}' (${attempt}/${MAX_TYPE_TO_CONFIRM_ATTEMPTS}): `)===cap)return!0;io.print(" mismatch.")}return!1}__name(typeToConfirm,"typeToConfirm");function renderCapabilityList(caps,io){if(caps.length===0){io.print(" (no capabilities declared \u2014 read-only default trio will apply at runtime)");return}io.print(""),io.print("This agent declares the following capabilities:");let byTier={low:[],medium:[],high:[],critical:[]};for(let cap of caps)byTier[riskTierOf(cap)].push(cap);let ORDER=["low","medium","high","critical"];for(let tier of ORDER){let list=byTier[tier];if(list.length!==0)for(let cap of list.sort())io.print(` \u25CF ${tier.padEnd(8)} ${cap}`)}io.print("")}__name(renderCapabilityList,"renderCapabilityList");function defaultConsentIO(){return{print(line){process.stdout.write(`${line}
@@ -2077,7 +2091,7 @@ export async function fimoCmsList(
2077
2091
  meta: { pagination: { page: 1, pageSize: data.length, pageCount: 1, total: data.length } },
2078
2092
  };
2079
2093
  }
2080
- `}__name(renderStrapiListSource,"renderStrapiListSource");import{existsSync as existsSync14,readdirSync as readdirSync16,readFileSync as readFileSync18}from"node:fs";import{join as join24}from"node:path";var MAX_BLOCK_VARIANTS2=40,MAX_COMPONENT_DEPTH=6,FIELD_NAME_PATTERN=/^[A-Za-z_]\w*$/,RESERVED_BLOCK_FIELD_NAMES=new Set(["_key","_type"]),COMPONENT_DISCRIMINATOR="__component",TO_MANY=new Set(["oneToMany","manyToMany"]);function pascalCase2(name){return name.replace(/[^a-zA-Z0-9]+(.)?/g,(_2,c2)=>c2?c2.toUpperCase():"").replace(/^(.)/,c2=>c2.toUpperCase())}__name(pascalCase2,"pascalCase");function variantUid(componentUid){let pascal=pascalCase2(componentUid);return/^[A-Za-z]/.test(pascal)?pascal:`Block${pascal}`}__name(variantUid,"variantUid");function mapAttribute(field,attr2,ctx){if(field==="slug")return null;switch(attr2.type){case"password":return null;case"string":case"text":case"email":case"uid":case"enumeration":return{type:"string"};case"richtext":case"blocks":return{type:"richtext"};case"integer":case"biginteger":case"decimal":case"float":return{type:"number"};case"boolean":return{type:"boolean"};case"date":case"datetime":case"time":case"timestamp":return{type:"date"};case"media":return{type:"media"};case"component":return mapComponentAttribute(attr2,ctx);case"dynamiczone":return mapDynamicZone(attr2,ctx);case"json":return{type:"json"};case"relation":{let targetName=attr2.target?ctx.uidToName.get(attr2.target):void 0;return targetName?(attr2.relation?TO_MANY.has(attr2.relation):!1)?{type:"reference",target:targetName,many:!0}:{type:"reference",target:targetName}:{type:"json"}}default:return{type:"json"}}}__name(mapAttribute,"mapAttribute");function componentFields(componentUid,ctx){if(!componentUid||ctx.depth>MAX_COMPONENT_DEPTH||ctx.visiting.has(componentUid))return null;let component=ctx.components.get(componentUid);if(!component)return null;let childCtx={...ctx,depth:ctx.depth+1,visiting:new Set(ctx.visiting).add(componentUid)},fields={};for(let[name,attr2]of Object.entries(component.attributes??{})){if(!FIELD_NAME_PATTERN.test(name)||RESERVED_BLOCK_FIELD_NAMES.has(name)||name===COMPONENT_DISCRIMINATOR)continue;let mapped=mapAttribute(name,attr2,childCtx);mapped&&(fields[name]=mapped)}return fields}__name(componentFields,"componentFields");function mapComponentAttribute(attr2,ctx){let fields=componentFields(attr2.component,ctx);if(!fields)return{type:"json"};let schema={fields};return attr2.repeatable?{type:"blocks",schema}:{type:"blocks",schema,max:1}}__name(mapComponentAttribute,"mapComponentAttribute");function mapDynamicZone(attr2,ctx){let componentUids=attr2.components??[];componentUids.length>MAX_BLOCK_VARIANTS2&&console.warn(`[migration] dynamic zone has ${componentUids.length} components; Fimo blocks allow at most ${MAX_BLOCK_VARIANTS2} variants \u2014 mapping the first ${MAX_BLOCK_VARIANTS2}, dropping ${componentUids.length-MAX_BLOCK_VARIANTS2}.`);let seenUids=new Set,variants=[];for(let componentUid of componentUids.slice(0,MAX_BLOCK_VARIANTS2)){let fields=componentFields(componentUid,ctx);if(!fields)continue;let uid=variantUid(componentUid);for(let n=2;seenUids.has(uid);n+=1)uid=`${variantUid(componentUid)}${n}`;seenUids.add(uid);let component=ctx.components.get(componentUid);variants.push({uid,label:component?.info?.displayName??uid,fields:{[COMPONENT_DISCRIMINATOR]:{type:"string",default:componentUid,editable:!1},...fields}})}return variants.length===0?{type:"json"}:{type:"blocks",schema:{oneOf:variants}}}__name(mapDynamicZone,"mapDynamicZone");function mapSchema(raw,uidToName,components){let info=raw.schema.info??{},name=uidToName.get(raw.uid),fields={},ctx={uidToName,components,visiting:new Set,depth:0};for(let[field,attr2]of Object.entries(raw.schema.attributes??{})){let mapped=mapAttribute(field,attr2,ctx);mapped&&(fields[field]=mapped)}return{name,label:info.displayName||name,isSingleton:raw.schema.kind==="singleType",localized:raw.schema.pluginOptions?.i18n?.localized===!0,fields,sourceUid:raw.uid,sourcePath:`/${info.pluralName??raw.contentTypeName}`}}__name(mapSchema,"mapSchema");function collectRawSchemas(strapiAppDir){let apiDir=join24(strapiAppDir,"src","api");if(!existsSync14(apiDir))return[];let raws=[];for(let apiEntry of readdirSync16(apiDir,{withFileTypes:!0})){if(!apiEntry.isDirectory())continue;let ctRoot=join24(apiDir,apiEntry.name,"content-types");if(existsSync14(ctRoot))for(let ctEntry of readdirSync16(ctRoot,{withFileTypes:!0})){if(!ctEntry.isDirectory())continue;let schemaPath=join24(ctRoot,ctEntry.name,"schema.json");if(existsSync14(schemaPath))try{let schema=JSON.parse(readFileSync18(schemaPath,"utf8"));raws.push({uid:`api::${apiEntry.name}.${ctEntry.name}`,apiName:apiEntry.name,contentTypeName:ctEntry.name,schema})}catch{}}}return raws}__name(collectRawSchemas,"collectRawSchemas");function collectComponents(strapiAppDir){let components=new Map,componentsDir=join24(strapiAppDir,"src","components");if(!existsSync14(componentsDir))return components;for(let categoryEntry of readdirSync16(componentsDir,{withFileTypes:!0})){if(!categoryEntry.isDirectory())continue;let categoryDir=join24(componentsDir,categoryEntry.name);for(let fileEntry of readdirSync16(categoryDir,{withFileTypes:!0})){if(!fileEntry.isFile()||!fileEntry.name.endsWith(".json"))continue;let uid=`${categoryEntry.name}.${fileEntry.name.slice(0,-5)}`;try{let parsed=JSON.parse(readFileSync18(join24(categoryDir,fileEntry.name),"utf8"));components.set(uid,{...parsed,uid})}catch{}}}return components}__name(collectComponents,"collectComponents");function readStrapiSchemas(strapiAppDir){let raws=collectRawSchemas(strapiAppDir),components=collectComponents(strapiAppDir),uidToName=new Map;for(let raw of raws){let singular=raw.schema.info?.singularName??raw.contentTypeName;uidToName.set(raw.uid,pascalCase2(singular))}return raws.map(raw=>mapSchema(raw,uidToName,components)).sort((a,b)=>a.name.localeCompare(b.name))}__name(readStrapiSchemas,"readStrapiSchemas");import{existsSync as existsSync15,readdirSync as readdirSync17,readFileSync as readFileSync19}from"node:fs";import{join as join25,relative as relative6}from"node:path";var SOURCE_EXT_RE2=/\.(tsx?|jsx?|mjs|cjs|astro|vue|svelte)$/,SKIP_DIRS5=new Set(["node_modules",".next",".nuxt",".svelte-kit",".turbo","dist","build","out","coverage",".vercel",".git"]),MAX_FILES=2e3,ENV_REF_RE=/\bSTRAPI[A-Z0-9_]*_URL\b|\bNEXT_PUBLIC_STRAPI[A-Z0-9_]*\b|\bSTRAPI[A-Z0-9_]*_TOKEN\b/,UID_REF_RE=/api::[a-z0-9-]+\.[a-z0-9-]+/,IMPORT_RE=/(?:from\s+|require\(\s*|import\(\s*)['"`]([^'"`]+)['"`]/,ENV_KEY_RE=/^\s*([A-Z0-9_]*STRAPI[A-Z0-9_]*)\s*=/;function readDeps2(dir){try{let pkg=JSON.parse(readFileSync19(join25(dir,"package.json"),"utf8"));return{...pkg.dependencies,...pkg.devDependencies}}catch{return{}}}__name(readDeps2,"readDeps");function classifyLine(line){let importMatch=IMPORT_RE.exec(line);return importMatch&&/strapi/i.test(importMatch[1])&&!/fimo/i.test(importMatch[1])?"strapi-sdk-import":ENV_REF_RE.test(line)?"env-ref":UID_REF_RE.test(line)?"uid-ref":null}__name(classifyLine,"classifyLine");function scanCallSites(frontendDir){let sites=[],scanned=0,walk2=__name(dir=>{if(scanned>=MAX_FILES)return;let entries2;try{entries2=readdirSync17(dir,{withFileTypes:!0})}catch{return}for(let entry of entries2){if(scanned>=MAX_FILES)return;if(entry.isDirectory()){!SKIP_DIRS5.has(entry.name)&&!entry.name.startsWith(".")&&walk2(join25(dir,entry.name));continue}if(!SOURCE_EXT_RE2.test(entry.name))continue;scanned+=1;let full=join25(dir,entry.name),text3;try{text3=readFileSync19(full,"utf8")}catch{continue}let lines=text3.split(`
2094
+ `}__name(renderStrapiListSource,"renderStrapiListSource");import{existsSync as existsSync14,readdirSync as readdirSync16,readFileSync as readFileSync18}from"node:fs";import{join as join24}from"node:path";var MAX_BLOCK_VARIANTS2=40,MAX_COMPONENT_DEPTH=6,FIELD_NAME_PATTERN=/^[A-Za-z_]\w*$/,RESERVED_BLOCK_FIELD_NAMES=new Set(["_key","_type"]),COMPONENT_DISCRIMINATOR="__component",TO_MANY=new Set(["oneToMany","manyToMany"]);function pascalCase2(name){return name.replace(/[^a-zA-Z0-9]+(.)?/g,(_2,c2)=>c2?c2.toUpperCase():"").replace(/^(.)/,c2=>c2.toUpperCase())}__name(pascalCase2,"pascalCase");function variantUid(componentUid){let pascal=pascalCase2(componentUid);return/^[A-Za-z]/.test(pascal)?pascal:`Block${pascal}`}__name(variantUid,"variantUid");function mapAttribute(field,attr2,ctx){if(field==="slug")return null;switch(attr2.type){case"password":return null;case"string":case"text":case"email":case"uid":return{type:"string"};case"enumeration":{let value=attr2.default??(Array.isArray(attr2.enum)?attr2.enum[0]:void 0);return{type:"string",...value!==void 0?{default:value}:{}}}case"richtext":case"blocks":return{type:"richtext"};case"integer":case"biginteger":case"decimal":case"float":return{type:"number"};case"boolean":return{type:"boolean"};case"date":case"datetime":case"time":case"timestamp":return{type:"date"};case"media":return{type:"media"};case"component":return mapComponentAttribute(attr2,ctx);case"dynamiczone":return mapDynamicZone(attr2,ctx);case"json":return{type:"json"};case"relation":{let targetName=attr2.target?ctx.uidToName.get(attr2.target):void 0;return targetName?(attr2.relation?TO_MANY.has(attr2.relation):!1)?{type:"reference",target:targetName,many:!0}:{type:"reference",target:targetName}:{type:"json"}}default:return{type:"json"}}}__name(mapAttribute,"mapAttribute");function componentFields(componentUid,ctx){if(!componentUid||ctx.depth>MAX_COMPONENT_DEPTH||ctx.visiting.has(componentUid))return null;let component=ctx.components.get(componentUid);if(!component)return null;let childCtx={...ctx,depth:ctx.depth+1,visiting:new Set(ctx.visiting).add(componentUid)},fields={};for(let[name,attr2]of Object.entries(component.attributes??{})){if(!FIELD_NAME_PATTERN.test(name)||RESERVED_BLOCK_FIELD_NAMES.has(name)||name===COMPONENT_DISCRIMINATOR)continue;let mapped=mapAttribute(name,attr2,childCtx);mapped&&(fields[name]=mapped)}return fields}__name(componentFields,"componentFields");function mapComponentAttribute(attr2,ctx){let fields=componentFields(attr2.component,ctx);if(!fields)return{type:"json"};let schema={fields};return attr2.repeatable?{type:"blocks",schema}:{type:"blocks",schema,max:1}}__name(mapComponentAttribute,"mapComponentAttribute");function mapDynamicZone(attr2,ctx){let componentUids=attr2.components??[];componentUids.length>MAX_BLOCK_VARIANTS2&&console.warn(`[migration] dynamic zone has ${componentUids.length} components; Fimo blocks allow at most ${MAX_BLOCK_VARIANTS2} variants \u2014 mapping the first ${MAX_BLOCK_VARIANTS2}, dropping ${componentUids.length-MAX_BLOCK_VARIANTS2}.`);let seenUids=new Set,variants=[];for(let componentUid of componentUids.slice(0,MAX_BLOCK_VARIANTS2)){let fields=componentFields(componentUid,ctx);if(!fields)continue;let uid=variantUid(componentUid);for(let n=2;seenUids.has(uid);n+=1)uid=`${variantUid(componentUid)}${n}`;seenUids.add(uid);let component=ctx.components.get(componentUid);variants.push({uid,label:component?.info?.displayName??uid,fields:{[COMPONENT_DISCRIMINATOR]:{type:"string",default:componentUid,editable:!1},...fields}})}return variants.length===0?{type:"json"}:{type:"blocks",schema:{oneOf:variants}}}__name(mapDynamicZone,"mapDynamicZone");function mapSchema(raw,uidToName,components){let info=raw.schema.info??{},name=uidToName.get(raw.uid),fields={},ctx={uidToName,components,visiting:new Set,depth:0};for(let[field,attr2]of Object.entries(raw.schema.attributes??{})){let mapped=mapAttribute(field,attr2,ctx);mapped&&(fields[field]=mapped)}let isSingleton=raw.schema.kind==="singleType";return{name,label:info.displayName||name,isSingleton,localized:raw.schema.pluginOptions?.i18n?.localized===!0,fields,sourceUid:raw.uid,sourcePath:`/${isSingleton?info.singularName??raw.contentTypeName:info.pluralName??raw.contentTypeName}`}}__name(mapSchema,"mapSchema");function collectRawSchemas(strapiAppDir){let apiDir=join24(strapiAppDir,"src","api");if(!existsSync14(apiDir))return[];let raws=[];for(let apiEntry of readdirSync16(apiDir,{withFileTypes:!0})){if(!apiEntry.isDirectory())continue;let ctRoot=join24(apiDir,apiEntry.name,"content-types");if(existsSync14(ctRoot))for(let ctEntry of readdirSync16(ctRoot,{withFileTypes:!0})){if(!ctEntry.isDirectory())continue;let schemaPath=join24(ctRoot,ctEntry.name,"schema.json");if(existsSync14(schemaPath))try{let schema=JSON.parse(readFileSync18(schemaPath,"utf8"));raws.push({uid:`api::${apiEntry.name}.${ctEntry.name}`,apiName:apiEntry.name,contentTypeName:ctEntry.name,schema})}catch{}}}return raws}__name(collectRawSchemas,"collectRawSchemas");function collectComponents(strapiAppDir){let components=new Map,componentsDir=join24(strapiAppDir,"src","components");if(!existsSync14(componentsDir))return components;for(let categoryEntry of readdirSync16(componentsDir,{withFileTypes:!0})){if(!categoryEntry.isDirectory())continue;let categoryDir=join24(componentsDir,categoryEntry.name);for(let fileEntry of readdirSync16(categoryDir,{withFileTypes:!0})){if(!fileEntry.isFile()||!fileEntry.name.endsWith(".json"))continue;let uid=`${categoryEntry.name}.${fileEntry.name.slice(0,-5)}`;try{let parsed=JSON.parse(readFileSync18(join24(categoryDir,fileEntry.name),"utf8"));components.set(uid,{...parsed,uid})}catch{}}}return components}__name(collectComponents,"collectComponents");function readStrapiSchemas(strapiAppDir){let raws=collectRawSchemas(strapiAppDir),components=collectComponents(strapiAppDir),uidToName=new Map;for(let raw of raws){let singular=raw.schema.info?.singularName??raw.contentTypeName;uidToName.set(raw.uid,pascalCase2(singular))}return raws.map(raw=>mapSchema(raw,uidToName,components)).sort((a,b)=>a.name.localeCompare(b.name))}__name(readStrapiSchemas,"readStrapiSchemas");import{existsSync as existsSync15,readdirSync as readdirSync17,readFileSync as readFileSync19}from"node:fs";import{join as join25,relative as relative6}from"node:path";var SOURCE_EXT_RE2=/\.(tsx?|jsx?|mjs|cjs|astro|vue|svelte)$/,SKIP_DIRS5=new Set(["node_modules",".next",".nuxt",".svelte-kit",".turbo","dist","build","out","coverage",".vercel",".git"]),MAX_FILES=2e3,ENV_REF_RE=/\bSTRAPI[A-Z0-9_]*_URL\b|\bNEXT_PUBLIC_STRAPI[A-Z0-9_]*\b|\bSTRAPI[A-Z0-9_]*_TOKEN\b/,UID_REF_RE=/api::[a-z0-9-]+\.[a-z0-9-]+/,IMPORT_RE=/(?:from\s+|require\(\s*|import\(\s*)['"`]([^'"`]+)['"`]/,ENV_KEY_RE=/^\s*([A-Z0-9_]*STRAPI[A-Z0-9_]*)\s*=/;function readDeps2(dir){try{let pkg=JSON.parse(readFileSync19(join25(dir,"package.json"),"utf8"));return{...pkg.dependencies,...pkg.devDependencies}}catch{return{}}}__name(readDeps2,"readDeps");function classifyLine(line){let importMatch=IMPORT_RE.exec(line);return importMatch&&/strapi/i.test(importMatch[1])&&!/fimo/i.test(importMatch[1])?"strapi-sdk-import":ENV_REF_RE.test(line)?"env-ref":UID_REF_RE.test(line)?"uid-ref":null}__name(classifyLine,"classifyLine");function scanCallSites(frontendDir){let sites=[],scanned=0,walk2=__name(dir=>{if(scanned>=MAX_FILES)return;let entries2;try{entries2=readdirSync17(dir,{withFileTypes:!0})}catch{return}for(let entry of entries2){if(scanned>=MAX_FILES)return;if(entry.isDirectory()){!SKIP_DIRS5.has(entry.name)&&!entry.name.startsWith(".")&&walk2(join25(dir,entry.name));continue}if(!SOURCE_EXT_RE2.test(entry.name))continue;scanned+=1;let full=join25(dir,entry.name),text3;try{text3=readFileSync19(full,"utf8")}catch{continue}let lines=text3.split(`
2081
2095
  `);for(let i=0;i<lines.length;i+=1){let reason=classifyLine(lines[i]);reason&&sites.push({file:relative6(frontendDir,full),line:i+1,reason,snippet:lines[i].trim().slice(0,200)})}}},"walk");return walk2(frontendDir),sites}__name(scanCallSites,"scanCallSites");function scanEnvKeys(frontendDir){let keys=new Set;for(let name of[".env",".env.local",".env.development",".env.production"]){let file=join25(frontendDir,name);if(existsSync15(file))try{for(let line of readFileSync19(file,"utf8").split(`
2082
2096
  `)){let match=ENV_KEY_RE.exec(line);match&&keys.add(match[1])}}catch{}}return[...keys]}__name(scanEnvKeys,"scanEnvKeys");function detectStrapiTouchpoints(frontendDir){let dependencies=Object.keys(readDeps2(frontendDir)).filter(name=>/strapi/i.test(name)),callSites=scanCallSites(frontendDir),envVars=scanEnvKeys(frontendDir);return{dependencies,callSites,envVars,clean:dependencies.length===0&&callSites.length===0}}__name(detectStrapiTouchpoints,"detectStrapiTouchpoints");import fs44 from"node:fs";import path48 from"node:path";import{parseSync as parseSync8}from"oxc-parser";function shapesFrom(fields){let shapes={};for(let[name,def]of Object.entries(fields))if(def.type==="blocks")if("oneOf"in def.schema){let variants={};for(let variant of def.schema.oneOf){let nested=shapesFrom(variant.fields);Object.keys(nested).length>0&&(variants[variant.uid]=nested)}shapes[name]={kind:"list",...Object.keys(variants).length>0?{variants}:{}}}else{let nested=shapesFrom(def.schema.fields);shapes[name]={kind:def.max===1?"single":"list",...Object.keys(nested).length>0?{fields:nested}:{}}}return shapes}__name(shapesFrom,"shapesFrom");function computeFieldShapes(schemas3){let byType={};for(let schema of schemas3){let shapes=shapesFrom(schema.fields);Object.keys(shapes).length>0&&(byType[schema.name]=shapes)}return byType}__name(computeFieldShapes,"computeFieldShapes");function strapiWiring(schemas3){let types={},singletons=[];for(let schema of schemas3){let pluralName=schema.sourcePath.replace(/^\/+/,"");pluralName&&(types[pluralName]=schema.name),schema.isSingleton&&singletons.push(schema.name)}return{types,singletons}}__name(strapiWiring,"strapiWiring");function renderFimoStrapiModule(schemas3){let{types,singletons}=strapiWiring(schemas3),typeEntries=Object.entries(types).map(([pluralName,typeName])=>` '${pluralName}': '${typeName}',`).join(`
2083
2097
  `),fieldShapes=JSON.stringify(computeFieldShapes(schemas3),null,2);return`// AUTO-GENERATED by \`fimo migrate --scaffold-cms\`. Serves this site's Strapi
@@ -2130,7 +2144,7 @@ ${error.stderrTail.slice(-800)}`:"";parityWarnings.push(`Could not capture the r
2130
2144
  `)}}let scaffoldTargets=dataSources.filter(source=>source.kind==="cms"&&source.decision==="ingest"&&source.contentState==="example"&&scaffoldableHosts.has(source.host)&&(options.force||!source.scaffolded));if(scaffoldTargets.length>0&&cmsSchemas.length>0){let{scaffoldCms:scaffoldCms2,extractSchemaTypeNames:extractSchemaTypeNames2}=await Promise.resolve().then(()=>(init_cms_scaffold(),cms_scaffold_exports)),i18nConfig=config.i18n,seedLocales=i18nConfig?.locales&&i18nConfig.locales.length>0?i18nConfig.locales:i18nConfig?.defaultLocale?[i18nConfig.defaultLocale]:[],existingTypes=extractSchemaTypeNames2(await api.listSchemas(ctx.projectId).catch(()=>null));if(cmsScaffold=await scaffoldCms2({schemas:cmsSchemas,cms:cmsConnector?.detection.cms??"CMS",ctx,api,locales:seedLocales,force:options.force,existingTypes}),fs52.writeFileSync(path58.join(cwd,".fimo","migration","cms-scaffold.json"),`${JSON.stringify(cmsScaffold,null,2)}
2131
2145
  `),cmsScaffold.failures.length>0){let first2=cmsScaffold.failures[0];parityWarnings.push(`${cmsScaffold.cms} scaffold: ${cmsScaffold.failures.length} issue(s) \u2014 see .fimo/migration/cms-scaffold.json (e.g. ${first2.type}: ${first2.message})`)}let wired=cmsConnector?.wireClient?.(cwd,cmsSchemas);wired&&(cmsWired=wired,parityWarnings.push(...wired.warnings));for(let source of scaffoldTargets)source.scaffolded=!0;writeDataSourceDecisions(cwd,dataSources)}}catch{}let migrationDir=path58.join(cwd,".fimo","migration"),wrapDir=path58.join(migrationDir,"wrap");fs52.mkdirSync(wrapDir,{recursive:!0}),stepTimer.mark(Date.now(),"Seed",cmsScaffold&&cmsScaffold.failures.length>0?"warn":"ok",`${contentSeed?.seeded??0} labels${cmsScaffold?`, ${cmsScaffold.schemas} CMS types`:""}`);let schemaDrafts=options.check?[]:writeSchemaDrafts(cwd,scan),stagedRows=options.check?0:writeRowsStaging(cwd,scan,schemaDrafts),collectionRewire=null;if(options.apply!==!1&&!options.check&&applied){let rewire=frameworkCollectionRewire(effectiveDetection);rewire&&(scan.collections.length>0||scan.dataFiles.length>0)&&(runSp?.update("Rewiring collection pages"),collectionRewire=rewire(cwd,scan,schemaDrafts),parityWarnings.push(...collectionRewire.warnings),collectionRewire.rewired>0&&(scan=scanWrappable(cwd,{i18nFimoWired,excludePrefixes:excludedSections.prefixes,dataDirs:frameworkDataDirs(framework),keepInternal:!0}),coverage={wrappable:scan.items.length,wrapped:scan.wrappedCount},schemaDrafts=writeSchemaDrafts(cwd,scan),stagedRows=writeRowsStaging(cwd,scan,schemaDrafts)))}for(let candidate of scan.collections)delete candidate.fieldValues,delete candidate.typedSample,delete candidate.typedRows,delete candidate.manyFields;for(let dataFile of scan.dataFiles)for(let collection of dataFile.collections)delete collection.fieldValues,delete collection.typedSample,delete collection.typedRows,delete collection.manyFields,delete collection.verbatimFields;let wiringStatus=checkRootWiring(cwd,effectiveDetection),structured=remainingStructuredWork(cwd,scan),structuredWorkLeft=structured.remainingCollections.length+structured.undecidedDataFiles.length,plan={generatedAt:new Date().toISOString(),framework:framework??null,routes:routeCount,coverage,items:scan.items,collections:scan.collections,dataFiles:scan.dataFiles},planPath=path58.join(migrationDir,"plan.json");fs52.writeFileSync(planPath,`${JSON.stringify(plan,null,2)}
2132
2146
  `);let cmsScaffoldAvailable=cmsConnector&&cmsSchemas.length>0&&!dataSources.some(source=>source.host===cmsConnector.detection.host&&source.decision==="ingest"&&source.contentState==="example")?{cms:cmsConnector.detection.cms,types:cmsSchemas.length}:void 0,cmsTouchpoints=cmsScaffold?cmsConnector?.detectTouchpoints?.(cwd):void 0,promptPath=path58.join(wrapDir,"agent-prompt.md");if(fs52.writeFileSync(promptPath,renderAgentPrompt(framework??"js",coverage,scan,dataSources,{applied:applied?applied.applied.length:0,seeded:contentSeed?.seeded??0,structuredWorkLeft,schemaDrafts:schemaDrafts.length,stagedRows,rewiredPages:collectionRewire?.rewired??0,i18nIngested:i18nIngest??void 0,i18nWired:i18nFimoWired,cmsScaffold:cmsScaffold??void 0,cmsScaffoldAvailable,cmsWired:cmsWired??void 0,cutover:cmsScaffold&&cmsTouchpoints?{cms:cmsScaffold.cms,...cmsTouchpoints}:void 0,wiring:wiringStatus?{present:wiringStatus.present,hint:wiringStatus.hint,reason:rootWiring?.warnings[0]?.replace(/^Root wiring not applied: /,"").replace(/ — follow [^—]*$/,"")??null}:null})),parityNote&&(plan.parity={baseline:".fimo/migration/parity-baseline.json",capturedAt:new Date().toISOString()},fs52.writeFileSync(planPath,`${JSON.stringify(plan,null,2)}
2133
- `)),runSp){let elapsed=Date.now()-spinnerStartedAt;elapsed<1500&&await new Promise(resolve9=>setTimeout(resolve9,1500-elapsed)),runSp.stop()}let dependencyChanged=wiring.changes.some(change=>change.startsWith("added")),promptRel=path58.relative(cwd,promptPath),data2={framework,routes:routeCount,files_scanned:scan.filesScanned,...coverage,wiring_changes:wiring.changes,applied:applied?{count:applied.applied.length,files:applied.filesChanged}:null,env_sync:envSync,package_manager:pm,plan_path:path58.relative(cwd,planPath),prompt_path:promptRel};"skipped"in envSync&&envSync.skipped==="unavailable"&&parityWarnings.push(`Could not import your env keys into the project (${envSync.error??"unknown"}) \u2014 pages with server-side external fetches may hang in the sandbox. Import manually with npx fimo secrets set / vars set.`),stepTimer.mark(Date.now(),"Finalize","ok",`${schemaDrafts.length} draft(s)`);let dashboardSteps=stepTimer.steps(),dashboardTotalMs=stepTimer.totalMs(Date.now());ui.result({command:"migrate wrap",data:data2,render:__name(ctx=>{let p2=ctx.color?palette:makeColorPalette({forceDisable:!0}),rootWiringDone=!wiringStatus||wiringStatus.present||scan.wrappedCount===0,migrationComplete=coverage.wrappable===0&&!dependencyChanged&&rootWiringDone&&structuredWorkLeft===0,dashboard=renderWrapDashboard({title:"Migration to Fimo \xB7 wrap in place",verdict:migrationComplete?"complete":"in progress",steps:dashboardSteps,gates:[{label:"Coverage",status:coverage.wrappable===0?"ok":"warn"},{label:"Wiring",status:rootWiringDone?"ok":"warn"}],totalMs:dashboardTotalMs,findings:parityWarnings.slice(0,5),colors:ctx.color?{ok:p2.green,warn:p2.yellow,fail:p2.red}:void 0,ascii:ctx.ascii}),facts=[["Files scanned",String(scan.filesScanned)],...migrationComplete?[]:[["Content to migrate",String(coverage.wrappable)]],...coverage.wrapped>0?[["Already migrated",String(coverage.wrapped)]]:[],["Pages found",String(routeCount)],["Render baseline",parityNote??"not captured"],...excludedSections.sections.length>0?[["Skipped sections",`${excludedSections.sections.map(sec=>`/${sec}`).join(", ")} (${excludedSections.source==="config"?"fimo-config migration.exclude":"back-office heuristic \u2014 override via migration.include"})`]]:[],...dataSources.length>0?[["Live data sources",dataSources.map(record=>`${record.host}${record.kind==="cms"?" [CMS]":""}${record.decision==="ingest"?" (to ingest)":""}${record.decidedBy==="default"?" (defaulted \u2014 confirm with the user)":""}`).join(", ")]]:[],...scan.collections.length>0?[["Collections found",`${scan.collections.length} (${scan.collections.slice(0,3).map(candidate=>candidate.name).join(", ")}${scan.collections.length>3?", \u2026":""}) \u2014 agent recipe in the prompt`]]:[],...applied&&applied.applied.length>0?[["Auto-wrapped",`${applied.applied.length} item(s) (${applied.applied.filter(a=>a.kind==="text").length} text, ${applied.applied.filter(a=>a.kind==="image").length} image, ${applied.applied.filter(a=>a.kind==="file-link").length} link)`]]:[],...wiringStatus?[["Root wiring",rootWiring&&rootWiring.changes.length>0?rootWiring.changes.join("; "):wiringStatus.present?"already in place":"needs the agent (recipe in the prompt)"]]:[],...collectionRewire?[["Collection pages",collectionRewire.rewired>0?`${collectionRewire.rewired} page(s) rewired through wrapCollection${collectionRewire.warnings.length>0?` (${collectionRewire.warnings.length} left to the agent)`:""}`:"none rewired (reasons in the prompt)"]]:[],...contentSeed?[["Content seeded",contentSeed.seeded>0?`${contentSeed.seeded} label(s)`:`up to date (${contentSeed.already})`]]:[],...cmsScaffold?[[`${cmsScaffold.cms} scaffolded`,`${cmsScaffold.schemas} type(s), ${cmsScaffold.entries} example entr${cmsScaffold.entries===1?"y":"ies"}${cmsScaffold.locales.length>0?` (${cmsScaffold.locales.join(", ")})`:""}${cmsScaffold.skipped>0?`, ${cmsScaffold.skipped} existing`:""}${cmsScaffold.failures.length>0?`, ${cmsScaffold.failures.length} issue(s)`:""}`]]:[],...cmsWired?[[`${cmsScaffold?.cms??"CMS"} reads`,cmsWired.wired?"wired to Fimo (whole site reads scaffolded content)":"not auto-wired (see prompt)"]]:[],..."skipped"in envSync?[]:[["Env keys",envSync.imported.length>0?`${envSync.imported.length} imported (${envSync.imported.filter(e=>e.kind==="secret").length} secrets)`:"already synced"]],["Migration plan",path58.relative(cwd,planPath)]],factWidth=Math.max(...facts.map(([label])=>label.length)),factLines=facts.map(([label,value])=>` ${p2.dim(label.padEnd(factWidth))} ${p2.cyan(value)}`);if(migrationComplete){let check=ctx.ascii?"[ok]":"\u2713",shipStep=`${p2.cyan("npx fimo deploy")} ${p2.dim("then edit your content live in your Fimo Studio")}`;return[dashboard,"",...factLines,"",`${p2.green(`${check} Migration complete!`)} Every piece of content on your site is editable from Fimo.`,"",...renderStepsBox([shipStep],ctx.ascii,p2,{title:"Ship it",numbered:!1})].join(`
2147
+ `)),runSp){let elapsed=Date.now()-spinnerStartedAt;elapsed<1500&&await new Promise(resolve9=>setTimeout(resolve9,1500-elapsed)),runSp.stop()}let dependencyChanged=wiring.changes.some(change=>change.startsWith("added")||change.startsWith("updated fimo ")),promptRel=path58.relative(cwd,promptPath),data2={framework,routes:routeCount,files_scanned:scan.filesScanned,...coverage,wiring_changes:wiring.changes,applied:applied?{count:applied.applied.length,files:applied.filesChanged}:null,env_sync:envSync,package_manager:pm,plan_path:path58.relative(cwd,planPath),prompt_path:promptRel};"skipped"in envSync&&envSync.skipped==="unavailable"&&parityWarnings.push(`Could not import your env keys into the project (${envSync.error??"unknown"}) \u2014 pages with server-side external fetches may hang in the sandbox. Import manually with npx fimo secrets set / vars set.`),stepTimer.mark(Date.now(),"Finalize","ok",`${schemaDrafts.length} draft(s)`);let dashboardSteps=stepTimer.steps(),dashboardTotalMs=stepTimer.totalMs(Date.now());ui.result({command:"migrate wrap",data:data2,render:__name(ctx=>{let p2=ctx.color?palette:makeColorPalette({forceDisable:!0}),rootWiringDone=!wiringStatus||wiringStatus.present||scan.wrappedCount===0,migrationComplete=coverage.wrappable===0&&!dependencyChanged&&rootWiringDone&&structuredWorkLeft===0,dashboard=renderWrapDashboard({title:"Migration to Fimo \xB7 wrap in place",verdict:migrationComplete?"complete":"in progress",steps:dashboardSteps,gates:[{label:"Coverage",status:coverage.wrappable===0?"ok":"warn"},{label:"Wiring",status:rootWiringDone?"ok":"warn"}],totalMs:dashboardTotalMs,findings:parityWarnings.slice(0,5),colors:ctx.color?{ok:p2.green,warn:p2.yellow,fail:p2.red}:void 0,ascii:ctx.ascii}),facts=[["Files scanned",String(scan.filesScanned)],...migrationComplete?[]:[["Content to migrate",String(coverage.wrappable)]],...coverage.wrapped>0?[["Already migrated",String(coverage.wrapped)]]:[],["Pages found",String(routeCount)],["Render baseline",parityNote??"not captured"],...excludedSections.sections.length>0?[["Skipped sections",`${excludedSections.sections.map(sec=>`/${sec}`).join(", ")} (${excludedSections.source==="config"?"fimo-config migration.exclude":"back-office heuristic \u2014 override via migration.include"})`]]:[],...dataSources.length>0?[["Live data sources",dataSources.map(record=>`${record.host}${record.kind==="cms"?" [CMS]":""}${record.decision==="ingest"?" (to ingest)":""}${record.decidedBy==="default"?" (defaulted \u2014 confirm with the user)":""}`).join(", ")]]:[],...scan.collections.length>0?[["Collections found",`${scan.collections.length} (${scan.collections.slice(0,3).map(candidate=>candidate.name).join(", ")}${scan.collections.length>3?", \u2026":""}) \u2014 agent recipe in the prompt`]]:[],...applied&&applied.applied.length>0?[["Auto-wrapped",`${applied.applied.length} item(s) (${applied.applied.filter(a=>a.kind==="text").length} text, ${applied.applied.filter(a=>a.kind==="image").length} image, ${applied.applied.filter(a=>a.kind==="file-link").length} link)`]]:[],...wiringStatus?[["Root wiring",rootWiring&&rootWiring.changes.length>0?rootWiring.changes.join("; "):wiringStatus.present?"already in place":"needs the agent (recipe in the prompt)"]]:[],...collectionRewire?[["Collection pages",collectionRewire.rewired>0?`${collectionRewire.rewired} page(s) rewired through wrapCollection${collectionRewire.warnings.length>0?` (${collectionRewire.warnings.length} left to the agent)`:""}`:"none rewired (reasons in the prompt)"]]:[],...contentSeed?[["Content seeded",contentSeed.seeded>0?`${contentSeed.seeded} label(s)`:`up to date (${contentSeed.already})`]]:[],...cmsScaffold?[[`${cmsScaffold.cms} scaffolded`,`${cmsScaffold.schemas} type(s), ${cmsScaffold.entries} example entr${cmsScaffold.entries===1?"y":"ies"}${cmsScaffold.locales.length>0?` (${cmsScaffold.locales.join(", ")})`:""}${cmsScaffold.skipped>0?`, ${cmsScaffold.skipped} existing`:""}${cmsScaffold.failures.length>0?`, ${cmsScaffold.failures.length} issue(s)`:""}`]]:[],...cmsWired?[[`${cmsScaffold?.cms??"CMS"} reads`,cmsWired.wired?"wired to Fimo (whole site reads scaffolded content)":"not auto-wired (see prompt)"]]:[],..."skipped"in envSync?[]:[["Env keys",envSync.imported.length>0?`${envSync.imported.length} imported (${envSync.imported.filter(e=>e.kind==="secret").length} secrets)`:"already synced"]],["Migration plan",path58.relative(cwd,planPath)]],factWidth=Math.max(...facts.map(([label])=>label.length)),factLines=facts.map(([label,value])=>` ${p2.dim(label.padEnd(factWidth))} ${p2.cyan(value)}`);if(migrationComplete){let check=ctx.ascii?"[ok]":"\u2713",shipStep=`${p2.cyan("npx fimo deploy")} ${p2.dim("then edit your content live in your Fimo Studio")}`;return[dashboard,"",...factLines,"",`${p2.green(`${check} Migration complete!`)} Every piece of content on your site is editable from Fimo.`,"",...renderStepsBox([shipStep],ctx.ascii,p2,{title:"Ship it",numbered:!1})].join(`
2134
2148
  `)}let rows=[];dependencyChanged&&rows.push(["install the new fimo dependency",`${pm} install`]),(coverage.wrappable>0||!rootWiringDone||structuredWorkLeft>0)&&rows.push(["ask your coding agent",`"Migrate the content to Fimo (follow ${promptRel})"`]),rows.push(["push and preview in your Fimo Studio","npx fimo deploy"]);let descWidth=Math.max(...rows.map(([desc])=>desc.length)),steps=rows.map(([desc,action])=>`${p2.dim(desc.padEnd(descWidth))} ${p2.cyan(action)}`),headline=coverage.wrappable>0?`${p2.bold("Your site is connected.")}
2135
2149
  The migration will make ${p2.cyan(String(coverage.wrappable))} piece(s) of content editable from Fimo \u2014 the migration plan lists every one (file and line).`:p2.bold("Your site is connected and fully migrated: every scanned piece of content is editable from Fimo.");return[dashboard,"",headline,"",...factLines,"",...renderStepsBox(steps,ctx.ascii,p2),"",p2.dim("Check progress anytime with `fimo migrate wrap --check` \u2014 it passes when everything is editable.")].join(`
2136
2150
  `)},"render"),warnings:[...wiring.warnings,...parityWarnings].length>0?[...wiring.warnings,...parityWarnings]:void 0})}__name(migrateWrapCommand,"migrateWrapCommand");var ANSI_PATTERN2=/\[[0-9;]*m/g;function visibleLength2(line){return line.replace(ANSI_PATTERN2,"").length}__name(visibleLength2,"visibleLength");function renderStepsBox(steps,ascii,p2,opts={}){let glyphs=ascii?{tl:"+",tr:"+",bl:"+",br:"+",h:"-",v:"|"}:{tl:"\u256D",tr:"\u256E",bl:"\u2570",br:"\u256F",h:"\u2500",v:"\u2502"},title=opts.title??"Next steps",lines=steps.map((step,index2)=>opts.numbered===!1?step:`${index2+1}. ${step}`),innerWidth=Math.max(...lines.map(visibleLength2),title.length+2)+4,top=p2.dim(`${glyphs.tl}${glyphs.h} `)+p2.bold(title)+p2.dim(` ${glyphs.h.repeat(Math.max(1,innerWidth-title.length-3))}${glyphs.tr}`),bottom=`${glyphs.bl}${glyphs.h.repeat(innerWidth)}${glyphs.br}`,empty2=`${glyphs.v}${" ".repeat(innerWidth)}${glyphs.v}`;return[top,p2.dim(empty2),...lines.map(line=>`${p2.dim(glyphs.v)} ${line}${" ".repeat(Math.max(0,innerWidth-visibleLength2(line)-2))}${p2.dim(glyphs.v)}`),p2.dim(empty2),p2.dim(bottom)]}__name(renderStepsBox,"renderStepsBox");init_migrate();init_dist();init_api();init_config();init_project_context();function padRight(s,width){return s.length>=width?s:s+" ".repeat(width-s.length)}__name(padRight,"padRight");function fmtUsd(n){let trimmed=n.toFixed(4).replace(/0+$/,"").replace(/\.$/,"");return trimmed.includes(".")?trimmed:`${trimmed}.00`}__name(fmtUsd,"fmtUsd");function priceRate(price){let unit=price.unit.replace(/s$/,"");return`${price.confidence==="metered"?"~":""}$${fmtUsd(price.unitPrice)} / ${unit}`}__name(priceRate,"priceRate");function bestFor(description){let start=description.indexOf("Best for:");if(start===-1)return`${description.split(". ")[0]}.`;let rest=description.slice(start),notFor=rest.indexOf("Not for:");return(notFor===-1?rest:rest.slice(0,notFor)).replace(/^Best for:\s*/,"").trim()}__name(bestFor,"bestFor");function flagSignature(flag2){if(flag2.type==="boolean")return`--${flag2.name}`;let type=flag2.enumValues?.length?flag2.enumValues.join("|"):flag2.type;return`--${flag2.name} <${type}>`}__name(flagSignature,"flagSignature");function flagMeta(flag2){let parts=[flag2.required?"required":"optional"];return flag2.isArray&&parts.push("repeatable"),flag2.defaultValue!==void 0&&parts.push(`default ${JSON.stringify(flag2.defaultValue)}`),parts.join(", ")}__name(flagMeta,"flagMeta");function flagExample(flag2){let name=`--${flag2.name}`;if(flag2.type==="boolean")return flag2.defaultValue===!0?`--no-${flag2.name}`:name;let value;return flag2.enumValues?.length?value=String(flag2.enumValues[0]):/url|image/i.test(flag2.name)?value="<url>":flag2.type==="integer"||flag2.type==="number"?value=flag2.defaultValue!==void 0?String(flag2.defaultValue):"<n>":value='"\u2026"',flag2.isArray?`${name} ${value} ${name} ${value}`:`${name} ${value}`}__name(flagExample,"flagExample");function apiClient(){return new FimoApiClient(loadConfig())}__name(apiClient,"apiClient");async function modelsListCommand(opts){let context=await getProjectContext({cwd:opts.cwd}),models=await apiClient().listModels(context.projectId,{modality:opts.modality,tag:opts.tag,q:opts.search,category:opts.category});if(models.length===0){ui.info({command:"models list",message:"No models match the filter."});return}ui.result({command:"models list",data:{models},render:__name(()=>renderModelsListHuman(models,opts.extended===!0),"render")})}__name(modelsListCommand,"modelsListCommand");function renderModelsListHuman(models,extended){let groups=new Map;for(let m of models){let arr=groups.get(m.modality)??[];arr.push(m),groups.set(m.modality,arr)}let modalityOrder=["image","video","audio"],sortedModalities=Array.from(groups.keys()).sort((a,b)=>modalityOrder.indexOf(a)-modalityOrder.indexOf(b)),out=[],first2=!0;for(let modality of sortedModalities){first2||out.push(""),first2=!1;let group=groups.get(modality)??[];if(out.push(modality.toUpperCase()),extended){for(let m of group)out.push(renderExtendedEntry(m));continue}let nameWidth=Math.max(...group.map(m=>m.id.length));for(let m of group){let headline=m.description.split(". ")[0],tags=m.tags.length>0?`[${m.tags.join(", ")}]`:"",price=m.price?` ${priceRate(m.price)}`:"";out.push(` ${padRight(m.id,nameWidth)} ${tags}${price}`),out.push(` ${padRight("",nameWidth)} ${headline}.`)}}return out.join(`
@@ -57,15 +57,40 @@ export function parseStrapiUrl(rawUrl, apiPrefix = '/api') {
57
57
  where: Object.keys(where).length > 0 ? where : undefined,
58
58
  };
59
59
  }
60
+ const isDev = () => typeof process !== 'undefined' && process.env != null && process.env.NODE_ENV !== 'production';
60
61
  /** Build a `fetch`-compatible function that serves scaffolded content from Fimo. */
61
62
  export function createFimoStrapiFetch(options, realFetch = fetch) {
62
63
  const singletons = new Set(options.singletons ?? []);
63
64
  const read = { apiUrl: options.apiUrl, rootDir: options.rootDir };
65
+ // Lowercased type-name index, used only to flag a *near-miss* fallthrough: a
66
+ // path segment that looks like a scaffolded type but doesn't match a TYPES key
67
+ // (the classic singular-vs-plural keying bug). Genuinely foreign paths
68
+ // (`/api/upload/files`) don't match and stay silent.
69
+ const typeNamesLower = new Set(Object.values(options.types).map((name) => name.toLowerCase()));
70
+ const warnedPaths = new Set();
71
+ const warnNearMiss = (segment) => {
72
+ if (!isDev() || warnedPaths.has(segment)) {
73
+ return;
74
+ }
75
+ const seg = segment.toLowerCase();
76
+ const nearMiss = typeNamesLower.has(seg) ||
77
+ typeNamesLower.has(seg.replace(/s$/, '')) ||
78
+ typeNamesLower.has(seg.replace(/ies$/, 'y'));
79
+ if (nearMiss) {
80
+ warnedPaths.add(segment);
81
+ console.warn(`[fimo/strapi] /api/${segment} looks like a scaffolded type but is not in the type map — ` +
82
+ `this read fell through to the live CMS. If a single type reads empty, its map key is likely ` +
83
+ `plural where the fetch is singular (regenerate with \`fimo migrate --scaffold-cms\`).`);
84
+ }
85
+ };
64
86
  return (async (input, init) => {
65
87
  const rawUrl = typeof input === 'string' ? input : input instanceof URL ? input.href : input.url;
66
88
  const parsed = parseStrapiUrl(rawUrl ?? '', options.apiPrefix);
67
89
  const typeName = parsed ? options.types[parsed.pluralName] : undefined;
68
90
  if (!parsed || !typeName) {
91
+ if (parsed) {
92
+ warnNearMiss(parsed.pluralName);
93
+ }
69
94
  return realFetch(input, init); // not a mapped content read → real CMS
70
95
  }
71
96
  const entries = await fetchEntries(typeName, {
@@ -73,11 +98,29 @@ export function createFimoStrapiFetch(options, realFetch = fetch) {
73
98
  ...(parsed.where ? { where: parsed.where } : {}),
74
99
  ...(parsed.pageSize ? { limit: parsed.pageSize } : {}),
75
100
  });
101
+ const shapes = options.fieldShapes?.[typeName];
102
+ const single = Boolean(parsed.documentId) || singletons.has(typeName);
76
103
  if (entries.length === 0) {
104
+ // A FILTERED miss on a type Fimo owns (it has entries) answers with the
105
+ // honest empty envelope — the document just doesn't exist. Falling
106
+ // through here would leak every unknown-slug read (404s included) to the
107
+ // old CMS: dead-CMS timeouts, and content resurrection when it's alive.
108
+ if (parsed.where || parsed.documentId) {
109
+ const any = await fetchEntries(typeName, { ...read, limit: 1 });
110
+ if (any.length > 0) {
111
+ // Canonical Strapi shapes: collection miss → 200 `{data: []}`,
112
+ // document miss → 404 with the Strapi error body.
113
+ const empty = single
114
+ ? { data: null, error: { status: 404, name: 'NotFoundError', message: 'Not Found', details: {} } }
115
+ : fimoEntriesToStrapiList([], { pageSize: parsed.pageSize, total: 0, shapes });
116
+ return new Response(JSON.stringify(empty), {
117
+ status: single ? 404 : 200,
118
+ headers: { 'content-type': 'application/json' },
119
+ });
120
+ }
121
+ }
77
122
  return realFetch(input, init); // nothing scaffolded → fall back to the CMS
78
123
  }
79
- const shapes = options.fieldShapes?.[typeName];
80
- const single = Boolean(parsed.documentId) || singletons.has(typeName);
81
124
  const payload = single
82
125
  ? fimoEntryToStrapiSingle(parsed.documentId ? (entries.find((e) => e.id === parsed.documentId) ?? entries[0]) : entries[0], shapes)
83
126
  : fimoEntriesToStrapiList(entries, { pageSize: parsed.pageSize, total: entries.length, shapes });
@@ -100,7 +100,14 @@ function fillBlocks(def, ctx) {
100
100
  const childCtx = { ...ctx, depth: ctx.depth + 1 };
101
101
  const schema = def.schema;
102
102
  if ('oneOf' in schema) {
103
- return schema.oneOf.slice(0, EXAMPLE_BLOCK_VARIANTS).map((variant) => ({
103
+ // Rotate the variant window by row index so example entries render
104
+ // DIFFERENT block sets (entry 1 → variants 0-4, entry 2 → 5-9, …) —
105
+ // distinct-looking pages, and collectively more of the page builder
106
+ // exercised than the first five variants over and over.
107
+ const total = schema.oneOf.length;
108
+ const take = Math.min(EXAMPLE_BLOCK_VARIANTS, total);
109
+ const offset = total > 0 ? ((ctx.index - 1) * take) % total : 0;
110
+ return Array.from({ length: take }, (_, k) => schema.oneOf[(offset + k) % total]).map((variant) => ({
104
111
  _type: variant.uid,
105
112
  ...fillItem(variant.fields, childCtx),
106
113
  }));
@@ -57,8 +57,15 @@ function mapAttribute(field, attr, ctx) {
57
57
  case 'text':
58
58
  case 'email':
59
59
  case 'uid':
60
- case 'enumeration':
61
60
  return { type: 'string' };
61
+ case 'enumeration': {
62
+ // Enum values drive rendering switches (link types, section variants) —
63
+ // an invalid member makes components filter the item out entirely. Bake
64
+ // the schema's declared default (else the first member) so generated
65
+ // content always carries a valid value; the field stays editable.
66
+ const value = attr.default ?? (Array.isArray(attr.enum) ? attr.enum[0] : undefined);
67
+ return { type: 'string', ...(value !== undefined ? { default: value } : {}) };
68
+ }
62
69
  case 'richtext':
63
70
  case 'blocks':
64
71
  return { type: 'richtext' };
@@ -184,14 +191,17 @@ function mapSchema(raw, uidToName, components) {
184
191
  fields[field] = mapped;
185
192
  }
186
193
  }
194
+ const isSingleton = raw.schema.kind === 'singleType';
187
195
  return {
188
196
  name,
189
197
  label: info.displayName || name,
190
- isSingleton: raw.schema.kind === 'singleType',
198
+ isSingleton,
191
199
  localized: raw.schema.pluginOptions?.i18n?.localized === true,
192
200
  fields,
193
201
  sourceUid: raw.uid,
194
- sourcePath: `/${info.pluralName ?? raw.contentTypeName}`,
202
+ // Strapi single types are fetched by SINGULAR name (`/api/global`), collections
203
+ // by plural (`/api/pages`); the wiring keys its type map off this path.
204
+ sourcePath: `/${isSingleton ? (info.singularName ?? raw.contentTypeName) : (info.pluralName ?? raw.contentTypeName)}`,
195
205
  };
196
206
  }
197
207
  /** Walk `<strapiAppDir>/src/api/<api>/content-types/<ct>/schema.json`. */
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "fimo",
3
- "version": "0.9.0-experimental.4",
3
+ "version": "0.9.0-experimental.5",
4
4
  "description": "Fimo CLI - create, deploy, and manage Fimo projects",
5
5
  "bin": {
6
6
  "fimo": "dist/cli/index.js"
@@ -21,7 +21,7 @@
21
21
  "cmdk": "^1.1.1",
22
22
  "date-fns": "^4.1.0",
23
23
  "embla-carousel-react": "^8.6.0",
24
- "fimo": "0.9.0-experimental.4",
24
+ "fimo": "0.9.0-experimental.5",
25
25
  "input-otp": "^1.4.2",
26
26
  "isbot": "^5",
27
27
  "lucide-react": "^0.577.0",