your-ai-workflow-firebase-os 1.2.39 → 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.
@@ -1,4 +1,4 @@
1
- import { Ht as e } from "./ContactPopup-Bm3DZkEB.js";
1
+ import { Bt as e } from "./ContactPopup-kZk-JP-d.js";
2
2
  import t, { forwardRef as n, useCallback as r, useEffect as i, useImperativeHandle as a, useMemo as o, useRef as s, useState as c } from "react";
3
3
  import { jsx as l, jsxs as u } from "react/jsx-runtime";
4
4
  //#region node_modules/react-international-phone/dist/index.mjs
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 +1 @@
1
- Object.defineProperty(exports,Symbol.toStringTag,{value:`Module`}),require(`./chunk-DYteiM-1.cjs`);const e=require(`./ContactPopup-DY0VLjCP.cjs`);exports.ContactPopup=e.t;
1
+ Object.defineProperty(exports,Symbol.toStringTag,{value:`Module`}),require(`./chunk-DYteiM-1.cjs`);const e=require(`./ContactPopup-D7dmsL5K.cjs`);exports.ContactPopup=e.t;
@@ -1,2 +1,2 @@
1
- import { t as e } from "./ContactPopup-Bm3DZkEB.js";
1
+ import { t as e } from "./ContactPopup-kZk-JP-d.js";
2
2
  export { e as ContactPopup };
