your-ai-workflow-firebase-os 1.2.40 → 1.2.41

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.d.ts CHANGED
@@ -6,6 +6,7 @@ export { useConfig } from './lib/ConfigContext';
6
6
  export type { FirebaseOSConfig } from './lib/ConfigContext';
7
7
  export { db, auth, storage } from './lib/firebase';
8
8
  export { ContactPopup } from './components/ContactPopup';
9
+ export { registerMicrocomponent } from './microcomponents/registry';
9
10
  export { Button } from './components/Button';
10
11
  export { Input } from './components/Input';
11
12
  export { CustomSelect } from './components/CustomSelect';
@@ -1,8 +1,8 @@
1
1
  /**
2
2
  * your-ai-workflow-firebase-os/vite — the plugin a consumer's vite.config.ts loads.
3
3
  */
4
- export { yourAiFirebase, restoreOwnedFiles, ownedMessage, VIRTUAL_ID, RESOLVED_VIRTUAL_ID } from './plugin';
5
- export { CANONICAL_APP_TSX, CANONICAL_MAIN_TSX, OWNED_BANNER, VIRTUAL_REGISTRIES_ID } from './canonical';
4
+ export { yourAiFirebase, restoreOwnedFiles, ensureConsumerRegistry, ownedMessage, VIRTUAL_ID, RESOLVED_VIRTUAL_ID } from './plugin';
5
+ export { CANONICAL_APP_TSX, CANONICAL_MAIN_TSX, CONSUMER_REGISTRY_TS, OWNED_BANNER, VIRTUAL_REGISTRIES_ID } from './canonical';
6
6
  export { discover, registrable, virtualModuleSource, virtualModuleSourceFor, classifyConfigFile, tabIdFromFile, pageIdFromFile, formIdFromFile, fileIdFromConfigFile, microcomponentKeysFromFile, isConsumerRoot, isDiscoveredPath, CONSUMER_DIR, CONFIGS_DIR, MICROCOMPONENTS_DIR, SHIPPED_CONFIG_FILES, } from './discover';
7
7
  export type { DiscoveredFile, DiscoveredKind, ConfigKind } from './discover';
8
8
  export { validate, validateDiscovered, stripComments } from './validate';
package/dist/vite.cjs.js CHANGED
@@ -6,6 +6,11 @@ import { FirebaseOS } from 'your-ai-workflow-firebase-os';
6
6
  import { Home } from './your-ai-workflow-firebase-os/Home';
7
7
  import { themeConfig } from './your-ai-workflow-firebase-os/configs/theme.config';
8
8
 
9
+ // The page-content list. In a build with the Your AI Firebase plugin this is
10
+ // a safety net (content files are found automatically); in a runtime without
11
+ // the plugin (Google AI Studio) its registrations are what loads them.
12
+ import './your-ai-workflow-firebase-os/microcomponents/registry';
13
+
9
14
  // Local Configuration Defaults (The Final Boss)
10
15
  import { contactFormConfig } from './your-ai-workflow-firebase-os/configs/contactForm.config';
11
16
  import { supportFormConfig } from './your-ai-workflow-firebase-os/configs/supportForm.config';
@@ -41,7 +46,18 @@ export default function App() {
41
46
  />
42
47
  );
43
48
  }
44
- `,o=`${r}
49
+ `,o=`// Your AI Firebase — the page-content list.
50
+ // The build finds content files automatically where the Your AI Firebase
51
+ // Vite plugin runs; in a runtime without it (Google AI Studio) the two lines
52
+ // per page below are the ONLY thing that loads a page's content file.
53
+ // Add two lines for every page. Never remove another page's lines.
54
+ //
55
+ // Example, for the page at /pricing filled by PricingContent.tsx:
56
+ // import PricingContent from './PricingContent';
57
+ // registerMicrocomponent('pricing', PricingContent);
58
+ import { registerMicrocomponent } from 'your-ai-workflow-firebase-os';
59
+ export { registerMicrocomponent };
60
+ `,s=`${r}
45
61
  import { StrictMode } from 'react'
46
62
  import { createRoot } from 'react-dom/client'
47
63
 
@@ -65,10 +81,10 @@ createRoot(document.getElementById('root')!).render(
65
81
  <App />
66
82
  </StrictMode>,
67
83
  )
