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 +1 -0
- package/dist/vite/index.d.ts +2 -2
- package/dist/vite.cjs.js +26 -10
- package/dist/vite.es.js +103 -94
- package/dist/your-ai-workflow-firebase-os.cjs.js +6 -6
- package/dist/your-ai-workflow-firebase-os.css +1 -1
- package/dist/your-ai-workflow-firebase-os.es.js +1677 -1589
- package/package.json +1 -1
- package/scripts/postinstall.js +64 -22
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';
|
package/dist/vite/index.d.ts
CHANGED
|
@@ -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
|
|
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
|
-
`,
|
|
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 = ${
|
|
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 = ${
|
|
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 = ${
|
|
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]=
|
|
115
|
-
microcomponents[${
|
|
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[${
|
|
120
|
-
}`)}}),`// Generated by Your AI Firebase from the files in ${
|
|
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
|
|
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
|
-
`,
|
|
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
|
-
],
|
|
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
|
-
},
|
|
81
|
-
function
|
|
85
|
+
}, g = (e) => t.basename(e).replace(/\.config\.ts$/, "");
|
|
86
|
+
function _(e) {
|
|
82
87
|
let n = t.basename(e);
|
|
83
|
-
return
|
|
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
|
|
86
|
-
function
|
|
87
|
-
let r =
|
|
88
|
-
return r.startsWith(
|
|
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
|
|
91
|
-
let n = [], r = t.join(e,
|
|
92
|
-
for (let e of
|
|
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 = `${
|
|
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:
|
|
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:
|
|
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:
|
|
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,
|
|
122
|
-
for (let e of
|
|
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: `${
|
|
129
|
+
file: `${l}/${e}`,
|
|
125
130
|
abs: t.join(i, e),
|
|
126
131
|
name: e,
|
|
127
|
-
id:
|
|
132
|
+
id: h(e)[0]
|
|
128
133
|
});
|
|
129
134
|
return n;
|
|
130
135
|
}
|
|
131
|
-
var
|
|
136
|
+
var S = (e) => e.filter((e) => e.kind !== "other"), C = [{
|
|
132
137
|
test: (e) => /Content\.tsx$/.test(e),
|
|
133
|
-
home:
|
|
138
|
+
home: l
|
|
134
139
|
}, {
|
|
135
140
|
test: (e) => /(Tab|Page|Form)\.config\.tsx?$/.test(e),
|
|
136
|
-
home:
|
|
137
|
-
}],
|
|
141
|
+
home: c
|
|
142
|
+
}], w = new Set([
|
|
138
143
|
"node_modules",
|
|
139
144
|
".git",
|
|
140
145
|
"dist",
|
|
141
146
|
"build"
|
|
142
147
|
]);
|
|
143
|
-
function
|
|
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
|
|
146
|
-
let e = t.join(i,
|
|
147
|
-
if (
|
|
148
|
-
|
|
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 (!(
|
|
152
|
-
for (let { test: e, home: t } of
|
|
153
|
-
r.push(`${
|
|
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
|
|
166
|
+
var E = (e) => {
|
|
162
167
|
let n = t.basename(e);
|
|
163
|
-
return
|
|
164
|
-
},
|
|
165
|
-
function
|
|
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
|
|
172
|
+
return S(e).forEach((e, a) => {
|
|
168
173
|
let o = `m${a}`;
|
|
169
|
-
if (t.push(`import * as ${o} from ${
|
|
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 = ${
|
|
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 = ${
|
|
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 = ${
|
|
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] =
|
|
224
|
+
let [t, n] = h(e.name);
|
|
220
225
|
i.push(`if (${o} && ${o}.default) {
|
|
221
|
-
microcomponents[${
|
|
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[${
|
|
230
|
+
microcomponents[${D(n)}] = ${o}.default;
|
|
226
231
|
}`);
|
|
227
232
|
}
|
|
228
|
-
}), `// Generated by Your AI Firebase from the files in ${
|
|
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
|
|
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
|
-
},
|
|
275
|
+
}, F = (e) => {
|
|
271
276
|
let t = e.match(/^(['"`])([^'"`]*)\1$/);
|
|
272
277
|
return t ? t[2] : null;
|
|
273
|
-
},
|
|
274
|
-
let t =
|
|
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
|
|
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 (
|
|
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 =
|
|
285
|
-
if (
|
|
286
|
-
let n =
|
|
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 (!
|
|
293
|
-
let { first: o, count: c } =
|
|
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 =
|
|
300
|
-
if (!
|
|
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(
|
|
308
|
-
let r = n === "tab" ?
|
|
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 =
|
|
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 =
|
|
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 =
|
|
321
|
-
r !== void 0 &&
|
|
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 (
|
|
326
|
-
|
|
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
|
|
337
|
-
return
|
|
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
|
|
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:
|
|
350
|
-
}],
|
|
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
|
|
360
|
+
function J(n, r = q) {
|
|
356
361
|
let i = [];
|
|
357
|
-
for (let { rel: a, source: o } of
|
|
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(
|
|
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
|
-
|
|
369
|
-
let r =
|
|
370
|
-
|
|
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
|
|
374
|
-
let t = process.cwd(), n = !1, r = "build", i =
|
|
375
|
-
let n =
|
|
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, ...
|
|
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 =
|
|
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 ===
|
|
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 ===
|
|
400
|
-
let e =
|
|
401
|
-
return n.length && this.error(n[0]),
|
|
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
|
|
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(
|
|
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 =
|
|
415
|
-
if (
|
|
416
|
-
|
|
423
|
+
let i = K(n);
|
|
424
|
+
if (X(t, i) !== null) {
|
|
425
|
+
J(t, e.config.logger);
|
|
417
426
|
return;
|
|
418
427
|
}
|
|
419
|
-
if (!
|
|
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,
|
|
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 };
|