@@ -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=e=>JSON.stringify(e);function w(e){let t=[],n=[],r=[],i=[];return S(e).forEach((e,a)=>{let o=`m${a}`;if(t.push(`import * as ${o} from ${C(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 = ${C(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 = ${C(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 = ${C(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[${C(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[${C(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 T=e=>w(x(e)),E=e=>e.split(`/`).pop()||e,D=e=>/\/configs\/[^/]+$/.test(e),O=e=>/\/microcomponents\/[^/]+\.tsx$/.test(e),k=e=>e.replace(/\/\*[\s\S]*?\*\//g,``).replace(/(^|[^:\\'"`])\/\/.*$/gm,`$1`),A=(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},j=e=>{let t=e.match(/^(['"`])([^'"`]*)\1$/);return t?t[2]:null},M=e=>/export\s+(?:const|let|var|function|class)\s+\w*Config\b/.test(e)||/export\s*\{[^}]*\b\w*Config\b[^}]*\}/.test(e),N=e=>/export\s+default\b/.test(e),P=e=>{let t=A(e,`route`);return{first:t[0],count:t.length}};function F(e){let t=[],n=[],r=new Map,i=new Set;for(let{file:t}of e)if(O(t))for(let e of h(E(t)))i.add(e);for(let{file:a,source:o}of e){let e=E(a),s=k(o);if(D(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(!M(s)&&!N(s)&&t.push(`${a}: export one const named ${g(e)}Config.`),n===`tab`||n===`page`){let{first:o,count:c}=P(s),l=o===void 0?null:j(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=A(s,`template`)[0],d=u===void 0?null:j(u);if(!d||![`board`,`table`,`calendar`,`drive`,`inline_form`,`popup_form`].includes(d)){let r=n===`tab`?f(e):p(e),o=A(s,n===`tab`?`tabName`:`pageName`)[0],c=o===void 0?null:j(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=A(s,`pageType`)[0],n=e===void 0?null:j(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=A(s,`systemName`)[0];r!==void 0&&j(r)!==n&&t.push(`${a}: systemName must be '${n}'.`)}continue}if(O(a)){N(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 I(e,t){return F(e.map(e=>({file:e.file,source:t(e.abs)})))}var L=i,R=`\0`+L,z=[{rel:`src/App.tsx`,source:a},{rel:`src/main.tsx`,source:o}],B=e=>`${e} is owned by Your AI Firebase; your edit was discarded.`,V=e=>e.replace(/[?#].*$/,``),H=e=>n.normalize(e).split(n.sep).join(`/`),U={info:e=>console.log(e),warn:e=>console.warn(e),error:e=>console.error(e)};function W(e,r=U){let i=[];for(let{rel:a,source:o}of z){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(B(a)))}return i}var G=(e,t)=>{let r=H(V(t));for(let{rel:t,source:i}of z)if(r===H(n.join(e,t)))return i;return null};function K(){let e=process.cwd(),n=!1,r=`build`,i=U,a=()=>I(x(e),e=>t.readFileSync(e,`utf8`));return{name:`your-ai-firebase`,enforce:`pre`,configResolved(t){e=t.root,r=t.command,i=t.logger,n=y(e),n&&W(e,i)},resolveId(e){return e===L?R: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(r){if(!n)return null;if(r===R){let n=x(e),{errors:r}=I(n,e=>t.readFileSync(e,`utf8`));return r.length&&this.error(r[0]),w(n)}return G(e,r)},configureServer(t){if(!n)return;let r=()=>{let e=t.moduleGraph.getModuleById(R);e&&t.moduleGraph.invalidateModule(e),t.ws.send({type:`full-reload`,path:`*`})},i=n=>{let i=H(n);if(G(e,i)!==null){W(e,t.config.logger);return}if(!b(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=R,exports.SHIPPED_CONFIG_FILES=u,exports.VIRTUAL_ID=L,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=B,exports.pageIdFromFile=p,exports.registrable=S,exports.restoreOwnedFiles=W,exports.stripComments=k,exports.tabIdFromFile=f,exports.validate=F,exports.validateDiscovered=I,exports.virtualModuleSource=T,exports.virtualModuleSourceFor=w,exports.yourAiFirebase=K;
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,25 +123,58 @@ 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 = (e) => JSON.stringify(e);
132
- function C(e) {
136
+ var S = (e) => e.filter((e) => e.kind !== "other"), C = [{
137
+ test: (e) => /Content\.tsx$/.test(e),
138
+ home: l
139
+ }, {
140
+ test: (e) => /(Tab|Page|Form)\.config\.tsx?$/.test(e),
141
+ home: c
142
+ }], w = new Set([
143
+ "node_modules",
144
+ ".git",
145
+ "dist",
146
+ "build"
147
+ ]);
148
+ function T(n) {
149
+ let r = [], i = t.join(n, "src"), a = (i, o) => {
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);
154
+ continue;
155
+ }
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}.`);
159
+ break;
160
+ }
161
+ }
162
+ }
163
+ };
164
+ return a(i, 0), r.sort();
165
+ }
166
+ var E = (e) => {
167
+ let n = t.basename(e);
168
+ return C.some((e) => e.test(n));
169
+ }, D = (e) => JSON.stringify(e);
170
+ function O(e) {
133
171
  let t = [], n = [], r = [], i = [];
134
- return x(e).forEach((e, a) => {
172
+ return S(e).forEach((e, a) => {
135
173
  let o = `m${a}`;
136
- if (t.push(`import * as ${o} from ${S(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(`{
137
175
  const configVal = pickConfig(${o});
138
176
  if (configVal) {
139
- const tabId = ${S(e.id)};
177
+ const tabId = ${D(e.id)};
140
178
  const finalTab = {
141
179
  config: {
142
180
  ...configVal,
@@ -155,7 +193,7 @@ function C(e) {
155
193
  else if (e.kind === "page") n.push(`{
156
194
  const configVal = pickConfig(${o});
157
195
  if (configVal) {
158
- const pageId = ${S(e.id)};
196
+ const pageId = ${D(e.id)};
159
197
  const finalPage = {
160
198
  config: {
161
199
  ...configVal,
@@ -174,7 +212,7 @@ function C(e) {
174
212
  else if (e.kind === "form") r.push(`{
175
213
  const configVal = pickConfig(${o});
176
214
  if (configVal) {
177
- const formId = ${S(e.id)};
215
+ const formId = ${D(e.id)};
178
216
  // The form's id is its file name; systemName always follows the file.
179
217
  const finalConfig = { ...configVal, systemName: formId };
180
218
  forms[formId] = finalConfig;
@@ -183,16 +221,16 @@ function C(e) {
183
221
  }
184
222
  }`);
185
223
  else if (e.kind === "microcomponent") {
186
- let [t, n] = m(e.name);
224
+ let [t, n] = h(e.name);
187
225
  i.push(`if (${o} && ${o}.default) {
188
- microcomponents[${S(t)}] = ${o}.default;
226
+ microcomponents[${D(t)}] = ${o}.default;
189
227
  // Also answer to the squashed form ("mobile_apps" -> "mobileapps"): a page id
190
228
  // derived from a file name (mobileAppsPage.config.ts -> "mobileapps") has no
191
229
  // underscores, and the page must still find MobileAppsContent.tsx.
192
- microcomponents[${S(n)}] = ${o}.default;
230
+ microcomponents[${D(n)}] = ${o}.default;
193
231
  }`);
194
232
  }
195
- }), `// Generated by Your AI Firebase from the files in ${o}/.
233
+ }), `// Generated by Your AI Firebase from the files in ${s}/.
196
234
  // Never written to disk; never edit. Add or remove a file and it updates itself.
197
235
  ${t.join("\n")}
198
236
 
@@ -230,67 +268,67 @@ export const formsRegistry = forms;
230
268
  export const microcomponentRegistry = microcomponents;
231
269
  `;
232
270
  }
233
- var w = (e) => C(b(e)), T = (e) => e.split("/").pop() || e, E = (e) => /\/configs\/[^/]+$/.test(e), D = (e) => /\/microcomponents\/[^/]+\.tsx$/.test(e), O = (e) => e.replace(/\/\*[\s\S]*?\*\//g, "").replace(/(^|[^:\\'"`])\/\/.*$/gm, "$1"), k = (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) => {
234
272
  let n = RegExp(`(?:^|[\\s,{])${t}\\s*:\\s*([^,\\n}]+)`, "g"), r = [], i;
235
273
  for (; i = n.exec(e);) r.push(i[1].trim());
236
274
  return r;
237
- }, A = (e) => {
275
+ }, F = (e) => {
238
276
  let t = e.match(/^(['"`])([^'"`]*)\1$/);
239
277
  return t ? t[2] : null;
240
- }, j = (e) => /export\s+(?:const|let|var|function|class)\s+\w*Config\b/.test(e) || /export\s*\{[^}]*\b\w*Config\b[^}]*\}/.test(e), M = (e) => /export\s+default\b/.test(e), N = (e) => {
241
- let t = k(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");
242
280
  return {
243
281
  first: t[0],
244
282
  count: t.length
245
283
  };
246
284
  };
247
- function P(e) {
285
+ function z(e) {
248
286
  let t = [], n = [], r = /* @__PURE__ */ new Map(), i = /* @__PURE__ */ new Set();
249
- for (let { file: t } of e) if (D(t)) for (let e of m(T(t))) i.add(e);
287
+ for (let { file: t } of e) if (M(t)) for (let e of h(A(t))) i.add(e);
250
288
  for (let { file: a, source: o } of e) {
251
- let e = T(a), s = O(o);
252
- if (E(a)) {
253
- let n = g(e);
289
+ let e = A(a), s = N(o);
290
+ if (j(a)) {
291
+ let n = _(e);
254
292
  if (n === "shipped") continue;
255
293
  if (n === "other") {
256
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).`);
257
295
  continue;
258
296
  }
259
- if (!j(s) && !M(s) && t.push(`${a}: export one const named ${h(e)}Config.`), n === "tab" || n === "page") {
260
- let { first: o, count: c } = N(s), l = o === void 0 ? null : A(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);
261
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.`);
262
300
  else {
263
301
  let e = r.get(l);
264
302
  e ? t.push(`${a}: route '${l}' is already used by ${e}. One page, one file, one address.`) : r.set(l, a);
265
303
  }
266
- let u = k(s, "template")[0], p = u === void 0 ? null : A(u);
267
- if (!p || ![
304
+ let u = P(s, "template")[0], d = u === void 0 ? null : F(u);
305
+ if (!d || ![
268
306
  "board",
269
307
  "table",
270
308
  "calendar",
271
309
  "drive",
272
310
  "inline_form",
273
311
  "popup_form"
274
- ].includes(p)) {
275
- let r = n === "tab" ? d(e) : f(e), o = k(s, n === "tab" ? "tabName" : "pageName")[0], c = o === void 0 ? null : A(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, "");
276
314
  if (![r, ...c ? [l(c), l(c).replace(/_/g, "")] : []].some((e) => i.has(e))) {
277
- 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("");
278
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.`);
279
317
  }
280
318
  }
281
319
  }
282
320
  if (n === "tab") {
283
- let e = k(s, "pageType")[0], n = e === void 0 ? null : A(e);
321
+ let e = P(s, "pageType")[0], n = e === void 0 ? null : F(e);
284
322
  n !== "user" && n !== "internal" && n !== "admin" && t.push(`${a}: pageType must be 'user', 'internal' or 'admin'. A dashboard page is never public.`);
285
323
  }
286
324
  if (n === "form") {
287
- let n = p(e), r = k(s, "systemName")[0];
288
- r !== void 0 && A(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}'.`);
289
327
  }
290
328
  continue;
291
329
  }
292
- if (D(a)) {
293
- M(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).`);
294
332
  let e = s.match(/\b(?:bg-(?:white|black)|(?:bg|text|border)-(?:gray|slate|zinc|neutral|stone)-\d+)\b/);
295
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.`);
296
334
  }
@@ -300,53 +338,63 @@ function P(e) {
300
338
  warnings: n
301
339
  };
302
340
  }
303
- function F(e, t) {
304
- return P(e.map((e) => ({
341
+ function B(e, t) {
342
+ return z(e.map((e) => ({
305
343
  file: e.file,
306
344
  source: t(e.abs)
307
345
  })));
308
346
  }
309
347
  //#endregion
310
348
  //#region src/vite/plugin.ts
311
- var I = r, L = "\0" + I, R = [{
349
+ var V = r, H = "\0" + V, U = [{
312
350
  rel: "src/App.tsx",
313
351
  source: i
314
352
  }, {
315
353
  rel: "src/main.tsx",
316
- source: a
317
- }], z = (e) => `${e} is owned by Your AI Firebase; your edit was discarded.`, B = (e) => e.replace(/[?#].*$/, ""), V = (e) => t.normalize(e).split(t.sep).join("/"), H = {
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 = {
318
356
  info: (e) => console.log(e),
319
357
  warn: (e) => console.warn(e),
320
358
  error: (e) => console.error(e)
321
359
  };
322
- function U(n, r = H) {
360
+ function J(n, r = q) {
323
361
  let i = [];
324
- for (let { rel: a, source: o } of R) {
362
+ for (let { rel: a, source: o } of U) {
325
363
  let s = t.join(n, a), c = null;
326
364
  try {
327
365
  c = e.readFileSync(s, "utf8");
328
366
  } catch {
329
367
  c = null;
330
368
  }
331
- c !== o && (e.mkdirSync(t.dirname(s), { recursive: !0 }), e.writeFileSync(s, o), i.push(a), c !== null && r.warn(z(a)));
369
+ c !== o && (e.mkdirSync(t.dirname(s), { recursive: !0 }), e.writeFileSync(s, o), i.push(a), c !== null && r.warn(W(a)));
332
370
  }
333
371
  return i;
334
372
  }
335
- var W = (e, n) => {
336
- let r = V(B(n));
337
- for (let { rel: n, source: i } of R) if (r === V(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;
338
380
  return null;
339
381
  };
340
- function G() {
341
- let t = process.cwd(), n = !1, r = "build", i = H, a = () => F(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"));
385
+ return {
386
+ ...n,
387
+ errors: [...n.errors, ...T(t)]
388
+ };
389
+ };
342
390
  return {
343
391
  name: "your-ai-firebase",
344
392
  enforce: "pre",
345
393
  configResolved(e) {
346
- t = e.root, r = e.command, i = e.logger, n = v(t), n && U(t, i);
394
+ t = e.root, r = e.command, i = e.logger, n = y(t), n && (J(t, i), Y(t));
347
395
  },
348
396
  resolveId(e) {
349
- return e === I ? L : null;
397
+ return e === V ? H : null;
350
398
  },
351
399
  buildStart() {
352
400
  if (!n) return;
@@ -355,29 +403,29 @@ function G() {
355
403
  if (e.length !== 0) if (r === "build") this.error(e[0]);
356
404
  else for (let t of e) i.error(t);
357
405
  },
358
- load(r) {
406
+ load(e) {
359
407
  if (!n) return null;
360
- if (r === L) {
361
- let n = b(t), { errors: r } = F(n, (t) => e.readFileSync(t, "utf8"));
362
- return r.length && this.error(r[0]), C(n);
408
+ if (e === H) {
409
+ let e = x(t), { errors: n } = a();
410
+ return n.length && this.error(n[0]), O(e);
363
411
  }
364
- return W(t, r);
412
+ return X(t, e);
365
413
  },
366
414
  configureServer(e) {
367
415
  if (!n) return;
368
416
  let r = () => {
369
- let t = e.moduleGraph.getModuleById(L);
417
+ let t = e.moduleGraph.getModuleById(H);
370
418
  t && e.moduleGraph.invalidateModule(t), e.ws.send({
371
419
  type: "full-reload",
372
420
  path: "*"
373
421
  });
374
422
  }, i = (n) => {
375
- let i = V(n);
376
- if (W(t, i) !== null) {
377
- U(t, e.config.logger);
423
+ let i = K(n);
424
+ if (X(t, i) !== null) {
425
+ J(t, e.config.logger);
378
426
  return;
379
427
  }
380
- if (!y(t, i)) return;
428
+ if (!b(t, i) && !E(i)) return;
381
429
  r();
382
430
  let { errors: o, warnings: s } = a();
383
431
  for (let t of s) e.config.logger.warn(t);
@@ -388,4 +436,4 @@ function G() {
388
436
  };
389
437
  }
390
438
  //#endregion
391
- 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, L as RESOLVED_VIRTUAL_ID, l as SHIPPED_CONFIG_FILES, I 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, z as ownedMessage, f as pageIdFromFile, x as registrable, U as restoreOwnedFiles, O as stripComments, d as tabIdFromFile, P as validate, F as validateDiscovered, w as virtualModuleSource, C as virtualModuleSourceFor, G 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 };