68
- `,s=`src/your-ai-workflow-firebase-os`,c=`${s}/configs`,l=`${s}/microcomponents`,u=[`theme.config.ts`,`contactForm.config.ts`,`supportForm.config.ts`,`registry.ts`,`pagesRegistry.ts`],d=e=>e.split(n.sep).join(`/`),f=e=>n.basename(e).replace(`.config.ts`,``).replace(/Tab$/,``).toLowerCase(),p=e=>n.basename(e).replace(`.config.ts`,``).replace(/Page$/,``).toLowerCase(),m=e=>n.basename(e).replace(`.config.ts`,``).replace(/Form$/,``).toLowerCase(),h=e=>{let t=n.basename(e).replace(/\.tsx$/,``).replace(/Content$/,``).replace(/([a-z0-9])([A-Z])/g,`$1_$2`).toLowerCase();return[t,t.replace(/_/g,``)]},g=e=>n.basename(e).replace(/\.config\.ts$/,``);function _(e){let t=n.basename(e);return u.includes(t)?`shipped`:/Tab\.config\.ts$/.test(t)?`tab`:/Page\.config\.ts$/.test(t)?`page`:/Form\.config\.ts$/.test(t)?`form`:`other`}var v=e=>t.existsSync(e)?t.readdirSync(e,{withFileTypes:!0}).filter(e=>e.isFile()).map(e=>e.name).sort():[],y=e=>t.existsSync(n.join(e,s));function b(e,t){let r=d(n.relative(e,t));return r.startsWith(c+`/`)||r.startsWith(l+`/`)}function x(e){let t=[],r=n.join(e,c);for(let e of v(r)){if(!/\.tsx?$/.test(e))continue;let i=n.join(r,e),a=`${c}/${e}`,o=_(e);o===`tab`?t.push({kind:o,file:a,abs:i,name:e,id:f(e)}):o===`page`?t.push({kind:o,file:a,abs:i,name:e,id:p(e)}):o===`form`||/Form\.config\.ts$/.test(e)?t.push({kind:`form`,file:a,abs:i,name:e,id:m(e)}):t.push({kind:`other`,file:a,abs:i,name:e,id:``})}let i=n.join(e,l);for(let e of v(i))e.endsWith(`.tsx`)&&e.replace(/\.tsx$/,``)!==`registry`&&t.push({kind:`microcomponent`,file:`${l}/${e}`,abs:n.join(i,e),name:e,id:h(e)[0]});return t}var S=e=>e.filter(e=>e.kind!==`other`),C=[{test:e=>/Content\.tsx$/.test(e),home:l},{test:e=>/(Tab|Page|Form)\.config\.tsx?$/.test(e),home:c}],w=new Set([`node_modules`,`.git`,`dist`,`build`]);function T(e){let r=[],i=n.join(e,`src`),a=(i,o)=>{if(!(o>6||!t.existsSync(i)))for(let s of t.readdirSync(i,{withFileTypes:!0})){let t=n.join(i,s.name),u=d(n.relative(e,t));if(s.isDirectory()){w.has(s.name)||a(t,o+1);continue}if(!(u===`${c}/${s.name}`||u===`${l}/${s.name}`)){for(let{test:e,home:t}of C)if(e(s.name)){r.push(`${u}: files like this are only found directly in ${t}/ — move it to ${t}/${s.name}.`);break}}}};return a(i,0),r.sort()}var E=e=>{let t=n.basename(e);return C.some(e=>e.test(t))},D=e=>JSON.stringify(e);function O(e){let t=[],n=[],r=[],i=[];return S(e).forEach((e,a)=>{let o=`m${a}`;if(t.push(`import * as ${o} from ${D(d(e.abs))};`),e.kind===`tab`)n.push(`{
84
+ `,c=`src/your-ai-workflow-firebase-os`,l=`${c}/configs`,u=`${c}/microcomponents`,d=[`theme.config.ts`,`contactForm.config.ts`,`supportForm.config.ts`,`registry.ts`,`pagesRegistry.ts`],f=e=>e.split(n.sep).join(`/`),p=e=>n.basename(e).replace(`.config.ts`,``).replace(/Tab$/,``).toLowerCase(),m=e=>n.basename(e).replace(`.config.ts`,``).replace(/Page$/,``).toLowerCase(),h=e=>n.basename(e).replace(`.config.ts`,``).replace(/Form$/,``).toLowerCase(),g=e=>{let t=n.basename(e).replace(/\.tsx$/,``).replace(/Content$/,``).replace(/([a-z0-9])([A-Z])/g,`$1_$2`).toLowerCase();return[t,t.replace(/_/g,``)]},_=e=>n.basename(e).replace(/\.config\.ts$/,``);function v(e){let t=n.basename(e);return d.includes(t)?`shipped`:/Tab\.config\.ts$/.test(t)?`tab`:/Page\.config\.ts$/.test(t)?`page`:/Form\.config\.ts$/.test(t)?`form`:`other`}var y=e=>t.existsSync(e)?t.readdirSync(e,{withFileTypes:!0}).filter(e=>e.isFile()).map(e=>e.name).sort():[],b=e=>t.existsSync(n.join(e,c));function x(e,t){let r=f(n.relative(e,t));return r.startsWith(l+`/`)||r.startsWith(u+`/`)}function S(e){let t=[],r=n.join(e,l);for(let e of y(r)){if(!/\.tsx?$/.test(e))continue;let i=n.join(r,e),a=`${l}/${e}`,o=v(e);o===`tab`?t.push({kind:o,file:a,abs:i,name:e,id:p(e)}):o===`page`?t.push({kind:o,file:a,abs:i,name:e,id:m(e)}):o===`form`||/Form\.config\.ts$/.test(e)?t.push({kind:`form`,file:a,abs:i,name:e,id:h(e)}):t.push({kind:`other`,file:a,abs:i,name:e,id:``})}let i=n.join(e,u);for(let e of y(i))e.endsWith(`.tsx`)&&e.replace(/\.tsx$/,``)!==`registry`&&t.push({kind:`microcomponent`,file:`${u}/${e}`,abs:n.join(i,e),name:e,id:g(e)[0]});return t}var C=e=>e.filter(e=>e.kind!==`other`),w=[{test:e=>/Content\.tsx$/.test(e),home:u},{test:e=>/(Tab|Page|Form)\.config\.tsx?$/.test(e),home:l}],T=new Set([`node_modules`,`.git`,`dist`,`build`]);function E(e){let r=[],i=n.join(e,`src`),a=(i,o)=>{if(!(o>6||!t.existsSync(i)))for(let s of t.readdirSync(i,{withFileTypes:!0})){let t=n.join(i,s.name),c=f(n.relative(e,t));if(s.isDirectory()){T.has(s.name)||a(t,o+1);continue}if(!(c===`${l}/${s.name}`||c===`${u}/${s.name}`)){for(let{test:e,home:t}of w)if(e(s.name)){r.push(`${c}: files like this are only found directly in ${t}/ — move it to ${t}/${s.name}.`);break}}}};return a(i,0),r.sort()}var D=e=>{let t=n.basename(e);return w.some(e=>e.test(t))},O=e=>JSON.stringify(e);function k(e){let t=[],n=[],r=[],i=[];return C(e).forEach((e,a)=>{let o=`m${a}`;if(t.push(`import * as ${o} from ${O(f(e.abs))};`),e.kind===`tab`)n.push(`{
69
85
  const configVal = pickConfig(${o});
70
86
  if (configVal) {
71
- const tabId = ${D(e.id)};
87
+ const tabId = ${O(e.id)};
72
88
  const finalTab = {
73
89
  config: {
74
90
  ...configVal,
@@ -86,7 +102,7 @@ createRoot(document.getElementById('root')!).render(
86
102
  }`);else if(e.kind===`page`)n.push(`{
87
103
  const configVal = pickConfig(${o});
88
104
  if (configVal) {
89
- const pageId = ${D(e.id)};
105
+ const pageId = ${O(e.id)};
90
106
  const finalPage = {
91
107
  config: {
92
108
  ...configVal,
@@ -104,20 +120,20 @@ createRoot(document.getElementById('root')!).render(
104
120
  }`);else if(e.kind===`form`)r.push(`{
105
121
  const configVal = pickConfig(${o});
106
122
  if (configVal) {
107
- const formId = ${D(e.id)};
123
+ const formId = ${O(e.id)};
108
124
  // The form's id is its file name; systemName always follows the file.
109
125
  const finalConfig = { ...configVal, systemName: formId };
110
126
  forms[formId] = finalConfig;
111
127
  forms[finalConfig.systemName] = finalConfig;
112
128
  forms[finalConfig.systemName.toLowerCase()] = finalConfig;
113
129
  }
114
- }`);else if(e.kind===`microcomponent`){let[t,n]=h(e.name);i.push(`if (${o} && ${o}.default) {
115
- microcomponents[${D(t)}] = ${o}.default;
130
+ }`);else if(e.kind===`microcomponent`){let[t,n]=g(e.name);i.push(`if (${o} && ${o}.default) {
131
+ microcomponents[${O(t)}] = ${o}.default;
116
132
  // Also answer to the squashed form ("mobile_apps" -> "mobileapps"): a page id
117
133
  // derived from a file name (mobileAppsPage.config.ts -> "mobileapps") has no
118
134
  // underscores, and the page must still find MobileAppsContent.tsx.
119
- microcomponents[${D(n)}] = ${o}.default;
120
- }`)}}),`// Generated by Your AI Firebase from the files in ${s}/.
135
+ microcomponents[${O(n)}] = ${o}.default;
136
+ }`)}}),`// Generated by Your AI Firebase from the files in ${c}/.
121
137
  // Never written to disk; never edit. Add or remove a file and it updates itself.
122
138
  ${t.join(`
123
139
  `)}
@@ -157,4 +173,4 @@ if (typeof window !== 'undefined') {
157
173
  export const pagesRegistry = pages;
158
174
  export const formsRegistry = forms;
159
175
  export const microcomponentRegistry = microcomponents;
160
- `}var k=e=>O(x(e)),A=e=>e.split(`/`).pop()||e,j=e=>/\/configs\/[^/]+$/.test(e),M=e=>/\/microcomponents\/[^/]+\.tsx$/.test(e),N=e=>e.replace(/\/\*[\s\S]*?\*\//g,``).replace(/(^|[^:\\'"`])\/\/.*$/gm,`$1`),P=(e,t)=>{let n=RegExp(`(?:^|[\\s,{])${t}\\s*:\\s*([^,\\n}]+)`,`g`),r=[],i;for(;i=n.exec(e);)r.push(i[1].trim());return r},F=e=>{let t=e.match(/^(['"`])([^'"`]*)\1$/);return t?t[2]:null},I=e=>/export\s+(?:const|let|var|function|class)\s+\w*Config\b/.test(e)||/export\s*\{[^}]*\b\w*Config\b[^}]*\}/.test(e),L=e=>/export\s+default\b/.test(e),R=e=>{let t=P(e,`route`);return{first:t[0],count:t.length}};function z(e){let t=[],n=[],r=new Map,i=new Set;for(let{file:t}of e)if(M(t))for(let e of h(A(t)))i.add(e);for(let{file:a,source:o}of e){let e=A(a),s=N(o);if(j(a)){let n=_(e);if(n===`shipped`)continue;if(n===`other`){t.push(`${a}: rename it to somethingTab.config.ts (a dashboard page), somethingPage.config.ts (a page for anyone) or somethingForm.config.ts (a form).`);continue}if(!I(s)&&!L(s)&&t.push(`${a}: export one const named ${g(e)}Config.`),n===`tab`||n===`page`){let{first:o,count:c}=R(s),l=o===void 0?null:F(o);if(c===0||l===null||!l.startsWith(`/`))t.push(`${a}: route must be one string starting with /, e.g. route: '/onboarding'. A page has exactly one web address.`);else{let e=r.get(l);e?t.push(`${a}: route '${l}' is already used by ${e}. One page, one file, one address.`):r.set(l,a)}let u=P(s,`template`)[0],d=u===void 0?null:F(u);if(!d||![`board`,`table`,`calendar`,`drive`,`inline_form`,`popup_form`].includes(d)){let r=n===`tab`?f(e):p(e),o=P(s,n===`tab`?`tabName`:`pageName`)[0],c=o===void 0?null:F(o),l=e=>e.toLowerCase().replace(/[^a-z0-9]+/g,`_`).replace(/^_|_$/g,``);if(![r,...c?[l(c),l(c).replace(/_/g,``)]:[]].some(e=>i.has(e))){let n=g(e).replace(/(Tab|Page)$/,``),r=(c||n).split(/[^a-zA-Z0-9]+/).filter(Boolean).map(e=>e.charAt(0).toUpperCase()+e.slice(1)).join(``);t.push(`${a}: no content file matches this page, so it renders empty — create src/your-ai-workflow-firebase-os/microcomponents/${r}Content.tsx with export default.`)}}}if(n===`tab`){let e=P(s,`pageType`)[0],n=e===void 0?null:F(e);n!==`user`&&n!==`internal`&&n!==`admin`&&t.push(`${a}: pageType must be 'user', 'internal' or 'admin'. A dashboard page is never public.`)}if(n===`form`){let n=m(e),r=P(s,`systemName`)[0];r!==void 0&&F(r)!==n&&t.push(`${a}: systemName must be '${n}'.`)}continue}if(M(a)){L(s)||t.push(`${a}: add export default.`),(/<form\b/.test(s)||/\bonSubmit\b/.test(s))&&!/ContactPopup/.test(s)&&n.push(`${a}: forms are *Form.config.ts files shown with <ContactPopup formId="…" />; a hand-written form never reaches the Inbox.`),/<style[\s>]/.test(s)&&t.push(`${a}: a <style> tag is global CSS and restyles every page of the app — delete it and style with theme classes (bg-background, text-foreground, glass-panel, btn-primary).`);let e=s.match(/\b(?:bg-(?:white|black)|(?:bg|text|border)-(?:gray|slate|zinc|neutral|stone)-\d+)\b/);e&&n.push(`${a}: ${e[0]} is a hard-coded colour that ignores the owner's theme and stays wrong in light or dark mode — use bg-background, text-foreground, border-[var(--panel-border)], glass-panel or btn-primary.`)}}return{errors:t,warnings:n}}function B(e,t){return z(e.map(e=>({file:e.file,source:t(e.abs)})))}var V=i,H=`\0`+V,U=[{rel:`src/App.tsx`,source:a},{rel:`src/main.tsx`,source:o}],W=e=>`${e} is owned by Your AI Firebase; your edit was discarded.`,G=e=>e.replace(/[?#].*$/,``),K=e=>n.normalize(e).split(n.sep).join(`/`),q={info:e=>console.log(e),warn:e=>console.warn(e),error:e=>console.error(e)};function J(e,r=q){let i=[];for(let{rel:a,source:o}of U){let s=n.join(e,a),c=null;try{c=t.readFileSync(s,`utf8`)}catch{c=null}c!==o&&(t.mkdirSync(n.dirname(s),{recursive:!0}),t.writeFileSync(s,o),i.push(a),c!==null&&r.warn(W(a)))}return i}var Y=(e,t)=>{let r=K(G(t));for(let{rel:t,source:i}of U)if(r===K(n.join(e,t)))return i;return null};function X(){let e=process.cwd(),n=!1,r=`build`,i=q,a=()=>{let n=B(x(e),e=>t.readFileSync(e,`utf8`));return{...n,errors:[...n.errors,...T(e)]}};return{name:`your-ai-firebase`,enforce:`pre`,configResolved(t){e=t.root,r=t.command,i=t.logger,n=y(e),n&&J(e,i)},resolveId(e){return e===V?H:null},buildStart(){if(!n)return;let{errors:e,warnings:t}=a();for(let e of t)this.warn(e);if(e.length!==0)if(r===`build`)this.error(e[0]);else for(let t of e)i.error(t)},load(t){if(!n)return null;if(t===H){let t=x(e),{errors:n}=a();return n.length&&this.error(n[0]),O(t)}return Y(e,t)},configureServer(t){if(!n)return;let r=()=>{let e=t.moduleGraph.getModuleById(H);e&&t.moduleGraph.invalidateModule(e),t.ws.send({type:`full-reload`,path:`*`})},i=n=>{let i=K(n);if(Y(e,i)!==null){J(e,t.config.logger);return}if(!b(e,i)&&!E(i))return;r();let{errors:o,warnings:s}=a();for(let e of s)t.config.logger.warn(e);for(let e of o)t.config.logger.error(e)};t.watcher.on(`add`,i),t.watcher.on(`unlink`,i),t.watcher.on(`change`,i)}}}exports.CANONICAL_APP_TSX=a,exports.CANONICAL_MAIN_TSX=o,exports.CONFIGS_DIR=c,exports.CONSUMER_DIR=s,exports.MICROCOMPONENTS_DIR=l,exports.OWNED_BANNER=r,exports.RESOLVED_VIRTUAL_ID=H,exports.SHIPPED_CONFIG_FILES=u,exports.VIRTUAL_ID=V,exports.VIRTUAL_REGISTRIES_ID=i,exports.classifyConfigFile=_,exports.discover=x,exports.fileIdFromConfigFile=g,exports.formIdFromFile=m,exports.isConsumerRoot=y,exports.isDiscoveredPath=b,exports.microcomponentKeysFromFile=h,exports.ownedMessage=W,exports.pageIdFromFile=p,exports.registrable=S,exports.restoreOwnedFiles=J,exports.stripComments=N,exports.tabIdFromFile=f,exports.validate=z,exports.validateDiscovered=B,exports.virtualModuleSource=k,exports.virtualModuleSourceFor=O,exports.yourAiFirebase=X;
176
+ `}var A=e=>k(S(e)),j=e=>e.split(`/`).pop()||e,M=e=>/\/configs\/[^/]+$/.test(e),N=e=>/\/microcomponents\/[^/]+\.tsx$/.test(e),P=e=>e.replace(/\/\*[\s\S]*?\*\//g,``).replace(/(^|[^:\\'"`])\/\/.*$/gm,`$1`),F=(e,t)=>{let n=RegExp(`(?:^|[\\s,{])${t}\\s*:\\s*([^,\\n}]+)`,`g`),r=[],i;for(;i=n.exec(e);)r.push(i[1].trim());return r},I=e=>{let t=e.match(/^(['"`])([^'"`]*)\1$/);return t?t[2]:null},L=e=>/export\s+(?:const|let|var|function|class)\s+\w*Config\b/.test(e)||/export\s*\{[^}]*\b\w*Config\b[^}]*\}/.test(e),R=e=>/export\s+default\b/.test(e),z=e=>{let t=F(e,`route`);return{first:t[0],count:t.length}};function B(e){let t=[],n=[],r=new Map,i=new Set;for(let{file:t}of e)if(N(t))for(let e of g(j(t)))i.add(e);for(let{file:a,source:o}of e){let e=j(a),s=P(o);if(M(a)){let n=v(e);if(n===`shipped`)continue;if(n===`other`){t.push(`${a}: rename it to somethingTab.config.ts (a dashboard page), somethingPage.config.ts (a page for anyone) or somethingForm.config.ts (a form).`);continue}if(!L(s)&&!R(s)&&t.push(`${a}: export one const named ${_(e)}Config.`),n===`tab`||n===`page`){let{first:o,count:c}=z(s),l=o===void 0?null:I(o);if(c===0||l===null||!l.startsWith(`/`))t.push(`${a}: route must be one string starting with /, e.g. route: '/onboarding'. A page has exactly one web address.`);else{let e=r.get(l);e?t.push(`${a}: route '${l}' is already used by ${e}. One page, one file, one address.`):r.set(l,a)}let u=F(s,`template`)[0],d=u===void 0?null:I(u);if(!d||![`board`,`table`,`calendar`,`drive`,`inline_form`,`popup_form`].includes(d)){let r=n===`tab`?p(e):m(e),o=F(s,n===`tab`?`tabName`:`pageName`)[0],c=o===void 0?null:I(o),l=e=>e.toLowerCase().replace(/[^a-z0-9]+/g,`_`).replace(/^_|_$/g,``);if(![r,...c?[l(c),l(c).replace(/_/g,``)]:[]].some(e=>i.has(e))){let n=_(e).replace(/(Tab|Page)$/,``),r=(c||n).split(/[^a-zA-Z0-9]+/).filter(Boolean).map(e=>e.charAt(0).toUpperCase()+e.slice(1)).join(``);t.push(`${a}: no content file matches this page, so it renders empty — create src/your-ai-workflow-firebase-os/microcomponents/${r}Content.tsx with export default.`)}}}if(n===`tab`){let e=F(s,`pageType`)[0],n=e===void 0?null:I(e);n!==`user`&&n!==`internal`&&n!==`admin`&&t.push(`${a}: pageType must be 'user', 'internal' or 'admin'. A dashboard page is never public.`)}if(n===`form`){let n=h(e),r=F(s,`systemName`)[0];r!==void 0&&I(r)!==n&&t.push(`${a}: systemName must be '${n}'.`)}continue}if(N(a)){R(s)||t.push(`${a}: add export default.`),(/<form\b/.test(s)||/\bonSubmit\b/.test(s))&&!/ContactPopup/.test(s)&&n.push(`${a}: forms are *Form.config.ts files shown with <ContactPopup formId="…" />; a hand-written form never reaches the Inbox.`),/<style[\s>]/.test(s)&&t.push(`${a}: a <style> tag is global CSS and restyles every page of the app — delete it and style with theme classes (bg-background, text-foreground, glass-panel, btn-primary).`);let e=s.match(/\b(?:bg-(?:white|black)|(?:bg|text|border)-(?:gray|slate|zinc|neutral|stone)-\d+)\b/);e&&n.push(`${a}: ${e[0]} is a hard-coded colour that ignores the owner's theme and stays wrong in light or dark mode — use bg-background, text-foreground, border-[var(--panel-border)], glass-panel or btn-primary.`)}}return{errors:t,warnings:n}}function V(e,t){return B(e.map(e=>({file:e.file,source:t(e.abs)})))}var H=i,U=`\0`+H,W=[{rel:`src/App.tsx`,source:a},{rel:`src/main.tsx`,source:s}],G=e=>`${e} is owned by Your AI Firebase; your edit was discarded.`,K=e=>e.replace(/[?#].*$/,``),q=e=>n.normalize(e).split(n.sep).join(`/`),J={info:e=>console.log(e),warn:e=>console.warn(e),error:e=>console.error(e)};function Y(e,r=J){let i=[];for(let{rel:a,source:o}of W){let s=n.join(e,a),c=null;try{c=t.readFileSync(s,`utf8`)}catch{c=null}c!==o&&(t.mkdirSync(n.dirname(s),{recursive:!0}),t.writeFileSync(s,o),i.push(a),c!==null&&r.warn(G(a)))}return i}function X(e){let r=n.join(e,u,`registry.ts`);return t.existsSync(r)?!1:(t.mkdirSync(n.dirname(r),{recursive:!0}),t.writeFileSync(r,o),!0)}var Z=(e,t)=>{let r=q(K(t));for(let{rel:t,source:i}of W)if(r===q(n.join(e,t)))return i;return null};function Q(){let e=process.cwd(),n=!1,r=`build`,i=J,a=()=>{let n=V(S(e),e=>t.readFileSync(e,`utf8`));return{...n,errors:[...n.errors,...E(e)]}};return{name:`your-ai-firebase`,enforce:`pre`,configResolved(t){e=t.root,r=t.command,i=t.logger,n=b(e),n&&(Y(e,i),X(e))},resolveId(e){return e===H?U:null},buildStart(){if(!n)return;let{errors:e,warnings:t}=a();for(let e of t)this.warn(e);if(e.length!==0)if(r===`build`)this.error(e[0]);else for(let t of e)i.error(t)},load(t){if(!n)return null;if(t===U){let t=S(e),{errors:n}=a();return n.length&&this.error(n[0]),k(t)}return Z(e,t)},configureServer(t){if(!n)return;let r=()=>{let e=t.moduleGraph.getModuleById(U);e&&t.moduleGraph.invalidateModule(e),t.ws.send({type:`full-reload`,path:`*`})},i=n=>{let i=q(n);if(Z(e,i)!==null){Y(e,t.config.logger);return}if(!x(e,i)&&!D(i))return;r();let{errors:o,warnings:s}=a();for(let e of s)t.config.logger.warn(e);for(let e of o)t.config.logger.error(e)};t.watcher.on(`add`,i),t.watcher.on(`unlink`,i),t.watcher.on(`change`,i)}}}exports.CANONICAL_APP_TSX=a,exports.CANONICAL_MAIN_TSX=s,exports.CONFIGS_DIR=l,exports.CONSUMER_DIR=c,exports.CONSUMER_REGISTRY_TS=o,exports.MICROCOMPONENTS_DIR=u,exports.OWNED_BANNER=r,exports.RESOLVED_VIRTUAL_ID=U,exports.SHIPPED_CONFIG_FILES=d,exports.VIRTUAL_ID=H,exports.VIRTUAL_REGISTRIES_ID=i,exports.classifyConfigFile=v,exports.discover=S,exports.ensureConsumerRegistry=X,exports.fileIdFromConfigFile=_,exports.formIdFromFile=h,exports.isConsumerRoot=b,exports.isDiscoveredPath=x,exports.microcomponentKeysFromFile=g,exports.ownedMessage=G,exports.pageIdFromFile=m,exports.registrable=C,exports.restoreOwnedFiles=Y,exports.stripComments=P,exports.tabIdFromFile=p,exports.validate=B,exports.validateDiscovered=V,exports.virtualModuleSource=A,exports.virtualModuleSourceFor=k,exports.yourAiFirebase=Q;
package/dist/vite.es.js CHANGED
@@ -9,6 +9,11 @@ import { FirebaseOS } from 'your-ai-workflow-firebase-os';
9
9
  import { Home } from './your-ai-workflow-firebase-os/Home';
10
10
  import { themeConfig } from './your-ai-workflow-firebase-os/configs/theme.config';
11
11
 
12
+ // The page-content list. In a build with the Your AI Firebase plugin this is
13
+ // a safety net (content files are found automatically); in a runtime without
14
+ // the plugin (Google AI Studio) its registrations are what loads them.
15
+ import './your-ai-workflow-firebase-os/microcomponents/registry';
16
+
12
17
  // Local Configuration Defaults (The Final Boss)
13
18
  import { contactFormConfig } from './your-ai-workflow-firebase-os/configs/contactForm.config';
14
19
  import { supportFormConfig } from './your-ai-workflow-firebase-os/configs/supportForm.config';
@@ -44,7 +49,7 @@ export default function App() {
44
49
  />
45
50
  );
46
51
  }
47
- `, a = `${n}
52
+ `, a = "// Your AI Firebase — the page-content list.\n// The build finds content files automatically where the Your AI Firebase\n// Vite plugin runs; in a runtime without it (Google AI Studio) the two lines\n// per page below are the ONLY thing that loads a page's content file.\n// Add two lines for every page. Never remove another page's lines.\n//\n// Example, for the page at /pricing filled by PricingContent.tsx:\n// import PricingContent from './PricingContent';\n// registerMicrocomponent('pricing', PricingContent);\nimport { registerMicrocomponent } from 'your-ai-workflow-firebase-os';\nexport { registerMicrocomponent };\n", o = `${n}
48
53
  import { StrictMode } from 'react'
49
54
  import { createRoot } from 'react-dom/client'
50
55
 
@@ -68,48 +73,48 @@ createRoot(document.getElementById('root')!).render(
68
73
  <App />
69
74
  </StrictMode>,
70
75
  )
71
- `, o = "src/your-ai-workflow-firebase-os", s = `${o}/configs`, c = `${o}/microcomponents`, l = [
76
+ `, s = "src/your-ai-workflow-firebase-os", c = `${s}/configs`, l = `${s}/microcomponents`, u = [
72
77
  "theme.config.ts",
73
78
  "contactForm.config.ts",
74
79
  "supportForm.config.ts",
75
80
  "registry.ts",
76
81
  "pagesRegistry.ts"
77
- ], u = (e) => e.split(t.sep).join("/"), d = (e) => t.basename(e).replace(".config.ts", "").replace(/Tab$/, "").toLowerCase(), f = (e) => t.basename(e).replace(".config.ts", "").replace(/Page$/, "").toLowerCase(), p = (e) => t.basename(e).replace(".config.ts", "").replace(/Form$/, "").toLowerCase(), m = (e) => {
82
+ ], d = (e) => e.split(t.sep).join("/"), f = (e) => t.basename(e).replace(".config.ts", "").replace(/Tab$/, "").toLowerCase(), p = (e) => t.basename(e).replace(".config.ts", "").replace(/Page$/, "").toLowerCase(), m = (e) => t.basename(e).replace(".config.ts", "").replace(/Form$/, "").toLowerCase(), h = (e) => {
78
83
  let n = t.basename(e).replace(/\.tsx$/, "").replace(/Content$/, "").replace(/([a-z0-9])([A-Z])/g, "$1_$2").toLowerCase();
79
84
  return [n, n.replace(/_/g, "")];
80
- }, h = (e) => t.basename(e).replace(/\.config\.ts$/, "");
81
- function g(e) {
85
+ }, g = (e) => t.basename(e).replace(/\.config\.ts$/, "");
86
+ function _(e) {
82
87
  let n = t.basename(e);
83
- return l.includes(n) ? "shipped" : /Tab\.config\.ts$/.test(n) ? "tab" : /Page\.config\.ts$/.test(n) ? "page" : /Form\.config\.ts$/.test(n) ? "form" : "other";
88
+ return u.includes(n) ? "shipped" : /Tab\.config\.ts$/.test(n) ? "tab" : /Page\.config\.ts$/.test(n) ? "page" : /Form\.config\.ts$/.test(n) ? "form" : "other";
84
89
  }
85
- var _ = (t) => e.existsSync(t) ? e.readdirSync(t, { withFileTypes: !0 }).filter((e) => e.isFile()).map((e) => e.name).sort() : [], v = (n) => e.existsSync(t.join(n, o));
86
- function y(e, n) {
87
- let r = u(t.relative(e, n));
88
- return r.startsWith(s + "/") || r.startsWith(c + "/");
90
+ var v = (t) => e.existsSync(t) ? e.readdirSync(t, { withFileTypes: !0 }).filter((e) => e.isFile()).map((e) => e.name).sort() : [], y = (n) => e.existsSync(t.join(n, s));
91
+ function b(e, n) {
92
+ let r = d(t.relative(e, n));
93
+ return r.startsWith(c + "/") || r.startsWith(l + "/");
89
94
  }
90
- function b(e) {
91
- let n = [], r = t.join(e, s);
92
- for (let e of _(r)) {
95
+ function x(e) {
96
+ let n = [], r = t.join(e, c);
97
+ for (let e of v(r)) {
93
98
  if (!/\.tsx?$/.test(e)) continue;
94
- let i = t.join(r, e), a = `${s}/${e}`, o = g(e);
99
+ let i = t.join(r, e), a = `${c}/${e}`, o = _(e);
95
100
  o === "tab" ? n.push({
96
101
  kind: o,
97
102
  file: a,
98
103
  abs: i,
99
104
  name: e,
100
- id: d(e)
105
+ id: f(e)
101
106
  }) : o === "page" ? n.push({
102
107
  kind: o,
103
108
  file: a,
104
109
  abs: i,
105
110
  name: e,
106
- id: f(e)
111
+ id: p(e)
107
112
  }) : o === "form" || /Form\.config\.ts$/.test(e) ? n.push({
108
113
  kind: "form",
109
114
  file: a,
110
115
  abs: i,
111
116
  name: e,
112
- id: p(e)
117
+ id: m(e)
113
118
  }) : n.push({
114
119
  kind: "other",
115
120
  file: a,
@@ -118,39 +123,39 @@ function b(e) {
118
123
  id: ""
119
124
  });
120
125
  }
121
- let i = t.join(e, c);
122
- for (let e of _(i)) e.endsWith(".tsx") && e.replace(/\.tsx$/, "") !== "registry" && n.push({
126
+ let i = t.join(e, l);
127
+ for (let e of v(i)) e.endsWith(".tsx") && e.replace(/\.tsx$/, "") !== "registry" && n.push({
123
128
  kind: "microcomponent",
124
- file: `${c}/${e}`,
129
+ file: `${l}/${e}`,
125
130
  abs: t.join(i, e),
126
131
  name: e,
127
- id: m(e)[0]
132
+ id: h(e)[0]
128
133
  });
129
134
  return n;
130
135
  }
131
- var x = (e) => e.filter((e) => e.kind !== "other"), S = [{
136
+ var S = (e) => e.filter((e) => e.kind !== "other"), C = [{
132
137
  test: (e) => /Content\.tsx$/.test(e),
133
- home: c
138
+ home: l
134
139
  }, {
135
140
  test: (e) => /(Tab|Page|Form)\.config\.tsx?$/.test(e),
136
- home: s
137
- }], C = new Set([
141
+ home: c
142
+ }], w = new Set([
138
143
  "node_modules",
139
144
  ".git",
140
145
  "dist",
141
146
  "build"
142
147
  ]);
143
- function w(n) {
148
+ function T(n) {
144
149
  let r = [], i = t.join(n, "src"), a = (i, o) => {
145
- if (!(o > 6 || !e.existsSync(i))) for (let l of e.readdirSync(i, { withFileTypes: !0 })) {
146
- let e = t.join(i, l.name), d = u(t.relative(n, e));
147
- if (l.isDirectory()) {
148
- C.has(l.name) || a(e, o + 1);
150
+ if (!(o > 6 || !e.existsSync(i))) for (let s of e.readdirSync(i, { withFileTypes: !0 })) {
151
+ let e = t.join(i, s.name), u = d(t.relative(n, e));
152
+ if (s.isDirectory()) {
153
+ w.has(s.name) || a(e, o + 1);
149
154
  continue;
150
155
  }
151
- if (!(d === `${s}/${l.name}` || d === `${c}/${l.name}`)) {
152
- for (let { test: e, home: t } of S) if (e(l.name)) {
153
- r.push(`${d}: files like this are only found directly in ${t}/ — move it to ${t}/${l.name}.`);
156
+ if (!(u === `${c}/${s.name}` || u === `${l}/${s.name}`)) {
157
+ for (let { test: e, home: t } of C) if (e(s.name)) {
158
+ r.push(`${u}: files like this are only found directly in ${t}/ — move it to ${t}/${s.name}.`);
154
159
  break;
155
160
  }
156
161
  }
@@ -158,18 +163,18 @@ function w(n) {
158
163
  };
159
164
  return a(i, 0), r.sort();
160
165
  }
161
- var T = (e) => {
166
+ var E = (e) => {
162
167
  let n = t.basename(e);
163
- return S.some((e) => e.test(n));
164
- }, E = (e) => JSON.stringify(e);
165
- function D(e) {
168
+ return C.some((e) => e.test(n));
169
+ }, D = (e) => JSON.stringify(e);
170
+ function O(e) {
166
171
  let t = [], n = [], r = [], i = [];
167
- return x(e).forEach((e, a) => {
172
+ return S(e).forEach((e, a) => {
168
173
  let o = `m${a}`;
169
- if (t.push(`import * as ${o} from ${E(u(e.abs))};`), e.kind === "tab") n.push(`{
174
+ if (t.push(`import * as ${o} from ${D(d(e.abs))};`), e.kind === "tab") n.push(`{
170
175
  const configVal = pickConfig(${o});
171
176
  if (configVal) {
172
- const tabId = ${E(e.id)};
177
+ const tabId = ${D(e.id)};
173
178
  const finalTab = {
174
179
  config: {
175
180
  ...configVal,
@@ -188,7 +193,7 @@ function D(e) {
188
193
  else if (e.kind === "page") n.push(`{
189
194
  const configVal = pickConfig(${o});
190
195
  if (configVal) {
191
- const pageId = ${E(e.id)};
196
+ const pageId = ${D(e.id)};
192
197
  const finalPage = {
193
198
  config: {
194
199
  ...configVal,
@@ -207,7 +212,7 @@ function D(e) {
207
212
  else if (e.kind === "form") r.push(`{
208
213
  const configVal = pickConfig(${o});
209
214
  if (configVal) {
210
- const formId = ${E(e.id)};
215
+ const formId = ${D(e.id)};
211
216
  // The form's id is its file name; systemName always follows the file.
212
217
  const finalConfig = { ...configVal, systemName: formId };
213
218
  forms[formId] = finalConfig;
@@ -216,16 +221,16 @@ function D(e) {
216
221
  }
217
222
  }`);
218
223
  else if (e.kind === "microcomponent") {
219
- let [t, n] = m(e.name);
224
+ let [t, n] = h(e.name);
220
225
  i.push(`if (${o} && ${o}.default) {
221
- microcomponents[${E(t)}] = ${o}.default;
226
+ microcomponents[${D(t)}] = ${o}.default;
222
227
  // Also answer to the squashed form ("mobile_apps" -> "mobileapps"): a page id
223
228
  // derived from a file name (mobileAppsPage.config.ts -> "mobileapps") has no
224
229
  // underscores, and the page must still find MobileAppsContent.tsx.
225
- microcomponents[${E(n)}] = ${o}.default;
230
+ microcomponents[${D(n)}] = ${o}.default;
226
231
  }`);
227
232
  }
228
- }), `// Generated by Your AI Firebase from the files in ${o}/.
233
+ }), `// Generated by Your AI Firebase from the files in ${s}/.
229
234
  // Never written to disk; never edit. Add or remove a file and it updates itself.
230
235
  ${t.join("\n")}
231
236
 
@@ -263,67 +268,67 @@ export const formsRegistry = forms;
263
268
  export const microcomponentRegistry = microcomponents;
264
269
  `;
265
270
  }
266
- var O = (e) => D(b(e)), k = (e) => e.split("/").pop() || e, A = (e) => /\/configs\/[^/]+$/.test(e), j = (e) => /\/microcomponents\/[^/]+\.tsx$/.test(e), M = (e) => e.replace(/\/\*[\s\S]*?\*\//g, "").replace(/(^|[^:\\'"`])\/\/.*$/gm, "$1"), N = (e, t) => {
271
+ var k = (e) => O(x(e)), A = (e) => e.split("/").pop() || e, j = (e) => /\/configs\/[^/]+$/.test(e), M = (e) => /\/microcomponents\/[^/]+\.tsx$/.test(e), N = (e) => e.replace(/\/\*[\s\S]*?\*\//g, "").replace(/(^|[^:\\'"`])\/\/.*$/gm, "$1"), P = (e, t) => {
267
272
  let n = RegExp(`(?:^|[\\s,{])${t}\\s*:\\s*([^,\\n}]+)`, "g"), r = [], i;
268
273
  for (; i = n.exec(e);) r.push(i[1].trim());
269
274
  return r;
270
- }, P = (e) => {
275
+ }, F = (e) => {
271
276
  let t = e.match(/^(['"`])([^'"`]*)\1$/);
272
277
  return t ? t[2] : null;
273
- }, F = (e) => /export\s+(?:const|let|var|function|class)\s+\w*Config\b/.test(e) || /export\s*\{[^}]*\b\w*Config\b[^}]*\}/.test(e), I = (e) => /export\s+default\b/.test(e), L = (e) => {
274
- let t = N(e, "route");
278
+ }, I = (e) => /export\s+(?:const|let|var|function|class)\s+\w*Config\b/.test(e) || /export\s*\{[^}]*\b\w*Config\b[^}]*\}/.test(e), L = (e) => /export\s+default\b/.test(e), R = (e) => {
279
+ let t = P(e, "route");
275
280
  return {
276
281
  first: t[0],
277
282
  count: t.length
278
283
  };
279
284
  };
280
- function R(e) {
285
+ function z(e) {
281
286
  let t = [], n = [], r = /* @__PURE__ */ new Map(), i = /* @__PURE__ */ new Set();
282
- for (let { file: t } of e) if (j(t)) for (let e of m(k(t))) i.add(e);
287
+ for (let { file: t } of e) if (M(t)) for (let e of h(A(t))) i.add(e);
283
288
  for (let { file: a, source: o } of e) {
284
- let e = k(a), s = M(o);
285
- if (A(a)) {
286
- let n = g(e);
289
+ let e = A(a), s = N(o);
290
+ if (j(a)) {
291
+ let n = _(e);
287
292
  if (n === "shipped") continue;
288
293
  if (n === "other") {
289
294
  t.push(`${a}: rename it to somethingTab.config.ts (a dashboard page), somethingPage.config.ts (a page for anyone) or somethingForm.config.ts (a form).`);
290
295
  continue;
291
296
  }
292
- if (!F(s) && !I(s) && t.push(`${a}: export one const named ${h(e)}Config.`), n === "tab" || n === "page") {
293
- let { first: o, count: c } = L(s), l = o === void 0 ? null : P(o);
297
+ if (!I(s) && !L(s) && t.push(`${a}: export one const named ${g(e)}Config.`), n === "tab" || n === "page") {
298
+ let { first: o, count: c } = R(s), l = o === void 0 ? null : F(o);
294
299
  if (c === 0 || l === null || !l.startsWith("/")) t.push(`${a}: route must be one string starting with /, e.g. route: '/onboarding'. A page has exactly one web address.`);
295
300
  else {
296
301
  let e = r.get(l);
297
302
  e ? t.push(`${a}: route '${l}' is already used by ${e}. One page, one file, one address.`) : r.set(l, a);
298
303
  }
299
- let u = N(s, "template")[0], p = u === void 0 ? null : P(u);
300
- if (!p || ![
304
+ let u = P(s, "template")[0], d = u === void 0 ? null : F(u);
305
+ if (!d || ![
301
306
  "board",
302
307
  "table",
303
308
  "calendar",
304
309
  "drive",
305
310
  "inline_form",
306
311
  "popup_form"
307
- ].includes(p)) {
308
- let r = n === "tab" ? d(e) : f(e), o = N(s, n === "tab" ? "tabName" : "pageName")[0], c = o === void 0 ? null : P(o), l = (e) => e.toLowerCase().replace(/[^a-z0-9]+/g, "_").replace(/^_|_$/g, "");
312
+ ].includes(d)) {
313
+ let r = n === "tab" ? f(e) : p(e), o = P(s, n === "tab" ? "tabName" : "pageName")[0], c = o === void 0 ? null : F(o), l = (e) => e.toLowerCase().replace(/[^a-z0-9]+/g, "_").replace(/^_|_$/g, "");
309
314
  if (![r, ...c ? [l(c), l(c).replace(/_/g, "")] : []].some((e) => i.has(e))) {
310
- let n = h(e).replace(/(Tab|Page)$/, ""), r = (c || n).split(/[^a-zA-Z0-9]+/).filter(Boolean).map((e) => e.charAt(0).toUpperCase() + e.slice(1)).join("");
315
+ let n = g(e).replace(/(Tab|Page)$/, ""), r = (c || n).split(/[^a-zA-Z0-9]+/).filter(Boolean).map((e) => e.charAt(0).toUpperCase() + e.slice(1)).join("");
311
316
  t.push(`${a}: no content file matches this page, so it renders empty — create src/your-ai-workflow-firebase-os/microcomponents/${r}Content.tsx with export default.`);
312
317
  }
313
318
  }
314
319
  }
315
320
  if (n === "tab") {
316
- let e = N(s, "pageType")[0], n = e === void 0 ? null : P(e);
321
+ let e = P(s, "pageType")[0], n = e === void 0 ? null : F(e);
317
322
  n !== "user" && n !== "internal" && n !== "admin" && t.push(`${a}: pageType must be 'user', 'internal' or 'admin'. A dashboard page is never public.`);
318
323
  }
319
324
  if (n === "form") {
320
- let n = p(e), r = N(s, "systemName")[0];
321
- r !== void 0 && P(r) !== n && t.push(`${a}: systemName must be '${n}'.`);
325
+ let n = m(e), r = P(s, "systemName")[0];
326
+ r !== void 0 && F(r) !== n && t.push(`${a}: systemName must be '${n}'.`);
322
327
  }
323
328
  continue;
324
329
  }
325
- if (j(a)) {
326
- I(s) || t.push(`${a}: add export default.`), (/<form\b/.test(s) || /\bonSubmit\b/.test(s)) && !/ContactPopup/.test(s) && n.push(`${a}: forms are *Form.config.ts files shown with <ContactPopup formId="…" />; a hand-written form never reaches the Inbox.`), /<style[\s>]/.test(s) && t.push(`${a}: a <style> tag is global CSS and restyles every page of the app — delete it and style with theme classes (bg-background, text-foreground, glass-panel, btn-primary).`);
330
+ if (M(a)) {
331
+ L(s) || t.push(`${a}: add export default.`), (/<form\b/.test(s) || /\bonSubmit\b/.test(s)) && !/ContactPopup/.test(s) && n.push(`${a}: forms are *Form.config.ts files shown with <ContactPopup formId="…" />; a hand-written form never reaches the Inbox.`), /<style[\s>]/.test(s) && t.push(`${a}: a <style> tag is global CSS and restyles every page of the app — delete it and style with theme classes (bg-background, text-foreground, glass-panel, btn-primary).`);
327
332
  let e = s.match(/\b(?:bg-(?:white|black)|(?:bg|text|border)-(?:gray|slate|zinc|neutral|stone)-\d+)\b/);
328
333
  e && n.push(`${a}: ${e[0]} is a hard-coded colour that ignores the owner's theme and stays wrong in light or dark mode — use bg-background, text-foreground, border-[var(--panel-border)], glass-panel or btn-primary.`);
329
334
  }
@@ -333,59 +338,63 @@ function R(e) {
333
338
  warnings: n
334
339
  };
335
340
  }
336
- function z(e, t) {
337
- return R(e.map((e) => ({
341
+ function B(e, t) {
342
+ return z(e.map((e) => ({
338
343
  file: e.file,
339
344
  source: t(e.abs)
340
345
  })));
341
346
  }
342
347
  //#endregion
343
348
  //#region src/vite/plugin.ts
344
- var B = r, V = "\0" + B, H = [{
349
+ var V = r, H = "\0" + V, U = [{
345
350
  rel: "src/App.tsx",
346
351
  source: i
347
352
  }, {
348
353
  rel: "src/main.tsx",
349
- source: a
350
- }], U = (e) => `${e} is owned by Your AI Firebase; your edit was discarded.`, W = (e) => e.replace(/[?#].*$/, ""), G = (e) => t.normalize(e).split(t.sep).join("/"), K = {
354
+ source: o
355
+ }], W = (e) => `${e} is owned by Your AI Firebase; your edit was discarded.`, G = (e) => e.replace(/[?#].*$/, ""), K = (e) => t.normalize(e).split(t.sep).join("/"), q = {
351
356
  info: (e) => console.log(e),
352
357
  warn: (e) => console.warn(e),
353
358
  error: (e) => console.error(e)
354
359
  };
355
- function q(n, r = K) {
360
+ function J(n, r = q) {
356
361
  let i = [];
357
- for (let { rel: a, source: o } of H) {
362
+ for (let { rel: a, source: o } of U) {
358
363
  let s = t.join(n, a), c = null;
359
364
  try {
360
365
  c = e.readFileSync(s, "utf8");
361
366
  } catch {
362
367
  c = null;
363
368
  }
364
- c !== o && (e.mkdirSync(t.dirname(s), { recursive: !0 }), e.writeFileSync(s, o), i.push(a), c !== null && r.warn(U(a)));
369
+ c !== o && (e.mkdirSync(t.dirname(s), { recursive: !0 }), e.writeFileSync(s, o), i.push(a), c !== null && r.warn(W(a)));
365
370
  }
366
371
  return i;
367
372
  }
368
- var J = (e, n) => {
369
- let r = G(W(n));
370
- for (let { rel: n, source: i } of H) if (r === G(t.join(e, n))) return i;
373
+ function Y(n) {
374
+ let r = t.join(n, l, "registry.ts");
375
+ return e.existsSync(r) ? !1 : (e.mkdirSync(t.dirname(r), { recursive: !0 }), e.writeFileSync(r, a), !0);
376
+ }
377
+ var X = (e, n) => {
378
+ let r = K(G(n));
379
+ for (let { rel: n, source: i } of U) if (r === K(t.join(e, n))) return i;
371
380
  return null;
372
381
  };
373
- function Y() {
374
- let t = process.cwd(), n = !1, r = "build", i = K, a = () => {
375
- let n = z(b(t), (t) => e.readFileSync(t, "utf8"));
382
+ function Z() {
383
+ let t = process.cwd(), n = !1, r = "build", i = q, a = () => {
384
+ let n = B(x(t), (t) => e.readFileSync(t, "utf8"));
376
385
  return {
377
386
  ...n,
378
- errors: [...n.errors, ...w(t)]
387
+ errors: [...n.errors, ...T(t)]
379
388
  };
380
389
  };
381
390
  return {
382
391
  name: "your-ai-firebase",
383
392
  enforce: "pre",
384
393
  configResolved(e) {
385
- t = e.root, r = e.command, i = e.logger, n = v(t), n && q(t, i);
394
+ t = e.root, r = e.command, i = e.logger, n = y(t), n && (J(t, i), Y(t));
386
395
  },
387
396
  resolveId(e) {
388
- return e === B ? V : null;
397
+ return e === V ? H : null;
389
398
  },
390
399
  buildStart() {
391
400
  if (!n) return;
@@ -396,27 +405,27 @@ function Y() {
396
405
  },
397
406
  load(e) {
398
407
  if (!n) return null;
399
- if (e === V) {
400
- let e = b(t), { errors: n } = a();
401
- return n.length && this.error(n[0]), D(e);
408
+ if (e === H) {
409
+ let e = x(t), { errors: n } = a();
410
+ return n.length && this.error(n[0]), O(e);
402
411
  }
403
- return J(t, e);
412
+ return X(t, e);
404
413
  },
405
414
  configureServer(e) {
406
415
  if (!n) return;
407
416
  let r = () => {
408
- let t = e.moduleGraph.getModuleById(V);
417
+ let t = e.moduleGraph.getModuleById(H);
409
418
  t && e.moduleGraph.invalidateModule(t), e.ws.send({
410
419
  type: "full-reload",
411
420
  path: "*"
412
421
  });
413
422
  }, i = (n) => {
414
- let i = G(n);
415
- if (J(t, i) !== null) {
416
- q(t, e.config.logger);
423
+ let i = K(n);
424
+ if (X(t, i) !== null) {
425
+ J(t, e.config.logger);
417
426
  return;
418
427
  }
419
- if (!y(t, i) && !T(i)) return;
428
+ if (!b(t, i) && !E(i)) return;
420
429
  r();
421
430
  let { errors: o, warnings: s } = a();
422
431
  for (let t of s) e.config.logger.warn(t);
@@ -427,4 +436,4 @@ function Y() {
427
436
  };
428
437
  }
429
438
  //#endregion
430
- export { i as CANONICAL_APP_TSX, a as CANONICAL_MAIN_TSX, s as CONFIGS_DIR, o as CONSUMER_DIR, c as MICROCOMPONENTS_DIR, n as OWNED_BANNER, V as RESOLVED_VIRTUAL_ID, l as SHIPPED_CONFIG_FILES, B as VIRTUAL_ID, r as VIRTUAL_REGISTRIES_ID, g as classifyConfigFile, b as discover, h as fileIdFromConfigFile, p as formIdFromFile, v as isConsumerRoot, y as isDiscoveredPath, m as microcomponentKeysFromFile, U as ownedMessage, f as pageIdFromFile, x as registrable, q as restoreOwnedFiles, M as stripComments, d as tabIdFromFile, R as validate, z as validateDiscovered, O as virtualModuleSource, D as virtualModuleSourceFor, Y as yourAiFirebase };
439
+ export { i as CANONICAL_APP_TSX, o as CANONICAL_MAIN_TSX, c as CONFIGS_DIR, s as CONSUMER_DIR, a as CONSUMER_REGISTRY_TS, l as MICROCOMPONENTS_DIR, n as OWNED_BANNER, H as RESOLVED_VIRTUAL_ID, u as SHIPPED_CONFIG_FILES, V as VIRTUAL_ID, r as VIRTUAL_REGISTRIES_ID, _ as classifyConfigFile, x as discover, Y as ensureConsumerRegistry, g as fileIdFromConfigFile, m as formIdFromFile, y as isConsumerRoot, b as isDiscoveredPath, h as microcomponentKeysFromFile, W as ownedMessage, p as pageIdFromFile, S as registrable, J as restoreOwnedFiles, N as stripComments, f as tabIdFromFile, z as validate, B as validateDiscovered, k as virtualModuleSource, O as virtualModuleSourceFor, Z as yourAiFirebase };